fix: restore removeExpired() mempool scan (was a no-op -> unbounded mempool DoS)
CTxMemPool::removeExpired() declared `transactionsToRemove` and looped over it without ever populating it -- the mapTx scan that collects expired txs had been dropped, so it evicted nothing. Expired txs (past nExpiryHeight) can never be mined yet were never removed, so a peer could wedge them into every node's mempool permanently at ~zero cost (never mined -> never pay a fee), growing the mempool without bound: a memory-exhaustion DoS against every node (incl. pool/payout nodes). Restore the upstream Zcash/Komodo scan: iterate mapTx, collect txs failing IsExpiredTx(tx, tipHeight) into a separate list, then remove() them (collect-then- remove avoids iterator invalidation; recursive=true also evicts the now-unmineable descendants). Also drops an unused CBlockIndex* local. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -469,10 +469,17 @@ extern char SMART_CHAIN_SYMBOL[];
|
||||
|
||||
std::vector<uint256> CTxMemPool::removeExpired(unsigned int nBlockHeight)
|
||||
{
|
||||
CBlockIndex *tipindex;
|
||||
// Remove expired txs from the mempool
|
||||
// Remove expired txs from the mempool. (Regression fix: the scan that populates
|
||||
// transactionsToRemove had been dropped, making this a no-op, so expired txs -- which
|
||||
// can never be mined -- were never evicted and accumulated without bound.)
|
||||
LOCK(cs);
|
||||
list<CTransaction> transactionsToRemove;
|
||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||
const CTransaction& tx = it->GetTx();
|
||||
if (IsExpiredTx(tx, nBlockHeight)) {
|
||||
transactionsToRemove.push_back(tx);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint256> ids;
|
||||
for (const CTransaction& tx : transactionsToRemove) {
|
||||
|
||||
Reference in New Issue
Block a user