Gaëtan Renaudeau
7 years ago
10 changed files with 121 additions and 191 deletions
@ -1,125 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import { connect } from 'react-redux' |
|||
import { ipcRenderer } from 'electron' |
|||
import type { Account } from '@ledgerhq/live-common/lib/types' |
|||
|
|||
import { sendEvent } from 'renderer/events' |
|||
import { getCurrentDevice } from 'reducers/devices' |
|||
import type { Device } from 'types/common' |
|||
|
|||
import type { State as StoreState } from 'reducers' |
|||
|
|||
type DeviceStatus = 'unconnected' | 'connected' | 'appOpened' |
|||
|
|||
type OwnProps = { |
|||
account?: Account, |
|||
onStatusChange?: DeviceStatus => void, |
|||
// FIXME prefer use of children function
|
|||
render?: DeviceStatus => React$Element<*>, |
|||
} |
|||
|
|||
type Props = OwnProps & { |
|||
currentDevice: ?Device, |
|||
} |
|||
|
|||
type State = { |
|||
status: DeviceStatus, |
|||
} |
|||
|
|||
const mapStateToProps = (state: StoreState, _props: OwnProps) => ({ |
|||
currentDevice: getCurrentDevice(state), |
|||
}) |
|||
|
|||
class DeviceMonit extends PureComponent<Props, State> { |
|||
state = { |
|||
status: this.props.currentDevice ? 'connected' : 'unconnected', |
|||
} |
|||
|
|||
componentDidMount() { |
|||
ipcRenderer.on('msg', this.handleMsgEvent) |
|||
if (this.props.currentDevice !== null) { |
|||
this.checkAppOpened() |
|||
} |
|||
} |
|||
|
|||
componentWillReceiveProps(nextProps) { |
|||
const { status } = this.state |
|||
const { currentDevice } = this.props |
|||
const { currentDevice: nextCurrentDevice } = nextProps |
|||
|
|||
if (status === 'unconnected' && !currentDevice && nextCurrentDevice) { |
|||
this.handleStatusChange('connected') |
|||
} |
|||
|
|||
if (status !== 'unconnected' && !nextCurrentDevice) { |
|||
this.handleStatusChange('unconnected') |
|||
} |
|||
} |
|||
|
|||
componentDidUpdate() { |
|||
const { currentDevice } = this.props |
|||
|
|||
if (currentDevice !== null) { |
|||
this.checkAppOpened() |
|||
} else { |
|||
clearTimeout(this._timeout) |
|||
} |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
ipcRenderer.removeListener('msg', this.handleMsgEvent) |
|||
clearTimeout(this._timeout) |
|||
} |
|||
|
|||
checkAppOpened = () => { |
|||
const { currentDevice, account } = this.props |
|||
|
|||
if (!currentDevice || !account) { |
|||
return |
|||
} |
|||
|
|||
sendEvent('devices', 'checkIfAppOpened', { |
|||
devicePath: currentDevice.path, |
|||
accountPath: account.path, |
|||
accountAddress: account.address, |
|||
segwit: account.path.startsWith("49'"), // TODO: store segwit info in account
|
|||
}) |
|||
} |
|||
|
|||
_timeout: any = null |
|||
|
|||
handleStatusChange = (status: DeviceStatus) => { |
|||
const { onStatusChange } = this.props |
|||
this.setState({ status }) |
|||
onStatusChange && onStatusChange(status) |
|||
} |
|||
|
|||
handleMsgEvent = (e, { type }) => { |
|||
if (type === 'devices.checkIfAppOpened.success') { |
|||
this.handleStatusChange('appOpened') |
|||
clearTimeout(this._timeout) |
|||
} |
|||
|
|||
if (type === 'devices.checkIfAppOpened.fail') { |
|||
this._timeout = setTimeout(this.checkAppOpened, 1e3) |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
const { status } = this.state |
|||
const { render } = this.props |
|||
if (render) { |
|||
return render(status) |
|||
} |
|||
return ( |
|||
<div> |
|||
<div>device connected {status !== 'unconnected' ? 'TRUE' : 'FALSE'}</div> |
|||
<div>app opened {status === 'appOpened' ? 'TRUE' : 'FALSE'}</div> |
|||
</div> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(DeviceMonit) |
@ -1,54 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import CommNodeHid from '@ledgerhq/hw-transport-node-hid' |
|||
import Btc from '@ledgerhq/hw-app-btc' |
|||
|
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
import { getPath } from 'internals/accounts/helpers' |
|||
|
|||
export default async ( |
|||
send: IPCSend, |
|||
{ |
|||
currencyId, |
|||
devicePath, |
|||
accountPath, |
|||
accountAddress, |
|||
segwit = true, |
|||
}: { |
|||
currencyId?: string, |
|||
devicePath: string, |
|||
accountPath: string, |
|||
accountAddress: string, |
|||
segwit: boolean, |
|||
}, |
|||
) => { |
|||
try { |
|||
const transport: Transport<*> = await CommNodeHid.open(devicePath) |
|||
const btc = new Btc(transport) |
|||
|
|||
// FIXME code should be moved into a map, otherwise it's going to be too much spaghetti
|
|||
if (currencyId === 'ethereum') { |
|||
// MOCK
|
|||
send('devices.checkIfAppOpened.success', { devicePath }) |
|||
return |
|||
} |
|||
|
|||
if (accountPath) { |
|||
const { bitcoinAddress } = await btc.getWalletPublicKey(accountPath, false, segwit) |
|||
if (bitcoinAddress === accountAddress) { |
|||
send('devices.checkIfAppOpened.success', { devicePath }) |
|||
} else { |
|||
throw new Error('Address is different') |
|||
} |
|||
} |
|||
if (currencyId) { |
|||
await btc.getWalletPublicKey(getPath({ currencyId, segwit }), false, segwit) |
|||
send('devices.checkIfAppOpened.success', { devicePath }) |
|||
} |
|||
} catch (err) { |
|||
send('devices.checkIfAppOpened.fail', { devicePath }) |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
// @flow
|
|||
|
|||
import invariant from 'invariant' |
|||
import CommNodeHid from '@ledgerhq/hw-transport-node-hid' |
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import type { IPCSend } from 'types/electron' |
|||
import getAdressFromCurrency from './getAddressForCurrency' |
|||
|
|||
export default async ( |
|||
send: IPCSend, |
|||
{ |
|||
currencyId, |
|||
devicePath, |
|||
accountPath, |
|||
accountAddress, |
|||
...options |
|||
}: { |
|||
currencyId: string, |
|||
devicePath: string, |
|||
accountPath: ?string, |
|||
accountAddress: ?string, |
|||
}, |
|||
) => { |
|||
try { |
|||
invariant(currencyId, 'currencyId is defined') |
|||
const transport: Transport<*> = await CommNodeHid.open(devicePath) |
|||
const resolver = getAdressFromCurrency(currencyId) |
|||
const address = await resolver(transport, currencyId, accountPath, options) |
|||
if (accountPath && accountAddress && address !== accountAddress) { |
|||
throw new Error('Account address is different than device address') |
|||
} |
|||
send('devices.ensureDeviceApp.success', { devicePath }) |
|||
} catch (err) { |
|||
send('devices.ensureDeviceApp.fail', { devicePath }) |
|||
} |
|||
} |
@ -0,0 +1,23 @@ |
|||
// @flow
|
|||
|
|||
import Btc from '@ledgerhq/hw-app-btc' |
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import { getPath } from 'internals/accounts/helpers' |
|||
|
|||
export default async ( |
|||
transport: Transport<*>, |
|||
currencyId: string, |
|||
bip32path: ?string, |
|||
{ |
|||
segwit = true, |
|||
verify = false, |
|||
}: { |
|||
segwit: boolean, |
|||
verify: boolean, |
|||
}, |
|||
) => { |
|||
const btc = new Btc(transport) |
|||
const path = bip32path || getPath({ currencyId, segwit }) |
|||
const { bitcoinAddress } = await btc.getWalletPublicKey(path, verify, segwit) |
|||
return bitcoinAddress |
|||
} |
@ -0,0 +1,17 @@ |
|||
// @flow
|
|||
|
|||
import Eth from '@ledgerhq/hw-app-eth' |
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import { getPath } from 'internals/accounts/helpers' |
|||
|
|||
export default async ( |
|||
transport: Transport<*>, |
|||
currencyId: string, |
|||
bip32path: ?string, |
|||
{ verify = false }: { verify: boolean }, |
|||
) => { |
|||
const eth = new Eth(transport) |
|||
const path = bip32path || getPath({ currencyId }) |
|||
const { address } = await eth.getAddress(path, verify) |
|||
return address |
|||
} |
@ -0,0 +1,28 @@ |
|||
// @flow
|
|||
|
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import btc from './btc' |
|||
|
|||
type Resolver = ( |
|||
transport: Transport<*>, |
|||
currencyId: string, |
|||
bip32path: ?string, // if provided use this path, otherwise resolve it
|
|||
options: *, |
|||
) => Promise<string> |
|||
|
|||
type Module = (currencyId: string) => Resolver |
|||
|
|||
const fallback: string => Resolver = currencyId => () => |
|||
Promise.reject(new Error(`${currencyId} device support not implemented`)) |
|||
|
|||
const all = { |
|||
bitcoin: btc, |
|||
bitcoin_testnet: btc, |
|||
|
|||
ethereum: btc, |
|||
ethereum_testnet: btc, |
|||
} |
|||
|
|||
const module: Module = (currencyId: string) => all[currencyId] || fallback(currencyId) |
|||
|
|||
export default module |
@ -1,2 +1,2 @@ |
|||
export listen from './listen' |
|||
export checkIfAppOpened from './checkIfAppOpened' |
|||
export ensureDeviceApp from './ensureDeviceApp' |
|||
|
Loading…
Reference in new issue