diff --git a/lib/bcoin/bst.js b/lib/bcoin/bst.js index b8fb524d..f60aa50f 100644 --- a/lib/bcoin/bst.js +++ b/lib/bcoin/bst.js @@ -680,11 +680,6 @@ Iterator.prototype.seek = function seek(key) { this.index = utils.binarySearch(this.items, key, function(a, b) { return self.tree.compare(a.key, b); }, true); - - item = this.items[this.index]; - - if (!item || this.tree.compare(item.key, key) !== 0) - this.index += 1; }; Iterator.prototype._end = function end() { diff --git a/lib/bcoin/fees.js b/lib/bcoin/fees.js index 1f5f3781..04e42e1d 100644 --- a/lib/bcoin/fees.js +++ b/lib/bcoin/fees.js @@ -9,6 +9,8 @@ 'use strict'; var bcoin = require('./env'); +var utils = bcoin.utils; +var assert = require('assert'); var constants = bcoin.protocol.constants; var global = bcoin.utils.global; var Float64Array = global.Float64Array || Array; @@ -55,11 +57,11 @@ function ConfirmStats(buckets, maxConfirms, decay, type) { this.type = type; this.buckets = new Float64Array(buckets.length); - this.bucketMap = {}; + this.bucketMap = new DoubleMap(); for (i = 0; i < buckets.length; i++) { this.buckets[i] = buckets[i]; - this.bucketMap[buckets[i]] = i; + this.bucketMap.insert(buckets[i], i); } this.confAvg = new Array(maxConfirms); @@ -109,7 +111,7 @@ ConfirmStats.prototype.record = function record(blocks, val) { if (blocks < 1) return; - bucketIndex = this.bucketMap[val]; + bucketIndex = this.bucketMap.search(val); for (i = blocks; i <= this.curBlockConf.length; i++) this.curBlockConf[i - 1][bucketIndex]++; @@ -210,9 +212,10 @@ ConfirmStats.prototype.estimateMedian = function estimateMedian(target, needed, } } - // bcoin.debug('estimatefee: target=%d.' - // + ' For conf success %s %d need %s %s: %d from buckets %d - %d.' - // + ' Cur Bucket stats %d% %d/%d (%d mempool).', + // bcoin.debug('estimatefee: ' + // + ' For confirmation success in %d blocks' + // + ' %s %d need %s %s: %d from buckets %d - %d.' + // + ' Current bucket stats %d% %d/%d (%d mempool).', // target, // greater ? '>' : '<', // breakpoint, @@ -237,10 +240,10 @@ ConfirmStats.prototype.estimateMedian = function estimateMedian(target, needed, */ ConfirmStats.prototype.addTX = function addTX(height, val) { - var bucketIndex = this.bucketMap[val]; + var bucketIndex = this.bucketMap.search(val); var blockIndex = height % this.unconfTX.length; this.unconfTX[blockIndex][bucketIndex]++; - bcoin.debug('estimatefee: Adding TX to %s.', this.type); + bcoin.debug('estimatefee: Adding tx to %s.', this.type); return bucketIndex; }; @@ -268,7 +271,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc this.oldUnconfTX[bucketIndex]--; } else { bcoin.debug('estimatefee:' - + ' Mempool tx removed from >25 blocks,bucketIndex=%d already.', + + ' Mempool tx removed >25 blocks (bucket=%d).', bucketIndex); } } else { @@ -277,7 +280,7 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc this.unconfTX[blockIndex][bucketIndex]--; } else { bcoin.debug('estimatefee:' - + ' Mempool tx removed from blockIndex=%d,bucketIndex=%d already.', + + ' Mempool tx removed (block=%d, bucket=%d).', blockIndex, bucketIndex); } } @@ -352,7 +355,7 @@ PolicyEstimator.prototype.removeTX = function removeTX(hash) { var item = this.map[hash]; if (!item) { - bcoin.debug('estimatefee: mempool tx %s not found.', hash); + bcoin.debug('estimatefee: Mempool tx %s not found.', hash); return; } @@ -437,7 +440,7 @@ PolicyEstimator.prototype.processTX = function processTX(entry, current) { }; bcoin.debug('estimatefee: Rate: %d.', this.estimateFee()); } else { - bcoin.debug('estimatefee: Not adding ts %s.', hash); + bcoin.debug('estimatefee: Not adding tx %s.', hash); } }; @@ -457,7 +460,11 @@ PolicyEstimator.prototype.processBlockTX = function processBlockTX(height, entry blocks = height - entry.height; if (blocks <= 0) { - bcoin.debug('estimatefee: TX had negative blocks to confirm.'); + bcoin.debug( + 'estimatefee: Block tx %s had negative blocks to confirm (%d, %d).', + entry.tx.hash('hex'), + height, + entry.height); return; } @@ -632,6 +639,37 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s return priority; }; +/** + * DoubleMap + * @private + */ + +function DoubleMap() { + if (!(this instanceof DoubleMap)) + return new DoubleMap(); + + this.buckets = []; +} + +DoubleMap.prototype.insert = function insert(key, value) { + var i = utils.binarySearch(this.buckets, key, compare, true); + this.buckets.splice(i, 0, [key, value]); +}; + +DoubleMap.prototype.search = function search(key) { + var i = utils.binarySearch(this.buckets, key, compare, true); + assert(this.buckets.length !== 0, 'Cannot search.'); + return this.buckets[i][1]; +}; + +/* + * Helpers + */ + +function compare(a, b) { + return a[0] - b; +} + /* * Expose */ diff --git a/lib/bcoin/http/server.js b/lib/bcoin/http/server.js index dc7013e7..eaf9977b 100644 --- a/lib/bcoin/http/server.js +++ b/lib/bcoin/http/server.js @@ -414,12 +414,8 @@ HTTPServer.prototype._init = function _init() { this.post('/wallet/:id/send', function(req, res, next, send) { var id = req.options.id; var options = req.options; - var outputs = req.options.outputs; - if (!Array.isArray(outputs)) - return send(400); - - self.walletdb.createTX(id, options, outputs, function(err, tx) { + self.walletdb.createTX(id, options, function(err, tx) { if (err) return next(err); diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 1c0f102f..a1dd5d72 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -727,8 +727,12 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { } if (this.requireStandard) { - if (!tx.isStandard(flags, ret)) - return callback(new VerifyError(tx, ret.reason, 0)); + if (!tx.isStandard(flags, ret)) { + return callback(new VerifyError(tx, + 'nonstandard', + ret.reason, + 0)); + } if (!this.chain.csvActive && tx.version >= 2) { return callback(new VerifyError(tx, diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 65d2eb36..c70a0e64 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -1371,7 +1371,7 @@ TX.prototype.checkInputs = function checkInputs(spendHeight, ret) { input = this.inputs[i]; coin = input.coin; - if (coin.coinbase) { + if (coin.coinbase && spendHeight != null) { if (spendHeight - coin.height < constants.tx.COINBASE_MATURITY) { ret.reason = 'bad-txns-premature-spend-of-coinbase'; ret.score = 0; diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 1f89ed17..2bff8188 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -2415,7 +2415,7 @@ utils.binarySearch = function binarySearch(items, key, compare, insert) { if (!insert) return -1; - return start - 1; + return start; }; /** @@ -2428,7 +2428,7 @@ utils.binarySearch = function binarySearch(items, key, compare, insert) { utils.binaryInsert = function binaryInsert(items, item, compare) { var i = utils.binarySearch(items, item, compare, true); - items.splice(i + 1, 0, item); + items.splice(i, 0, item); return items.length; }; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 06a3de22..6d53ad41 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -592,34 +592,21 @@ Wallet.prototype.fill = function fill(tx, options, callback) { * sort the members according to BIP69, set locktime, * and sign it (accesses db). * @param {Object} options - See {@link Wallet#fill options}. - * @param {Object[]} outputs - See {@link Script.createOutputScript}. + * @param {Object[]} options.outputs - See {@link Script.createOutputScript}. * @param {Function} callback - Returns [Error, {@link MTX}]. */ -Wallet.prototype.createTX = function createTX(options, outputs, callback) { +Wallet.prototype.createTX = function createTX(options, callback) { var self = this; - var height = 0xffffffff; - var tx, i; - - if (typeof outputs === 'function') { - callback = outputs; - outputs = null; - } - - if (!outputs) { - outputs = options; - options = {}; - } - - if (!Array.isArray(outputs)) - outputs = [outputs]; - - if (options.height >= 0) - height = options.height; + var outputs = options.outputs || []; + var i, tx; // Create mutable tx tx = bcoin.mtx(); + if (outputs.length === 0) + outputs.push(options); + // Add the outputs for (i = 0; i < outputs.length; i++) { try { @@ -648,7 +635,7 @@ Wallet.prototype.createTX = function createTX(options, outputs, callback) { if (!tx.isSane()) return callback(new Error('CheckTransaction failed.')); - if (!tx.checkInputs(height)) + if (!tx.checkInputs(options.height)) return callback(new Error('CheckInputs failed.')); self.scriptInputs(tx, function(err, total) {