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++) {
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
*/