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 listen from './listen' |
||||
export checkIfAppOpened from './checkIfAppOpened' |
export ensureDeviceApp from './ensureDeviceApp' |
||||
|
Loading…
Reference in new issue