Skip to content

Commit bcc514d

Browse files
author
Justin Kook
committed
ETH BWS/BWC changes rebased on eth-module-squashed
1 parent 9f3573f commit bcc514d

File tree

22 files changed

+638
-235
lines changed

22 files changed

+638
-235
lines changed

packages/bitcore-wallet-client/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"bitcore-lib": "^8.6.0",
3333
"bitcore-lib-cash": "^8.6.0",
3434
"bitcore-mnemonic": "^8.6.0",
35+
"crypto-wallet-core": "^8.6.0",
3536
"json-stable-stringify": "^1.0.1",
3637
"lodash": "^4.17.15",
3738
"preconditions": "^2.2.3",

packages/bitcore-wallet-client/src/lib/api.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { EventEmitter } from 'events';
44
import _ from 'lodash';
5+
import * as CWC from 'crypto-wallet-core';
56
import sjcl from 'sjcl';
67
import { Constants, Utils } from './common';
78
import { Credentials } from './credentials';
@@ -17,7 +18,8 @@ var events = require('events');
1718
var Bitcore = require('bitcore-lib');
1819
var Bitcore_ = {
1920
btc: Bitcore,
20-
bch: require('bitcore-lib-cash')
21+
bch: require('bitcore-lib-cash'),
22+
eth: Bitcore
2123
};
2224
var Mnemonic = require('bitcore-mnemonic');
2325
var url = require('url');
@@ -53,6 +55,7 @@ export class API extends EventEmitter {
5355
static PayPro = PayPro;
5456
static Key = Key;
5557
static Verifier = Verifier;
58+
static Core = CWC;
5659
static Utils = Utils;
5760
static sjcl = sjcl;
5861
static errors = Errors;
@@ -2056,6 +2059,19 @@ export class API extends EventEmitter {
20562059
});
20572060
}
20582061

2062+
// /**
2063+
// * Returns gas limit estimate.
2064+
// * @param {Object} opts - tx Object
2065+
// * @return {Callback} cb - Return error (if exists) and gas limit
2066+
// */
2067+
getEstimateGas(opts, cb) {
2068+
var url = '/v3/estimateGas/';
2069+
this.request.post(url, opts, (err, gasLimit) => {
2070+
if (err) return cb(err);
2071+
return cb(null, gasLimit);
2072+
});
2073+
}
2074+
20592075
// /**
20602076
// * Get wallet status based on a string identifier (one of: walletId, address, txid)
20612077
// *
@@ -2336,6 +2352,8 @@ export class API extends EventEmitter {
23362352
// coin, network, multisig
23372353
['btc', 'livenet'],
23382354
['bch', 'livenet'],
2355+
['eth', 'livenet'],
2356+
['eth', 'testnet'],
23392357
['btc', 'livenet', true],
23402358
['bch', 'livenet', true]
23412359
];

packages/bitcore-wallet-client/src/lib/common/constants.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const Constants = {
1212
BIP48: 'BIP48',
1313
},
1414
PATHS: {
15+
SINGLE_ADDRESS: "m/0/0",
1516
REQUEST_KEY: "m/1'/0",
1617
// TXPROPOSAL_KEY: "m/1'/1",
1718
REQUEST_KEY_AUTH: 'm/2', // relative to BASE
@@ -29,6 +30,28 @@ export const Constants = {
2930
minDecimals: 2,
3031
}
3132
},
33+
bch: {
34+
toSatoshis: 100000000,
35+
full: {
36+
maxDecimals: 8,
37+
minDecimals: 8,
38+
},
39+
short: {
40+
maxDecimals: 6,
41+
minDecimals: 2,
42+
}
43+
},
44+
eth: {
45+
toSatoshis: 1e18,
46+
full: {
47+
maxDecimals: 8,
48+
minDecimals: 8,
49+
},
50+
short: {
51+
maxDecimals: 6,
52+
minDecimals: 2,
53+
}
54+
},
3255
bit: {
3356
toSatoshis: 100,
3457
full: {
@@ -41,5 +64,6 @@ export const Constants = {
4164
}
4265
},
4366
},
44-
COINS: ['btc', 'bch']
67+
COINS: ['btc', 'bch', 'eth'],
68+
UTXO_COINS: ['btc', 'bch']
4569
};

