Skip to content

Commit a8fa5cf

Browse files
authored
Make orphan TX map limiting dependent on total TX size instead of TX count (#3121)
1 parent 746b5f8 commit a8fa5cf

File tree

5 files changed

+23
-12
lines changed

5 files changed

+23
-12
lines changed

src/init.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ std::string HelpMessage(HelpMessageMode mode)
458458
}
459459
strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache));
460460
strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"));
461-
strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS));
461+
strUsage += HelpMessageOpt("-maxorphantxsize=<n>", strprintf(_("Maximum total size of all orphan transactions in megabytes (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE));
462462
strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE));
463463
strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY));
464464
if (showDebug) {
@@ -1431,6 +1431,10 @@ bool AppInitParameterInteraction()
14311431
return InitError("LLMQ type for ChainLocks can only be overridden on devnet.");
14321432
}
14331433

1434+
if (gArgs.IsArgSet("-maxorphantx")) {
1435+
InitWarning("-maxorphantx is not supported anymore. Use -maxorphantxsize instead.");
1436+
}
1437+
14341438
return true;
14351439
}
14361440

src/net_processing.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,12 @@ struct COrphanTx {
7474
CTransactionRef tx;
7575
NodeId fromPeer;
7676
int64_t nTimeExpire;
77+
size_t nTxSize;
7778
};
7879
static CCriticalSection g_cs_orphans;
7980
std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
8081
std::map<COutPoint, std::set<std::map<uint256, COrphanTx>::iterator, IteratorComparator>> mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans);
82+
size_t nMapOrphanTransactionsSize = 0;
8183
void EraseOrphansFor(NodeId peer);
8284

8385
static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
@@ -681,14 +683,16 @@ bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRE
681683
return false;
682684
}
683685

684-
auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME});
686+
auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME, sz});
685687
assert(ret.second);
686688
for (const CTxIn& txin : tx->vin) {
687689
mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first);
688690
}
689691

690692
AddToCompactExtraTransactions(tx);
691693

694+
nMapOrphanTransactionsSize += sz;
695+
692696
LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", hash.ToString(),
693697
mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
694698
return true;
@@ -708,6 +712,8 @@ int static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
708712
if (itPrev->second.empty())
709713
mapOrphanTransactionsByPrev.erase(itPrev);
710714
}
715+
assert(nMapOrphanTransactionsSize >= it->second.nTxSize);
716+
nMapOrphanTransactionsSize -= it->second.nTxSize;
711717
mapOrphanTransactions.erase(it);
712718
return 1;
713719
}
@@ -729,7 +735,7 @@ void EraseOrphansFor(NodeId peer)
729735
}
730736

731737

732-
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
738+
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphansSize)
733739
{
734740
LOCK(g_cs_orphans);
735741

@@ -754,7 +760,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
754760
nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL;
755761
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx due to expiration\n", nErased);
756762
}
757-
while (mapOrphanTransactions.size() > nMaxOrphans)
763+
while (!mapOrphanTransactions.empty() && nMapOrphanTransactionsSize > nMaxOrphansSize)
758764
{
759765
// Evict a random orphan:
760766
uint256 randomhash = GetRandHash();
@@ -2491,8 +2497,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
24912497
AddOrphanTx(ptx, pfrom->GetId());
24922498

24932499
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
2494-
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
2495-
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
2500+
unsigned int nMaxOrphanTxSize = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantxsize", DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE)) * 1000000;
2501+
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTxSize);
24962502
if (nEvicted > 0) {
24972503
LogPrint(BCLog::MEMPOOL, "mapOrphan overflow, removed %u tx\n", nEvicted);
24982504
}
@@ -3962,5 +3968,6 @@ class CNetProcessingCleanup
39623968
// orphan transactions
39633969
mapOrphanTransactions.clear();
39643970
mapOrphanTransactionsByPrev.clear();
3971+
nMapOrphanTransactionsSize = 0;
39653972
}
39663973
} instance_of_cnetprocessingcleanup;

src/net_processing.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
#include "validationinterface.h"
1111
#include "consensus/params.h"
1212

13-
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
14-
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
13+
/** Default for -maxorphantxsize, maximum size in megabytes the orphan map can grow before entries are removed */
14+
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE = 10; // this allows around 100 TXs of max size (and many more of normal size)
1515
/** Expiration time for orphan transactions in seconds */
1616
static const int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
1717
/** Minimum time between orphan transactions expire time checks in seconds */

test/functional/mempool_packages.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
class MempoolPackagesTest(BitcoinTestFramework):
1515
def set_test_params(self):
1616
self.num_nodes = 2
17-
self.extra_args = [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]]
17+
self.extra_args = [["-maxorphantxsize=1000"], ["-maxorphantxsize=1000", "-limitancestorcount=5"]]
1818

1919
# Build a transaction that spends parent_txid:vout
2020
# Return amount sent

test/functional/smartfees.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ def setup_network(self):
150150
But first we need to use one node to create a lot of outputs
151151
which we will use to generate our transactions.
152152
"""
153-
self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"],
154-
["-blockmaxsize=17000", "-maxorphantx=1000"],
155-
["-blockmaxsize=8000", "-maxorphantx=1000"]])
153+
self.add_nodes(3, extra_args=[["-maxorphantxsize=1000", "-whitelist=127.0.0.1"],
154+
["-blockmaxsize=17000", "-maxorphantxsize=1000"],
155+
["-blockmaxsize=8000", "-maxorphantxsize=1000"]])
156156
# Use node0 to mine blocks for input splitting
157157
# Node1 mines small blocks but that are bigger than the expected transaction rate.
158158
# NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes,

0 commit comments

Comments
 (0)