Browse Source

Optimize a bit libcoreSignAndBroadcast and libcoreSyncAccount perf

master
Gaëtan Renaudeau 7 years ago
parent
commit
d19546b08f
  1. 26
      src/bridge/LibcoreBridge.js
  2. 51
      src/commands/libcoreSignAndBroadcast.js
  3. 9
      src/commands/libcoreSyncAccount.js
  4. 4
      src/components/EnsureDeviceApp.js
  5. 4
      src/components/modals/Receive/steps/04-step-receive-funds.js
  6. 8
      src/helpers/bip32.js
  7. 32
      src/helpers/libcore.js
  8. 2
      src/logger/logger.js

26
src/bridge/LibcoreBridge.js

@ -120,7 +120,14 @@ const LibcoreBridge: WalletBridge<Transaction> = {
}, },
synchronize: account => synchronize: account =>
libcoreSyncAccount.send({ rawAccount: encodeAccount(account) }).pipe( libcoreSyncAccount
.send({
accountId: account.id,
freshAddressPath: account.freshAddressPath,
index: account.index,
currencyId: account.currency.id,
})
.pipe(
map(rawSyncedAccount => { map(rawSyncedAccount => {
const syncedAccount = decodeAccount(rawSyncedAccount) const syncedAccount = decodeAccount(rawSyncedAccount)
return account => { return account => {
@ -201,11 +208,15 @@ const LibcoreBridge: WalletBridge<Transaction> = {
.catch(() => BigNumber(0)) .catch(() => BigNumber(0))
.then(totalFees => a.balance.minus(totalFees || 0)), .then(totalFees => a.balance.minus(totalFees || 0)),
signAndBroadcast: (account, transaction, deviceId) => { signAndBroadcast: (account, transaction, deviceId) =>
const encodedAccount = encodeAccount(account) // FIXME no need to send the whole account over the threads libcoreSignAndBroadcast
return libcoreSignAndBroadcast
.send({ .send({
account: encodedAccount, accountId: account.id,
currencyId: account.currency.id,
xpub: account.xpub,
freshAddress: account.freshAddress,
freshAddressPath: account.freshAddressPath,
index: account.index,
transaction: serializeTransaction(transaction), transaction: serializeTransaction(transaction),
deviceId, deviceId,
}) })
@ -215,14 +226,13 @@ const LibcoreBridge: WalletBridge<Transaction> = {
case 'broadcasted': case 'broadcasted':
return { return {
type: 'broadcasted', type: 'broadcasted',
operation: decodeOperation(encodedAccount, e.operation), operation: decodeOperation(encodeAccount(account), e.operation),
} }
default: default:
return e return e
} }
}), }),
) ),
},
addPendingOperation: (account, operation) => ({ addPendingOperation: (account, operation) => ({
...account, ...account,

51
src/commands/libcoreSignAndBroadcast.js

@ -2,11 +2,11 @@
import logger from 'logger' import logger from 'logger'
import { BigNumber } from 'bignumber.js' import { BigNumber } from 'bignumber.js'
import type { AccountRaw, OperationRaw } from '@ledgerhq/live-common/lib/types' import type { OperationRaw } from '@ledgerhq/live-common/lib/types'
import Btc from '@ledgerhq/hw-app-btc' import Btc from '@ledgerhq/hw-app-btc'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies' import { getCryptoCurrencyById } from '@ledgerhq/live-common/lib/helpers/currencies'
import { isSegwitAccount } from 'helpers/bip32' import { isSegwitPath } from 'helpers/bip32'
import { libcoreAmountToBigNumber, bigNumberToLibcoreAmount } from 'helpers/libcore' import { libcoreAmountToBigNumber, bigNumberToLibcoreAmount } from 'helpers/libcore'
import withLibcore from 'helpers/withLibcore' import withLibcore from 'helpers/withLibcore'
@ -21,7 +21,12 @@ type BitcoinLikeTransaction = {
} }
type Input = { type Input = {
account: AccountRaw, // FIXME there is no reason we send the whole AccountRaw accountId: string,
currencyId: string,
xpub: string,
freshAddress: string,
freshAddressPath: string,
index: number,
transaction: BitcoinLikeTransaction, transaction: BitcoinLikeTransaction,
deviceId: string, deviceId: string,
} }
@ -32,13 +37,18 @@ type Result = { type: 'signed' } | { type: 'broadcasted', operation: OperationRa
const cmd: Command<Input, Result> = createCommand( const cmd: Command<Input, Result> = createCommand(
'libcoreSignAndBroadcast', 'libcoreSignAndBroadcast',
({ account, transaction, deviceId }) => ({ accountId, currencyId, xpub, freshAddress, freshAddressPath, index, transaction, deviceId }) =>
Observable.create(o => { Observable.create(o => {
let unsubscribed = false let unsubscribed = false
const isCancelled = () => unsubscribed const isCancelled = () => unsubscribed
withLibcore(core => withLibcore(core =>
doSignAndBroadcast({ doSignAndBroadcast({
account, accountId,
currencyId,
xpub,
freshAddress,
freshAddressPath,
index,
transaction, transaction,
deviceId, deviceId,
core, core,
@ -151,7 +161,12 @@ async function signTransaction({
} }
export async function doSignAndBroadcast({ export async function doSignAndBroadcast({
account, accountId,
currencyId,
xpub,
freshAddress,
freshAddressPath,
index,
transaction, transaction,
deviceId, deviceId,
core, core,
@ -159,7 +174,12 @@ export async function doSignAndBroadcast({
onSigned, onSigned,
onOperationBroadcasted, onOperationBroadcasted,
}: { }: {
account: AccountRaw, accountId: string,
currencyId: string,
xpub: string,
freshAddress: string,
freshAddressPath: string,
index: number,
transaction: BitcoinLikeTransaction, transaction: BitcoinLikeTransaction,
deviceId: string, deviceId: string,
core: *, core: *,
@ -167,10 +187,10 @@ export async function doSignAndBroadcast({
onSigned: () => void, onSigned: () => void,
onOperationBroadcasted: (optimisticOp: $Exact<OperationRaw>) => void, onOperationBroadcasted: (optimisticOp: $Exact<OperationRaw>) => void,
}): Promise<void> { }): Promise<void> {
const { walletName } = accountIdHelper.decode(account.id) const { walletName } = accountIdHelper.decode(accountId)
const njsWallet = await core.getPoolInstance().getWallet(walletName) const njsWallet = await core.getPoolInstance().getWallet(walletName)
if (isCancelled()) return if (isCancelled()) return
const njsAccount = await njsWallet.getAccount(account.index) const njsAccount = await njsWallet.getAccount(index)
if (isCancelled()) return if (isCancelled()) return
const bitcoinLikeAccount = njsAccount.asBitcoinLikeAccount() const bitcoinLikeAccount = njsAccount.asBitcoinLikeAccount()
const njsWalletCurrency = njsWallet.getCurrency() const njsWalletCurrency = njsWallet.getCurrency()
@ -194,16 +214,16 @@ export async function doSignAndBroadcast({
const hasTimestamp = !!njsWalletCurrency.bitcoinLikeNetworkParameters.UsesTimestampedTransaction const hasTimestamp = !!njsWalletCurrency.bitcoinLikeNetworkParameters.UsesTimestampedTransaction
// TODO: const timestampDelay = njsWalletCurrency.bitcoinLikeNetworkParameters.TimestampDelay // TODO: const timestampDelay = njsWalletCurrency.bitcoinLikeNetworkParameters.TimestampDelay
const currency = getCryptoCurrencyById(account.currencyId) const currency = getCryptoCurrencyById(currencyId)
const signedTransaction = await withDevice(deviceId)(async transport => const signedTransaction = await withDevice(deviceId)(async transport =>
signTransaction({ signTransaction({
hwApp: new Btc(transport), hwApp: new Btc(transport),
currencyId: account.currencyId, currencyId,
transaction: builded, transaction: builded,
sigHashType: parseInt(sigHashType, 16), sigHashType: parseInt(sigHashType, 16),
supportsSegwit: !!currency.supportsSegwit, supportsSegwit: !!currency.supportsSegwit,
isSegwit: isSegwitAccount(account), isSegwit: isSegwitPath(freshAddressPath),
hasTimestamp, hasTimestamp,
}), }),
) )
@ -221,7 +241,7 @@ export async function doSignAndBroadcast({
// NB we don't check isCancelled() because the broadcast is not cancellable now! // NB we don't check isCancelled() because the broadcast is not cancellable now!
onOperationBroadcasted({ onOperationBroadcasted({
id: `${account.xpub}-${txHash}-OUT`, id: `${xpub}-${txHash}-OUT`,
hash: txHash, hash: txHash,
type: 'OUT', type: 'OUT',
value: BigNumber(transaction.amount) value: BigNumber(transaction.amount)
@ -230,9 +250,10 @@ export async function doSignAndBroadcast({
fee: fee.toString(), fee: fee.toString(),
blockHash: null, blockHash: null,
blockHeight: null, blockHeight: null,
senders: [account.freshAddress], // FIXME for senders and recipients, can we ask the libcore?
senders: [freshAddress],
recipients: [transaction.recipient], recipients: [transaction.recipient],
accountId: account.id, accountId,
date: new Date().toISOString(), date: new Date().toISOString(),
}) })
} }

9
src/commands/libcoreSyncAccount.js

@ -8,13 +8,16 @@ import { syncAccount } from 'helpers/libcore'
import withLibcore from 'helpers/withLibcore' import withLibcore from 'helpers/withLibcore'
type Input = { type Input = {
rawAccount: AccountRaw, // FIXME there is no reason we send the whole AccountRaw accountId: string,
freshAddressPath: string,
currencyId: string,
index: number,
} }
type Result = AccountRaw type Result = AccountRaw
const cmd: Command<Input, Result> = createCommand('libcoreSyncAccount', ({ rawAccount }) => const cmd: Command<Input, Result> = createCommand('libcoreSyncAccount', accountInfos =>
fromPromise(withLibcore(core => syncAccount({ rawAccount, core }))), fromPromise(withLibcore(core => syncAccount({ ...accountInfos, core }))),
) )
export default cmd export default cmd

4
src/components/EnsureDeviceApp.js

@ -11,7 +11,7 @@ import logger from 'logger'
import getAddress from 'commands/getAddress' import getAddress from 'commands/getAddress'
import { createCancelablePolling } from 'helpers/promise' import { createCancelablePolling } from 'helpers/promise'
import { standardDerivation } from 'helpers/derivations' import { standardDerivation } from 'helpers/derivations'
import { isSegwitAccount } from 'helpers/bip32' import { isSegwitPath } from 'helpers/bip32'
import { BtcUnmatchedApp } from 'helpers/getAddressForCurrency/btc' import { BtcUnmatchedApp } from 'helpers/getAddressForCurrency/btc'
import DeviceInteraction from 'components/DeviceInteraction' import DeviceInteraction from 'components/DeviceInteraction'
@ -124,7 +124,7 @@ async function getAddressFromAccountOrCurrency(device, account, currency) {
path: account path: account
? account.freshAddressPath ? account.freshAddressPath
: standardDerivation({ currency, segwit: false, x: 0 }), : standardDerivation({ currency, segwit: false, x: 0 }),
segwit: account ? isSegwitAccount(account) : false, segwit: account ? isSegwitPath(account.freshAddressPath) : false,
}) })
.toPromise() .toPromise()
return address return address

4
src/components/modals/Receive/steps/04-step-receive-funds.js

@ -5,7 +5,7 @@ import React, { PureComponent } from 'react'
import TrackPage from 'analytics/TrackPage' import TrackPage from 'analytics/TrackPage'
import getAddress from 'commands/getAddress' import getAddress from 'commands/getAddress'
import { isSegwitAccount } from 'helpers/bip32' import { isSegwitPath } from 'helpers/bip32'
import Box from 'components/base/Box' import Box from 'components/base/Box'
import CurrentAddressForAccount from 'components/CurrentAddressForAccount' import CurrentAddressForAccount from 'components/CurrentAddressForAccount'
import { DisconnectedDevice, WrongDeviceForAccount } from 'config/errors' import { DisconnectedDevice, WrongDeviceForAccount } from 'config/errors'
@ -29,7 +29,7 @@ export default class StepReceiveFunds extends PureComponent<StepProps> {
currencyId: account.currency.id, currencyId: account.currency.id,
devicePath: device.path, devicePath: device.path,
path: account.freshAddressPath, path: account.freshAddressPath,
segwit: isSegwitAccount(account), segwit: isSegwitPath(account.freshAddressPath),
verify: true, verify: true,
} }
const { address } = await getAddress.send(params).toPromise() const { address } = await getAddress.send(params).toPromise()

8
src/helpers/bip32.js

@ -1,16 +1,11 @@
// @flow // @flow
import type { Account, AccountRaw } from '@ledgerhq/live-common/lib/types'
type SplitConfig = { type SplitConfig = {
coinType: number, coinType: number,
} }
export const isSegwitPath = (path: string): boolean => path.startsWith("49'") export const isSegwitPath = (path: string): boolean => path.startsWith("49'")
export const isSegwitAccount = (account: Account | AccountRaw): boolean =>
isSegwitPath(account.freshAddressPath)
export const isUnsplitPath = (path: string, splitConfig: SplitConfig) => { export const isUnsplitPath = (path: string, splitConfig: SplitConfig) => {
try { try {
const coinType = parseInt(path.split('/')[1], 10) const coinType = parseInt(path.split('/')[1], 10)
@ -19,6 +14,3 @@ export const isUnsplitPath = (path: string, splitConfig: SplitConfig) => {
return false return false
} }
} }
export const isUnsplitAccount = (account: Account | AccountRaw, splitConfig: ?SplitConfig) =>
!!splitConfig && isUnsplitPath(account.freshAddressPath, splitConfig)

32
src/helpers/libcore.js

@ -12,7 +12,7 @@ import { SHOW_LEGACY_NEW_ACCOUNT } from 'config/constants'
import type { AccountRaw, OperationRaw, OperationType } from '@ledgerhq/live-common/lib/types' import type { AccountRaw, OperationRaw, OperationType } from '@ledgerhq/live-common/lib/types'
import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgercore_doc' import type { NJSAccount, NJSOperation } from '@ledgerhq/ledger-core/src/ledgercore_doc'
import { isSegwitAccount, isUnsplitAccount } from 'helpers/bip32' import { isSegwitPath, isUnsplitPath } from 'helpers/bip32'
import * as accountIdHelper from 'helpers/accountId' import * as accountIdHelper from 'helpers/accountId'
import { createCustomErrorClass, deserializeError } from './errors' import { createCustomErrorClass, deserializeError } from './errors'
import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName' import { getAccountPlaceholderName, getNewAccountPlaceholderName } from './accountName'
@ -482,10 +482,22 @@ function buildOperationRaw({
} }
} }
export async function syncAccount({ rawAccount, core }: { core: *, rawAccount: AccountRaw }) { export async function syncAccount({
const decodedAccountId = accountIdHelper.decode(rawAccount.id) accountId,
const isSegwit = isSegwitAccount(rawAccount) freshAddressPath,
const isUnsplit = isUnsplitAccount(rawAccount, SPLITTED_CURRENCIES[rawAccount.currencyId]) currencyId,
index,
core,
}: {
core: *,
accountId: string,
freshAddressPath: string,
currencyId: string,
index: number,
}) {
const decodedAccountId = accountIdHelper.decode(accountId)
const isSegwit = isSegwitPath(freshAddressPath)
const isUnsplit = isUnsplitPath(freshAddressPath, SPLITTED_CURRENCIES[currencyId])
let njsWallet let njsWallet
try { try {
njsWallet = await core.getPoolInstance().getWallet(decodedAccountId.walletName) njsWallet = await core.getPoolInstance().getWallet(decodedAccountId.walletName)
@ -494,7 +506,7 @@ export async function syncAccount({ rawAccount, core }: { core: *, rawAccount: A
njsWallet = await getOrCreateWallet( njsWallet = await getOrCreateWallet(
core, core,
decodedAccountId.walletName, decodedAccountId.walletName,
rawAccount.currencyId, currencyId,
isSegwit, isSegwit,
isUnsplit, isUnsplit,
) )
@ -502,10 +514,10 @@ export async function syncAccount({ rawAccount, core }: { core: *, rawAccount: A
let njsAccount let njsAccount
try { try {
njsAccount = await njsWallet.getAccount(rawAccount.index) njsAccount = await njsWallet.getAccount(index)
} catch (e) { } catch (e) {
logger.warn(`Have to recreate the account... (${e.message})`) logger.warn(`Have to recreate the account... (${e.message})`)
const extendedInfos = await njsWallet.getExtendedKeyAccountCreationInfo(rawAccount.index) const extendedInfos = await njsWallet.getExtendedKeyAccountCreationInfo(index)
extendedInfos.extendedKeys.push(decodedAccountId.xpub) extendedInfos.extendedKeys.push(decodedAccountId.xpub)
njsAccount = await njsWallet.newAccountWithExtendedKeyInfo(extendedInfos) njsAccount = await njsWallet.newAccountWithExtendedKeyInfo(extendedInfos)
} }
@ -521,9 +533,9 @@ export async function syncAccount({ rawAccount, core }: { core: *, rawAccount: A
njsAccount, njsAccount,
isSegwit, isSegwit,
isUnsplit, isUnsplit,
accountIndex: rawAccount.index, accountIndex: index,
wallet: njsWallet, wallet: njsWallet,
currencyId: rawAccount.currencyId, currencyId,
core, core,
ops, ops,
}) })

2
src/logger/logger.js

@ -146,7 +146,7 @@ const logNetwork = !__DEV__ || DEBUG_NETWORK
const logAnalytics = !__DEV__ || DEBUG_ANALYTICS const logAnalytics = !__DEV__ || DEBUG_ANALYTICS
const logApdu = !__DEV__ || DEBUG_DEVICE const logApdu = !__DEV__ || DEBUG_DEVICE
const blacklistTooVerboseCommandInput = ['libcoreSyncAccount', 'libcoreSignAndBroadcast'] const blacklistTooVerboseCommandInput = []
const blacklistTooVerboseCommandResponse = [ const blacklistTooVerboseCommandResponse = [
'libcoreSyncAccount', 'libcoreSyncAccount',
'libcoreScanAccounts', 'libcoreScanAccounts',

Loading…
Cancel
Save