diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 693071f3..dc612cfa 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -800,7 +800,7 @@ HDPrivateKey._generate = function _generate(options, network) { var privateKey, entropy; if (!options) - opitons = {}; + options = {}; if (Buffer.isBuffer(options)) options = { privateKey: options }; diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 97f3f7e9..105d9c91 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -730,11 +730,10 @@ MTX.prototype.isScripted = function isScripted() { for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; - if (input.script.code.length === 0) - return false; - - if (input.witness.items.length === 0) + if (input.script.code.length === 0 + && input.witness.items.length === 0) { return false; + } } return true; @@ -776,7 +775,6 @@ MTX.prototype.maxSize = function maxSize(options, force) { input = this.inputs[i]; size = input.script.getSize(); total -= utils.sizeVarint(size) + size; - total += 1; } // Add size for signatures and public keys @@ -784,6 +782,7 @@ MTX.prototype.maxSize = function maxSize(options, force) { input = this.inputs[i]; size = 0; witness = false; + redeem = null; // We're out of luck here. // Just assume it's a p2pkh. @@ -817,10 +816,17 @@ MTX.prototype.maxSize = function maxSize(options, force) { if (prev.isWitnessProgram()) { witness = true; - // Now calculating vsize. The regular - // redeem script (if there was one) - // is now worth 4 points. - size *= 4; + // Now calculating vsize. + if (redeem) { + // The regular redeem script + // is now worth 4 points. + size += utils.sizeVarint(size); + size *= 4; + } else { + // Add one varint byte back + // for the 0-byte input script. + size += 1 * 4; + } // Add 2 bytes for flag and marker. if (!hadWitness) @@ -898,14 +904,14 @@ MTX.prototype.maxSize = function maxSize(options, force) { } } - // Byte for varint size of input script or witness. - size += utils.sizeVarint(size); - - // Calculate vsize if we're a witness program. if (witness) { - // Add one byte back for the 0-byte input script. - size += 1 * 4; + // Calculate vsize if + // we're a witness program. size = (size + scale - 1) / scale | 0; + } else { + // Byte for varint + // size of input script. + size += utils.sizeVarint(size); } total += size; @@ -920,10 +926,10 @@ MTX.prototype.maxSize = function maxSize(options, force) { * @param {Object?} options * @param {String?} options.selection - Coin selection priority. Can * be `age`, `random`, or `all`. (default=age). - * @param {Boolean} options.accurate - Whether to use "accurate" - * fee calculatation rather than rounding to the nearest kilobyte. - * See {@link TX#getMinFee} vs. {@link TX#getMaxFee}. * @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 {Boolean} options.free - Do not apply a fee if the * transaction priority is high enough to be considered free. * @param {Amount?} options.fee - Use a hard fee rather than calculating one. @@ -1042,10 +1048,10 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) { tryFree = false; } - if (options.accurate) - fee = tx.getMinFee(size, options.rate); + if (options.round) + fee = tx.getRoundFee(size, options.rate); else - fee = tx.getMaxFee(size, options.rate); + fee = tx.getMinFee(size, options.rate); // Failed to get enough funds, add more coins. if (!isFull()) diff --git a/lib/bcoin/protocol/network.js b/lib/bcoin/protocol/network.js index 0374d936..b5f1dcda 100644 --- a/lib/bcoin/protocol/network.js +++ b/lib/bcoin/protocol/network.js @@ -428,7 +428,7 @@ main.minRelay = 10000; * @default */ -main.feeRate = 40000; +main.feeRate = 50000; /** * Default min rate. @@ -444,7 +444,7 @@ main.minRate = 10000; * @default */ -main.maxRate = 40000; +main.maxRate = 50000; /* * Testnet (v3) diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index b726834f..cac9a33d 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -1540,7 +1540,7 @@ TX.prototype.getRate = function getRate(size) { * @returns {Amount} fee */ -TX.prototype.getMaxFee = function getMaxFee(size, rate) { +TX.prototype.getRoundFee = function getRoundFee(size, rate) { var fee; if (size == null) diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index fe6cd7d9..a59911cb 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -677,9 +677,9 @@ Wallet.prototype.ownOutput = function ownOutput(tx, index) { * @param {Object?} options * @param {String?} options.selection - Coin selection priority. Can * be `age`, `random`, or `all`. (default=age). - * @param {Boolean} options.accurate - Whether to use "accurate" - * fee calculatation rather than rounding to the nearest kilobyte. - * See {@link TX#getMinFee} vs. {@link TX#getMaxFee}. + * @param {Boolean} options.round - Whether to round to the nearest + * kilobyte for fee calculation. + * See {@link TX#getMinFee} vs. {@link TX#getRoundFee}. * @param {Rate} options.rate - Rate used for fee calculation. * @param {Boolean} options.confirmed - Select only confirmed coins. * @param {Boolean} options.free - Do not apply a fee if the @@ -709,14 +709,16 @@ Wallet.prototype.fill = function fill(tx, options, callback) { try { tx.fill(coins, { selection: options.selection || 'age', - accurate: options.accurate, + round: options.round, confirmed: options.confirmed, free: options.free, fee: options.fee, subtractFee: options.subtractFee, changeAddress: self.changeAddress.getAddress(), height: self.network.height, - rate: self.network.getMinRelay(), + rate: options.rate != null + ? options.rate + : self.network.getRate(), wallet: self, m: self.m, n: self.n diff --git a/test/mempool-test.js b/test/mempool-test.js index afa2cdb4..00335016 100644 --- a/test/mempool-test.js +++ b/test/mempool-test.js @@ -100,27 +100,27 @@ describe('Mempool', function() { assert.ifError(err); mempool.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '0'); + assert.equal(balance.total, 0); mempool.addTX(t1, function(err) { assert.ifError(err); mempool.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '60000'); + assert.equal(balance.total, 60000); mempool.addTX(t2, function(err) { assert.ifError(err); mempool.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '50000'); + assert.equal(balance.total, 50000); mempool.addTX(t3, function(err) { assert.ifError(err); mempool.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '22000'); + assert.equal(balance.total, 22000); mempool.addTX(f1, function(err) { assert.ifError(err); mempool.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '20000'); + assert.equal(balance.total, 20000); mempool.getHistory(function(err, txs) { assert(txs.some(function(tx) { return tx.hash('hex') === f1.hash('hex'); diff --git a/test/wallet-test.js b/test/wallet-test.js index 56711853..7c2d8713 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -5,6 +5,16 @@ var network = bcoin.protocol.network; var utils = bcoin.utils; var assert = require('assert'); +var KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt' + + 'qUP9iWfcHgJofs25xbaUpCps9GDXj83NiWvQCAkWQhVj5J4CorfnpKX94AZ'; + +KEY1 = { xprivkey: KEY1 }; + +var KEY2 = 'xprv9s21ZrQH143K3mqiSThzPtWAabQ22Pjp3uSNnZ53A5bQ4udp' + + 'faKekc2m4AChLYH1XDzANhrSdxHYWUeTWjYJwFwWFyHkTMnMeAcW4JyRCZa'; + +KEY2 = { xprivkey: KEY2 }; + var dummyInput = { prevout: { hash: constants.NULL_HASH, @@ -24,6 +34,18 @@ var dummyInput = { sequence: 0xffffffff }; +assert.range = function range(value, lo, hi, message) { + if (!(value >= lo && value <= hi)) { + throw new assert.AssertionError({ + message: message, + actual: value, + expected: lo + ', ' + hi, + operator: '>= && <=', + stackStartFunction: range + }); + } +}; + describe('Wallet', function() { var wdb = new bcoin.walletdb({ name: 'wallet-test', @@ -199,33 +221,33 @@ describe('Wallet', function() { assert.ifError(err); w.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '22500'); + assert.equal(balance.total, 22500); wdb.addTX(t1, function(err) { w.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '73000'); + assert.equal(balance.total, 73000); wdb.addTX(t2, function(err) { assert.ifError(err); w.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '47000'); + assert.equal(balance.total, 47000); wdb.addTX(t3, function(err) { assert.ifError(err); w.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '22000'); + assert.equal(balance.total, 22000); wdb.addTX(f1, function(err) { assert.ifError(err); w.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '11000'); + assert.equal(balance.total, 11000); w.getHistory(function(err, txs) { assert(txs.some(function(tx) { return tx.hash('hex') === f1.hash('hex'); })); var w2 = bcoin.wallet.fromJSON(w.toJSON()); - // assert.equal(w2.getBalance().toString(10), '11000'); + // assert.equal(w2.getBalance(), 11000); // assert(w2.getHistory().some(function(tx) { // return tx.hash('hex') === f1.hash('hex'); // })); @@ -253,7 +275,7 @@ describe('Wallet', function() { assert.ifError(err); dw.getBalance(function(err, balance) { assert.ifError(err); - assert.equal(balance.total.toString(10), '11000'); + assert.equal(balance.total, 11000); cb(); }); }); @@ -280,22 +302,23 @@ describe('Wallet', function() { // Create new transaction var t2 = bcoin.mtx().addOutput(w2, 5460); - w1.fill(t2, function(err) { + w1.fill(t2, { rate: 10000, round: true }, function(err) { assert.ifError(err); w1.sign(t2); assert(t2.verify()); - assert.equal(t2.getInputValue().toString(10), 16380); + assert.equal(t2.getInputValue(), 16380); // If change < dust and is added to outputs: - // assert.equal(t2.getOutputValue().toString(10), 6380); - // If change < dust and is added to fee: - assert.equal(t2.getOutputValue().toString(10), 5460); + // assert.equal(t2.getOutputValue(), 6380); + // If change > dust and is added to fee: + assert.equal(t2.getOutputValue(), 5460); + assert.equal(t2.getFee(), 10920); // Create new transaction var t3 = bcoin.mtx().addOutput(w2, 15000); - w1.fill(t3, function(err) { + w1.fill(t3, { rate: 10000, round: true }, function(err) { assert(err); - assert.equal(err.requiredFunds.toString(10), 25000); + assert.equal(err.requiredFunds, 25000); cb(); }); }); @@ -304,6 +327,59 @@ describe('Wallet', function() { }); }); + it('should fill tx with inputs with accurate fee', function(cb) { + wdb.create({ master: KEY1 }, function(err, w1) { + assert.ifError(err); + wdb.create({ master: KEY2 }, 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); + + // Fake TX should temporarly change output + wdb.addTX(t1, function(err) { + assert.ifError(err); + + // Create new transaction + var t2 = bcoin.mtx().addOutput(w2, 5460); + w1.fill(t2, { rate: 10000 }, function(err) { + assert.ifError(err); + w1.sign(t2); + assert(t2.verify()); + + assert.equal(t2.getInputValue(), 16380); + + // Should now have a change output: + assert.equal(t2.getOutputValue(), 11130); + + assert.equal(t2.getFee(), 5250); + + assert.equal(t2.getCost(), 2084); + assert.equal(t2.getBaseSize(), 521); + assert.equal(t2.getSize(), 521); + assert.equal(t2.getVirtualSize(), 521); + + // Create new transaction + wdb.addTX(t2, function(err) { + assert.ifError(err); + var t3 = bcoin.mtx().addOutput(w2, 15000); + w1.fill(t3, { rate: 10000 }, function(err) { + assert(err); + cb(); + }); + }); + }); + }); + }); + }); + }); + it('should sign multiple inputs using different keys', function(cb) { wdb.create({}, function(err, w1) { assert.ifError(err); @@ -483,7 +559,7 @@ describe('Wallet', function() { var send = bcoin.mtx(); send.addOutput({ address: receive.getAddress(), value: 5460 }); assert(!send.verify(null, true, flags)); - w1.fill(send, { m: w1.m, n: w1.n }, function(err) { + w1.fill(send, { rate: 10000, round: true }, function(err) { assert.ifError(err); w1.sign(send);