Browse Source

Ability to make an interaction polling throw on specific errors

master
meriadec 7 years ago
parent
commit
7a19d8b264
No known key found for this signature in database GPG Key ID: 1D2FC2305E2CB399
  1. 75
      src/components/EnsureDeviceApp.js
  2. 2
      src/helpers/getAddressForCurrency/btc.js
  3. 13
      src/helpers/promise.js

75
src/components/EnsureDeviceApp.js

@ -12,6 +12,7 @@ import getAddress from 'commands/getAddress'
import { createCancelablePolling } from 'helpers/promise'
import { standardDerivation } from 'helpers/derivations'
import { isSegwitAccount } from 'helpers/bip32'
import { BtcUnmatchedApp } from 'helpers/getAddressForCurrency/btc'
import DeviceInteraction from 'components/DeviceInteraction'
import Text from 'components/base/Text'
@ -45,44 +46,31 @@ class EnsureDeviceApp extends Component<{
})
openAppInteractionHandler = ({ device }) =>
createCancelablePolling(async () => {
const { account, currency } = this.props
const cur = account ? account.currency : currency
invariant(cur, 'No currency given')
const { address } = await getAddress
.send({
devicePath: device.path,
currencyId: cur.id,
path: account
? account.freshAddressPath
: standardDerivation({ currency: cur, segwit: false, x: 0 }),
segwit: account ? isSegwitAccount(account) : false,
})
.toPromise()
.catch(e => {
if (
e &&
(e.name === 'TransportStatusError' ||
// we don't want these error to appear (caused by usb disconnect..)
e.message === 'could not read from HID device' ||
e.message === 'Cannot write to HID device')
) {
throw new WrongAppOpened(`WrongAppOpened ${cur.id}`, { currencyName: cur.name })
createCancelablePolling(
async () => {
const { account, currency: _currency } = this.props
const currency = account ? account.currency : _currency
invariant(currency, 'No currency given')
const address = await getAddressFromAccountOrCurrency(device, account, currency)
if (account) {
const { freshAddress } = account
if (account && freshAddress !== address) {
logger.warn({ freshAddress, address })
throw new WrongDeviceForAccount(`WrongDeviceForAccount ${account.name}`, {
accountName: account.name,
})
}
throw e
})
if (account) {
const { freshAddress } = account
if (account && freshAddress !== address) {
logger.warn({ freshAddress, address })
throw new WrongDeviceForAccount(`WrongDeviceForAccount ${account.name}`, {
accountName: account.name,
})
}
}
return address
})
return address
},
{
shouldThrow: (err: Error) => {
const isWrongApp = err instanceof BtcUnmatchedApp
const isWrongDevice = err instanceof WrongDeviceForAccount
return isWrongApp || isWrongDevice
},
},
)
renderOpenAppTitle = () => {
const { account, currency } = this.props
@ -103,6 +91,7 @@ class EnsureDeviceApp extends Component<{
const Icon = cur ? getCryptoCurrencyIcon(cur) : null
return (
<DeviceInteraction
shouldRenderRetry
steps={[
{
id: 'device',
@ -128,4 +117,18 @@ class EnsureDeviceApp extends Component<{
}
}
async function getAddressFromAccountOrCurrency(device, account, currency) {
const { address } = await getAddress
.send({
devicePath: device.path,
currencyId: currency.id,
path: account
? account.freshAddressPath
: standardDerivation({ currency, segwit: false, x: 0 }),
segwit: account ? isSegwitAccount(account) : false,
})
.toPromise()
return address
}
export default connect(mapStateToProps)(EnsureDeviceApp)

2
src/helpers/getAddressForCurrency/btc.js

@ -6,7 +6,7 @@ import type Transport from '@ledgerhq/hw-transport'
import getBitcoinLikeInfo from '../devices/getBitcoinLikeInfo'
import { createCustomErrorClass } from '../errors'
const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export const BtcUnmatchedApp = createCustomErrorClass('BtcUnmatchedApp')
export default async (
transport: Transport<*>,

13
src/helpers/promise.js

@ -32,20 +32,29 @@ export function idleCallback() {
return new Promise(resolve => window.requestIdleCallback(resolve))
}
type CancellablePollingOpts = {
pollingInterval?: number,
shouldThrow?: Error => boolean,
}
export function createCancelablePolling(
job: any => Promise<any>,
{ pollingInterval = 500 }: { pollingInterval: number } = {},
{ pollingInterval = 500, shouldThrow }: CancellablePollingOpts = {},
) {
let isUnsub = false
const unsubscribe = () => (isUnsub = true)
const getUnsub = () => isUnsub
const promise = new Promise(resolve => {
const promise = new Promise((resolve, reject) => {
async function poll() {
try {
const res = await job()
if (getUnsub()) return
resolve(res)
} catch (err) {
if (shouldThrow && shouldThrow(err)) {
reject(err)
return
}
await delay(pollingInterval)
if (getUnsub()) return
poll()

Loading…
Cancel
Save