Gaëtan Renaudeau
7 years ago
10 changed files with 171 additions and 230 deletions
@ -0,0 +1,35 @@ |
|||
// @flow
|
|||
|
|||
import type { AccountRaw } from '@ledgerhq/live-common/lib/types' |
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { Observable } from 'rxjs' |
|||
import { scanAccountsOnDevice } from 'helpers/libcore' |
|||
|
|||
type Input = { |
|||
devicePath: string, |
|||
currencyId: string, |
|||
} |
|||
|
|||
type Result = AccountRaw |
|||
|
|||
const cmd: Command<Input, Result> = createCommand( |
|||
'devices', |
|||
'libcoreScanAccounts', |
|||
({ devicePath, currencyId }) => |
|||
Observable.create(o => { |
|||
// TODO scanAccountsOnDevice should directly return a Observable so we just have to pass-in
|
|||
scanAccountsOnDevice({ |
|||
devicePath, |
|||
currencyId, |
|||
onAccountScanned: account => o.next(account), |
|||
}).then(() => o.complete(), e => o.error(e)) |
|||
|
|||
function unsubscribe() { |
|||
// FIXME not implemented
|
|||
} |
|||
|
|||
return unsubscribe |
|||
}), |
|||
) |
|||
|
|||
export default cmd |
@ -0,0 +1,82 @@ |
|||
// @flow
|
|||
|
|||
import type { AccountRaw, OperationRaw } from '@ledgerhq/live-common/lib/types' |
|||
import Btc from '@ledgerhq/hw-app-btc' |
|||
import { createCommand, Command } from 'helpers/ipc' |
|||
import { withDevice } from 'helpers/deviceAccess' |
|||
import { getWalletIdentifier } from 'helpers/libcore' |
|||
import { fromPromise } from 'rxjs/observable/fromPromise' |
|||
|
|||
type BitcoinLikeTransaction = { |
|||
amount: number, |
|||
feePerByte: number, |
|||
recipient: string, |
|||
} |
|||
|
|||
type Input = { |
|||
account: AccountRaw, |
|||
transaction: BitcoinLikeTransaction, |
|||
deviceId: string, |
|||
} |
|||
|
|||
type Result = $Exact<OperationRaw> |
|||
|
|||
const cmd: Command<Input, Result> = createCommand( |
|||
'devices', |
|||
'libcoreSignAndBroadcast', |
|||
({ account, transaction, deviceId }) => { |
|||
// TODO: investigate why importing it on file scope causes trouble
|
|||
const core = require('init-ledger-core')() |
|||
|
|||
return fromPromise( |
|||
withDevice(deviceId)(async transport => { |
|||
const hwApp = new Btc(transport) |
|||
|
|||
const WALLET_IDENTIFIER = await getWalletIdentifier({ |
|||
hwApp, |
|||
isSegwit: !!account.isSegwit, |
|||
currencyId: account.currencyId, |
|||
devicePath: deviceId, |
|||
}) |
|||
|
|||
const njsWallet = await core.getWallet(WALLET_IDENTIFIER) |
|||
const njsAccount = await njsWallet.getAccount(account.index) |
|||
const bitcoinLikeAccount = njsAccount.asBitcoinLikeAccount() |
|||
const njsWalletCurrency = njsWallet.getCurrency() |
|||
const amount = core.createAmount(njsWalletCurrency, transaction.amount) |
|||
const fees = core.createAmount(njsWalletCurrency, transaction.feePerByte) |
|||
const transactionBuilder = bitcoinLikeAccount.buildTransaction() |
|||
|
|||
// TODO: check if is valid address. if not, it will fail silently on invalid
|
|||
|
|||
transactionBuilder.sendToAddress(amount, transaction.recipient) |
|||
// TODO: don't use hardcoded value for sequence (and first also maybe)
|
|||
transactionBuilder.pickInputs(0, 0xffffff) |
|||
transactionBuilder.setFeesPerByte(fees) |
|||
|
|||
const builded = await transactionBuilder.build() |
|||
const signedTransaction = await core.signTransaction(hwApp, builded) |
|||
|
|||
const txHash = await njsAccount |
|||
.asBitcoinLikeAccount() |
|||
.broadcastRawTransaction(signedTransaction) |
|||
|
|||
// optimistic operation
|
|||
return { |
|||
id: txHash, |
|||
hash: txHash, |
|||
type: 'OUT', |
|||
value: amount, |
|||
blockHash: null, |
|||
blockHeight: null, |
|||
senders: [account.freshAddress], |
|||
recipients: [transaction.recipient], |
|||
accountId: account.id, |
|||
date: new Date().toISOString(), |
|||
} |
|||
}), |
|||
) |
|||
}, |
|||
) |
|||
|
|||
export default cmd |
@ -1,48 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
import scanAccountsOnDevice from './scanAccountsOnDevice' |
|||
import signAndBroadcastTransactionBTCLike from './signAndBroadcastTransaction/btc' |
|||
|
|||
import sync from './sync' |
|||
|
|||
export default { |
|||
sync, |
|||
signAndBroadcastTransactionBTCLike, |
|||
scan: async ( |
|||
send: IPCSend, |
|||
{ |
|||
devicePath, |
|||
currencyId, |
|||
}: { |
|||
devicePath: string, |
|||
currencyId: string, |
|||
}, |
|||
) => { |
|||
try { |
|||
send('accounts.scanAccountsOnDevice.start', { pid: process.pid }, { kill: false }) |
|||
const accounts = await scanAccountsOnDevice({ |
|||
devicePath, |
|||
currencyId, |
|||
onAccountScanned: account => { |
|||
send('accounts.scanAccountsOnDevice.accountScanned', account, { kill: false }) |
|||
}, |
|||
}) |
|||
send('accounts.scanAccountsOnDevice.success', accounts) |
|||
} catch (err) { |
|||
send('accounts.scanAccountsOnDevice.fail', formatErr(err)) |
|||
} |
|||
}, |
|||
} |
|||
|
|||
// TODO: move this to a helper
|
|||
function formatErr(err) { |
|||
if (err instanceof Error) { |
|||
return err.message || err.code |
|||
} |
|||
if (typeof err === 'string') { |
|||
return err |
|||
} |
|||
return 'unknown error' |
|||
} |
@ -1,71 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import Btc from '@ledgerhq/hw-app-btc' |
|||
import { withDevice } from 'helpers/deviceAccess' |
|||
import type { AccountRaw } from '@ledgerhq/live-common/lib/types' |
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
import { getWalletIdentifier } from '../scanAccountsOnDevice' |
|||
|
|||
type BitcoinLikeTransaction = { |
|||
amount: number, |
|||
feePerByte: number, |
|||
recipient: string, |
|||
} |
|||
|
|||
export default async function signAndBroadcastTransactionBTCLike( |
|||
send: IPCSend, |
|||
{ |
|||
account, |
|||
transaction, |
|||
deviceId, // which is in fact `devicePath`
|
|||
}: { |
|||
account: AccountRaw, |
|||
transaction: BitcoinLikeTransaction, |
|||
deviceId: string, |
|||
}, |
|||
) { |
|||
try { |
|||
// TODO: investigate why importing it on file scope causes trouble
|
|||
const core = require('init-ledger-core')() |
|||
|
|||
const txHash = await withDevice(deviceId)(async transport => { |
|||
const hwApp = new Btc(transport) |
|||
|
|||
const WALLET_IDENTIFIER = await getWalletIdentifier({ |
|||
hwApp, |
|||
isSegwit: !!account.isSegwit, |
|||
currencyId: account.currencyId, |
|||
devicePath: deviceId, |
|||
}) |
|||
|
|||
const njsWallet = await core.getWallet(WALLET_IDENTIFIER) |
|||
const njsAccount = await njsWallet.getAccount(account.index) |
|||
const bitcoinLikeAccount = njsAccount.asBitcoinLikeAccount() |
|||
const njsWalletCurrency = njsWallet.getCurrency() |
|||
const amount = core.createAmount(njsWalletCurrency, transaction.amount) |
|||
const fees = core.createAmount(njsWalletCurrency, transaction.feePerByte) |
|||
const transactionBuilder = bitcoinLikeAccount.buildTransaction() |
|||
|
|||
// TODO: check if is valid address. if not, it will fail silently on invalid
|
|||
|
|||
transactionBuilder.sendToAddress(amount, transaction.recipient) |
|||
// TODO: don't use hardcoded value for sequence (and first also maybe)
|
|||
transactionBuilder.pickInputs(0, 0xffffff) |
|||
transactionBuilder.setFeesPerByte(fees) |
|||
|
|||
const builded = await transactionBuilder.build() |
|||
const signedTransaction = await core.signTransaction(hwApp, builded) |
|||
|
|||
const txHash = await njsAccount |
|||
.asBitcoinLikeAccount() |
|||
.broadcastRawTransaction(signedTransaction) |
|||
|
|||
return txHash |
|||
}) |
|||
|
|||
send('accounts.signAndBroadcastTransactionBTCLike.success', txHash) |
|||
} catch (err) { |
|||
send('accounts.signAndBroadcastTransactionBTCLike.fail', err) |
|||
} |
|||
} |
@ -1,7 +0,0 @@ |
|||
// @flow
|
|||
|
|||
import type { IPCSend } from 'types/electron' |
|||
|
|||
export default (send: IPCSend) => { |
|||
setTimeout(() => send('accounts.sync.success'), 5e3) |
|||
} |
Loading…
Reference in new issue