From 2e54d9fb4d96b616bdc03d5604ca92903a9c0b5b Mon Sep 17 00:00:00 2001 From: DanS Date: Thu, 9 Jul 2026 04:29:54 +0200 Subject: [PATCH] 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) --- src/txmempool.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/txmempool.cpp b/src/txmempool.cpp index fb473158c..53b971809 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -469,10 +469,17 @@ extern char SMART_CHAIN_SYMBOL[]; std::vector 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 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 ids; for (const CTransaction& tx : transactionsToRemove) {