diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index f952059b..8e59a38c 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -127,7 +127,7 @@ Mempool.prototype._open = co(function* open() { for (i = 0; i < entries.length; i++) { entry = entries[i]; - this.updateAncestors(entry); + this.updateAncestors(entry, addFee); if (this.options.indexAddress) { view = yield this.getCoinView(entry.tx); @@ -385,7 +385,7 @@ Mempool.prototype.limitSize = function limitSize(entryHash) { if (!trimmed && hash === entryHash) trimmed = true; - this.removeEntry(entry, true); + this.evictEntry(entry); if (this.getSize() <= this.options.maxSize) return trimmed; @@ -403,7 +403,7 @@ Mempool.prototype.limitSize = function limitSize(entryHash) { if (!trimmed && hash === entryHash) trimmed = true; - this.removeEntry(entry, true); + this.evictEntry(entry); if (this.getSize() <= this.options.maxSize) return trimmed; @@ -957,7 +957,7 @@ Mempool.prototype.verify = co(function* verify(entry, view) { throw new VerifyError(tx, 'highfee', 'absurdly-high-fee', 0); // Why do we have this here? Nested transactions are cool. - if (this.updateAncestors(entry) > this.options.maxAncestors) { + if (this.countAncestors(entry) + 1 > this.options.maxAncestors) { throw new VerifyError(tx, 'nonstandard', 'too-long-mempool-chain', @@ -1076,6 +1076,8 @@ Mempool.prototype.addEntry = co(function* addEntry(entry, view) { this.trackEntry(entry, view); + this.updateAncestors(entry, addFee); + this.emit('tx', tx, view); this.emit('add entry', entry); @@ -1090,27 +1092,16 @@ Mempool.prototype.addEntry = co(function* addEntry(entry, view) { }); /** - * Remove a transaction from the mempool. Generally - * only called when a new block is added to the main chain. + * Remove a transaction from the mempool. + * Generally only called when a new block + * is added to the main chain. * @param {MempoolEntry} entry - * @param {Boolean} limit */ -Mempool.prototype.removeEntry = function removeEntry(entry, limit) { +Mempool.prototype.removeEntry = function removeEntry(entry) { var tx = entry.tx; var hash = tx.hash('hex'); - // 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); - this.logger.debug('Evicting %s from the mempool.', tx.txid()); - } else { - this.logger.spam('Removing block tx %s from mempool.', tx.txid()); - } - this.untrackEntry(entry); if (this.fees) @@ -1121,6 +1112,45 @@ Mempool.prototype.removeEntry = function removeEntry(entry, limit) { this.emit('remove entry', entry); }; +/** + * Evict a transaction from the mempool. + * Recursively remove its spenders. + * @param {MempoolEntry} entry + */ + +Mempool.prototype.evictEntry = function evictEntry(entry) { + var tx = entry.tx; + + this.logger.debug('Evicting %s from the mempool.', tx.txid()); + + this.removeSpenders(entry); + this.updateAncestors(entry, removeFee); + + this.removeEntry(entry); +}; + +/** + * Recursively remove spenders of a transaction. + * @private + * @param {MempoolEntry} entry + */ + +Mempool.prototype.removeSpenders = function removeSpenders(entry) { + var tx = entry.tx; + var hash = tx.hash('hex'); + var i, spender; + + for (i = 0; i < tx.outputs.length; i++) { + spender = this.getSpent(hash, i); + + if (!spender) + continue; + + this.removeSpenders(spender); + this.removeEntry(spender); + } +}; + /** * Count the highest number of * ancestors a transaction may have. @@ -1129,7 +1159,7 @@ Mempool.prototype.removeEntry = function removeEntry(entry, limit) { */ Mempool.prototype.countAncestors = function countAncestors(entry) { - return this._countAncestors(entry, 0, {}, entry, false); + return this._countAncestors(entry, 0, {}, entry, nop); }; /** @@ -1137,11 +1167,12 @@ Mempool.prototype.countAncestors = function countAncestors(entry) { * ancestors a transaction may have. * Update descendant fees and size. * @param {MempoolEntry} entry + * @param {Function} map * @returns {Number} */ -Mempool.prototype.updateAncestors = function updateAncestors(entry) { - return this._countAncestors(entry, 0, {}, entry, true); +Mempool.prototype.updateAncestors = function updateAncestors(entry, map) { + return this._countAncestors(entry, 0, {}, entry, map); }; /** @@ -1151,20 +1182,20 @@ Mempool.prototype.updateAncestors = function updateAncestors(entry) { * @param {Number} count * @param {Object} set * @param {MempoolEntry} child - * @param {Boolean} update + * @param {Function} map * @returns {Number} */ -Mempool.prototype._countAncestors = function countAncestors(entry, count, set, child, update) { +Mempool.prototype._countAncestors = function countAncestors(entry, count, set, child, map) { var tx = entry.tx; - var i, input, hash, pentry; + var i, input, hash, parent; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; hash = input.prevout.hash; - pentry = this.getEntry(hash); + parent = this.getEntry(hash); - if (!pentry) + if (!parent) continue; if (set[hash]) @@ -1173,15 +1204,12 @@ Mempool.prototype._countAncestors = function countAncestors(entry, count, set, c set[hash] = true; count += 1; - if (update) { - pentry.descFee += child.fee; - pentry.descSize += child.size; - } + map(parent, child); if (count > this.options.maxAncestors) break; - count = this._countAncestors(pentry, count, set, child, update); + count = this._countAncestors(parent, count, set, child, map); if (count > this.options.maxAncestors) break; @@ -1214,23 +1242,23 @@ Mempool.prototype.countDescendants = function countDescendants(entry) { Mempool.prototype._countDescendants = function countDescendants(entry, count, set) { var tx = entry.tx; var hash = tx.hash('hex'); - var i, next, nhash; + var i, child, next; for (i = 0; i < tx.outputs.length; i++) { - next = this.getSpent(hash, i); + child = this.getSpent(hash, i); - if (!next) + if (!child) continue; - nhash = next.tx.hash('hex'); + next = child.tx.hash('hex'); - if (set[nhash]) + if (set[next]) continue; - set[nhash] = true; + set[next] = true; count += 1; - count = this._countDescendants(next, count, set); + count = this._countDescendants(child, count, set); } return count; @@ -1256,23 +1284,23 @@ Mempool.prototype.getAncestors = function getAncestors(tx) { */ Mempool.prototype._getAncestors = function getAncestors(tx, entries, set) { - var i, hash, input, prev; + var i, hash, input, parent; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; hash = input.prevout.hash; - prev = this.getTX(hash); + parent = this.getTX(hash); - if (!prev) + if (!parent) continue; if (set[hash]) continue; set[hash] = true; - entries.push(prev); + entries.push(parent); - this._getAncestors(prev, entries, set); + this._getAncestors(parent, entries, set); } return entries; @@ -1298,23 +1326,23 @@ Mempool.prototype.getDescendants = function getDescendants(tx) { Mempool.prototype._getDescendants = function getDescendants(tx, entries, set) { var hash = tx.hash('hex'); - var i, next, nhash; + var i, child, next; for (i = 0; i < tx.outputs.length; i++) { - next = this.getSpentTX(hash, i); + child = this.getSpentTX(hash, i); - if (!next) + if (!child) continue; - nhash = next.hash('hex'); + next = child.hash('hex'); - if (set[nhash]) + if (set[next]) continue; - set[nhash] = true; - entries.push(next); + set[next] = true; + entries.push(child); - this._getDescendants(next, entries, set); + this._getDescendants(child, entries, set); } return entries; @@ -1831,27 +1859,6 @@ Mempool.prototype.unindexEntry = function unindexEntry(entry) { this.coinIndex.remove(hash, i); }; -/** - * Recursively remove spenders of a transaction. - * @private - * @param {MempoolEntry} entry - */ - -Mempool.prototype.removeSpenders = function removeSpenders(entry) { - var tx = entry.tx; - var hash = tx.hash('hex'); - var i, spender; - - for (i = 0; i < tx.outputs.length; i++) { - spender = this.getSpent(hash, i); - - if (!spender) - continue; - - this.removeEntry(spender, true); - } -}; - /** * Recursively remove double spenders * of a mined transaction's outpoints. @@ -1874,7 +1881,7 @@ Mempool.prototype.removeDoubleSpends = function removeDoubleSpends(tx) { 'Removing double spender from mempool: %s.', spent.tx.rhash()); - this.removeEntry(spent, true); + this.evictEntry(spent); this.emit('double spend', spent); } @@ -2492,6 +2499,24 @@ MempoolCache.prototype.wipe = co(function* wipe() { this.logger.info('Removed %d mempool entries from disk.', keys.length); }); +/* + * Helpers + */ + +function nop(parent, child) { + ; +} + +function addFee(parent, child) { + parent.descFee += child.fee; + parent.descSize += child.size; +} + +function removeFee(parent, child) { + parent.descFee -= child.descFee; + parent.descSize -= child.descSize; +} + /* * Expose */