Transaction signing

This commit is contained in:
Will O'Beirne 2018-05-04 00:00:34 -04:00
parent e9101516e7
commit b9bd734cd0
No known key found for this signature in database
GPG Key ID: 44C190DB5DEAF9F6
7 changed files with 87 additions and 51 deletions

View File

@ -38,30 +38,39 @@ export class LedgerWallet extends HardwareWallet implements IFullWallet {
// modeled after // modeled after
// https://github.com/kvhnuke/etherwallet/blob/3f7ff809e5d02d7ea47db559adaca1c930025e24/app/scripts/uiFuncs.js#L58 // https://github.com/kvhnuke/etherwallet/blob/3f7ff809e5d02d7ea47db559adaca1c930025e24/app/scripts/uiFuncs.js#L58
public signRawTransaction(t: EthTx): Promise<Buffer> { public async signRawTransaction(t: EthTx): Promise<Buffer> {
const txFields = getTransactionFields(t);
if (process.env.BUILD_ELECTRON) {
const res = await EnclaveAPI.signTransaction({
walletType: WalletTypes.LEDGER,
transaction: txFields,
path: this.getPath()
});
return new EthTx(res.signedTransaction).serialize();
}
t.v = Buffer.from([t._chainId]); t.v = Buffer.from([t._chainId]);
t.r = toBuffer(0); t.r = toBuffer(0);
t.s = toBuffer(0); t.s = toBuffer(0);
return new Promise((resolve, reject) => { try {
this.ethApp const result = await this.ethApp.signTransaction_async(
.signTransaction_async(this.getPath(), t.serialize().toString('hex')) this.getPath(),
.then(result => { t.serialize().toString('hex')
const strTx = getTransactionFields(t); );
const txToSerialize: TxObj = {
...strTx,
v: addHexPrefix(result.v),
r: addHexPrefix(result.r),
s: addHexPrefix(result.s)
};
const serializedTx = new EthTx(txToSerialize).serialize(); const txToSerialize: TxObj = {
resolve(serializedTx); ...txFields,
}) v: addHexPrefix(result.v),
.catch(err => { r: addHexPrefix(result.r),
return reject(Error(err + '. Check to make sure contract data is on')); s: addHexPrefix(result.s)
}); };
});
return new EthTx(txToSerialize).serialize();
} catch (err) {
throw Error(err + '. Check to make sure contract data is on');
}
} }
// modeled after // modeled after

View File

@ -1,3 +1,4 @@
import EthTx from 'ethereumjs-tx';
import { makeRequest } from './requests'; import { makeRequest } from './requests';
import { import {
EnclaveMethods, EnclaveMethods,
@ -18,7 +19,7 @@ const api = {
return makeRequest<GetChainCodeResponse>(EnclaveMethods.GET_CHAIN_CODE, params); return makeRequest<GetChainCodeResponse>(EnclaveMethods.GET_CHAIN_CODE, params);
}, },
signTransaction(params: SignTransactionParams) { async signTransaction(params: SignTransactionParams) {
return makeRequest<SignTransactionResponse>(EnclaveMethods.SIGN_TRANSACTION, params); return makeRequest<SignTransactionResponse>(EnclaveMethods.SIGN_TRANSACTION, params);
} }
}; };

View File

@ -1,10 +1,7 @@
import { getWalletLib } from 'shared/enclave/server/wallets';
import { SignTransactionParams, SignTransactionResponse } from 'shared/enclave/types'; import { SignTransactionParams, SignTransactionResponse } from 'shared/enclave/types';
export default function(params: SignTransactionParams): SignTransactionResponse { export default function(params: SignTransactionParams): SignTransactionResponse {
console.log('Sign transaction called with', params); const wallet = getWalletLib(params.walletType);
return { return wallet.signTransaction(params.transaction, params.path);
s: 'test',
v: 'test',
r: 'test'
};
} }

View File

