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
// 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.r = toBuffer(0);
t.s = toBuffer(0);
return new Promise((resolve, reject) => {
this.ethApp
.signTransaction_async(this.getPath(), t.serialize().toString('hex'))
.then(result => {
const strTx = getTransactionFields(t);
const txToSerialize: TxObj = {
...strTx,
v: addHexPrefix(result.v),
r: addHexPrefix(result.r),
s: addHexPrefix(result.s)
};
try {
const result = await this.ethApp.signTransaction_async(
this.getPath(),
t.serialize().toString('hex')
);
const serializedTx = new EthTx(txToSerialize).serialize();
resolve(serializedTx);
})
.catch(err => {
return reject(Error(err + '. Check to make sure contract data is on'));
});
});
const txToSerialize: TxObj = {
...txFields,
v: addHexPrefix(result.v),
r: addHexPrefix(result.r),
s: addHexPrefix(result.s)
};
return new EthTx(txToSerialize).serialize();
} catch (err) {
throw Error(err + '. Check to make sure contract data is on');
}
}
// modeled after

View File

@ -1,3 +1,4 @@
import EthTx from 'ethereumjs-tx';
import { makeRequest } from './requests';
import {
EnclaveMethods,
@ -18,7 +19,7 @@ const api = {
return makeRequest<GetChainCodeResponse>(EnclaveMethods.GET_CHAIN_CODE, params);
},
signTransaction(params: SignTransactionParams) {
async signTransaction(params: SignTransactionParams) {
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';
export default function(params: SignTransactionParams): SignTransactionResponse {
console.log('Sign transaction called with', params);
return {
s: 'test',
v: 'test',
r: 'test'
};
const wallet = getWalletLib(params.walletType);
return wallet.signTransaction(params.transaction, params.path);
}

View File

@ -31,23 +31,6 @@ export function registerServer(app: App) {
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 {

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 LedgerEth from '@ledgerhq/hw-app-eth';
async function getEthApp() {
const transport = await TransportNodeHid.create();
return new LedgerEth(transport);
try {
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 = {
@ -17,9 +29,29 @@ const Ledger: WalletLib = {
chainCode: res.chainCode
};
} catch (err) {
console.log('wtf', err);
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' };
},
async signTransaction() {
throw new Error('Not yet implemented');
}
};

View File

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