Skip to content

Commit

Permalink
mtx: support HD paths
Browse files Browse the repository at this point in the history
  • Loading branch information
boymanjor committed Apr 22, 2019
1 parent 14e05d1 commit b85c2cc
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 5 deletions.
6 changes: 4 additions & 2 deletions lib/primitives/input.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,11 @@ class Input extends bio.Struct {
* for JSON serialization.
* @param {Network} network
* @param {Coin} coin
* @param {Path} path
* @returns {Object}
*/

getJSON(network, coin) {
getJSON(network, coin, path) {
network = Network.get(network);

let addr;
Expand All @@ -187,7 +188,8 @@ class Input extends bio.Struct {
witness: this.witness.toJSON(),
sequence: this.sequence,
address: addr,
coin: coin ? coin.getJSON(network, true) : undefined
coin: coin ? coin.getJSON(network, true) : undefined,
path: path ? path.getJSON(network) : undefined
};
}

Expand Down
17 changes: 15 additions & 2 deletions lib/primitives/mtx.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ const Input = require('./input');
const Output = require('./output');
const Coin = require('./coin');
const Outpoint = require('./outpoint');
const CoinView = require('../coins/coinview');
const CoinView = require('../coins/coinview.js');
const Path = require('../wallet/path');
const WalletCoinView = require('../wallet/walletcoinview.js');
const Address = require('./address');
const consensus = require('../protocol/consensus');
const policy = require('../protocol/policy');
Expand All @@ -31,7 +33,7 @@ const {types} = rules;
* @alias module:primitives.MTX
* @extends TX
* @property {Number} changeIndex
* @property {CoinView} view
* @property {WalletCoinView} view
*/

class MTX extends TX {
Expand Down Expand Up @@ -1343,6 +1345,17 @@ class MTX extends TX {
coin.index = prevout.index;

this.view.addCoin(coin);

if (!input.path)
continue;

if(!(this.view instanceof WalletCoinView))
this.view = WalletCoinView.fromCoinView(this.view);

const outpoint = Outpoint.fromJSON(prevout);
const path = Path.fromJSON(input.path);

this.view.addPath(outpoint, path);
}

return this;
Expand Down
3 changes: 2 additions & 1 deletion lib/primitives/tx.js
Original file line number Diff line number Diff line change
Expand Up @@ -1748,7 +1748,8 @@ class TX extends bio.Struct {
version: this.version,
inputs: this.inputs.map((input) => {
const coin = view ? view.getCoinFor(input) : null;
return input.getJSON(network, coin);
const path = view ? view.getPathFor(input) : null;
return input.getJSON(network, coin, path);
}),
outputs: this.outputs.map((output) => {
return output.getJSON(network);
Expand Down
13 changes: 13 additions & 0 deletions lib/wallet/rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -1434,6 +1434,7 @@ class RPC extends RPCBase {
const wallet = this.wallet;

const options = {
paths: true,
account: opts.account,
subtractFee: opts.subtract,
outputs: [{
Expand Down Expand Up @@ -1931,6 +1932,7 @@ class RPC extends RPCBase {
const opts = this._validateOpen(args, help, 'createopen');
const wallet = this.wallet;
const mtx = await wallet.createOpen(opts.name, opts.force, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -1968,6 +1970,7 @@ class RPC extends RPCBase {
const opts = this._validateBid(args, help, 'createbid');
const wallet = this.wallet;
const mtx = await wallet.createBid(opts.name, opts.bid, opts.value, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2026,6 +2029,7 @@ class RPC extends RPCBase {

if (!opts.name) {
const mtx = await wallet.createRevealAll({
paths: true,
account: opts.account
});

Expand All @@ -2036,6 +2040,7 @@ class RPC extends RPCBase {
throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');

const mtx = await wallet.createReveal(opts.name, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2078,6 +2083,7 @@ class RPC extends RPCBase {

if (!opts.name) {
const mtx = await wallet.createRedeemAll({
paths: true,
account: opts.account
});
return mtx.getJSON(this.network);
Expand All @@ -2087,6 +2093,7 @@ class RPC extends RPCBase {
throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');

const mtx = await wallet.createRedeem(opts.name, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2118,6 +2125,7 @@ class RPC extends RPCBase {
const opts = this._validateUpdate(args, help, 'createupdate');
const wallet = this.wallet;
const mtx = await wallet.createUpdate(opts.name, opts.resource, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2161,6 +2169,7 @@ class RPC extends RPCBase {
const wallet = this.wallet;
const opts = this._validateRenewal(args, help, 'createrenewal');
const mtx = await wallet.createRenewal(opts.name, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2195,6 +2204,7 @@ class RPC extends RPCBase {
const opts = this._validateTransfer(args, help, 'createtransfer');
const wallet = this.wallet;
const tx = await wallet.createTransfer(opts.name, opts.address, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2240,6 +2250,7 @@ class RPC extends RPCBase {
const opts = this._validateCancel(args, help, 'createcancel');
const wallet = this.wallet;
const mtx = await wallet.createCancel(opts.name, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2274,6 +2285,7 @@ class RPC extends RPCBase {
const opts = this._validateFinalize(args, help, 'createfinalize');
const wallet = this.wallet;
const mtx = await wallet.createFinalize(opts.name, {
paths: true,
account: opts.account
});

Expand Down Expand Up @@ -2308,6 +2320,7 @@ class RPC extends RPCBase {
const opts = this._validateRevoke(args, help, 'createrevoke');
const wallet = this.wallet;
const mtx = await wallet.createRevoke(opts.name, {
paths: true,
account: opts.account
});

Expand Down
51 changes: 51 additions & 0 deletions lib/wallet/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const common = require('./common');
const Address = require('../primitives/address');
const MTX = require('../primitives/mtx');
const Script = require('../script/script');
const WalletCoinView = require('./walletcoinview');
const WalletKey = require('./walletkey');
const HD = require('../hd/hd');
const Output = require('../primitives/output');
Expand Down Expand Up @@ -2968,6 +2969,10 @@ class Wallet extends EventEmitter {
assert(mtx.verifyInputs(this.wdb.height + 1, this.network),
'TX failed context check.');

// Set the HD paths.
if (options.paths !== false)
mtx.view = await this.getWalletCoinView(mtx);

const total = await this.template(mtx);

if (total === 0)
Expand Down Expand Up @@ -3430,6 +3435,52 @@ class Wallet extends EventEmitter {
return this.txdb.getCoinView(tx);
}

/**
* Get a wallet coin viewpoint with HD paths.
* @param {TX} tx
* @returns {Promise} - Returns {@link WalletCoinView}.
*/

async getWalletCoinView(tx) {
let view = tx.view;

if (!tx.hasCoins(view))
view = await this.txdb.getCoinView(tx);

view = WalletCoinView.fromCoinView(view);

for (const input of tx.inputs) {
const prevout = input.prevout;
const coin = view.getCoin(prevout);
const path = await this.getPath(coin.address);

if (!path)
continue;

const account = await this.getAccount(path.account);

if (!account)
continue;

// The account index in the db may be wrong.
// We must read it from the stored xpub to be
// sure of its correctness.
//
// For more details see:
// https://github.com/bcoin-org/bcoin/issues/698.
path.account = account.accountKey.childIndex;

// Unharden the account index, if necessary.
if (path.account & HD.common.HARDENED)
path.account ^= HD.common.HARDENED;

// Add path to the viewpoint.
view.addPath(prevout, path);
}

return view;
}

/**
* Get a historical coin viewpoint.
* @param {TX} tx
Expand Down

0 comments on commit b85c2cc

Please sign in to comment.