Valentin D. Pinkman
7 years ago
35 changed files with 969 additions and 347 deletions
@ -0,0 +1,24 @@ |
|||
// @flow
|
|||
|
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|||
import { withDevice } from 'helpers/deviceAccess' |
|||
|
|||
import getDeviceInfo from 'helpers/devices/getDeviceInfo' |
|||
|
|||
type Input = { |
|||
devicePath: string, |
|||
} |
|||
|
|||
type Result = { |
|||
targetId: number | string, |
|||
version: string, |
|||
final: boolean, |
|||
mcu: boolean, |
|||
} |
|||
|
|||
const cmd: Command<Input, Result> = createCommand('devices', 'getDeviceInfo', ({ devicePath }) => |
|||
fromPromise(withDevice(devicePath)(transport => getDeviceInfo(transport))), |
|||
) |
|||
|
|||
export default cmd |
@ -0,0 +1,19 @@ |
|||
// @flow
|
|||
|
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|||
|
|||
import getFirmwareInfo from 'helpers/devices/getFirmwareInfo' |
|||
|
|||
type Input = { |
|||
targetId: string | number, |
|||
version: string, |
|||
} |
|||
|
|||
type Result = * |
|||
|
|||
const cmd: Command<Input, Result> = createCommand('devices', 'getFirmwareInfo', data => |
|||
fromPromise(getFirmwareInfo(data)), |
|||
) |
|||
|
|||
export default cmd |
@ -0,0 +1,15 @@ |
|||
// @flow
|
|||
|
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|||
|
|||
import getIsGenuine from 'helpers/devices/getIsGenuine' |
|||
|
|||
type Input = * |
|||
type Result = boolean |
|||
|
|||
const cmd: Command<Input, Result> = createCommand('devices', 'getIsGenuine', () => |
|||
fromPromise(getIsGenuine()), |
|||
) |
|||
|
|||
export default cmd |
@ -0,0 +1,19 @@ |
|||
// @flow
|
|||
|
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|||
|
|||
import getLatestFirmwareForDevice from '../helpers/devices/getLatestFirmwareForDevice' |
|||
|
|||
type Input = { |
|||
targetId: string | number, |
|||
version: string, |
|||
} |
|||
|
|||
type Result = * |
|||
|
|||
const cmd: Command<Input, Result> = createCommand('devices', 'getLatestFirmwareForDevice', data => |
|||
fromPromise(getLatestFirmwareForDevice(data)), |
|||
) |
|||
|
|||
export default cmd |
@ -0,0 +1,25 @@ |
|||
// @flow
|
|||
|
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|||
import { withDevice } from 'helpers/deviceAccess' |
|||
|
|||
import installApp from 'helpers/apps/installApp' |
|||
|
|||
import type { LedgerScriptParams } from 'helpers/common' |
|||
|
|||
type Input = { |
|||
appParams: LedgerScriptParams, |
|||
devicePath: string, |
|||
} |
|||
|
|||
type Result = * |
|||
|
|||
const cmd: Command<Input, Result> = createCommand( |
|||
'devices', |
|||
'installApp', |
|||
({ devicePath, ...rest }) => |
|||
fromPromise(withDevice(devicePath)(transport => installApp(transport, rest))), |
|||
) |
|||
|
|||
export default cmd |
@ -0,0 +1,15 @@ |
|||
// @flow
|
|||
|
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|||
|
|||
import listApps from 'helpers/apps/listApps' |
|||
|
|||
type Input = * |
|||
type Result = * |
|||
|
|||
const cmd: Command<Input, Result> = createCommand('manager', 'listApps', () => |
|||
fromPromise(listApps()), |
|||
) |
|||
|
|||
export default cmd |
@ -0,0 +1,93 @@ |
|||
// @flow
|
|||
import React, { PureComponent, Fragment } from 'react' |
|||
import { translate } from 'react-i18next' |
|||
import isEqual from 'lodash/isEqual' |
|||
|
|||
// import type { Device, T } from 'types/common'
|
|||
import type { Device } from 'types/common' |
|||
|
|||
import getDeviceInfo from 'commands/getDeviceInfo' |
|||
|
|||
type DeviceInfo = { |
|||
targetId: number | string, |
|||
version: string, |
|||
final: boolean, |
|||
mcu: boolean, |
|||
} |
|||
|
|||
type Props = { |
|||
// t: T,
|
|||
device: Device, |
|||
children: Function, |
|||
} |
|||
|
|||
type State = { |
|||
deviceInfo: ?DeviceInfo, |
|||
error: ?{ |
|||
message: string, |
|||
stack: string, |
|||
}, |
|||
} |
|||
|
|||
class EnsureDashboard extends PureComponent<Props, State> { |
|||
static defaultProps = { |
|||
children: null, |
|||
device: null, |
|||
} |
|||
|
|||
state = { |
|||
deviceInfo: null, |
|||
error: null, |
|||
} |
|||
|
|||
componentDidMount() { |
|||
this.checkForDashboard() |
|||
} |
|||
|
|||
componentDidUpdate() { |
|||
this.checkForDashboard() |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
this._unmounting = true |
|||
} |
|||
|
|||
_checking = false |
|||
_unmounting = false |
|||
|
|||
async checkForDashboard() { |
|||
const { device } = this.props |
|||
if (device && !this._checking) { |
|||
this._checking = true |
|||
try { |
|||
const deviceInfo = await getDeviceInfo.send({ devicePath: device.path }).toPromise() |
|||
if (!isEqual(this.state.deviceInfo, deviceInfo) || this.state.error) { |
|||
!this._unmounting && this.setState({ deviceInfo, error: null }) |
|||
} |
|||
} catch (err) { |
|||
if (!isEqual(err, this.state.error)) { |
|||
!this._unmounting && this.setState({ error: err, deviceInfo: null }) |
|||
} |
|||
} |
|||
this._checking = false |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
const { deviceInfo, error } = this.state |
|||
const { children } = this.props |
|||
|
|||
if (deviceInfo) { |
|||
return children(deviceInfo) |
|||
} |
|||
|
|||
return error ? ( |
|||
<Fragment> |
|||
<span>{error.message}</span> |
|||
<span>Please make sure your device is on the dashboard screen</span> |
|||
</Fragment> |
|||
) : null |
|||
} |
|||
} |
|||
|
|||
export default translate()(EnsureDashboard) |
@ -0,0 +1,37 @@ |
|||
// @flow
|
|||
import React, { PureComponent } from 'react' |
|||
import { connect } from 'react-redux' |
|||
import { translate } from 'react-i18next' |
|||
import { compose } from 'redux' |
|||
|
|||
// import type { Device, T } from 'types/common'
|
|||
import type { Device } from 'types/common' |
|||
|
|||
import { getCurrentDevice, getDevices } from 'reducers/devices' |
|||
|
|||
const mapStateToProps = state => ({ |
|||
device: getCurrentDevice(state), |
|||
nbDevices: getDevices(state).length, |
|||
}) |
|||
|
|||
type Props = { |
|||
// t: T,
|
|||
device: ?Device, |
|||
nbDevices: number, |
|||
children: Function, |
|||
} |
|||
|
|||
type State = {} |
|||
|
|||
class EnsureDevice extends PureComponent<Props, State> { |
|||
static defaultProps = { |
|||
device: null, |
|||
} |
|||
|
|||
render() { |
|||
const { device, nbDevices, children } = this.props |
|||
return device ? children(device, nbDevices) : <span>Please connect your device</span> |
|||
} |
|||
} |
|||
|
|||
export default compose(translate(), connect(mapStateToProps))(EnsureDevice) |
@ -0,0 +1,87 @@ |
|||
// @flow
|
|||
import React, { PureComponent, Fragment } from 'react' |
|||
import { translate } from 'react-i18next' |
|||
import isEqual from 'lodash/isEqual' |
|||
|
|||
import type { Node } from 'react' |
|||
// import type { Device, T } from 'types/common'
|
|||
import type { Device } from 'types/common' |
|||
|
|||
import getIsGenuine from 'commands/getIsGenuine' |
|||
|
|||
type Props = { |
|||
// t: T,
|
|||
device: Device, |
|||
children: Node, |
|||
} |
|||
|
|||
type State = { |
|||
genuine: boolean, |
|||
error: ?{ |
|||
message: string, |
|||
stack: string, |
|||
}, |
|||
} |
|||
|
|||
class EnsureGenuine extends PureComponent<Props, State> { |
|||
static defaultProps = { |
|||
children: null, |
|||
firmwareInfo: null, |
|||
} |
|||
|
|||
state = { |
|||
error: null, |
|||
genuine: false, |
|||
} |
|||
|
|||
componentDidMount() { |
|||
this.checkIsGenuine() |
|||
} |
|||
|
|||
componentDidUpdate() { |
|||
this.checkIsGenuine() |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
this._unmounting = true |
|||
} |
|||
|
|||
_checking = false |
|||
_unmounting = false |
|||
|
|||
async checkIsGenuine() { |
|||
const { device } = this.props |
|||
if (device && !this._checking) { |
|||
this._checking = true |
|||
try { |
|||
const isGenuine = await getIsGenuine.send().toPromise() |
|||
if (!this.state.genuine || this.state.error) { |
|||
!this._unmounting && this.setState({ genuine: isGenuine, error: null }) |
|||
} |
|||
} catch (err) { |
|||
if (!isEqual(this.state.error, err)) { |
|||
!this._unmounting && this.setState({ genuine: false, error: err }) |
|||
} |
|||
} |
|||
this._checking = false |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
const { error, genuine } = this.state |
|||
const { children } = this.props |
|||
|
|||
if (genuine) { |
|||
return children |
|||
} |
|||
|
|||
return error ? ( |
|||
<Fragment> |
|||
<span>{error.message}</span> |
|||
<span>You did not approve request on your device or your device is not genuine</span> |
|||
</Fragment> |
|||
) : null |
|||
} |
|||
} |
|||
|
|||
export default translate()(EnsureGenuine) |
@ -0,0 +1,78 @@ |
|||
// @flow
|
|||
|
|||
import React, { PureComponent } from 'react' |
|||
import { translate } from 'react-i18next' |
|||
import type { Device, T } from 'types/common' |
|||
|
|||
// import runJob from 'renderer/runJob'
|
|||
|
|||
import Box, { Card } from 'components/base/Box' |
|||
// import Button from 'components/base/Button'
|
|||
|
|||
type DeviceInfos = { |
|||
targetId: number, |
|||
version: string, |
|||
} |
|||
|
|||
type Props = { |
|||
t: T, |
|||
device: Device, |
|||
infos: DeviceInfos, |
|||
} |
|||
|
|||
type State = { |
|||
// latestFirmware: ?FirmwareInfos,
|
|||
} |
|||
|
|||
class FirmwareUpdate extends PureComponent<Props, State> { |
|||
state = { |
|||
// latestFirmware: null,
|
|||
} |
|||
|
|||
componentDidMount() {} |
|||
|
|||
componentWillUnmount() { |
|||
this._unmounting = true |
|||
} |
|||
|
|||
_unmounting = false |
|||
|
|||
// handleInstallFinalFirmware = async () => {
|
|||
// try {
|
|||
// const { latestFirmware } = this.state
|
|||
// this.setState(state => ({ ...state, installing: true }))
|
|||
// const {
|
|||
// device: { path: devicePath },
|
|||
// } = this.props
|
|||
// await runJob({
|
|||
// channel: 'manager',
|
|||
// job: 'installFinalFirmware',
|
|||
// successResponse: 'device.finalFirmwareInstallSuccess',
|
|||
// errorResponse: 'device.finalFirmwareInstallError',
|
|||
// data: {
|
|||
// devicePath,
|
|||
// firmware: latestFirmware,
|
|||
// },
|
|||
// })
|
|||
// } catch (err) {
|
|||
// console.log(err)
|
|||
// }
|
|||
// }
|
|||
|
|||
render() { |
|||
const { t, ...props } = this.props |
|||
|
|||
return ( |
|||
<Box flow={4} {...props}> |
|||
<Box color="dark" ff="Museo Sans" fontSize={6}> |
|||
{t('manager:firmwareUpdate')} |
|||
</Box> |
|||
<Card flow={2} {...props}> |
|||
<Box horizontal align="center" flow={2} /> |
|||
</Card> |
|||
</Box> |
|||
) |
|||
} |
|||
} |
|||
|
|||
export default translate()(FirmwareUpdate) |
@ -0,0 +1,16 @@ |
|||
// @flow
|
|||
|
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
|
|||
import { createSocketDialog } from 'helpers/common' |
|||
import type { LedgerScriptParams } from 'helpers/common' |
|||
|
|||
/** |
|||
* Install an app on the device |
|||
*/ |
|||
export default async function installApp( |
|||
transport: Transport<*>, |
|||
{ appParams }: { appParams: LedgerScriptParams }, |
|||
): Promise<void> { |
|||
return createSocketDialog(transport, '/update/install', appParams) |
|||
} |
@ -0,0 +1,14 @@ |
|||
// @flow
|
|||
|
|||
import axios from 'axios' |
|||
|
|||
export default async () => { |
|||
try { |
|||
const { data } = await axios.get('https://api.ledgerwallet.com/update/applications') |
|||
return data['nanos-1.4'] |
|||
} catch (err) { |
|||
const error = Error(err.message) |
|||
error.stack = err.stack |
|||
throw err |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
// @flow
|
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
// import { createTransportHandler, uninstallApp } from 'helpers/common'
|
|||
|
|||
// export default (send: IPCSend, data: any) =>
|
|||
// createTransportHandler(send, {
|
|||
// action: uninstallApp,
|
|||
// successResponse: 'manager.appUninstalled',
|
|||
// errorResponse: 'manager.appUninstallError',
|
|||
// })(data)
|
@ -0,0 +1,236 @@ |
|||
// @flow
|
|||
|
|||
import chalk from 'chalk' |
|||
import Websocket from 'ws' |
|||
import qs from 'qs' |
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
|
|||
import { BASE_SOCKET_URL, APDUS } from './constants' |
|||
|
|||
type WebsocketType = { |
|||
send: (string, any) => void, |
|||
on: (string, Function) => void, |
|||
} |
|||
|
|||
type Message = { |
|||
nonce: number, |
|||
query?: string, |
|||
response?: string, |
|||
data: any, |
|||
} |
|||
|
|||
export type LedgerScriptParams = { |
|||
firmware?: string, |
|||
firmwareKey?: string, |
|||
delete?: string, |
|||
deleteKey?: string, |
|||
} |
|||
|
|||
type FirmwareUpdateType = 'osu' | 'final' |
|||
|
|||
// /**
|
|||
// * Install an app on the device
|
|||
// */
|
|||
// export async function installApp(
|
|||
// transport: Transport<*>,
|
|||
// { appParams }: { appParams: LedgerScriptParams },
|
|||
// ): Promise<void> {
|
|||
// return createSocketDialog(transport, '/update/install', appParams)
|
|||
// }
|
|||
|
|||
/** |
|||
* Uninstall an app on the device |
|||
*/ |
|||
export async function uninstallApp( |
|||
transport: Transport<*>, |
|||
{ appParams }: { appParams: LedgerScriptParams }, |
|||
): Promise<void> { |
|||
return createSocketDialog(transport, '/update/install', { |
|||
...appParams, |
|||
firmware: appParams.delete, |
|||
firmwareKey: appParams.deleteKey, |
|||
}) |
|||
} |
|||
|
|||
export async function getMemInfos(transport: Transport<*>): Promise<Object> { |
|||
const { targetId } = await getFirmwareInfo(transport) |
|||
// Dont ask me about this `perso_11`: I don't know. But we need it.
|
|||
return createSocketDialog(transport, '/get-mem-infos', { targetId, perso: 'perso_11' }) |
|||
} |
|||
|
|||
/** |
|||
* Send data through ws |
|||
*/ |
|||
function socketSend(ws: WebsocketType, msg: Message) { |
|||
logWS('SEND', msg) |
|||
const strMsg = JSON.stringify(msg) |
|||
ws.send(strMsg) |
|||
} |
|||
|
|||
/** |
|||
* Exchange data on transport |
|||
*/ |
|||
export async function exchange( |
|||
ws: WebsocketType, |
|||
transport: Transport<*>, |
|||
msg: Message, |
|||
): Promise<void> { |
|||
const { data, nonce } = msg |
|||
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') |
|||
socketSend(ws, { |
|||
nonce, |
|||
response: strStatus === '9000' ? 'success' : 'error', |
|||
data: buffer.toString('hex'), |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* Bulk update on transport |
|||
*/ |
|||
export async function bulk(ws: WebsocketType, transport: Transport<*>, msg: Message) { |
|||
const { data, nonce } = msg |
|||
|
|||
// 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 Error('No status collected from bulk') |
|||
} |
|||
|
|||
const strStatus = lastStatus.toString('hex') |
|||
socketSend(ws, { |
|||
nonce, |
|||
response: strStatus === '9000' ? 'success' : 'error', |
|||
data: strStatus === '9000' ? '' : strStatus, |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* Open socket connection with firmware api, and init a dialog |
|||
* with the device |
|||
*/ |
|||
export async function createSocketDialog( |
|||
transport: Transport<*>, |
|||
endpoint: string, |
|||
params: LedgerScriptParams, |
|||
) { |
|||
return new Promise(async (resolve, reject) => { |
|||
try { |
|||
let lastData |
|||
const url = `${BASE_SOCKET_URL}${endpoint}?${qs.stringify(params)}` |
|||
|
|||
log('WS CONNECTING', url) |
|||
const ws: WebsocketType = new Websocket(url) |
|||
|
|||
ws.on('open', () => log('WS CONNECTED')) |
|||
|
|||
ws.on('close', () => { |
|||
log('WS CLOSED') |
|||
resolve(lastData) |
|||
}) |
|||
|
|||
ws.on('message', async rawMsg => { |
|||
const handlers = { |
|||
exchange: msg => exchange(ws, transport, msg), |
|||
bulk: msg => bulk(ws, transport, msg), |
|||
success: msg => { |
|||
if (msg.data) { |
|||
lastData = msg.data |
|||
} |
|||
}, |
|||
error: msg => { |
|||
log('WS ERROR', ':(') |
|||
throw new Error(msg.data) |
|||
}, |
|||
} |
|||
try { |
|||
const msg = JSON.parse(rawMsg) |
|||
if (!(msg.query in handlers)) { |
|||
throw new Error(`Cannot handle msg of type ${msg.query}`) |
|||
} |
|||
logWS('RECEIVE', msg) |
|||
await handlers[msg.query](msg) |
|||
} catch (err) { |
|||
log('ERROR', err.toString()) |
|||
reject(err) |
|||
} |
|||
}) |
|||
} catch (err) { |
|||
reject(err) |
|||
} |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* Retrieve targetId and firmware version from device |
|||
*/ |
|||
export async function getFirmwareInfo(transport: Transport<*>) { |
|||
try { |
|||
const res = await transport.send(...APDUS.GET_FIRMWARE) |
|||
const byteArray = [...res] |
|||
const data = byteArray.slice(0, byteArray.length - 2) |
|||
const targetIdStr = Buffer.from(data.slice(0, 4)) |
|||
const targetId = targetIdStr.readUIntBE(0, 4) |
|||
const versionLength = data[4] |
|||
const version = Buffer.from(data.slice(5, 5 + versionLength)).toString() |
|||
return { targetId, version } |
|||
} catch (err) { |
|||
const error = new Error(err.message) |
|||
error.stack = err.stack |
|||
throw error |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Debug helper |
|||
*/ |
|||
export function log(namespace: string, str: string = '', color?: string) { |
|||
namespace = namespace.padEnd(15) |
|||
// $FlowFixMe
|
|||
const coloredNamespace = color ? chalk[color](namespace) : namespace |
|||
if (__DEV__) { |
|||
console.log(`${chalk.bold(`> ${coloredNamespace}`)} ${str}`) // eslint-disable-line no-console
|
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Log a socket send/receive |
|||
*/ |
|||
export function logWS(type: string, msg: Message) { |
|||
const arrow = type === 'SEND' ? '↑' : '↓' |
|||
const namespace = `${arrow} WS ${type}` |
|||
const color = type === 'SEND' ? 'blue' : 'red' |
|||
if (msg.nonce) { |
|||
let d = '' |
|||
if (msg.query === 'exchange') { |
|||
d = msg.data.length > 100 ? `${msg.data.substr(0, 97)}...` : msg.data |
|||
} else if (msg.query === 'bulk') { |
|||
d = `[bulk x ${msg.data.length}]` |
|||
} |
|||
log( |
|||
namespace, |
|||
`${String(msg.nonce).padEnd(2)} ${(msg.response || msg.query || '').padEnd(10)} ${d}`, |
|||
color, |
|||
) |
|||
} else { |
|||
log(namespace, JSON.stringify(msg), color) |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Helpers to build OSU and Final firmware params |
|||
*/ |
|||
export const buildParamsFromFirmware = (type: FirmwareUpdateType): Function => ( |
|||
data: any, |
|||
): LedgerScriptParams => ({ |
|||
firmware: data[`${type}_firmware`], |
|||
firmwareKey: data[`${type}_firmware_key`], |
|||
perso: data[`${type}_perso`], |
|||
targetId: data[`${type}_target_id`], |
|||
}) |
@ -0,0 +1,13 @@ |
|||
// Socket endpoint
|
|||
|
|||
export const BASE_SOCKET_URL = 'ws://api.ledgerwallet.com' |
|||
// If you want to test locally with https://github.com/LedgerHQ/ledger-update-python-api
|
|||
// export const BASE_SOCKET_URL = 'ws://localhost:3001/update'
|
|||
|
|||
// 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], |
|||
} |
@ -0,0 +1,26 @@ |
|||
// @flow
|
|||
|
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
|
|||
import { getFirmwareInfo } from 'helpers/common' |
|||
|
|||
type Result = { |
|||
targetId: string | number, |
|||
version: string, |
|||
mcu: boolean, |
|||
final: boolean, |
|||
} |
|||
|
|||
export default async (transport: Transport<*>): Promise<Result> => { |
|||
try { |
|||
const { targetId, version } = await getFirmwareInfo(transport) |
|||
const finalReady = version.endsWith('-osu') |
|||
const mcuReady = targetId === 0x01000001 |
|||
|
|||
return { targetId, version, final: finalReady, mcu: mcuReady } |
|||
} catch (err) { |
|||
const error = Error(err.message) |
|||
error.stack = err.stack |
|||
throw error |
|||
} |
|||
} |
@ -0,0 +1,31 @@ |
|||
// @flow
|
|||
import axios from 'axios' |
|||
import isEmpty from 'lodash/isEmpty' |
|||
|
|||
const { API_BASE_URL } = process.env |
|||
|
|||
type Input = { |
|||
version: string, |
|||
targetId: string | number, |
|||
} |
|||
|
|||
let error |
|||
export default async (data: Input) => { |
|||
try { |
|||
const { data: seFirmwareVersion } = await axios.post(`${API_BASE_URL}/firmware_versions_name`, { |
|||
se_firmware_name: data.version, |
|||
target_id: data.targetId, |
|||
}) |
|||
|
|||
if (!isEmpty(seFirmwareVersion)) { |
|||
return seFirmwareVersion |
|||
} |
|||
|
|||
error = Error('could not retrieve firmware informations, try again later') |
|||
throw error |
|||
} catch (err) { |
|||
error = Error(err.message) |
|||
error.stack = err.stack |
|||
throw error |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
// @flow
|
|||
|
|||
// import type Transport from '@ledgerhq/hw-transport'
|
|||
|
|||
export default async (/* transport: Transport<*> */) => new Promise(resolve => resolve(true)) |
@ -0,0 +1,43 @@ |
|||
// @flow
|
|||
import axios from 'axios' |
|||
import isEmpty from 'lodash/isEmpty' |
|||
|
|||
import getFirmwareInfo from './getFirmwareInfo' |
|||
|
|||
const { API_BASE_URL } = process.env |
|||
|
|||
type Input = { |
|||
targetId: string | number, |
|||
version: string, |
|||
} |
|||
|
|||
export default async (data: Input) => { |
|||
try { |
|||
// Get firmware infos with firmware name and device version
|
|||
const seFirmwareVersion = await getFirmwareInfo(data) |
|||
|
|||
// Get device infos from targetId
|
|||
const { data: deviceVersion } = await axios.get( |
|||
`${API_BASE_URL}/device_versions_target_id/${data.targetId}`, |
|||
) |
|||
|
|||
// Fetch next possible firmware
|
|||
const { data: serverData } = await axios.post(`${API_BASE_URL}/get_latest_firmware`, { |
|||
current_se_firmware_version: seFirmwareVersion.id, |
|||
device_version: deviceVersion.id, |
|||
providers: [1], |
|||
}) |
|||
|
|||
const { se_firmware_version } = serverData |
|||
|
|||
if (!isEmpty(se_firmware_version)) { |
|||
return se_firmware_version |
|||
} |
|||
|
|||
return null |
|||
} catch (err) { |
|||
const error = Error(err.message) |
|||
error.stack = err.stack |
|||
throw error |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
// @flow
|
|||
|
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
|
|||
import { createSocketDialog, buildParamsFromFirmware } from 'helpers/common' |
|||
|
|||
type Input = { |
|||
devicePath: string, |
|||
firmware: Object, |
|||
} |
|||
|
|||
type Result = * |
|||
|
|||
const buildOsuParams = buildParamsFromFirmware('osu') |
|||
|
|||
export default async (transport: Transport<*>, data: Input): Result => { |
|||
try { |
|||
const osuData = buildOsuParams(data.firmware) |
|||
await createSocketDialog(transport, '/update/install', osuData) |
|||
return { success: true } |
|||
} catch (err) { |
|||
const error = Error(err.message) |
|||
error.stack = err.stack |
|||
const result = { success: false, error } |
|||
throw result |
|||
} |
|||
} |
@ -1,26 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import type Transport from '@ledgerhq/hw-transport' |
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
import { createTransportHandler, getFirmwareInfo } from './helpers' |
|||
|
|||
const handler = async (transport: Transport<*>) => |
|||
new Promise(async resolve => { |
|||
try { |
|||
const { targetId, version } = await getFirmwareInfo(transport) |
|||
const finalReady = version.endsWith('-osu') |
|||
const mcuReady = targetId === 0x01000001 |
|||
resolve({ targetId, version, final: finalReady, mcu: mcuReady }) |
|||
} catch (err) { |
|||
throw err |
|||
} |
|||
}) |
|||
|
|||
export default async (send: IPCSend, data: any) => |
|||
createTransportHandler(send, { |
|||
action: handler, |
|||
successResponse: 'device.getFirmwareInfoSuccess', |
|||
errorResponse: 'device.getFirmwareInfoError', |
|||
})(data) |
@ -1,44 +0,0 @@ |
|||
// @flow
|
|||
import axios from 'axios' |
|||
import CommNodeHid from '@ledgerhq/hw-transport-node-hid' |
|||
import isEmpty from 'lodash/isEmpty' |
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
import { getFirmwareInfo } from './helpers' |
|||
|
|||
const { API_BASE_URL } = process.env |
|||
|
|||
export default async (send: IPCSend, data: any) => { |
|||
try { |
|||
const transport = await CommNodeHid.open(data.devicePath) |
|||
const infos = await getFirmwareInfo(transport) |
|||
// Get device infos from targetId
|
|||
const { data: deviceVersion } = await axios.get( |
|||
`${API_BASE_URL}/device_versions_target_id/${infos.targetId}`, |
|||
) |
|||
|
|||
// Get firmware infos with firmware name and device version
|
|||
const { data: seFirmwareVersion } = await axios.post(`${API_BASE_URL}/firmware_versions_name`, { |
|||
device_version: deviceVersion.id, |
|||
se_firmware_name: infos.version, |
|||
}) |
|||
|
|||
// Fetch next possible firmware
|
|||
const { data: serverData } = await axios.post(`${API_BASE_URL}/get_latest_firmware`, { |
|||
current_se_firmware_version: seFirmwareVersion.id, |
|||
device_version: deviceVersion.id, |
|||
providers: [1], |
|||
}) |
|||
|
|||
const { se_firmware_version } = serverData |
|||
|
|||
if (!isEmpty(se_firmware_version)) { |
|||
send('manager.getLatestFirmwareForDeviceSuccess', se_firmware_version) |
|||
} else { |
|||
send('manager.getLatestFirmwareForDeviceError', { name: 'yolo', notes: 'fake' }) |
|||
} |
|||
} catch (error) { |
|||
send('manager.getLatestFirmwareForDeviceError', { name: 'yolo', notes: 'fake', error }) |
|||
} |
|||
} |
@ -1,12 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
import { createTransportHandler, installApp } from './helpers' |
|||
|
|||
export default (send: IPCSend, data: any) => |
|||
createTransportHandler(send, { |
|||
action: installApp, |
|||
successResponse: 'manager.appInstalled', |
|||
errorResponse: 'manager.appInstallError', |
|||
})(data) |
@ -1,24 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import CommNodeHid from '@ledgerhq/hw-transport-node-hid' |
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
import { createSocketDialog, buildParamsFromFirmware } from './helpers' |
|||
|
|||
type DataType = { |
|||
devicePath: string, |
|||
firmware: Object, |
|||
} |
|||
|
|||
const buildOsuParams = buildParamsFromFirmware('osu') |
|||
|
|||
export default async (send: IPCSend, data: DataType) => { |
|||
try { |
|||
const transport = await CommNodeHid.open(data.devicePath) |
|||
const osuData = buildOsuParams(data.firmware) |
|||
await createSocketDialog(transport, '/update/install', osuData) |
|||
send('device.osuFirmwareInstallSuccess', { success: true }) |
|||
} catch (err) { |
|||
send('device.osuFirmwareInstallError', { success: false }) |
|||
} |
|||
} |
@ -1,14 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import axios from 'axios' |
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
export default async (send: IPCSend) => { |
|||
try { |
|||
const { data } = await axios.get('https://api.ledgerwallet.com/update/applications') |
|||
send('manager.listAppsSuccess', data['nanos-1.4']) |
|||
} catch (err) { |
|||
send('manager.listAppsError', { message: err.message, stack: err.stack }) |
|||
} |
|||
} |
@ -1,12 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
import { createTransportHandler, uninstallApp } from './helpers' |
|||
|
|||
export default (send: IPCSend, data: any) => |
|||
createTransportHandler(send, { |
|||
action: uninstallApp, |
|||
successResponse: 'manager.appUninstalled', |
|||
errorResponse: 'manager.appUninstallError', |
|||
})(data) |
Loading…
Reference in new issue