From 9ed45173ed520d1c32fe4fbfd2b397d8c9d7f4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Renaudeau?= Date: Tue, 22 May 2018 18:15:57 +0200 Subject: [PATCH] helpers/derivations to derivate different paths --- src/bridge/EthereumJSBridge.js | 91 +++++++++++++------------ src/components/EnsureDeviceApp/index.js | 4 +- src/helpers/bip32path.js | 32 --------- src/helpers/derivations.js | 29 ++++++++ 4 files changed, 78 insertions(+), 78 deletions(-) delete mode 100644 src/helpers/bip32path.js create mode 100644 src/helpers/derivations.js diff --git a/src/bridge/EthereumJSBridge.js b/src/bridge/EthereumJSBridge.js index 0415b2c8..163b7b8c 100644 --- a/src/bridge/EthereumJSBridge.js +++ b/src/bridge/EthereumJSBridge.js @@ -4,7 +4,7 @@ import EthereumKind from 'components/FeesField/EthereumKind' import type { Account, Operation } from '@ledgerhq/live-common/lib/types' import { apiForCurrency } from 'api/Ethereum' import type { Tx } from 'api/Ethereum' -import { makeBip44Path } from 'helpers/bip32path' +import { getDerivations } from 'helpers/derivations' import getAddressCommand from 'commands/getAddress' import signTransactionCommand from 'commands/signTransaction' import type { EditProps, WalletBridge } from './types' @@ -30,8 +30,8 @@ const toAccountOperation = (account: Account) => (tx: Tx): Operation => { hash: tx.hash, address: sending ? tx.to : tx.from, amount: (sending ? -1 : 1) * tx.value, - blockHeight: tx.block.height, - blockHash: tx.block.hash, + blockHeight: tx.block && tx.block.height, + blockHash: tx.block && tx.block.hash, accountId: account.id, senders: [tx.from], recipients: [tx.to], @@ -86,37 +86,39 @@ const EthereumBridge: WalletBridge = { async function stepAddress( index, { address, path }, + isStandard, ): { account?: Account, complete?: boolean } { const balance = await api.getAccountBalance(address) if (finished) return {} if (balance === 0) { - if (balanceZerosCount === 0) { - // first zero account will emit one account as opportunity to create a new account.. - const currentBlock = await lazyCurrentBlock() - const accountId = `${currency.id}_${address}` - const account: Account = { - id: accountId, - xpub: '', - path, - walletPath: String(index), - name: 'New Account', - isSegwit: false, - address, - addresses: [address], - balance, - blockHeight: currentBlock.height, - archived: true, - index, - currency, - operations: [], - unit: currency.units[0], - lastSyncDate: new Date(), + if (isStandard) { + if (balanceZerosCount === 0) { + // first zero account will emit one account as opportunity to create a new account.. + const currentBlock = await lazyCurrentBlock() + const accountId = `${currency.id}_${address}` + const account: Account = { + id: accountId, + xpub: '', + path, + walletPath: String(index), + name: 'New Account', + isSegwit: false, + address, + addresses: [address], + balance, + blockHeight: currentBlock.height, + archived: true, + index, + currency, + operations: [], + unit: currency.units[0], + lastSyncDate: new Date(), + } + return { account, complete: true } } - // NB we currently stop earlier. in future we shouldn't stop here, just continue & user will stop at the end! - // NB (what's the max tho?) - return { account, complete: true } + balanceZerosCount++ } - balanceZerosCount++ + // NB for legacy addresses we might not want to stop at first zero but continue forever return { complete: true } } @@ -149,24 +151,25 @@ const EthereumBridge: WalletBridge = { async function main() { try { - for (let index = 0; index < 255; index++) { - const res = await getAddressCommand - .send({ - currencyId: currency.id, - devicePath: deviceId, - path: makeBip44Path({ - currency, - x: index, - }), - }) - .toPromise() - const r = await stepAddress(index, res) - if (r.account) next(r.account) - if (r.complete) { - complete() - break + const derivations = getDerivations(currency) + const last = derivations[derivations.length - 1] + for (const derivation of derivations) { + const isStandard = last === derivation + for (let index = 0; index < 255; index++) { + const path = derivation({ currency, x: index, segwit: false }) + console.log(path) + const res = await getAddressCommand + .send({ currencyId: currency.id, devicePath: deviceId, path }) + .toPromise() + const r = await stepAddress(index, res, isStandard) + console.log('=>', r.account) + if (r.account) next(r.account) + if (r.complete) { + break + } } } + complete() } catch (e) { error(e) } diff --git a/src/components/EnsureDeviceApp/index.js b/src/components/EnsureDeviceApp/index.js index 65da5c53..0a5addea 100644 --- a/src/components/EnsureDeviceApp/index.js +++ b/src/components/EnsureDeviceApp/index.js @@ -2,7 +2,7 @@ import invariant from 'invariant' import { PureComponent } from 'react' import { connect } from 'react-redux' -import { makeBip44Path } from 'helpers/bip32path' +import { standardDerivation } from 'helpers/derivations' import type { Account, CryptoCurrency } from '@ledgerhq/live-common/lib/types' import type { Device } from 'types/common' @@ -110,7 +110,7 @@ class EnsureDeviceApp extends PureComponent { options = { devicePath: deviceSelected.path, currencyId: currency.id, - path: makeBip44Path({ currency }), + path: standardDerivation({ currency, x: 0, segwit: false }), } } else { throw new Error('either currency or account is required') diff --git a/src/helpers/bip32path.js b/src/helpers/bip32path.js deleted file mode 100644 index 0eb4b7bb..00000000 --- a/src/helpers/bip32path.js +++ /dev/null @@ -1,32 +0,0 @@ -// @flow -import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' - -function shouldDerivateChangeFieldInsteadOfAccount(c: CryptoCurrency) { - // ethereum have a special way of derivating things - return c.id.indexOf('ethereum') === 0 -} - -// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki -// x is a derivation index. we don't always derivate the same part of the path -export function makeBip44Path({ - currency, - segwit, - x, -}: { - currency: CryptoCurrency, - segwit?: boolean, - x?: number, -}): string { - const purpose = segwit ? 49 : 44 - const coinType = currency.coinType - let path = `${purpose}'/${coinType}'` - if (shouldDerivateChangeFieldInsteadOfAccount(currency)) { - path += "/0'" - if (x !== undefined) { - path += `/${x}` - } - } else if (x !== undefined) { - path += `/${x}'` - } - return path -} diff --git a/src/helpers/derivations.js b/src/helpers/derivations.js new file mode 100644 index 00000000..a5832b83 --- /dev/null +++ b/src/helpers/derivations.js @@ -0,0 +1,29 @@ +// @flow +import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types' + +type Derivation = ({ + currency: CryptoCurrency, + segwit: boolean, + x: number, +}) => string + +const ethLegacyMEW: Derivation = ({ x }) => `44'/60'/0'/${x}` + +const etcLegacyMEW: Derivation = ({ x }) => `44'/60'/160720'/${x}` + +const legacyDerivations = { + ethereum: [ethLegacyMEW], + ethereum_classic: [etcLegacyMEW], +} + +export const standardDerivation: Derivation = ({ currency, segwit, x }) => { + const purpose = segwit ? 49 : 44 + const { coinType } = currency + return `${purpose}'/${coinType}'/${x}'/0/0` +} + +// return an array of ways to derivate, by convention the latest is the standard one. +export const getDerivations = (currency: CryptoCurrency): Derivation[] => [ + ...(legacyDerivations[currency.id] || []), + standardDerivation, +]