@ -31,23 +31,6 @@ export function registerServer(app: App) {
cb(JSON.stringify(res)); cb(JSON.stringify(res));
}); });
}); });
// Fix trezord requests for every new browser window
app.on('web-contents-created', (_, webContents) => {
const { session } = webContents;
if (!session.webRequest) {
return;
}
session.webRequest.onBeforeSendHeaders((details: any, callback: any) => {
const url = details.url;
if (url.startsWith('https://localback.net:21324')) {
if (details.requestHeaders.Origin === 'null') {
delete details.requestHeaders.Origin;
}
}
callback({ cancel: false, requestHeaders: details.requestHeaders });
});
});
} }
function getMethod(req: Electron.RegisterStringProtocolRequest): EnclaveMethods { function getMethod(req: Electron.RegisterStringProtocolRequest): EnclaveMethods {

View File

@ -1,10 +1,22 @@
import { WalletLib } from 'shared/enclave/types'; import EthTx from 'ethereumjs-tx';
import { addHexPrefix, toBuffer } from 'ethereumjs-util';
import { WalletLib, RawTransaction } from 'shared/enclave/types';
import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'; import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
import LedgerEth from '@ledgerhq/hw-app-eth'; import LedgerEth from '@ledgerhq/hw-app-eth';
async function getEthApp() { async function getEthApp() {
const transport = await TransportNodeHid.create(); try {
return new LedgerEth(transport); const transport = await TransportNodeHid.create();
return new LedgerEth(transport);
} catch (err) {
console.log(err.message);
if (err && err.message && err.message.includes('cannot open device with path')) {
throw new Error(
'Failed to connect with your Ledger. It may be in use with another application. Try plugging the device back in.'
);
}
throw err;
}
} }
const Ledger: WalletLib = { const Ledger: WalletLib = {
@ -17,9 +29,29 @@ const Ledger: WalletLib = {
chainCode: res.chainCode chainCode: res.chainCode
}; };
} catch (err) { } catch (err) {
console.log('wtf', err);
throw new Error('Failed to connect to Ledger'); throw new Error('Failed to connect to Ledger');
} }
},
async signTransaction(tx: RawTransaction, path: string) {
const app = await getEthApp();
const ethTx = new EthTx({
...tx,
v: Buffer.from([tx.chainId]),
r: toBuffer(0),
s: toBuffer(0)
});
const rsv = await app.signTransaction(path, ethTx.serialize().toString('hex'));
const signedTx = new EthTx({
...tx,
r: addHexPrefix(rsv.r),
s: addHexPrefix(rsv.s),
v: addHexPrefix(rsv.v)
});
return {
signedTransaction: signedTx.serialize().toString('hex')
};
} }
}; };

View File

@ -14,6 +14,10 @@ const Trezor: WalletLib = {
// }); // });
// //
// return { chainCode: 'test', publicKey: 'test' }; // return { chainCode: 'test', publicKey: 'test' };
},
async signTransaction() {
throw new Error('Not yet implemented');
} }
}; };

View File

@ -11,6 +11,16 @@ export enum WalletTypes {
KEEPKEY = 'keepkey' KEEPKEY = 'keepkey'
} }
export interface RawTransaction {
chainId: number;
gasLimit: string;
gasPrice: string;
to: string;
nonce: string;
data: string;
value: string;
}
// Get Addresses Request // Get Addresses Request
export interface GetAddressesParams { export interface GetAddressesParams {
walletType: WalletTypes; walletType: WalletTypes;
@ -33,14 +43,13 @@ export interface GetChainCodeResponse {
// Sign Transaction Request // Sign Transaction Request
export interface SignTransactionParams { export interface SignTransactionParams {
walletType: WalletTypes;
transaction: RawTransaction;
path: string; path: string;
rawTxHex: string;
} }
export interface SignTransactionResponse { export interface SignTransactionResponse {
s: string; signedTransaction: string;
v: string;
r: string;
} }
// All Requests & Responses // All Requests & Responses
@ -72,4 +81,5 @@ export type EnclaveResponse<T = EnclaveMethodResponse> =
// Wallet lib // Wallet lib
export interface WalletLib { export interface WalletLib {
getChainCode(dpath: string): Promise<GetChainCodeResponse>; getChainCode(dpath: string): Promise<GetChainCodeResponse>;
signTransaction(transaction: RawTransaction, path: string): Promise<SignTransactionResponse>;
} }