mempool: refactor descendants handling.

This commit is contained in:
Christopher Jeffrey 2017-03-01 17:45:51 -08:00
parent 8977e99906
commit 46a646aebd
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

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