Gaëtan Renaudeau
6 years ago
23 changed files with 214 additions and 206 deletions
@ -1,64 +0,0 @@ |
|||
// @flow
|
|||
import logger from 'logger' |
|||
import throttle from 'lodash/throttle' |
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import TransportNodeHid from '@ledgerhq/hw-transport-node-hid' |
|||
import { DisconnectedDevice, CantOpenDevice } from 'config/errors' |
|||
import { retry } from './promise' |
|||
|
|||
// all open to device must use openDevice so we can prevent race conditions
|
|||
// and guarantee we do one device access at a time. It also will handle the .close()
|
|||
// NOTE optim: in the future we can debounce the close & reuse the same transport instance.
|
|||
|
|||
type WithDevice = (devicePath: string) => <T>(job: (Transport<*>) => Promise<*>) => Promise<T> |
|||
|
|||
const mapError = e => { |
|||
if (e && e.message && e.message.indexOf('cannot open device with path') >= 0) { |
|||
throw new CantOpenDevice(e.message) |
|||
} |
|||
if (e && e.message && e.message.indexOf('HID') >= 0) { |
|||
throw new DisconnectedDevice(e.message) |
|||
} |
|||
throw e |
|||
} |
|||
|
|||
let queue = Promise.resolve() |
|||
|
|||
let busy = false |
|||
|
|||
TransportNodeHid.setListenDevicesPollingSkip(() => busy) |
|||
|
|||
const refreshBusyUIState = throttle(() => { |
|||
if (process.env.CLI) return |
|||
process.send({ |
|||
type: 'setDeviceBusy', |
|||
busy, |
|||
}) |
|||
}, 100) |
|||
|
|||
export const withDevice: WithDevice = devicePath => job => { |
|||
const p = queue.then(async () => { |
|||
busy = true |
|||
refreshBusyUIState() |
|||
try { |
|||
// $FlowFixMe not sure what's wrong
|
|||
const t = await retry(() => TransportNodeHid.open(devicePath), { maxRetry: 2 }).catch( |
|||
mapError, |
|||
) |
|||
t.setDebugMode(logger.apdu) |
|||
try { |
|||
const res = await job(t).catch(mapError) |
|||
return res |
|||
} finally { |
|||
await t.close() |
|||
} |
|||
} finally { |
|||
busy = false |
|||
refreshBusyUIState() |
|||
} |
|||
}) |
|||
|
|||
queue = p.catch(() => null) |
|||
|
|||
return p |
|||
} |
@ -0,0 +1,50 @@ |
|||
// @flow
|
|||
import logger from 'logger' |
|||
import { throwError } from 'rxjs' |
|||
import { registerTransportModule } from '@ledgerhq/live-common/lib/hw' |
|||
import { addAccessHook, setErrorRemapping } from '@ledgerhq/live-common/lib/hw/deviceAccess' |
|||
import throttle from 'lodash/throttle' |
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import TransportNodeHid from '@ledgerhq/hw-transport-node-hid' |
|||
import { DisconnectedDevice, CantOpenDevice } from 'config/errors' |
|||
import { retry } from './promise' |
|||
|
|||
let busy = false |
|||
|
|||
TransportNodeHid.setListenDevicesPollingSkip(() => busy) |
|||
|
|||
const refreshBusyUIState = throttle(() => { |
|||
if (process.env.CLI) return |
|||
process.send({ |
|||
type: 'setDeviceBusy', |
|||
busy, |
|||
}) |
|||
}, 100) |
|||
|
|||
addAccessHook(() => { |
|||
busy = true |
|||
refreshBusyUIState() |
|||
return () => { |
|||
busy = false |
|||
refreshBusyUIState() |
|||
} |
|||
}) |
|||
|
|||
setErrorRemapping(e => { |
|||
// NB ideally we should solve it in ledgerjs
|
|||
if (e && e.message && e.message.indexOf('HID') >= 0) { |
|||
return throwError(new DisconnectedDevice(e.message)) |
|||
} |
|||
return throwError(e) |
|||
}) |
|||
|
|||
registerTransportModule({ |
|||
id: 'hid', |
|||
open: async devicePath => { |
|||
// $FlowFixMe
|
|||
const t = await retry(() => TransportNodeHid.open(devicePath), { maxRetry: 2 }) |
|||
t.setDebugMode(logger.apdu) |
|||
return t |
|||
}, |
|||
disconnect: () => Promise.resolve(), |
|||
}) |
@ -0,0 +1,8 @@ |
|||
// @flow
|
|||
import { setNetwork } from '@ledgerhq/live-common/lib/network' |
|||
import { setEnv } from '@ledgerhq/live-common/lib/env' |
|||
import network from 'api/network' |
|||
import * as constants from 'config/constants' |
|||
|
|||
setNetwork(network) |
|||
setEnv('FORCE_PROVIDER', constants.FORCE_PROVIDER) |
Loading…
Reference in new issue