mempool: refactor descendants handling.
This commit is contained in:
parent
8977e99906
commit
46a646aebd
@ -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
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user