diff --git a/src/blockencodings.h b/src/blockencodings.h --- a/src/blockencodings.h +++ b/src/blockencodings.h @@ -6,6 +6,7 @@ #define BITCOIN_BLOCK_ENCODINGS_H #include "primitives/block.h" +#include "rwcollection.h" #include @@ -228,9 +229,10 @@ // extra_txn is a list of extra transactions to look at, in form. - ReadStatus - InitData(const CBlockHeaderAndShortTxIDs &cmpctblock, - const std::vector> &extra_txn); + ReadStatus InitData( + const CBlockHeaderAndShortTxIDs &cmpctblock, + const RWCollection>>:: + ReadView extra_txn); bool IsTxAvailable(size_t index) const; ReadStatus FillBlock(CBlock &block, const std::vector &vtx_missing); diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -9,6 +9,7 @@ #include "consensus/validation.h" #include "hash.h" #include "random.h" +#include "rwcollection.h" #include "streams.h" #include "txmempool.h" #include "util.h" @@ -48,7 +49,8 @@ ReadStatus PartiallyDownloadedBlock::InitData( const CBlockHeaderAndShortTxIDs &cmpctblock, - const std::vector> &extra_txns) { + const RWCollection>>:: + ReadView extra_txns) { if (cmpctblock.header.IsNull() || (cmpctblock.shorttxids.empty() && cmpctblock.prefilledtxn.empty())) { return READ_STATUS_INVALID; diff --git a/src/net_processing.h b/src/net_processing.h --- a/src/net_processing.h +++ b/src/net_processing.h @@ -8,6 +8,7 @@ #include "consensus/params.h" #include "net.h" +#include "rwcollection.h" #include "validationinterface.h" class Config; @@ -118,4 +119,74 @@ /** 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; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// OrphanPool +// +struct IteratorComparator { + template bool operator()(const I &a, const I &b) { + return &(*a) < &(*b); + } +}; + +class OrphanPool { +private: + typedef RWCollection> MapTxns; + MapTxns mapOrphanTransactions; + + typedef RWCollection< + std::map::iterator, + IteratorComparator>>> + MapTxnsByPrev; + MapTxnsByPrev mapOrphanTransactionsByPrev; + + mutable CCriticalSection cs_extraTxn; + size_t vExtraTxnForCompactIt = 0; + typedef RWCollection>> + TxnForCompact; + TxnForCompact vExtraTxnForCompact; + + // For internal use + int EraseOrphanTxInternal(uint256 hash, MapTxns::WriteView &orphanTxns, + MapTxnsByPrev::WriteView &orphanTxnsByPrev); + +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); + + bool HaveTransaction(uint256 txid) { + return mapOrphanTransactions.getReadView()->count(txid) > 0; + } + + MapTxns::ReadView OrphanTransactions() { + return mapOrphanTransactions.getReadView(); + } + + MapTxnsByPrev::ReadView OrphanTransactionsByPrev() { + return mapOrphanTransactionsByPrev.getReadView(); + } + + TxnForCompact::ReadView ExtraTxnForCompact() { + return vExtraTxnForCompact.getReadView(); + } + + void clear() { + mapOrphanTransactions.getWriteView()->clear(); + mapOrphanTransactionsByPrev.getWriteView()->clear(); + } +}; + +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 @@ -41,29 +41,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; -}; - -static CCriticalSection g_cs_orphans; -std::map mapOrphanTransactions GUARDED_BY(g_cs_orphans); -std::map::iterator, IteratorComparator>> - mapOrphanTransactionsByPrev GUARDED_BY(g_cs_orphans); -void EraseOrphansFor(NodeId peer); - -static size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0; -static std::vector> - vExtraTxnForCompact GUARDED_BY(g_cs_orphans); +OrphanPool g_orphanPool; // SHA256("main address relay")[0:8] static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; @@ -715,7 +693,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); @@ -760,27 +738,30 @@ // mapOrphanTransactions // -static void AddToCompactExtraTransactions(const CTransactionRef &tx) - EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) { +void OrphanPool::AddToCompactExtraTransactions(const CTransactionRef &tx) { + LOCK(cs_extraTxn); + size_t max_extra_txn = gArgs.GetArg("-blockreconstructionextratxn", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN); if (max_extra_txn <= 0) { return; } - if (!vExtraTxnForCompact.size()) { - vExtraTxnForCompact.resize(max_extra_txn); + auto extraTxns = vExtraTxnForCompact.getWriteView(); + if (!extraTxns->size()) { + extraTxns->resize(max_extra_txn); } - vExtraTxnForCompact[vExtraTxnForCompactIt] = - std::make_pair(tx->GetId(), tx); + extraTxns[vExtraTxnForCompactIt] = std::make_pair(tx->GetId(), tx); vExtraTxnForCompactIt = (vExtraTxnForCompactIt + 1) % max_extra_txn; } -bool AddOrphanTx(const CTransactionRef &tx, NodeId peer) - EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) { +bool OrphanPool::AddOrphanTx(const CTransactionRef &tx, NodeId peer) { const uint256 &txid = tx->GetId(); - if (mapOrphanTransactions.count(txid)) { + auto orphanTxns = mapOrphanTransactions.getWriteView(); + auto orphanTxnsByPrev = mapOrphanTransactionsByPrev.getWriteView(); + + if (orphanTxns->count(txid)) { return false; } @@ -798,78 +779,86 @@ return false; } - auto ret = mapOrphanTransactions.emplace( + auto ret = orphanTxns->emplace( txid, COrphanTx{tx, peer, GetTime() + ORPHAN_TX_EXPIRE_TIME}); assert(ret.second); for (const CTxIn &txin : tx->vin) { - mapOrphanTransactionsByPrev[txin.prevout].insert(ret.first); + orphanTxnsByPrev[txin.prevout].insert(ret.first); } AddToCompactExtraTransactions(tx); LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u outsz %u)\n", - txid.ToString(), mapOrphanTransactions.size(), - mapOrphanTransactionsByPrev.size()); + txid.ToString(), orphanTxns->size(), orphanTxnsByPrev->size()); return true; } -static int EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans) { - std::map::iterator it = - mapOrphanTransactions.find(hash); - if (it == mapOrphanTransactions.end()) { +int OrphanPool::EraseOrphanTxInternal( + uint256 hash, MapTxns::WriteView &orphanTxns, + MapTxnsByPrev::WriteView &orphanTxnsByPrev) { + std::map::iterator it = orphanTxns->find(hash); + if (it == orphanTxns.end()) { return 0; } for (const CTxIn &txin : it->second.tx->vin) { - auto itPrev = mapOrphanTransactionsByPrev.find(txin.prevout); - if (itPrev == mapOrphanTransactionsByPrev.end()) { + auto itPrev = orphanTxnsByPrev->find(txin.prevout); + if (itPrev == orphanTxnsByPrev.end()) { continue; } itPrev->second.erase(it); if (itPrev->second.empty()) { - mapOrphanTransactionsByPrev.erase(itPrev); + orphanTxnsByPrev->erase(itPrev); } } - mapOrphanTransactions.erase(it); + orphanTxns->erase(it); return 1; } -void EraseOrphansFor(NodeId peer) { - LOCK(g_cs_orphans); +int OrphanPool::EraseOrphanTx(uint256 hash) { + auto orphanTxns = mapOrphanTransactions.getWriteView(); + auto orphanTxnsByPrev = mapOrphanTransactionsByPrev.getWriteView(); + return EraseOrphanTxInternal(hash, orphanTxns, orphanTxnsByPrev); +} + +void OrphanPool::EraseOrphansFor(NodeId peer) { int nErased = 0; - std::map::iterator iter = mapOrphanTransactions.begin(); - while (iter != mapOrphanTransactions.end()) { - // Increment to avoid iterator becoming invalid. - std::map::iterator maybeErase = iter++; - if (maybeErase->second.fromPeer == peer) { - nErased += EraseOrphanTx(maybeErase->second.tx->GetId()); + auto orphanTxns = mapOrphanTransactions.getWriteView(); + auto orphanTxnsByPrev = mapOrphanTransactionsByPrev.getWriteView(); + + for (const auto &maybeErase : orphanTxns) { + if (maybeErase.second.fromPeer == peer) { + nErased += EraseOrphanTxInternal(maybeErase.second.tx->GetId(), + orphanTxns, orphanTxnsByPrev); } } + if (nErased > 0) { LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer); } } -unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) { - LOCK(g_cs_orphans); - +unsigned int OrphanPool::LimitOrphanTxSize(unsigned int nMaxOrphans) { unsigned int nEvicted = 0; static int64_t nNextSweep; int64_t nNow = GetTime(); + + auto orphanTxns = mapOrphanTransactions.getWriteView(); + auto orphanTxnsByPrev = mapOrphanTransactionsByPrev.getWriteView(); + if (nNextSweep <= nNow) { // Sweep out expired orphan pool entries: int nErased = 0; int64_t nMinExpTime = nNow + ORPHAN_TX_EXPIRE_TIME - ORPHAN_TX_EXPIRE_INTERVAL; - std::map::iterator iter = - mapOrphanTransactions.begin(); - while (iter != mapOrphanTransactions.end()) { - std::map::iterator maybeErase = iter++; - if (maybeErase->second.nTimeExpire <= nNow) { - nErased += EraseOrphanTx(maybeErase->second.tx->GetId()); + for (const auto &maybeErase : orphanTxns) { + if (maybeErase.second.nTimeExpire <= nNow) { + EraseOrphanTxInternal(maybeErase.second.tx->GetId(), orphanTxns, + orphanTxnsByPrev); + nErased++; } else { nMinExpTime = - std::min(maybeErase->second.nTimeExpire, nMinExpTime); + std::min(maybeErase.second.nTimeExpire, nMinExpTime); } } // Sweep again 5 minutes after the next entry that expires in order to @@ -880,15 +869,15 @@ nErased); } } - while (mapOrphanTransactions.size() > nMaxOrphans) { + while (orphanTxns->size() > nMaxOrphans) { // Evict a random orphan: uint256 randomhash = GetRandHash(); std::map::iterator it = - mapOrphanTransactions.lower_bound(randomhash); - if (it == mapOrphanTransactions.end()) { - it = mapOrphanTransactions.begin(); + orphanTxns->lower_bound(randomhash); + if (it == orphanTxns.end()) { + it = orphanTxns.begin(); } - EraseOrphanTx(it->first); + EraseOrphanTxInternal(it->first, orphanTxns, orphanTxnsByPrev); ++nEvicted; } return nEvicted; @@ -979,25 +968,27 @@ void PeerLogicValidation::BlockConnected( const std::shared_ptr &pblock, const CBlockIndex *pindex, const std::vector &vtxConflicted) { - LOCK(g_cs_orphans); - std::vector vOrphanErase; - for (const CTransactionRef &ptx : pblock->vtx) { - const CTransaction &tx = *ptx; + { + auto orphansByPrev = g_orphanPool.OrphanTransactionsByPrev(); - // 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()) { - continue; - } + for (const CTransactionRef &ptx : pblock->vtx) { + const CTransaction &tx = *ptx; + + // Which orphan pool entries must we evict? + for (size_t j = 0; j < tx.vin.size(); j++) { + auto itByPrev = orphansByPrev->find(tx.vin[j].prevout); + if (itByPrev == orphansByPrev.end()) { + continue; + } - for (auto mi = itByPrev->second.begin(); - mi != itByPrev->second.end(); ++mi) { - const CTransaction &orphanTx = *(*mi)->second.tx; - const uint256 &orphanHash = orphanTx.GetHash(); - vOrphanErase.push_back(orphanHash); + for (auto mi = itByPrev->second.begin(); + mi != itByPrev->second.end(); ++mi) { + const CTransaction &orphanTx = *(*mi)->second.tx; + const uint256 &orphanHash = orphanTx.GetHash(); + vOrphanErase.push_back(orphanHash); + } } } } @@ -1006,7 +997,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", @@ -1172,13 +1163,6 @@ recentRejects->reset(); } - { - LOCK(g_cs_orphans); - if (mapOrphanTransactions.count(inv.hash)) { - return true; - } - } - // Use pcoinsTip->HaveCoinInCache as a quick approximation to // exclude requesting or processing some txs which have already been // included in a block. As this is best effort, we only check for @@ -1186,6 +1170,7 @@ // diminishing returns with 2 onward. return recentRejects->contains(inv.hash) || g_mempool.exists(inv.hash) || + g_orphanPool.HaveTransaction(inv.hash) || pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 0)) || pcoinsTip->HaveCoinInCache(COutPoint(inv.hash, 1)); } @@ -2485,7 +2470,7 @@ CInv inv(MSG_TX, tx.GetId()); pfrom->AddInventoryKnown(inv); - LOCK2(cs_main, g_cs_orphans); + LOCK(cs_main); bool fMissingInputs = false; CValidationState state; @@ -2513,75 +2498,83 @@ // Recursively process any orphan transactions that depended on this // one std::unordered_map rejectCountPerNode; - while (!vWorkQueue.empty()) { - auto itByPrev = - mapOrphanTransactionsByPrev.find(vWorkQueue.front()); - vWorkQueue.pop_front(); - if (itByPrev == mapOrphanTransactionsByPrev.end()) { - continue; - } - for (auto mi = itByPrev->second.begin(); - mi != itByPrev->second.end(); ++mi) { - const CTransactionRef &porphanTx = (*mi)->second.tx; - const CTransaction &orphanTx = *porphanTx; - const uint256 &orphanId = orphanTx.GetId(); - NodeId fromPeer = (*mi)->second.fromPeer; - bool fMissingInputs2 = false; - // Use a dummy CValidationState so someone can't setup nodes - // to counter-DoS based on orphan resolution (that is, - // feeding people an invalid transaction based on LegitTxX - // in order to get anyone relaying LegitTxX banned) - CValidationState stateDummy; - - auto it = rejectCountPerNode.find(fromPeer); - if (it != rejectCountPerNode.end() && - it->second > MAX_NON_STANDARD_ORPHAN_PER_NODE) { + { + auto orphanByPrev = g_orphanPool.OrphanTransactionsByPrev(); + while (!vWorkQueue.empty()) { + auto itByPrev = orphanByPrev->find(vWorkQueue.front()); + vWorkQueue.pop_front(); + if (itByPrev == orphanByPrev.end()) { continue; } - - if (AcceptToMemoryPool(config, g_mempool, stateDummy, - porphanTx, true, &fMissingInputs2)) { - LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", - orphanId.ToString()); - RelayTransaction(orphanTx, connman); - for (size_t i = 0; i < orphanTx.vout.size(); i++) { - vWorkQueue.emplace_back(orphanId, i); + for (auto mi = itByPrev->second.begin(); + mi != itByPrev->second.end(); ++mi) { + const CTransactionRef &porphanTx = (*mi)->second.tx; + const CTransaction &orphanTx = *porphanTx; + const uint256 &orphanId = orphanTx.GetId(); + NodeId fromPeer = (*mi)->second.fromPeer; + bool fMissingInputs2 = false; + // Use a dummy CValidationState so someone can't setup + // nodes to counter-DoS based on orphan resolution (that + // is, feeding people an invalid transaction based on + // LegitTxX in order to get anyone relaying LegitTxX + // banned) + CValidationState stateDummy; + + auto it = rejectCountPerNode.find(fromPeer); + if (it != rejectCountPerNode.end() && + it->second > MAX_NON_STANDARD_ORPHAN_PER_NODE) { + continue; } - vEraseQueue.push_back(orphanId); - } else if (!fMissingInputs2) { - int nDos = 0; - if (stateDummy.IsInvalid(nDos)) { - rejectCountPerNode[fromPeer]++; - if (nDos > 0) { - // Punish peer that gave us an invalid orphan tx - Misbehaving(fromPeer, nDos, - "invalid-orphan-tx"); - LogPrint(BCLog::MEMPOOL, - " invalid orphan tx %s\n", - orphanId.ToString()); + + if (AcceptToMemoryPool(config, g_mempool, stateDummy, + porphanTx, true, + &fMissingInputs2)) { + LogPrint(BCLog::MEMPOOL, + " accepted orphan tx %s\n", + orphanId.ToString()); + RelayTransaction(orphanTx, connman); + for (size_t i = 0; i < orphanTx.vout.size(); i++) { + vWorkQueue.emplace_back(orphanId, i); + } + vEraseQueue.push_back(orphanId); + } else if (!fMissingInputs2) { + int nDos = 0; + if (stateDummy.IsInvalid(nDos)) { + rejectCountPerNode[fromPeer]++; + if (nDos > 0) { + // Punish peer that gave us an invalid + // orphan tx + Misbehaving(fromPeer, nDos, + "invalid-orphan-tx"); + LogPrint(BCLog::MEMPOOL, + " invalid orphan tx %s\n", + orphanId.ToString()); + } + } + // Has inputs but not accepted to mempool + // Probably non-standard or insufficient + // fee/priority + LogPrint(BCLog::MEMPOOL, + " removed orphan tx %s\n", + orphanId.ToString()); + vEraseQueue.push_back(orphanId); + if (!stateDummy.CorruptionPossible()) { + // Do not use rejection cache for witness + // transactions or witness-stripped + // transactions, as they can have been + // malleated. See + // https://github.com/bitcoin/bitcoin/issues/8279 + // for details. + assert(recentRejects); + recentRejects->insert(orphanId); } } - // Has inputs but not accepted to mempool - // Probably non-standard or insufficient fee/priority - LogPrint(BCLog::MEMPOOL, " removed orphan tx %s\n", - orphanId.ToString()); - vEraseQueue.push_back(orphanId); - if (!stateDummy.CorruptionPossible()) { - // Do not use rejection cache for witness - // transactions or witness-stripped transactions, as - // they can have been malleated. See - // https://github.com/bitcoin/bitcoin/issues/8279 - // for details. - assert(recentRejects); - recentRejects->insert(orphanId); - } + g_mempool.check(pcoinsTip.get()); } - g_mempool.check(pcoinsTip.get()); } } - 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 @@ -2602,14 +2595,15 @@ pfrom->AskFor(_inv); } } - AddOrphanTx(ptx, pfrom->GetId()); + g_orphanPool.AddOrphanTx(ptx, pfrom->GetId()); // DoS prevention: do not allow mapOrphanTransactions to grow // unbounded unsigned int nMaxOrphanTx = (unsigned int)std::max( 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); @@ -2631,7 +2625,7 @@ assert(recentRejects); recentRejects->insert(tx.GetId()); if (RecursiveDynamicUsage(*ptx) < 100000) { - AddToCompactExtraTransactions(ptx); + g_orphanPool.AddToCompactExtraTransactions(ptx); } } @@ -2750,7 +2744,7 @@ bool fBlockReconstructed = false; { - LOCK2(cs_main, g_cs_orphans); + LOCK(cs_main); // If AcceptBlockHeader returned true, it set pindex assert(pindex); UpdateBlockAvailability(pfrom->GetId(), pindex->GetBlockHash()); @@ -2826,8 +2820,8 @@ PartiallyDownloadedBlock &partialBlock = *(*queuedBlockIt)->partialBlock; - ReadStatus status = - partialBlock.InitData(cmpctblock, vExtraTxnForCompact); + ReadStatus status = partialBlock.InitData( + cmpctblock, g_orphanPool.ExtraTxnForCompact()); if (status == READ_STATUS_INVALID) { // Reset in-flight state in case of whitelist MarkBlockAsReceived(pindex->GetBlockHash()); @@ -2869,8 +2863,8 @@ // download from. Optimistically try to reconstruct anyway // since we might be able to without any round trips. PartiallyDownloadedBlock tempBlock(config, &g_mempool); - ReadStatus status = - tempBlock.InitData(cmpctblock, vExtraTxnForCompact); + ReadStatus status = tempBlock.InitData( + cmpctblock, g_orphanPool.ExtraTxnForCompact()); if (status != READ_STATUS_OK) { // TODO: don't ignore failures return true; @@ -4356,7 +4350,6 @@ CNetProcessingCleanup() {} ~CNetProcessingCleanup() { // orphan transactions - mapOrphanTransactions.clear(); - mapOrphanTransactionsByPrev.clear(); + g_orphanPool.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; @@ -309,11 +298,11 @@ } CTransactionRef RandomOrphan() { - std::map::iterator it; - LOCK(cs_main); - it = mapOrphanTransactions.lower_bound(InsecureRand256()); - if (it == mapOrphanTransactions.end()) { - it = mapOrphanTransactions.begin(); + std::map::const_iterator it; + auto mapOrphans = g_orphanPool.OrphanTransactions(); + it = mapOrphans->lower_bound(InsecureRand256()); + if (it == mapOrphans.end()) { + it = mapOrphans.begin(); } return it->second.tx; } @@ -335,7 +324,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: @@ -351,7 +340,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: @@ -373,24 +362,26 @@ 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)); } - LOCK(cs_main); + size_t sizeBefore; // Test EraseOrphansFor: for (NodeId i = 0; i < 3; i++) { - size_t sizeBefore = mapOrphanTransactions.size(); - EraseOrphansFor(i); - BOOST_CHECK(mapOrphanTransactions.size() < sizeBefore); + { + sizeBefore = g_orphanPool.OrphanTransactions()->size(); + } + g_orphanPool.EraseOrphansFor(i); + { BOOST_CHECK(g_orphanPool.OrphanTransactions()->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.OrphanTransactions()->size() <= 40); } + g_orphanPool.LimitOrphanTxSize(10); + { BOOST_CHECK(g_orphanPool.OrphanTransactions()->size() <= 10); } + g_orphanPool.LimitOrphanTxSize(0); + { BOOST_CHECK(g_orphanPool.OrphanTransactions()->empty()); } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -7,12 +7,13 @@ #include "config.h" #include "consensus/merkle.h" #include "random.h" +#include "rwcollection.h" #include "test/test_bitcoin.h" #include -std::vector> extra_txn; +RWCollection>> extra_txn; struct RegtestingSetup : public TestingSetup { RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {} @@ -81,7 +82,7 @@ stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(GetConfig(), &pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn.getReadView()) == READ_STATUS_OK); BOOST_CHECK(partialBlock.IsTxAvailable(0)); BOOST_CHECK(!partialBlock.IsTxAvailable(1)); @@ -199,7 +200,7 @@ stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(GetConfig(), &pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn.getReadView()) == READ_STATUS_OK); BOOST_CHECK(!partialBlock.IsTxAvailable(0)); BOOST_CHECK(partialBlock.IsTxAvailable(1)); @@ -282,7 +283,7 @@ stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(GetConfig(), &pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn.getReadView()) == READ_STATUS_OK); BOOST_CHECK(partialBlock.IsTxAvailable(0)); BOOST_CHECK(partialBlock.IsTxAvailable(1)); @@ -349,7 +350,7 @@ stream >> shortIDs2; PartiallyDownloadedBlock partialBlock(GetConfig(), &pool); - BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn) == + BOOST_CHECK(partialBlock.InitData(shortIDs2, extra_txn.getReadView()) == READ_STATUS_OK); BOOST_CHECK(partialBlock.IsTxAvailable(0));