Transaction signing
This commit is contained in:
parent
e9101516e7
commit
b9bd734cd0
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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'
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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')
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue