From 481c057b280442103005fc1c217b57947d25cc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Fri, 18 May 2018 19:21:55 +0200 Subject: [PATCH] internals refactoring & rename DeviceMonitNew to EnsureDeviceApp --- src/components/DeviceMonit/index.js | 125 ------------------ .../index.js | 10 +- src/components/modals/StepConnectDevice.js | 4 +- src/internals/accounts/helpers.js | 13 +- src/internals/devices/checkIfAppOpened.js | 54 -------- src/internals/devices/ensureDeviceApp.js | 36 +++++ .../devices/getAddressForCurrency/btc.js | 23 ++++ .../devices/getAddressForCurrency/ethereum.js | 17 +++ .../devices/getAddressForCurrency/index.js | 28 ++++ src/internals/devices/index.js | 2 +- 10 files changed, 121 insertions(+), 191 deletions(-) delete mode 100644 src/components/DeviceMonit/index.js rename src/components/{DeviceMonitNew => EnsureDeviceApp}/index.js (92%) delete mode 100644 src/internals/devices/checkIfAppOpened.js create mode 100644 src/internals/devices/ensureDeviceApp.js create mode 100644 src/internals/devices/getAddressForCurrency/btc.js create mode 100644 src/internals/devices/getAddressForCurrency/ethereum.js create mode 100644 src/internals/devices/getAddressForCurrency/index.js diff --git a/src/components/DeviceMonit/index.js b/src/components/DeviceMonit/index.js deleted file mode 100644 index c40084b3..00000000 --- a/src/components/DeviceMonit/index.js +++ /dev/null @@ -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 { - 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 ( -
-
device connected {status !== 'unconnected' ? 'TRUE' : 'FALSE'}
-
app opened {status === 'appOpened' ? 'TRUE' : 'FALSE'}
-
- ) - } -} - -export default connect(mapStateToProps)(DeviceMonit) diff --git a/src/components/DeviceMonitNew/index.js b/src/components/EnsureDeviceApp/index.js similarity index 92% rename from src/components/DeviceMonitNew/index.js rename to src/components/EnsureDeviceApp/index.js index 21833687..6ad972a0 100644 --- a/src/components/DeviceMonitNew/index.js +++ b/src/components/EnsureDeviceApp/index.js @@ -43,7 +43,7 @@ const mapStateToProps = (state: StoreState) => ({ devices: getDevices(state), }) -class DeviceMonit extends PureComponent { +class EnsureDeviceApp extends PureComponent { state = { appStatus: 'progress', deviceStatus: this.props.deviceSelected ? 'connected' : 'unconnected', @@ -110,7 +110,7 @@ class DeviceMonit extends PureComponent { } } - sendEvent('devices', 'checkIfAppOpened', { + sendEvent('devices', 'ensureDeviceApp', { devicePath: deviceSelected.path, ...options, }) @@ -133,12 +133,12 @@ class DeviceMonit extends PureComponent { return } - if (type === 'devices.checkIfAppOpened.success' && deviceSelected.path === data.devicePath) { + if (type === 'devices.ensureDeviceApp.success' && deviceSelected.path === data.devicePath) { this.handleStatusChange(deviceStatus, 'success') this._timeout = setTimeout(this.checkAppOpened, 1e3) } - if (type === 'devices.checkIfAppOpened.fail' && deviceSelected.path === data.devicePath) { + if (type === 'devices.ensureDeviceApp.fail' && deviceSelected.path === data.devicePath) { this.handleStatusChange(deviceStatus, 'fail') this._timeout = setTimeout(this.checkAppOpened, 1e3) } @@ -164,4 +164,4 @@ class DeviceMonit extends PureComponent { } } -export default connect(mapStateToProps)(DeviceMonit) +export default connect(mapStateToProps)(EnsureDeviceApp) diff --git a/src/components/modals/StepConnectDevice.js b/src/components/modals/StepConnectDevice.js index f5ad86ee..9c6d58eb 100644 --- a/src/components/modals/StepConnectDevice.js +++ b/src/components/modals/StepConnectDevice.js @@ -6,7 +6,7 @@ import type { Account, CryptoCurrency } from '@ledgerhq/live-common/lib/types' import type { Device } from 'types/common' import DeviceConnect from 'components/DeviceConnect' -import DeviceMonit from 'components/DeviceMonitNew' +import EnsureDeviceApp from 'components/EnsureDeviceApp' type Props = { accountName?: string, @@ -25,7 +25,7 @@ const StepConnectDevice = ({ onChangeDevice, onStatusChange, }: Props) => ( - { - 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 }) - } -} diff --git a/src/internals/devices/ensureDeviceApp.js b/src/internals/devices/ensureDeviceApp.js new file mode 100644 index 00000000..e5284b52 --- /dev/null +++ b/src/internals/devices/ensureDeviceApp.js @@ -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 }) + } +} diff --git a/src/internals/devices/getAddressForCurrency/btc.js b/src/internals/devices/getAddressForCurrency/btc.js new file mode 100644 index 00000000..2dc40d65 --- /dev/null +++ b/src/internals/devices/getAddressForCurrency/btc.js @@ -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 +} diff --git a/src/internals/devices/getAddressForCurrency/ethereum.js b/src/internals/devices/getAddressForCurrency/ethereum.js new file mode 100644 index 00000000..152383cb --- /dev/null +++ b/src/internals/devices/getAddressForCurrency/ethereum.js @@ -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 +} diff --git a/src/internals/devices/getAddressForCurrency/index.js b/src/internals/devices/getAddressForCurrency/index.js new file mode 100644 index 00000000..c23f31a4 --- /dev/null +++ b/src/internals/devices/getAddressForCurrency/index.js @@ -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 + +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 diff --git a/src/internals/devices/index.js b/src/internals/devices/index.js index 008738f3..1f33c58e 100644 --- a/src/internals/devices/index.js +++ b/src/internals/devices/index.js @@ -1,2 +1,2 @@ export listen from './listen' -export checkIfAppOpened from './checkIfAppOpened' +export ensureDeviceApp from './ensureDeviceApp'