From 8770e49d5264bdc9f6daec61dee74ca74dabdc0a Mon Sep 17 00:00:00 2001 From: Bitflash Date: Tue, 28 Jul 2026 15:25:29 -0300 Subject: main: cap orphan transactions held in memory (#16) A transaction naming a parent the node does not have is never validated, just stored in mapOrphanTransactions -- and there was no ceiling. Sending them costs a peer nothing: no proof of work, no valid signature, no fee, and the node keeps every one. Any peer could grow that map until the node died of memory exhaustion. Cap at 100, evicting at random rather than in map order so an attacker cannot keep its own orphans resident by choosing hashes that sort low. Local policy, not consensus -- no effect on block or transaction validity. --- src/main.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a7298ea..17e446e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -249,6 +249,26 @@ void AddOrphanTx(const CDataStream& vMsg) CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg); foreach(const CTxIn& txin, tx.vin) mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg)); + LimitOrphanTx(MAX_ORPHAN_TRANSACTIONS); +} + +// Orphans cost nothing to create -- a transaction naming a parent that does +// not exist is never validated, just held -- so without a ceiling any peer can +// grow this map until the node runs out of memory. Evict at random rather than +// in map order, so an attacker cannot keep its own orphans resident by +// choosing hashes that sort low. +void LimitOrphanTx(unsigned int nMaxOrphans) +{ + while (mapOrphanTransactions.size() > nMaxOrphans) + { + uint256 randomhash; + for (int i = 0; i < 4; i++) + ((uint64*)&randomhash)[i] = GetRand(_UI64_MAX); + map::iterator it = mapOrphanTransactions.lower_bound(randomhash); + if (it == mapOrphanTransactions.end()) + it = mapOrphanTransactions.begin(); + EraseOrphanTx(it->first); + } } void EraseOrphanTx(uint256 hash) -- cgit v1.2.3