Gaëtan Renaudeau
7 years ago
committed by
GitHub
53 changed files with 729 additions and 380 deletions
@ -1,7 +1,6 @@ |
|||
// @flow
|
|||
import type { Currency } from '@ledgerhq/live-common/lib/types' |
|||
|
|||
const BASE_URL = process.env.LEDGER_REST_API_BASE || 'https://api.ledgerwallet.com/' |
|||
import { LEDGER_REST_API_BASE } from 'config/constants' |
|||
|
|||
export const blockchainBaseURL = ({ ledgerExplorerId }: Currency): ?string => |
|||
ledgerExplorerId ? `${BASE_URL}blockchain/v2/${ledgerExplorerId}` : null |
|||
ledgerExplorerId ? `${LEDGER_REST_API_BASE}blockchain/v2/${ledgerExplorerId}` : null |
|||
|
@ -1,6 +1,7 @@ |
|||
// @flow
|
|||
import { LEDGER_DEBUG_ALL_LANGS } from 'config/constants' |
|||
|
|||
const allLanguages = ['en', 'fr'] |
|||
const prodStableLanguages = ['en'] |
|||
const languages = process.env.LEDGER_DEBUG_ALL_LANGS ? allLanguages : prodStableLanguages |
|||
const languages = LEDGER_DEBUG_ALL_LANGS ? allLanguages : prodStableLanguages |
|||
export default languages |
|||
|
@ -1,13 +0,0 @@ |
|||
// Socket endpoint
|
|||
|
|||
export const BASE_SOCKET_URL = 'ws://api.ledgerwallet.com/update' |
|||
export const MANAGER_API_URL = 'wss://api.ledgerwallet.com/update' |
|||
export const API_BASE_URL = process.env.API_BASE_URL || 'https://beta.manager.live.ledger.fr/api' |
|||
|
|||
// List of APDUS
|
|||
export const APDUS = { |
|||
GET_FIRMWARE: [0xe0, 0x01, 0x00, 0x00], |
|||
// we dont have common call that works inside app & dashboard
|
|||
// TODO: this should disappear.
|
|||
GET_FIRMWARE_FALLBACK: [0xe0, 0xc4, 0x00, 0x00], |
|||
} |
@ -1,11 +1,12 @@ |
|||
// @flow
|
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import { createSocketDialog } from 'helpers/common' |
|||
import { SKIP_GENUINE } from 'config/constants' |
|||
|
|||
export default async ( |
|||
transport: Transport<*>, |
|||
{ targetId }: { targetId: string | number }, |
|||
): Promise<*> => |
|||
process.env.SKIP_GENUINE > 0 |
|||
): Promise<string> => |
|||
SKIP_GENUINE |
|||
? new Promise(resolve => setTimeout(() => resolve('0000'), 1000)) |
|||
: createSocketDialog(transport, '/genuine', { targetId }, true) |
|||
|
@ -0,0 +1,128 @@ |
|||
// @flow
|
|||
|
|||
import invariant from 'invariant' |
|||
import logger from 'logger' |
|||
import Websocket from 'ws' |
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
import { Observable } from 'rxjs' |
|||
import createCustomErrorClass from './createCustomErrorClass' |
|||
|
|||
const WebsocketConnectionError = createCustomErrorClass('WebsocketConnectionError') |
|||
const WebsocketConnectionFailed = createCustomErrorClass('WebsocketConnectionFailed') |
|||
const DeviceSocketFail = createCustomErrorClass('DeviceSocketFail') |
|||
const DeviceSocketNoBulkStatus = createCustomErrorClass('DeviceSocketNoBulkStatus') |
|||
const DeviceSocketNoHandler = createCustomErrorClass('DeviceSocketNoHandler') |
|||
|
|||
/** |
|||
* use Ledger WebSocket API to exchange data with the device |
|||
* Returns an Observable of the final result |
|||
*/ |
|||
export const createDeviceSocket = (transport: Transport<*>, url: string) => |
|||
Observable.create(o => { |
|||
let ws |
|||
let lastMessage: ?string |
|||
|
|||
try { |
|||
ws = new Websocket(url) |
|||
} catch (err) { |
|||
o.error(new WebsocketConnectionFailed(err.message)) |
|||
return () => {} |
|||
} |
|||
invariant(ws, 'websocket is available') |
|||
|
|||
ws.on('open', () => { |
|||
logger.websocket('OPENED', url) |
|||
}) |
|||
|
|||
ws.on('error', e => { |
|||
logger.websocket('ERROR', e) |
|||
o.error(new WebsocketConnectionError(e.message)) |
|||
}) |
|||
|
|||
ws.on('close', () => { |
|||
logger.websocket('CLOSE') |
|||
o.next(lastMessage || '') |
|||
o.complete() |
|||
}) |
|||
|
|||
const send = (nonce, response, data) => { |
|||
const msg = { |
|||
nonce, |
|||
response, |
|||
data, |
|||
} |
|||
logger.websocket('SEND', msg) |
|||
const strMsg = JSON.stringify(msg) |
|||
ws.send(strMsg) |
|||
} |
|||
|
|||
const handlers = { |
|||
exchange: async input => { |
|||
const { data, nonce } = input |
|||
const r: Buffer = await transport.exchange(Buffer.from(data, 'hex')) |
|||
const status = r.slice(r.length - 2) |
|||
const buffer = r.slice(0, r.length - 2) |
|||
const strStatus = status.toString('hex') |
|||
send(nonce, strStatus === '9000' ? 'success' : 'error', buffer.toString('hex')) |
|||
}, |
|||
|
|||
bulk: async input => { |
|||
const { data, nonce } = input |
|||
|
|||
// Execute all apdus and collect last status
|
|||
let lastStatus = null |
|||
for (const apdu of data) { |
|||
const r: Buffer = await transport.exchange(Buffer.from(apdu, 'hex')) |
|||
lastStatus = r.slice(r.length - 2) |
|||
} |
|||
if (!lastStatus) { |
|||
throw new DeviceSocketNoBulkStatus() |
|||
} |
|||
|
|||
const strStatus = lastStatus.toString('hex') |
|||
|
|||
send( |
|||
nonce, |
|||
strStatus === '9000' ? 'success' : 'error', |
|||
strStatus === '9000' ? '' : strStatus, |
|||
) |
|||
}, |
|||
|
|||
success: msg => { |
|||
lastMessage = msg.data || msg.result |
|||
ws.close() |
|||
}, |
|||
|
|||
error: msg => { |
|||
logger.websocket('ERROR', msg.data) |
|||
throw new DeviceSocketFail(msg.data) |
|||
}, |
|||
} |
|||
|
|||
const stackMessage = async rawMsg => { |
|||
try { |
|||
const msg = JSON.parse(rawMsg) |
|||
if (!(msg.query in handlers)) { |
|||
throw new DeviceSocketNoHandler(`Cannot handle msg of type ${msg.query}`, { |
|||
query: msg.query, |
|||
}) |
|||
} |
|||
logger.websocket('RECEIVE', msg) |
|||
await handlers[msg.query](msg) |
|||
} catch (err) { |
|||
logger.websocket('ERROR', err.toString()) |
|||
o.error(err) |
|||
} |
|||
} |
|||
|
|||
ws.on('message', async rawMsg => { |
|||
stackMessage(rawMsg) |
|||
}) |
|||
|
|||
return () => { |
|||
if (ws.readyState === 1) { |
|||
lastMessage = null |
|||
ws.close() |
|||
} |
|||
} |
|||
}) |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 10 KiB |
Loading…
Reference in new issue