diff --git a/src/net_processing.h b/src/net_processing.h --- a/src/net_processing.h +++ b/src/net_processing.h @@ -118,4 +118,45 @@ /** Increase a node's misbehavior score. */ void Misbehaving(NodeId nodeid, int howmuch, const std::string &reason); +struct COrphanTx { + // When modifying, adapt the copy of this definition in tests/DoS_tests. + CTransactionRef tx; + NodeId fromPeer; + int64_t nTimeExpire; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// mapOrphanTransactions +// +struct IteratorComparator { + template bool operator()(const I &a, const I &b) { + return &(*a) < &(*b); + } +}; + +class OrphanPool { +private: + mutable CCriticalSection cs; + +public: + std::map mapOrphanTransactions; + std::map::iterator, + IteratorComparator>> + mapOrphanTransactionsByPrev; + + size_t vExtraTxnForCompactIt = 0; + std::vector> vExtraTxnForCompact; + +public: + int EraseOrphanTx(uint256 hash); + void AddToCompactExtraTransactions(const CTransactionRef &tx); + bool AddOrphanTx(const CTransactionRef &tx, NodeId peer); + void EraseOrphansFor(NodeId peer); + + unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans); +}; + +extern OrphanPool g_orphanPool; + #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/net_processing.cpp b/src/net_processing.cpp --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -42,27 +42,7 @@ // Used only to inform the wallet of when we last received a block. std::atomic nTimeBestReceived(0); -struct IteratorComparator { - template bool operator()(const I &a, const I &b) { - return &(*a) < &(*b); - } -}; - -struct COrphanTx { - // When modifying, adapt the copy of this definition in tests/DoS_tests. - CTransactionRef tx; - NodeId fromPeer; - int64_t nTimeExpire; -}; -std::map mapOrphanTransactions GUARDED_BY(cs_main); -std::map::iterator, IteratorComparator>> - mapOrphanTransactionsByPrev GUARDED_BY(cs_main); -void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - -static size_t vExtraTxnForCompactIt = 0; -static std::vector> - vExtraTxnForCompact GUARDED_BY(cs_main); +OrphanPool g_orphanPool; // SHA256("main address relay")[0:8] static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; @@ -693,7 +673,7 @@ for (const QueuedBlock &entry : state->vBlocksInFlight) { mapBlocksInFlight.erase(entry.hash); } - EraseOrphansFor(nodeid); + g_orphanPool.EraseOrphansFor(nodeid); nPreferredDownload -= state->fPreferredDownload; nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); assert(nPeersWithValidatedDownloads >= 0); @@ -738,8 +718,9 @@ // mapOrphanTransactions // -static void AddToCompactExtraTransactions(const CTransactionRef &tx) - EXCLUSIVE_LOCKS_REQUIRED(cs_main) { +void OrphanPool::AddToCompactExtraTransactions(const CTransactionRef &tx) { + LOCK(cs); + size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); if (max_extra_txn <= 0) { @@ -755,8 +736,8 @@ vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn; } -bool AddOrphanTx(const CTransactionRef &tx, NodeId peer) - EXCLUSIVE_LOCKS_REQUIRED(cs_main) { +bool OrphanPool::AddOrphanTx(const CTransactionRef &tx, NodeId peer) { + LOCK(cs); const uint256 &txid = tx->GetId(); if (mapOrphanTransactions.count(txid)) { return false; @@ -791,7 +772,9 @@ return true; } -static int EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { +int OrphanPool::EraseOrphanTx(uint256 hash) { + LOCK(cs); + std::map::iterator it = mapOrphanTransactions.find(hash); if (it == mapOrphanTransactions.end()) { @@ -811,7 +794,9 @@ return 1; } -void EraseOrphansFor(NodeId peer) { +void OrphanPool::EraseOrphansFor(NodeId peer) { + LOCK(cs); + int nErased = 0; std::map::iterator iter = mapOrphanTransactions.begin(); while (iter != mapOrphanTransactions.end()) { @@ -827,8 +812,8 @@ } } -unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) - EXCLUSIVE_LOCKS_REQUIRED(cs_main) { +unsigned int OrphanPool::LimitOrphanTxSize(unsigned int nMaxOrphans) { + LOCK(cs); unsigned int nEvicted = 0; static int64_t nNextSweep; int64_t nNow = GetTime(); @@ -848,7 +833,8 @@ std::min(maybeErase->second.nTimeExpire, nMinExpTime); } } - // Sweep again 5 minutes after the next entry that expires in order to + // Sweep again 5 minutes after the next entry that expires in order + // to // batch the linear scan. nNextSweep = nMinExpTime + ORPHAN_TX_EXPIRE_INTERVAL; if (nErased > 0) { @@ -939,8 +925,9 @@ // Which orphan pool entries must we evict? for (size_t j = 0; j < tx.vin.size(); j++) { - auto itByPrev = mapOrphanTransactionsByPrev.find(tx.vin[j].prevout); - if (itByPrev == mapOrphanTransactionsByPrev.end()) { + auto itByPrev = g_orphanPool.mapOrphanTransactionsByPrev.find( + tx.vin[j].prevout); + if (itByPrev == g_orphanPool.mapOrphanTransactionsByPrev.end()) { continue; } @@ -957,7 +944,7 @@ if (vOrphanErase.size()) { int nErased = 0; for (uint256 &orphanId : vOrphanErase) { - nErased += EraseOrphanTx(orphanId); + nErased += g_orphanPool.EraseOrphanTx(orphanId); } LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", @@ -1124,7 +1111,7 @@ // diminishing returns with 2 onward. return recentRejects->contains(inv.hash) || mempool.exists(inv.hash) || - mapOrphanTransactions.count(inv.hash) || + g_orphanPool.mapOrphanTransactions.count(inv.hash) || pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1)); } @@ -2410,10 +2397,11 @@ // one std::set setMisbehaving; while (!vWorkQueue.empty()) { - auto itByPrev = - mapOrphanTransactionsByPrev.find(vWorkQueue.front()); + auto itByPrev = g_orphanPool.mapOrphanTransactionsByPrev.find( + vWorkQueue.front()); vWorkQueue.pop_front(); - if (itByPrev == mapOrphanTransactionsByPrev.end()) { + if (itByPrev == + g_orphanPool.mapOrphanTransactionsByPrev.end()) { continue; } for (auto mi = itByPrev->second.begin(); @@ -2471,7 +2459,7 @@ } for (const uint256 &hash : vEraseQueue) { - EraseOrphanTx(hash); + g_orphanPool.EraseOrphanTx(hash); } } else if (fMissingInputs) { // It may be the case that the orphans parents have all been @@ -2492,7 +2480,7 @@ pfrom->AskFor(_inv); } } - AddOrphanTx(ptx, pfrom->GetId()); + g_orphanPool.AddOrphanTx(ptx, pfrom->GetId()); // DoS prevention: do not allow mapOrphanTransactions to grow // unbounded @@ -2500,7 +2488,8 @@ int64_t(0), gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); + unsigned int nEvicted = + g_orphanPool.LimitOrphanTxSize(nMaxOrphanTx); if (nEvicted > 0) { LogPrint(BCLog::MEMPOOL, "mapOrphan overflow, removed %u tx\n", nEvicted); @@ -2522,7 +2511,7 @@ assert(recentRejects); recentRejects->insert(tx.GetId()); if (RecursiveDynamicUsage(*ptx) < 100000) { - AddToCompactExtraTransactions(ptx); + g_orphanPool.AddToCompactExtraTransactions(ptx); } } @@ -2715,8 +2704,8 @@ PartiallyDownloadedBlock &partialBlock = *(*queuedBlockIt)->partialBlock; - ReadStatus status = - partialBlock.InitData(cmpctblock, vExtraTxnForCompact); + ReadStatus status = partialBlock.InitData( + cmpctblock, g_orphanPool.vExtraTxnForCompact); if (status == READ_STATUS_INVALID) { // Reset in-flight state in case of whitelist MarkBlockAsReceived(pindex->GetBlockHash()); @@ -2758,8 +2747,8 @@ // download from. Optimistically try to reconstruct anyway // since we might be able to without any round trips. PartiallyDownloadedBlock tempBlock(config, &mempool); - ReadStatus status = - tempBlock.InitData(cmpctblock, vExtraTxnForCompact); + ReadStatus status = tempBlock.InitData( + cmpctblock, g_orphanPool.vExtraTxnForCompact); if (status != READ_STATUS_OK) { // TODO: don't ignore failures return true; @@ -4216,7 +4205,7 @@ CNetProcessingCleanup() {} ~CNetProcessingCleanup() { // orphan transactions - mapOrphanTransactions.clear(); - mapOrphanTransactionsByPrev.clear(); + g_orphanPool.mapOrphanTransactions.clear(); + g_orphanPool.mapOrphanTransactionsByPrev.clear(); } } instance_of_cnetprocessingcleanup; diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -21,17 +21,6 @@ #include -// Tests these internal-to-net_processing.cpp methods: -extern bool AddOrphanTx(const CTransactionRef &tx, NodeId peer); -extern void EraseOrphansFor(NodeId peer); -extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans); -struct COrphanTx { - CTransactionRef tx; - NodeId fromPeer; - int64_t nTimeExpire; -}; -extern std::map mapOrphanTransactions; - CService ip(uint32_t i) { struct in_addr s; s.s_addr = i; @@ -282,9 +271,9 @@ CTransactionRef RandomOrphan() { std::map::iterator it; - it = mapOrphanTransactions.lower_bound(InsecureRand256()); - if (it == mapOrphanTransactions.end()) { - it = mapOrphanTransactions.begin(); + it = g_orphanPool.mapOrphanTransactions.lower_bound(InsecureRand256()); + if (it == g_orphanPool.mapOrphanTransactions.end()) { + it = g_orphanPool.mapOrphanTransactions.begin(); } return it->second.tx; } @@ -306,7 +295,7 @@ tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID()); - AddOrphanTx(MakeTransactionRef(tx), i); + g_orphanPool.AddOrphanTx(MakeTransactionRef(tx), i); } // ... and 50 that depend on other orphans: @@ -322,7 +311,7 @@ GetScriptForDestination(key.GetPubKey().GetID()); SignSignature(keystore, *txPrev, tx, 0, SigHashType()); - AddOrphanTx(MakeTransactionRef(tx), i); + g_orphanPool.AddOrphanTx(MakeTransactionRef(tx), i); } // This really-big orphan should be ignored: @@ -344,23 +333,23 @@ for (unsigned int j = 1; j < tx.vin.size(); j++) tx.vin[j].scriptSig = tx.vin[0].scriptSig; - BOOST_CHECK(!AddOrphanTx(MakeTransactionRef(tx), i)); + BOOST_CHECK(!g_orphanPool.AddOrphanTx(MakeTransactionRef(tx), i)); } // Test EraseOrphansFor: for (NodeId i = 0; i < 3; i++) { - size_t sizeBefore = mapOrphanTransactions.size(); - EraseOrphansFor(i); - BOOST_CHECK(mapOrphanTransactions.size() < sizeBefore); + size_t sizeBefore = g_orphanPool.mapOrphanTransactions.size(); + g_orphanPool.EraseOrphansFor(i); + BOOST_CHECK(g_orphanPool.mapOrphanTransactions.size() < sizeBefore); } // Test LimitOrphanTxSize() function: - LimitOrphanTxSize(40); - BOOST_CHECK(mapOrphanTransactions.size() <= 40); - LimitOrphanTxSize(10); - BOOST_CHECK(mapOrphanTransactions.size() <= 10); - LimitOrphanTxSize(0); - BOOST_CHECK(mapOrphanTransactions.empty()); + g_orphanPool.LimitOrphanTxSize(40); + BOOST_CHECK(g_orphanPool.mapOrphanTransactions.size() <= 40); + g_orphanPool.LimitOrphanTxSize(10); + BOOST_CHECK(g_orphanPool.mapOrphanTransactions.size() <= 10); + g_orphanPool.LimitOrphanTxSize(0); + BOOST_CHECK(g_orphanPool.mapOrphanTransactions.empty()); } BOOST_AUTO_TEST_SUITE_END()