packages/bitcore-wallet-client/src/lib/common/utils.ts

Lines changed: 87 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import * as _ from 'lodash';
44
import { Constants } from './constants';
5+
import { Deriver, Transactions } from 'crypto-wallet-core';
56
import { Defaults } from './defaults';
67

78
var $ = require('preconditions').singleton();
@@ -11,7 +12,8 @@ var Stringify = require('json-stable-stringify');
1112
var Bitcore = require('bitcore-lib');
1213
var Bitcore_ = {
1314
btc: Bitcore,
14-
bch: require('bitcore-lib-cash')
15+
bch: require('bitcore-lib-cash'),
16+
eth: Bitcore
1517
};
1618
var PrivateKey = Bitcore.PrivateKey;
1719
var PublicKey = Bitcore.PublicKey;
@@ -135,6 +137,13 @@ export class Utils {
135137
return [toAddress, amount, message || '', payProUrl || ''].join('|');
136138
}
137139

140+
static parseDerivationPath = function(path: string) {
141+
const pathIndex = /m\/([0-9]*)\/([0-9]*)/;
142+
const [_input, changeIndex, addressIndex] = path.match(pathIndex);
143+
const isChange = Number.parseInt(changeIndex) > 0;
144+
return { _input, addressIndex, isChange };
145+
}
146+
138147
static deriveAddress(scriptType, publicKeyRing, path, m, network, coin) {
139148
$.checkArgument(_.includes(_.values(Constants.SCRIPT_TYPES), scriptType));
140149

@@ -152,7 +161,19 @@ export class Utils {
152161
break;
153162
case Constants.SCRIPT_TYPES.P2PKH:
154163
$.checkState(_.isArray(publicKeys) && publicKeys.length == 1);
155-
bitcoreAddress = bitcore.Address.fromPublicKey(publicKeys[0], network);
164+
if (Constants.UTXO_COINS.includes(coin)) {
165+
bitcoreAddress = bitcore.Address.fromPublicKey(publicKeys[0], network);
166+
} else {
167+
const { addressIndex, isChange } = this.parseDerivationPath(path);
168+
const [{ xPubKey }] = publicKeyRing;
169+
bitcoreAddress = Deriver.deriveAddress(
170+
coin.toUpperCase(),
171+
network,
172+
xPubKey,
173+
addressIndex,
174+
isChange
175+
);
176+
}
156177
break;
157178
}
158179

@@ -231,79 +252,81 @@ export class Utils {
231252
static buildTx(txp) {
232253
var coin = txp.coin || 'btc';
233254

234-
var bitcore = Bitcore_[coin];
255+
if (Constants.UTXO_COINS.includes(coin)) {
235256

236-
var t = new bitcore.Transaction();
257+
var bitcore = Bitcore_[coin];
237258

238-
$.checkState(_.includes(_.values(Constants.SCRIPT_TYPES), txp.addressType));
259+
var t = new bitcore.Transaction();
239260

240-
switch (txp.addressType) {
241-
case Constants.SCRIPT_TYPES.P2SH:
242-
_.each(txp.inputs, i => {
243-
t.from(i, i.publicKeys, txp.requiredSignatures);
244-
});
245-
break;
246-
case Constants.SCRIPT_TYPES.P2PKH:
247-
t.from(txp.inputs);
248-
break;
249-
}
261+
$.checkState(_.includes(_.values(Constants.SCRIPT_TYPES), txp.addressType));
250262

251-
if (txp.toAddress && txp.amount && !txp.outputs) {
252-
t.to(txp.toAddress, txp.amount);
253-
} else if (txp.outputs) {
254-
_.each(txp.outputs, o => {
255-
$.checkState(
256-
o.script || o.toAddress,
257-
'Output should have either toAddress or script specified'
258-
);
259-
if (o.script) {
260-
t.addOutput(
261-
new bitcore.Transaction.Output({
263+
switch (txp.addressType) {
264+
case Constants.SCRIPT_TYPES.P2SH:
265+
_.each(txp.inputs, (i) => {
266+
t.from(i, i.publicKeys, txp.requiredSignatures);
267+
});
268+
break;
269+
case Constants.SCRIPT_TYPES.P2PKH:
270+
t.from(txp.inputs);
271+
break;
272+
}
273+
274+
if (txp.toAddress && txp.amount && !txp.outputs) {
275+
t.to(txp.toAddress, txp.amount);
276+
} else if (txp.outputs) {
277+
_.each(txp.outputs, (o) => {
278+
$.checkState(o.script || o.toAddress, 'Output should have either toAddress or script specified');
279+
if (o.script) {
280+
t.addOutput(new bitcore.Transaction.Output({
262281
script: o.script,
263282
satoshis: o.amount
264-
})
265-
);
266-
} else {
267-
t.to(o.toAddress, o.amount);
268-
}
269-
});
270-
}
283+
}));
284+
} else {
285+
t.to(o.toAddress, o.amount);
286+
}
287+
});
288+
}
271289

272-
t.fee(txp.fee);
273-
t.change(txp.changeAddress.address);
290+
t.fee(txp.fee);
291+
t.change(txp.changeAddress.address);
274292

275-
// Shuffle outputs for improved privacy
276-
if (t.outputs.length > 1) {
277-
var outputOrder = _.reject(txp.outputOrder, order => {
278-
return order >= t.outputs.length;
279-
});
280-
$.checkState(t.outputs.length == outputOrder.length);
281-
t.sortOutputs(outputs => {
282-
return _.map(outputOrder, i => {
283-
return outputs[i];
293+
// Shuffle outputs for improved privacy
294+
if (t.outputs.length > 1) {
295+
var outputOrder = _.reject(txp.outputOrder, (order) => {
296+
return order >= t.outputs.length;
284297
});
285-
});
286-
}
298+
$.checkState(t.outputs.length == outputOrder.length);
299+
t.sortOutputs((outputs) => {
300+
return _.map(outputOrder, (i) => {
301+
return outputs[i];
302+
});
303+
});
304+
}
287305

288-
// Validate inputs vs outputs independently of Bitcore
289-
var totalInputs = _.reduce(
290-
txp.inputs,
291-
(memo, i) => {
306+
// Validate inputs vs outputs independently of Bitcore
307+
var totalInputs = _.reduce(txp.inputs, (memo, i) => {
292308
return +i.satoshis + memo;
293-
},
294-
0
295-
);
296-
var totalOutputs = _.reduce(
297-
t.outputs,
298-
(memo, o) => {
309+
}, 0);
310+
var totalOutputs = _.reduce(t.outputs, (memo, o) => {
299311
return +o.satoshis + memo;
300-
},
301-
0
302-
);
303-
304-
$.checkState(totalInputs - totalOutputs >= 0);
305-
$.checkState(totalInputs - totalOutputs <= Defaults.MAX_TX_FEE);
306-
307-
return t;
312+
}, 0);
313+
314+
$.checkState(totalInputs - totalOutputs >= 0);
315+
$.checkState(totalInputs - totalOutputs <= Defaults.MAX_TX_FEE);
316+
317+
return t;
318+
} else {
319+
const { outputs, amount, from, nonce, gasPrice, data, gasLimit } = txp;
320+
const rawTx = Transactions.create({
321+
chain: coin.toUpperCase(),
322+
recipients: [{ address: outputs[0].toAddress, amount }],
323+
from,
324+
nonce,
325+
fee: gasPrice,
326+
data,
327+
gasLimit
328+
});
329+
return { uncheckedSerialize: () => rawTx };
330+
}
308331
}
309332
}

packages/bitcore-wallet-client/src/lib/credentials.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ export class Credentials {
148148
}
149149

150150
var coin = '0';
151-
if (this.network != 'livenet') {
151+
if (this.network != 'livenet' && this.coin !== 'eth') {
152152
coin = '1';
153153
} else if (this.coin == 'bch') {
154154
if (this.use145forBCH) {
@@ -158,6 +158,8 @@ export class Credentials {
158158
}
159159
} else if (this.coin == 'btc') {
160160
coin = '0';
161+
} else if (this.coin == 'eth') {
162+
coin = '60';
161163
} else {
162164
throw new Error('unknown coin: ' + this.coin);
163165
}

packages/bitcore-wallet-client/src/lib/key.ts

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import * as _ from 'lodash';
55
import { Constants, Utils } from './common';
66
import { Credentials } from './credentials';
77

8+
import { Transactions } from 'crypto-wallet-core';
9+
810
var Bitcore = require('bitcore-lib');
911
var Mnemonic = require('bitcore-mnemonic');
1012
var sjcl = require('sjcl');
@@ -278,7 +280,7 @@ export class Key {
278280
let purpose = opts.n == 1 || this.use44forMultisig ? '44' : '48';
279281
var coinCode = '0';
280282

281-
if (opts.network == 'testnet') {
283+
if (opts.network == 'testnet' && opts.coin !== 'eth') {
282284
coinCode = '1';
283285
} else if (opts.coin == 'bch') {
284286
if (this.use0forBCH) {
@@ -383,28 +385,38 @@ export class Key {
383385
var derived = this.derive(password, rootPath);
384386
var xpriv = new Bitcore.HDPrivateKey(derived);
385387

386-
_.each(txp.inputs, function (i) {
387-
$.checkState(
388-
i.path,
389-
'Input derivation path not available (signing transaction)'
390-
);
391-
if (!derived[i.path]) {
392-
derived[i.path] = xpriv.deriveChild(i.path).privateKey;
393-
privs.push(derived[i.path]);
394-
}
395-
});
396-
397388
var t = Utils.buildTx(txp);
398-
var signatures = _.map(privs, function (priv, i) {
399-
return t.getSignatures(priv);
400-
});
401389

402-
signatures = _.map(_.sortBy(_.flatten(signatures), 'inputIndex'), function (
403-
s
404-
) {
405-
return s.signature.toDER().toString('hex');
406-
});
390+
if (Constants.UTXO_COINS.includes(txp.coin)) {
391+
_.each(txp.inputs, function (i) {
392+
$.checkState(i.path, 'Input derivation path not available (signing transaction)');
393+
if (!derived[i.path]) {
394+
derived[i.path] = xpriv.deriveChild(i.path).privateKey;
395+
privs.push(derived[i.path]);
396+
}
397+
});
398+
399+
var signatures = _.map(privs, function (priv, i) {
400+
return t.getSignatures(priv);
401+
});
407402

408-
return signatures;
403+
signatures = _.map(_.sortBy(_.flatten(signatures), 'inputIndex'), function (s) {
404+
return s.signature.toDER().toString('hex');
405+
});
406+
407+
return signatures;
408+
} else {
409+
const addressPath = Constants.PATHS.SINGLE_ADDRESS;
410+
const privKey = xpriv.deriveChild(addressPath).privateKey;
411+
const tx = t.uncheckedSerialize();
412+
const signedRawTx = Transactions.sign({
413+
chain: txp.coin.toUpperCase(),
414+
tx,
415+
key: { privKey: privKey.toString('hex') },
416+
from: txp.from
417+
});
418+
419+
return Object.assign(txp, { rawTx: signedRawTx, status: 'accepted' });
420+
}
409421
};
410422
}

0 commit comments

Comments
 (0)