From ee4e918024ea3fcd44f9191cb03ddd047ab48768 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 8 Jan 2017 21:45:56 -0800 Subject: [PATCH] mtx/tests: more refactoring. --- lib/primitives/mtx.js | 110 +++++++++++++++++++++---------------- lib/primitives/outpoint.js | 11 +++- lib/primitives/tx.js | 6 +- lib/script/witness.js | 5 +- test/wallet-test.js | 36 +++++------- 5 files changed, 90 insertions(+), 78 deletions(-) diff --git a/lib/primitives/mtx.js b/lib/primitives/mtx.js index 132ce472..c2bc3d36 100644 --- a/lib/primitives/mtx.js +++ b/lib/primitives/mtx.js @@ -71,12 +71,12 @@ MTX.prototype.fromOptions = function fromOptions(options) { var i; if (options.version != null) { - assert(util.isNumber(options.version)); + assert(util.isUInt32(options.version)); this.version = options.version; } if (options.flag != null) { - assert(util.isNumber(options.flag)); + assert(util.isUInt8(options.flag)); this.flag = options.flag; } @@ -93,7 +93,7 @@ MTX.prototype.fromOptions = function fromOptions(options) { } if (options.locktime != null) { - assert(util.isNumber(options.locktime)); + assert(util.isUInt32(options.locktime)); this.locktime = options.locktime; } @@ -126,34 +126,45 @@ MTX.prototype.clone = function clone() { /** * Add an input to the transaction. - * @example - * tx.addInput({ prevout: { hash: ... }, script: ... }); - * tx.addInput(new Input()); * @param {Input|Object} options + * @returns {Input} + * + * @example + * mtx.addInput({ prevout: { hash: ... }, script: ... }); + * mtx.addInput(new Input()); */ MTX.prototype.addInput = function addInput(options) { var input = Input.fromOptions(options); this.inputs.push(input); - return this; + return input; }; /** * Add an outpoint as an input. * @param {Outpoint|Object} outpoint + * @returns {Input} + * + * @example + * mtx.addOutpoint({ hash: ..., index: 0 }); + * mtx.addOutpoint(new Outpoint(hash, index)); */ MTX.prototype.addOutpoint = function addOutpoint(outpoint) { var prevout = Outpoint.fromOptions(outpoint); var input = Input.fromOutpoint(prevout); this.inputs.push(input); - return this; + return input; }; /** * Add a coin as an input. Note that this will * add the coin to the internal coin viewpoint. * @param {Coin} coin + * @returns {Input} + * + * @example + * mtx.addCoin(Coin.fromTX(tx, 0, -1)); */ MTX.prototype.addCoin = function addCoin(coin) { @@ -166,7 +177,7 @@ MTX.prototype.addCoin = function addCoin(coin) { this.inputs.push(input); this.view.addCoin(coin); - return this; + return input; }; /** @@ -176,6 +187,10 @@ MTX.prototype.addCoin = function addCoin(coin) { * @param {TX} tx * @param {Number} index * @param {Number?} height + * @returns {Input} + * + * @example + * mtx.addTX(tx, 0); */ MTX.prototype.addTX = function addTX(tx, index, height) { @@ -192,16 +207,20 @@ MTX.prototype.addTX = function addTX(tx, index, height) { this.inputs.push(input); this.view.addCoin(coin); - return this; + return input; }; /** * Add an output. - * @example - * tx.addOutput({ address: ..., value: 100000 }); - * tx.addOutput(address, Amount.value('0.1')); * @param {Address|Script|Output|Object} options * @param {Amount?} value - Only needs to be present for non-output options. + * @returns {Output} + * + * @example + * mtx.addOutput(new Output()); + * mtx.addOutput({ address: ..., value: 100000 }); + * mtx.addOutput(address, 100000); + * mtx.addOutput(script, 100000); */ MTX.prototype.addOutput = function addOutput(options, value) { @@ -224,7 +243,7 @@ MTX.prototype.addOutput = function addOutput(options, value) { this.outputs.push(output); - return this; + return output; }; /** @@ -269,7 +288,6 @@ MTX.prototype.getInputValue = function getInputValue() { /** * Get all input addresses. - * @private * @returns {Address[]} addresses */ @@ -325,6 +343,16 @@ MTX.prototype.getSigopsCost = function getSigopsCost(flags) { return TX.prototype.getSigopsCost.call(this, this.view, flags); }; +/** + * Calculate the virtual size of the transaction + * (weighted against bytes per sigop cost). + * @returns {Number} vsize + */ + +MTX.prototype.getSigopsSize = function getSigopsSize() { + return TX.prototype.getSigopsSize.call(this, this.getSigopsCost()); +}; + /** * Perform contextual checks to verify input, output, * and fee values, as well as coinbase spend maturity @@ -525,6 +553,7 @@ MTX.prototype.scriptVector = function scriptVector(prev, vector, ring) { * Sign a transaction input on the worker pool * (if workers are enabled). * @param {Number} index + * @param {Coin|Output} coin * @param {KeyRing} ring * @param {SighashType?} type * @returns {Promise} @@ -799,6 +828,7 @@ MTX.prototype.isSigned = function isSigned() { /** * Test whether an input is fully-signed. * @param {Number} index + * @param {Coin|Output} coin * @returns {Boolean} */ @@ -890,10 +920,10 @@ MTX.prototype.isVectorSigned = function isVectorSigned(prev, vector) { }; /** - * Built input scripts (or witnesses) and sign the inputs. + * Build input scripts (or witnesses). * @param {KeyRing} ring - Address used to sign. The address * must be able to redeem the coin. - * @returns {Boolean} Whether the input was able to be signed. + * @returns {Number} Number of inputs templated. */ MTX.prototype.template = function template(ring) { @@ -931,7 +961,7 @@ MTX.prototype.template = function template(ring) { * @param {KeyRing} ring - Address used to sign. The address * must be able to redeem the coin. * @param {SighashType} type - * @returns {Boolean} Whether the input was able to be signed. + * @returns {Number} Number of inputs signed. */ MTX.prototype.sign = function sign(ring, type) { @@ -976,7 +1006,6 @@ MTX.prototype.sign = function sign(ring, type) { * @param {KeyRing} ring * @param {SighashType?} type * @returns {Promise} - * @returns {Boolean} Whether the inputs are valid. */ MTX.prototype.signAsync = function signAsync(ring, type) { @@ -1113,20 +1142,8 @@ MTX.prototype.estimateSize = co(function* estimateSize(estimate) { * Select necessary coins based on total output value. * @param {Coin[]} coins * @param {Object?} options - * @param {String?} options.selection - Coin selection priority. Can - * be `age`, `random`, or `all`. (default=age). - * @param {Boolean} options.confirmed - Select only confirmed coins. - * @param {Boolean} options.round - Whether to round to the nearest - * kilobyte for fee calculation. - * See {@link TX#getMinFee} vs. {@link TX#getRoundFee}. - * @param {Amount?} options.hardFee - Use a hard fee rather - * than calculating one. - * @param {Rate?} options.rate - Rate used for fee calculation. - * @param {Number|Boolean} options.subtractFee - Whether to subtract the - * fee from * existing outputs rather than adding more inputs. * @returns {CoinSelection} * @throws on not enough funds available. - * @throws on unable to subtract fee. */ MTX.prototype.selectCoins = function selectCoins(coins, options) { @@ -1357,7 +1374,10 @@ MTX.prototype.getJSON = function getJSON(network) { }; /** - * @see TX.fromJSON + * Instantiate a transaction from a + * jsonified transaction object. + * @param {Object} json - The jsonified transaction object. + * @returns {MTX} */ MTX.fromJSON = function fromJSON(json) { @@ -1365,7 +1385,9 @@ MTX.fromJSON = function fromJSON(json) { }; /** - * @see TX.fromReader + * Instantiate a transaction from a buffer reader. + * @param {BufferReader} br + * @returns {MTX} */ MTX.fromReader = function fromReader(br) { @@ -1373,7 +1395,10 @@ MTX.fromReader = function fromReader(br) { }; /** - * @see TX.fromRaw + * Instantiate a transaction from a serialized Buffer. + * @param {Buffer} data + * @param {String?} enc - Encoding, can be `'hex'` or null. + * @returns {MTX} */ MTX.fromRaw = function fromRaw(data, enc) { @@ -1391,17 +1416,6 @@ MTX.prototype.toTX = function toTX() { return new TX(this); }; -/** - * Inject properties from transaction. - * @private - * @param {TX} tx - * @returns {MTX} - */ - -MTX.prototype.fromTX = function fromTX(tx, view) { - return this.fromRaw(tx.toRaw()); -}; - /** * Instantiate MTX from TX. * @param {TX} tx @@ -1409,7 +1423,7 @@ MTX.prototype.fromTX = function fromTX(tx, view) { */ MTX.fromTX = function fromTX(tx) { - return new MTX().fromTX(tx); + return new MTX(tx); }; /** @@ -1830,8 +1844,8 @@ function sortRandom(a, b) { } function sortInputs(a, b) { - var ahash = util.revHex(a.prevout.hash); - var bhash = util.revHex(b.prevout.hash); + var ahash = a.prevout.rhash(); + var bhash = b.prevout.rhash(); var cmp = util.strcmp(ahash, bhash); if (cmp !== 0) diff --git a/lib/primitives/outpoint.js b/lib/primitives/outpoint.js index a021ddbd..cf958509 100644 --- a/lib/primitives/outpoint.js +++ b/lib/primitives/outpoint.js @@ -64,6 +64,15 @@ Outpoint.prototype.isNull = function isNull() { return this.index === 0xffffffff && this.hash === encoding.NULL_HASH; }; +/** + * Get little-endian hash. + * @returns {Hash} + */ + +Outpoint.prototype.rhash = function rhash() { + return util.revHex(this.hash); +}; + /** * Serialize outpoint to a key * suitable for a hash table. @@ -254,7 +263,7 @@ Outpoint.toKey = function toKey(hash, index) { */ Outpoint.prototype.inspect = function inspect() { - return ''; + return ''; }; /* diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 7ca53442..b9e02723 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -907,7 +907,7 @@ TX.prototype.getOutputValue = function getOutputValue() { * Get all input addresses. * @private * @param {CoinView} view - * @returns {Array} + * @returns {Object} */ TX.prototype._getInputAddresses = function getInputAddresses(view) { @@ -940,7 +940,7 @@ TX.prototype._getInputAddresses = function getInputAddresses(view) { /** * Get all output addresses. * @private - * @returns {Array} + * @returns {Object} */ TX.prototype._getOutputAddresses = function getOutputAddresses() { @@ -970,7 +970,7 @@ TX.prototype._getOutputAddresses = function getOutputAddresses() { * Get all addresses. * @private * @param {CoinView} view - * @returns {Array} + * @returns {Object} */ TX.prototype._getAddresses = function getAddresses(view) { diff --git a/lib/script/witness.js b/lib/script/witness.js index 9694edcf..bad0fea4 100644 --- a/lib/script/witness.js +++ b/lib/script/witness.js @@ -160,10 +160,7 @@ Witness.prototype.toASM = function toASM(decode) { }; /** - * Clone the witness object. Note that the raw - * encoded witness data will be lost. This is - * because the function assumes you are going - * to be altering the stack items by hand. + * Clone the witness object. * @returns {Witness} A clone of the current witness object. */ diff --git a/test/wallet-test.js b/test/wallet-test.js index 6cc2e353..65d24582 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -497,7 +497,7 @@ describe('Wallet', function() { var view, t1, t2, t3, balance, err; // Coinbase - t1 = new MTX() + t1 = new MTX(); t1.addInput(dummy(encoding.NULL_HASH)); t1.addOutput(w1.getAddress(), 5460); t1.addOutput(w1.getAddress(), 5460); @@ -508,7 +508,8 @@ describe('Wallet', function() { yield walletdb.addTX(t1); // Create new transaction - t2 = new MTX().addOutput(w2.getAddress(), 5460); + t2 = new MTX(); + t2.addOutput(w2.getAddress(), 5460); yield w1.fund(t2, { rate: 10000 }); yield w1.sign(t2); @@ -553,7 +554,7 @@ describe('Wallet', function() { var w1 = yield walletdb.create(); var w2 = yield walletdb.create(); var to = yield walletdb.create(); - var t1, t2, tx, cost, total, coins1, coins2, left; + var t1, t2, tx, cost, total, coins1, coins2; // Coinbase t1 = new MTX(); @@ -586,20 +587,11 @@ describe('Wallet', function() { coins1 = yield w1.getCoins(); coins2 = yield w2.getCoins(); - // Add dummy output (for `left`) to calculate maximum TX size - tx.addOutput(w1.getAddress(), 0); - // Add our unspent inputs to sign tx.addCoin(coins1[0]); tx.addCoin(coins1[1]); tx.addCoin(coins2[0]); - left = tx.getInputValue() - total; - if (left === 0) - tx.outputs.pop(); - else - tx.outputs[tx.outputs.length - 1].value = left; - // Sign transaction total = yield w1.sign(tx); assert.equal(total, 2); @@ -670,19 +662,19 @@ describe('Wallet', function() { assert.equal(w2.account[rec].getAddress('base58'), b58); assert.equal(w3.account[rec].getAddress('base58'), b58); - paddr = w1.getNested('base58'); - assert.equal(w1.getNested('base58'), paddr); - assert.equal(w2.getNested('base58'), paddr); - assert.equal(w3.getNested('base58'), paddr); + paddr = w1.getNested(); + + if (witness) { + assert(paddr); + assert.equal(w1.getNested('base58'), paddr.toBase58()); + assert.equal(w2.getNested('base58'), paddr.toBase58()); + assert.equal(w3.getNested('base58'), paddr.toBase58()); + } // Add a shared unspent transaction to our wallets utx = new MTX(); - if (bullshitNesting) - utx.addOutput({ address: paddr, value: 5460 * 10 }); - else - utx.addOutput({ address: addr, value: 5460 * 10 }); - utx.addInput(dummy()); + utx.addOutput(bullshitNesting ? paddr : addr, 5460 * 10); utx = utx.toTX(); // Simulate a confirmation @@ -704,7 +696,7 @@ describe('Wallet', function() { // Create a tx requiring 2 signatures send = new MTX(); - send.addOutput({ address: receive.getAddress(), value: 5460 }); + send.addOutput(receive.getAddress(), 5460); assert(!send.verify(flags)); yield w1.fund(send, { rate: 10000, round: true });