Gaëtan Renaudeau
7 years ago
committed by
GitHub
53 changed files with 729 additions and 380 deletions
@ -1,7 +1,6 @@ |
|||||
// @flow
|
// @flow
|
||||
import type { Currency } from '@ledgerhq/live-common/lib/types' |
import type { Currency } from '@ledgerhq/live-common/lib/types' |
||||
|
import { LEDGER_REST_API_BASE } from 'config/constants' |
||||
const BASE_URL = process.env.LEDGER_REST_API_BASE || 'https://api.ledgerwallet.com/' |
|
||||
|
|
||||
export const blockchainBaseURL = ({ ledgerExplorerId }: Currency): ?string => |
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
|
// @flow
|
||||
|
import { LEDGER_DEBUG_ALL_LANGS } from 'config/constants' |
||||
|
|
||||
const allLanguages = ['en', 'fr'] |
const allLanguages = ['en', 'fr'] |
||||
const prodStableLanguages = ['en'] |
const prodStableLanguages = ['en'] |
||||
const languages = process.env.LEDGER_DEBUG_ALL_LANGS ? allLanguages : prodStableLanguages |
const languages = LEDGER_DEBUG_ALL_LANGS ? allLanguages : prodStableLanguages |
||||
export default languages |
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
|
// @flow
|
||||
import type Transport from '@ledgerhq/hw-transport' |
import type Transport from '@ledgerhq/hw-transport' |
||||
import { createSocketDialog } from 'helpers/common' |
import { createSocketDialog } from 'helpers/common' |
||||
|
import { SKIP_GENUINE } from 'config/constants' |
||||
|
|
||||
export default async ( |
export default async ( |
||||
transport: Transport<*>, |
transport: Transport<*>, |
||||
{ targetId }: { targetId: string | number }, |
{ targetId }: { targetId: string | number }, |
||||
): Promise<*> => |
): Promise<string> => |
||||
process.env.SKIP_GENUINE > 0 |
SKIP_GENUINE |
||||
? new Promise(resolve => setTimeout(() => resolve('0000'), 1000)) |
? new Promise(resolve => setTimeout(() => resolve('0000'), 1000)) |
||||
: createSocketDialog(transport, '/genuine', { targetId }, true) |
: 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