diff --git a/lib/bcoin/http/rpc.js b/lib/bcoin/http/rpc.js index 866e80dc..dd026c0b 100644 --- a/lib/bcoin/http/rpc.js +++ b/lib/bcoin/http/rpc.js @@ -1597,20 +1597,7 @@ RPC.prototype.prioritisetransaction = function prioritisetransaction(args, callb if (entry.fees < 0) entry.fees = 0; - this.mempool.fillAllCoins(entry.tx, function(err) { - if (err) - return callback(err); - - if (!entry.tx.hasCoins()) - return callback(new RPCError('Transaction not in mempool.')); - - self.mempool.addUnchecked(entry, function(err) { - if (err) - return callback(err); - - callback(null, true); - }); - }); + return callback(null, true); }; RPC.prototype.submitblock = function submitblock(args, callback) { diff --git a/lib/bcoin/mempool.js b/lib/bcoin/mempool.js index 2465d0bf..994916e9 100644 --- a/lib/bcoin/mempool.js +++ b/lib/bcoin/mempool.js @@ -149,54 +149,37 @@ Mempool.prototype._lock = function _lock(func, args, force) { * @param {Function} callback */ -Mempool.prototype.addBlock = function addBlock(block, callback, force) { - var self = this; - var unlock = this._lock(addBlock, [block, callback], force); - var len, entries, entry; +Mempool.prototype.addBlock = function addBlock(block, callback) { + var entries = []; + var i, entry, tx, hash; - if (!unlock) - return; - - callback = utils.wrap(callback, unlock); - len = block.txs.length - 1; - entries = []; - - utils.forRangeSerial(0, block.txs.length, function(i, next) { - var tx = block.txs[len--]; - var hash = tx.hash('hex'); + for (i = block.txs.length - 1; i >= 0; i--) { + tx = block.txs[i]; + hash = tx.hash('hex'); if (tx.isCoinbase()) - return next(); + continue; - entry = self.getEntry(hash); + entry = this.getEntry(hash); if (!entry) { - self.removeOrphan(hash); - return next(); + this.removeOrphan(hash); + continue; } - self.removeUnchecked(entry, false, function(err) { - if (err) - return next(err); + this.removeUnchecked(entry); + this.emit('confirmed', tx, block); - self.emit('confirmed', tx, block); + entries.push(entry); + } - entries.push(entry); + this.blockSinceBump = true; + this.lastFeeUpdate = utils.now(); - return next(); - }, true); - }, function(err) { - if (err) - return callback(err); + if (this.fees) + this.fees.processBlock(block.height, entries, this.chain.isFull()); - self.blockSinceBump = true; - self.lastFeeUpdate = utils.now(); - - if (self.fees) - self.fees.processBlock(block.height, entries, self.chain.isFull()); - - return callback(); - }); + return callback(); }; /** @@ -245,62 +228,54 @@ Mempool.prototype.removeBlock = function removeBlock(block, callback, force) { * @param {Function} callback */ -Mempool.prototype.limitMempoolSize = function limitMempoolSize(entryHash, callback) { - var self = this; +Mempool.prototype.limitMempoolSize = function limitMempoolSize(entryHash) { var trimmed = false; - var hashes, end, entry; + var i, hashes, hash, end, entry; if (this.getSize() <= this.maxSize) - return callback(null, trimmed); + return trimmed; - hashes = Object.keys(this.tx); + hashes = this.getSnapshot(); end = utils.now() - constants.mempool.MEMPOOL_EXPIRY; - utils.forEachSerial(hashes, function(hash, next) { - var entry = self.getEntry(hash); + for (i = 0; i < hashes.length; i++) { + hash = hashes[i]; + entry = this.getEntry(hash); if (!entry) - return next(); - - if (self.getSize() <= self.maxSize) - return callback(null, trimmed); + continue; if (entry.ts >= end) - return next(); + continue; if (!trimmed && hash === entryHash) trimmed = true; - self.removeUnchecked(entry, true, next, true); - }, function(err) { - if (err) - return callback(err); + this.removeUnchecked(entry, true); - if (self.getSize() <= self.maxSize) - return callback(null, trimmed); + if (this.getSize() <= this.maxSize) + return trimmed; + } - hashes = Object.keys(self.tx); + hashes = this.getSnapshot(); - utils.forEachSerial(hashes, function(hash, next) { - if (self.getSize() <= self.maxSize) - return callback(null, trimmed); + for (i = 0; i < hashes.length; i++) { + hash = hashes[i]; + entry = this.getEntry(hash); - entry = self.getEntry(hash); + if (!entry) + continue; - if (!entry) - return next(); + if (!trimmed && hash === entryHash) + trimmed = true; - if (!trimmed && hash === entryHash) - trimmed = true; + this.removeUnchecked(entry, true); - self.removeUnchecked(entry, true, next, true); - }, function(err) { - if (err) - return callback(err); + if (this.getSize() <= this.maxSize) + return trimmed; + } - return callback(null, trimmed); - }); - }); + return trimmed; }; /** @@ -595,11 +570,11 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { } } - this.chain.checkFinal(this.chain.tip, tx, lockFlags, function(err, isFinal) { + this.chain.checkFinal(this.chain.tip, tx, lockFlags, function(err, result) { if (err) return callback(err); - if (!isFinal) { + if (!result) { return callback(new VerifyError(tx, 'nonstandard', 'non-final', @@ -650,19 +625,14 @@ Mempool.prototype.addTX = function addTX(tx, callback, force) { if (err) return callback(err); - self.limitMempoolSize(hash, function(err, trimmed) { - if (err) - return callback(err); + if (self.limitMempoolSize(hash)) { + return callback(new VerifyError(tx, + 'insufficientfee', + 'mempool full', + 0)); + } - if (trimmed) { - return callback(new VerifyError(tx, - 'insufficientfee', - 'mempool full', - 0)); - } - - return callback(); - }); + return callback(); }, true); }); }); @@ -739,48 +709,35 @@ Mempool.prototype.addUnchecked = function addUnchecked(entry, callback, force) { * @param {Function} callback */ -Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit, callback, force) { - var self = this; - var unlock, rate, hash; - - unlock = this._lock(removeUnchecked, [entry, limit, callback], force); - - if (!unlock) - return; - - callback = utils.wrap(callback, unlock); +Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit) { + var rate, hash; this.removeOrphan(entry.tx); - this._removeUnchecked(entry, limit, function(err) { - if (err) - return callback(err); + this._removeUnchecked(entry, limit); - self.totalSpent -= entry.tx.inputs.length; - self.size -= self.memUsage(entry.tx); - self.totalTX--; + this.totalSpent -= entry.tx.inputs.length; + this.size -= this.memUsage(entry.tx); + this.totalTX--; - if (self.fees) { - hash = entry.tx.hash('hex'); - self.fees.removeTX(hash); + if (this.fees) { + hash = entry.tx.hash('hex'); + this.fees.removeTX(hash); + } + + if (limit) { + this.logger.spam('Removed tx %s from mempool.', entry.tx.rhash); + rate = bcoin.tx.getRate(entry.sizes, entry.fees); + rate += this.minReasonableFee; + if (rate > this.minFeeRate) { + this.minFeeRate = rate; + this.blockSinceBump = false; } + } else { + this.logger.spam('Removed block tx %s from mempool.', entry.tx.rhash); + } - if (limit) { - self.logger.spam('Removed tx %s from mempool.', entry.tx.rhash); - rate = bcoin.tx.getRate(entry.sizes, entry.fees); - rate += self.minReasonableFee; - if (rate > self.minFeeRate) { - self.minFeeRate = rate; - self.blockSinceBump = false; - } - } else { - self.logger.spam('Removed block tx %s from mempool.', entry.tx.rhash); - } - - self.emit('remove tx', entry.tx); - - return callback(); - }); + this.emit('remove tx', entry.tx); }; /** @@ -1001,7 +958,7 @@ Mempool.prototype.countDescendants = function countDescendants(tx) { if (!next) continue; count = 1; - count += this.countDescendants(next); + count += this.countDescendants(next.tx); if (count > max) max = count; } @@ -1081,13 +1038,15 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx) { */ Mempool.prototype.getBalance = function getBalance() { - var keys = Object.keys(this.tx); + var hashes = this.getSnapshot(); var total = 0; - var i, j, key, tx, hash, coin; + var i, j, tx, hash, coin; - for (i = 0; i < keys.length; i++) { - key = keys[i]; - tx = this.tx[key].tx; + for (i = 0; i < hashes.length; i++) { + hash = hashes[i]; + tx = this.getTX(hash); + if (!tx) + continue; hash = tx.hash('hex'); for (j = 0; j < tx.outputs.length; j++) { coin = this.getCoin(hash, j); @@ -1105,13 +1064,13 @@ Mempool.prototype.getBalance = function getBalance() { */ Mempool.prototype.getHistory = function getHistory() { - var keys = Object.keys(this.tx); + var hashes = this.getSnapshot(); var txs = []; - var i, key, tx; + var i, hash, tx; - for (i = 0; i < keys.length; i++) { - key = keys[i]; - tx = this.getTX(key); + for (i = 0; i < hashes.length; i++) { + hash = hashes[i]; + tx = this.getTX(hash); if (!tx) continue; txs.push(tx); @@ -1304,7 +1263,7 @@ Mempool.prototype.fillAllCoins = function fillAllCoins(tx, callback) { /** * Get a snapshot of all transaction hashes in the mempool. Used * for generating INV packets in response to MEMPOOL packets. - * @param {Function} callback - Returns [Error, {@link Hash}[]]. + * @returns {Hash[]} */ Mempool.prototype.getSnapshot = function getSnapshot() { @@ -1421,16 +1380,16 @@ Mempool.prototype._addUnchecked = function _addUnchecked(entry) { if (this.options.indexAddress) this.coinIndex.removeCoin(input.coin); - this.spents[key] = bcoin.outpoint.fromTX(tx, i); + this.spents[key] = entry; } - for (i = 0; i < tx.outputs.length; i++) { - output = tx.outputs[i]; + if (this.options.indexAddress) { + for (i = 0; i < tx.outputs.length; i++) { + output = tx.outputs[i]; - if (output.script.isUnspendable()) - continue; + if (output.script.isUnspendable()) + continue; - if (this.options.indexAddress) { coin = bcoin.coin.fromTX(tx, i); this.coinIndex.addCoin(coin); } @@ -1460,50 +1419,49 @@ Mempool.prototype._addUnchecked = function _addUnchecked(entry) { * @param {Function} callback */ -Mempool.prototype._removeUnchecked = function _removeUnchecked(entry, limit, callback) { - var self = this; +Mempool.prototype._removeUnchecked = function _removeUnchecked(entry, limit) { var tx = entry.tx; var hash = tx.hash('hex'); var i, input, output, key, coin; - this._removeSpenders(entry, limit, function(err) { - if (err) - return callback(err); + // We do not remove spenders if this is + // being removed for a block. The spenders + // are still spending valid coins (which + // now exist on the blockchain). + if (limit) + this._removeSpenders(entry); - delete self.tx[hash]; + delete this.tx[hash]; - if (self.options.indexAddress) - self.txIndex.addTX(tx); + if (this.options.indexAddress) + this.txIndex.removeTX(tx); - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - key = input.prevout.hash + input.prevout.index; + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + key = input.prevout.hash + input.prevout.index; - if (tx.isCoinbase()) - break; + if (tx.isCoinbase()) + break; - delete self.spents[key]; + delete this.spents[key]; - if (self.options.indexAddress) { - assert(input.coin); - self.coinIndex.removeCoin(input.coin); - } - } + assert(input.coin); + if (this.options.indexAddress) + this.coinIndex.removeCoin(input.coin); + } + + if (this.options.indexAddress) { for (i = 0; i < tx.outputs.length; i++) { output = tx.outputs[i]; if (output.script.isUnspendable()) continue; - if (self.options.indexAddress) { - coin = bcoin.coin.fromTX(tx, i); - self.coinIndex.removeCoin(coin); - } + coin = bcoin.coin.fromTX(tx, i); + this.coinIndex.removeCoin(coin); } - - return callback(); - }); + } }; /** @@ -1514,33 +1472,19 @@ Mempool.prototype._removeUnchecked = function _removeUnchecked(entry, limit, cal * @param {Function} callback */ -Mempool.prototype._removeSpenders = function _removeSpenders(entry, limit, callback) { - var self = this; +Mempool.prototype._removeSpenders = function _removeSpenders(entry) { var tx = entry.tx; - var hash, spender; + var hash = tx.hash('hex'); + var i, spender; - // We do not remove spenders if this is - // being removed for a block. The spenders - // are still spending valid coins (which - // now exist on the blockchain). - if (!limit) - return callback(); - - hash = tx.hash('hex'); - - utils.forEachSerial(tx.outputs, function(output, next, i) { - spender = self.isSpent(hash, i); + for (i = 0; i < tx.outputs.length; i++) { + spender = this.isSpent(hash, i); if (!spender) - return next(); + continue; - entry = self.getEntry(spender.hash); - - if (!entry) - return next(); - - self.removeUnchecked(entry, limit, next, true); - }, callback); + this.removeUnchecked(spender, true); + } }; /** @@ -1575,11 +1519,14 @@ Mempool.prototype.memUsage = function memUsage(tx) { mem += 8; + // Mempool Entry + mem += 40; + // Map entry mem += 64; // Spent entry - mem += tx.outputs.length * 72; + mem += 64; return mem; };