From 8b262a7ff9bad0356f8ee62e3ba36a6f9208ef03 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 20 Jun 2016 16:49:09 -0700 Subject: [PATCH] tests. asserts. subtractFee. --- lib/bcoin/input.js | 3 +- lib/bcoin/mtx.js | 71 +++++++++++++++++------------- lib/bcoin/output.js | 3 +- lib/bcoin/protocol/framer.js | 4 +- lib/bcoin/script.js | 12 +++--- lib/bcoin/tx.js | 19 +++++--- test/wallet-test.js | 84 ++++++++++++++++++++++++++++++++++++ 7 files changed, 152 insertions(+), 44 deletions(-) diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index d1f65fe1..e25bf7a7 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -477,7 +477,8 @@ Input.prototype.toJSON = function toJSON() { coin: this.coin ? this.coin.toJSON() : null, script: this.script.toJSON(), witness: this.witness.toJSON(), - sequence: this.sequence + sequence: this.sequence, + address: this.getAddress() }; }; diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 979ef343..c7ed0d13 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -1091,35 +1091,6 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) { // How much money is left after filling outputs. change = tx.getInputValue() - total(); - // Attempt to subtract fee. - if (options.subtractFee != null) { - if (typeof options.subtractFee === 'number') { - i = options.subtractFee; - output = tx.outputs[i]; - - if (!output) - throw new Error('Subtraction index does not exist.'); - - min = fee + output.getDustThreshold(); - - if (output.value < min) - throw new Error('Could not subtract fee.'); - - output.value -= fee; - } else { - for (i = 0; i < tx.outputs.length; i++) { - output = tx.outputs[i]; - min = fee + output.getDustThreshold(); - if (output.value >= min) { - output.value -= fee; - break; - } - } - if (i === tx.outputs.length) - throw new Error('Could not subtract fee.'); - } - } - // Return necessary inputs and change. return { coins: chosen, @@ -1129,6 +1100,44 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) { }; }; +/** + * Attempt to subtract a fee from outputs. + * @param {Amount} fee + * @param {Number?} index + */ + +MTX.prototype.subtractFee = function subtractFee(fee, index) { + var i, min, output; + + if (typeof index === 'number') { + output = this.outputs[index]; + + if (!output) + throw new Error('Subtraction index does not exist.'); + + min = fee + output.getDustThreshold(); + + if (output.value < min) + throw new Error('Could not subtract fee.'); + + output.value -= fee; + + return; + } + + for (i = 0; i < this.outputs.length; i++) { + output = this.outputs[i]; + min = fee + output.getDustThreshold(); + if (output.value >= min) { + output.value -= fee; + break; + } + } + + if (i === this.outputs.length) + throw new Error('Could not subtract fee.'); +}; + /** * Select coins and fill the inputs. * @param {Coin[]} coins @@ -1164,6 +1173,10 @@ MTX.prototype.fill = function fill(coins, options) { for (i = 0; i < result.coins.length; i++) this.addInput(result.coins[i]); + // Attempt to subtract fee. + if (options.subtractFee || options.subtractFee === 0) + this.subtractFee(result.fee, options.subtractFee); + // Add a change output. this.addOutput({ address: changeAddress, diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index c8526d5c..2564db10 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -166,7 +166,8 @@ Output.prototype.inspect = function inspect() { Output.prototype.toJSON = function toJSON() { return { value: utils.btc(this.value), - script: this.script.toJSON() + script: this.script.toJSON(), + address: this.getAddress() }; }; diff --git a/lib/bcoin/protocol/framer.js b/lib/bcoin/protocol/framer.js index 3e91e6e5..1c8610a5 100644 --- a/lib/bcoin/protocol/framer.js +++ b/lib/bcoin/protocol/framer.js @@ -628,7 +628,7 @@ Framer.tx = function _tx(tx, writer) { */ Framer.witnessTX = function _witnessTX(tx, writer) { - return tx.toRaw(writer); + return tx.toWitness(writer); }; /** @@ -651,7 +651,7 @@ Framer.block = function _block(block, writer) { */ Framer.witnessBlock = function _witnessBlock(block, writer) { - return block.toRaw(writer); + return block.toWitness(writer); }; /** diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index e1652e81..36f7aabe 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -2422,7 +2422,7 @@ Script.isCode = function isCode(raw) { */ Script.fromPubkey = function fromPubkey(key) { - assert(key.length >= 33); + assert(Buffer.isBuffer(key) && key.length >= 33); return Script.fromArray([key, opcodes.OP_CHECKSIG]); }; @@ -2433,7 +2433,7 @@ Script.fromPubkey = function fromPubkey(key) { */ Script.fromPubkeyhash = function fromPubkeyhash(hash) { - assert(hash.length === 20); + assert(Buffer.isBuffer(hash) && hash.length === 20); return Script.fromArray([ opcodes.OP_DUP, opcodes.OP_HASH160, @@ -2455,6 +2455,8 @@ Script.fromMultisig = function fromMultisig(m, n, keys) { var code = []; var i; + assert(utils.isNumber(m) && utils.isNumber(n)); + assert(Array.isArray(keys)); assert(keys.length === n, '`n` keys are required for multisig.'); assert(m >= 1 && m <= n); assert(n >= 1 && n <= 15); @@ -2479,7 +2481,7 @@ Script.fromMultisig = function fromMultisig(m, n, keys) { */ Script.fromScripthash = function fromScripthash(hash) { - assert(hash.length === 20); + assert(Buffer.isBuffer(hash) && hash.length === 20); return Script.fromArray([ opcodes.OP_HASH160, hash, @@ -2510,8 +2512,8 @@ Script.fromNulldata = function fromNulldata(flags) { */ Script.fromProgram = function fromProgram(version, data) { - assert(typeof version === 'number' && version >= 0 && version <= 16); - assert(data.length >= 2 && data.length <= 32); + assert(utils.isNumber(version) && version >= 0 && version <= 16); + assert(Buffer.isBuffer(data) && data.length >= 2 && data.length <= 40); return Script.fromArray([version === 0 ? 0 : version + 0x50, data]); }; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 6d5dc323..cf874600 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -57,6 +57,7 @@ function TX(options) { this.inputs = []; this.outputs = []; this.locktime = 0; + this.ts = 0; this.block = null; this.index = -1; @@ -100,7 +101,15 @@ TX.prototype.fromOptions = function fromOptions(options) { this.version = options.version; this.flag = options.flag; + + for (i = 0; i < options.inputs.length; i++) + this.inputs.push(new bcoin.input(options.inputs[i])); + + for (i = 0; i < options.outputs.length; i++) + this.outputs.push(new bcoin.output(options.outputs[i])); + this.locktime = options.locktime; + this.ts = options.ts || 0; this.block = options.block || null; this.index = options.index != null @@ -121,12 +130,6 @@ TX.prototype.fromOptions = function fromOptions(options) { ? options._witnessSize : null; - for (i = 0; i < options.inputs.length; i++) - this.inputs.push(new bcoin.input(options.inputs[i])); - - for (i = 0; i < options.outputs.length; i++) - this.outputs.push(new bcoin.output(options.outputs[i])); - return this; }; @@ -1890,6 +1893,8 @@ TX.prototype.toJSON = function toJSON() { ps: this.ps, index: this.index, changeIndex: this.changeIndex || -1, + fee: utils.btc(this.getFee()), + confirmations: this.getConfirmations(), version: this.version, flag: this.flag, inputs: this.inputs.map(function(input) { @@ -2286,8 +2291,10 @@ TX.fromExtended = function fromExtended(data, saveCoins, enc) { enc = saveCoins; saveCoins = false; } + if (typeof data === 'string') data = new Buffer(data, enc); + return new TX().fromExtended(data, saveCoins); }; diff --git a/test/wallet-test.js b/test/wallet-test.js index b73ee838..e6e6901e 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -903,6 +903,90 @@ describe('Wallet', function() { }); }); + it('should fill tx with inputs with subtract fee', function(cb) { + walletdb.create(function(err, w1) { + assert.ifError(err); + walletdb.create(function(err, w2) { + assert.ifError(err); + + // Coinbase + var t1 = bcoin.mtx() + .addOutput(w1, 5460) + .addOutput(w1, 5460) + .addOutput(w1, 5460) + .addOutput(w1, 5460); + + t1.addInput(dummyInput); + + walletdb.addTX(t1, function(err) { + assert.ifError(err); + + // Create new transaction + var t2 = bcoin.mtx().addOutput(w2, 21840); + w1.fill(t2, { rate: 10000, round: true, subtractFee: true }, function(err) { + assert.ifError(err); + w1.sign(t2, function(err) { + assert.ifError(err); + + assert(t2.verify()); + + assert.equal(t2.getInputValue(), 5460 * 4); + assert.equal(t2.getOutputValue(), 21840 - 10000); + assert.equal(t2.getFee(), 10000); + + cb(); + }); + }); + }); + }); + }); + }); + + it('should fill tx with inputs with subtract fee with create tx', function(cb) { + walletdb.create(function(err, w1) { + assert.ifError(err); + walletdb.create(function(err, w2) { + assert.ifError(err); + + // Coinbase + var t1 = bcoin.mtx() + .addOutput(w1, 5460) + .addOutput(w1, 5460) + .addOutput(w1, 5460) + .addOutput(w1, 5460); + + t1.addInput(dummyInput); + + walletdb.addTX(t1, function(err) { + assert.ifError(err); + + var options = { + subtractFee: true, + rate: 10000, + round: true, + outputs: [{ address: w2.getAddress(), value: 21840 }] + }; + + // Create new transaction + w1.createTX(options, function(err, t2) { + assert.ifError(err); + w1.sign(t2, function(err) { + assert.ifError(err); + + assert(t2.verify()); + + assert.equal(t2.getInputValue(), 5460 * 4); + assert.equal(t2.getOutputValue(), 21840 - 10000); + assert.equal(t2.getFee(), 10000); + + cb(); + }); + }); + }); + }); + }); + }); + it('should cleanup', function(cb) { constants.tx.COINBASE_MATURITY = 100; cb();