mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
Merge bitcoin/bitcoin#26295: Replace global g_cs_orphans lock with local
7082ce3e88
scripted-diff: rename and de-globalise g_cs_orphans (Anthony Towns)733d85f79c
Move all g_cs_orphans locking to txorphanage (Anthony Towns)a936f41a5d
txorphanage: make m_peer_work_set private (Anthony Towns)3614819864
txorphange: move orphan workset to txorphanage (Anthony Towns)6f8e442ba6
net_processing: Localise orphan_work_set handling to ProcessOrphanTx (Anthony Towns)0027174b39
net_processing: move ProcessOrphanTx docs to declaration (Anthony Towns)9910ed755c
net_processing: Pass a Peer& to ProcessOrphanTx (Anthony Towns)89e2e0da0b
net_processing: move extra transactions to msgproc mutex (Anthony Towns)ff8d44d196
Remove unnecessary includes of txorphange.h (Anthony Towns) Pull request description: Moves extra transactions to be under the `m_msgproc_mutex` lock rather than `g_cs_orphans` and refactors orphan handling so that the lock can be internal to the `TxOrphange` class. ACKs for top commit: dergoegge: Code review ACK7082ce3e88
glozow: ACK7082ce3e88
via code review and some [basic testing](https://github.com/glozow/bitcoin/blob/review-26295/src/test/orphanage_tests.cpp#L150). I think putting txorphanage in charge of handling peer work sets is the right direction. Tree-SHA512: 1ec454c3a69ebd45ff652770d6a55c6b183db71aba4d12639ed70f525f0035e069a81d06e9b65b66e87929c607080a1c5e5dcd2ca91eaa2cf202dc6c02aa6818
This commit is contained in:
commit
a79b720092
8 changed files with 124 additions and 97 deletions
|
@ -67,7 +67,6 @@
|
|||
#include <torcontrol.h>
|
||||
#include <txdb.h>
|
||||
#include <txmempool.h>
|
||||
#include <txorphanage.h>
|
||||
#include <util/asmap.h>
|
||||
#include <util/check.h>
|
||||
#include <util/moneystr.h>
|
||||
|
|
|
@ -365,9 +365,6 @@ struct Peer {
|
|||
/** Total number of addresses that were processed (excludes rate-limited ones). */
|
||||
std::atomic<uint64_t> m_addr_processed{0};
|
||||
|
||||
/** Set of txids to reconsider once their parent transactions have been accepted **/
|
||||
std::set<uint256> m_orphan_work_set GUARDED_BY(g_cs_orphans);
|
||||
|
||||
/** Whether we've sent this peer a getheaders in response to an inv prior to initial-headers-sync completing */
|
||||
bool m_inv_triggered_getheaders_before_sync GUARDED_BY(NetEventsInterface::g_msgproc_mutex){false};
|
||||
|
||||
|
@ -584,8 +581,17 @@ private:
|
|||
*/
|
||||
bool MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer);
|
||||
|
||||
void ProcessOrphanTx(std::set<uint256>& orphan_work_set) EXCLUSIVE_LOCKS_REQUIRED(cs_main, g_cs_orphans)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
|
||||
/**
|
||||
* Reconsider orphan transactions after a parent has been accepted to the mempool.
|
||||
*
|
||||
* @peer[in] peer The peer whose orphan transactions we will reconsider. Generally only one
|
||||
* orphan will be reconsidered on each call of this function. This set
|
||||
* may be added to if accepting an orphan causes its children to be
|
||||
* reconsidered.
|
||||
* @return True if there are still orphans in this peer's work set.
|
||||
*/
|
||||
bool ProcessOrphanTx(Peer& peer)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
|
||||
/** Process a single headers message from a peer.
|
||||
*
|
||||
* @param[in] pfrom CNode of the peer
|
||||
|
@ -919,14 +925,14 @@ private:
|
|||
/** Storage for orphan information */
|
||||
TxOrphanage m_orphanage;
|
||||
|
||||
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
||||
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||
|
||||
/** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
|
||||
* The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
|
||||
* these are kept in a ring buffer */
|
||||
std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_cs_orphans);
|
||||
std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUARDED_BY(g_msgproc_mutex);
|
||||
/** Offset into vExtraTxnForCompact to insert the next tx */
|
||||
size_t vExtraTxnForCompactIt GUARDED_BY(g_cs_orphans) = 0;
|
||||
size_t vExtraTxnForCompactIt GUARDED_BY(g_msgproc_mutex) = 0;
|
||||
|
||||
/** Check whether the last unknown block a peer advertised is not yet known. */
|
||||
void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
@ -1490,7 +1496,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
|
|||
for (const QueuedBlock& entry : state->vBlocksInFlight) {
|
||||
mapBlocksInFlight.erase(entry.pindex->GetBlockHash());
|
||||
}
|
||||
WITH_LOCK(g_cs_orphans, m_orphanage.EraseForPeer(nodeid));
|
||||
m_orphanage.EraseForPeer(nodeid);
|
||||
m_txrequest.DisconnectedPeer(nodeid);
|
||||
if (m_txreconciliation) m_txreconciliation->ForgetPeer(nodeid);
|
||||
m_num_preferred_download_peers -= state->fPreferredDownload;
|
||||
|
@ -2880,33 +2886,24 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer,
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconsider orphan transactions after a parent has been accepted to the mempool.
|
||||
*
|
||||
* @param[in,out] orphan_work_set The set of orphan transactions to reconsider. Generally only one
|
||||
* orphan will be reconsidered on each call of this function. This set
|
||||
* may be added to if accepting an orphan causes its children to be
|
||||
* reconsidered.
|
||||
*/
|
||||
void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
||||
bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
|
||||
{
|
||||
AssertLockHeld(g_msgproc_mutex);
|
||||
AssertLockHeld(cs_main);
|
||||
AssertLockHeld(g_cs_orphans);
|
||||
|
||||
while (!orphan_work_set.empty()) {
|
||||
const uint256 orphanHash = *orphan_work_set.begin();
|
||||
orphan_work_set.erase(orphan_work_set.begin());
|
||||
|
||||
const auto [porphanTx, from_peer] = m_orphanage.GetTx(orphanHash);
|
||||
if (porphanTx == nullptr) continue;
|
||||
CTransactionRef porphanTx = nullptr;
|
||||
NodeId from_peer = -1;
|
||||
bool more = false;
|
||||
|
||||
while (CTransactionRef porphanTx = m_orphanage.GetTxToReconsider(peer.m_id, from_peer, more)) {
|
||||
const MempoolAcceptResult result = m_chainman.ProcessTransaction(porphanTx);
|
||||
const TxValidationState& state = result.m_state;
|
||||
const uint256& orphanHash = porphanTx->GetHash();
|
||||
|
||||
if (result.m_result_type == MempoolAcceptResult::ResultType::VALID) {
|
||||
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
|
||||
RelayTransaction(orphanHash, porphanTx->GetWitnessHash());
|
||||
m_orphanage.AddChildrenToWorkSet(*porphanTx, orphan_work_set);
|
||||
m_orphanage.AddChildrenToWorkSet(*porphanTx, peer.m_id);
|
||||
m_orphanage.EraseTx(orphanHash);
|
||||
for (const CTransactionRef& removedTx : result.m_replaced_transactions.value()) {
|
||||
AddToCompactExtraTransactions(removedTx);
|
||||
|
@ -2957,6 +2954,8 @@ void PeerManagerImpl::ProcessOrphanTx(std::set<uint256>& orphan_work_set)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return more;
|
||||
}
|
||||
|
||||
bool PeerManagerImpl::PrepareBlockFilterRequest(CNode& node, Peer& peer,
|
||||
|
@ -3990,7 +3989,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
AddKnownTx(*peer, txid);
|
||||
}
|
||||
|
||||
LOCK2(cs_main, g_cs_orphans);
|
||||
LOCK(cs_main);
|
||||
|
||||
m_txrequest.ReceivedResponse(pfrom.GetId(), txid);
|
||||
if (tx.HasWitness()) m_txrequest.ReceivedResponse(pfrom.GetId(), wtxid);
|
||||
|
@ -4031,7 +4030,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
m_txrequest.ForgetTxHash(tx.GetHash());
|
||||
m_txrequest.ForgetTxHash(tx.GetWitnessHash());
|
||||
RelayTransaction(tx.GetHash(), tx.GetWitnessHash());
|
||||
m_orphanage.AddChildrenToWorkSet(tx, peer->m_orphan_work_set);
|
||||
m_orphanage.AddChildrenToWorkSet(tx, peer->m_id);
|
||||
|
||||
pfrom.m_last_tx_time = GetTime<std::chrono::seconds>();
|
||||
|
||||
|
@ -4045,7 +4044,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
}
|
||||
|
||||
// Recursively process any orphan transactions that depended on this one
|
||||
ProcessOrphanTx(peer->m_orphan_work_set);
|
||||
ProcessOrphanTx(*peer);
|
||||
}
|
||||
else if (state.GetResult() == TxValidationResult::TX_MISSING_INPUTS)
|
||||
{
|
||||
|
@ -4226,7 +4225,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||
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());
|
||||
|
@ -4854,16 +4853,17 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
|
|||
}
|
||||
}
|
||||
|
||||
bool has_more_orphans;
|
||||
{
|
||||
LOCK2(cs_main, g_cs_orphans);
|
||||
if (!peer->m_orphan_work_set.empty()) {
|
||||
ProcessOrphanTx(peer->m_orphan_work_set);
|
||||
}
|
||||
LOCK(cs_main);
|
||||
has_more_orphans = ProcessOrphanTx(*peer);
|
||||
}
|
||||
|
||||
if (pfrom->fDisconnect)
|
||||
return false;
|
||||
|
||||
if (has_more_orphans) return true;
|
||||
|
||||
// this maintains the order of responses
|
||||
// and prevents m_getdata_requests to grow unbounded
|
||||
{
|
||||
|
@ -4871,11 +4871,6 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
|
|||
if (!peer->m_getdata_requests.empty()) return true;
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
if (!peer->m_orphan_work_set.empty()) return true;
|
||||
}
|
||||
|
||||
// Don't bother if send buffer is too full to respond anyway
|
||||
if (pfrom->fPauseSend) return false;
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <test/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <test/util/validation.h>
|
||||
#include <txorphanage.h>
|
||||
#include <validationinterface.h>
|
||||
#include <version.h>
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <test/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <test/util/validation.h>
|
||||
#include <txorphanage.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
|
|||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||
|
||||
TxOrphanage orphanage;
|
||||
std::set<uint256> orphan_work_set;
|
||||
std::vector<COutPoint> outpoints;
|
||||
// initial outpoints used to construct transactions later
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
|
@ -86,15 +85,19 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
|
|||
CallOneOf(
|
||||
fuzzed_data_provider,
|
||||
[&] {
|
||||
LOCK(g_cs_orphans);
|
||||
orphanage.AddChildrenToWorkSet(*tx, orphan_work_set);
|
||||
orphanage.AddChildrenToWorkSet(*tx, peer_id);
|
||||
},
|
||||
[&] {
|
||||
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
bool get_tx = orphanage.GetTx(tx->GetHash()).first != nullptr;
|
||||
Assert(have_tx == get_tx);
|
||||
NodeId originator;
|
||||
bool more = true;
|
||||
CTransactionRef ref = orphanage.GetTxToReconsider(peer_id, originator, more);
|
||||
if (!ref) {
|
||||
Assert(!more);
|
||||
} else {
|
||||
bool have_tx = orphanage.HaveTx(GenTxid::Txid(ref->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(ref->GetHash()));
|
||||
Assert(have_tx);
|
||||
}
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
|
@ -102,14 +105,12 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
|
|||
// AddTx should return false if tx is too big or already have it
|
||||
// tx weight is unknown, we only check when tx is already in orphanage
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
bool add_tx = orphanage.AddTx(tx, peer_id);
|
||||
// have_tx == true -> add_tx == false
|
||||
Assert(!have_tx || !add_tx);
|
||||
}
|
||||
have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
bool add_tx = orphanage.AddTx(tx, peer_id);
|
||||
// if have_tx is still false, it must be too big
|
||||
Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT));
|
||||
|
@ -120,25 +121,22 @@ FUZZ_TARGET_INIT(txorphan, initialize_orphanage)
|
|||
bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
// EraseTx should return 0 if m_orphans doesn't have the tx
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
Assert(have_tx == orphanage.EraseTx(tx->GetHash()));
|
||||
}
|
||||
have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetHash()));
|
||||
// have_tx should be false and EraseTx should fail
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
Assert(!have_tx && !orphanage.EraseTx(tx->GetHash()));
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
LOCK(g_cs_orphans);
|
||||
orphanage.EraseForPeer(peer_id);
|
||||
},
|
||||
[&] {
|
||||
// test mocktime and expiry
|
||||
SetMockTime(ConsumeTime(fuzzed_data_provider));
|
||||
auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
||||
WITH_LOCK(g_cs_orphans, orphanage.LimitOrphans(limit));
|
||||
orphanage.LimitOrphans(limit);
|
||||
Assert(orphanage.Size() <= limit);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -20,13 +20,15 @@ BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
|
|||
class TxOrphanageTest : public TxOrphanage
|
||||
{
|
||||
public:
|
||||
inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
|
||||
inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
{
|
||||
LOCK(m_mutex);
|
||||
return m_orphans.size();
|
||||
}
|
||||
|
||||
CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans)
|
||||
CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
{
|
||||
LOCK(m_mutex);
|
||||
std::map<uint256, OrphanTx>::iterator it;
|
||||
it = m_orphans.lower_bound(InsecureRand256());
|
||||
if (it == m_orphans.end())
|
||||
|
@ -59,8 +61,6 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||
FillableSigningProvider keystore;
|
||||
BOOST_CHECK(keystore.AddKey(key));
|
||||
|
||||
LOCK(g_cs_orphans);
|
||||
|
||||
// 50 orphan transactions:
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
|
|
|
@ -15,11 +15,10 @@ static constexpr int64_t ORPHAN_TX_EXPIRE_TIME = 20 * 60;
|
|||
/** Minimum time between orphan transactions expire time checks in seconds */
|
||||
static constexpr int64_t ORPHAN_TX_EXPIRE_INTERVAL = 5 * 60;
|
||||
|
||||
RecursiveMutex g_cs_orphans;
|
||||
|
||||
bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
||||
{
|
||||
AssertLockHeld(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
|
||||
const uint256& hash = tx->GetHash();
|
||||
if (m_orphans.count(hash))
|
||||
|
@ -55,7 +54,13 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
|||
|
||||
int TxOrphanage::EraseTx(const uint256& txid)
|
||||
{
|
||||
AssertLockHeld(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
return _EraseTx(txid);
|
||||
}
|
||||
|
||||
int TxOrphanage::_EraseTx(const uint256& txid)
|
||||
{
|
||||
AssertLockHeld(m_mutex);
|
||||
std::map<uint256, OrphanTx>::iterator it = m_orphans.find(txid);
|
||||
if (it == m_orphans.end())
|
||||
return 0;
|
||||
|
@ -87,7 +92,9 @@ int TxOrphanage::EraseTx(const uint256& txid)
|
|||
|
||||
void TxOrphanage::EraseForPeer(NodeId peer)
|
||||
{
|
||||
AssertLockHeld(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
|
||||
m_peer_work_set.erase(peer);
|
||||
|
||||
int nErased = 0;
|
||||
std::map<uint256, OrphanTx>::iterator iter = m_orphans.begin();
|
||||
|
@ -96,7 +103,7 @@ void TxOrphanage::EraseForPeer(NodeId peer)
|
|||
std::map<uint256, OrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
|
||||
if (maybeErase->second.fromPeer == peer)
|
||||
{
|
||||
nErased += EraseTx(maybeErase->second.tx->GetHash());
|
||||
nErased += _EraseTx(maybeErase->second.tx->GetHash());
|
||||
}
|
||||
}
|
||||
if (nErased > 0) LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx from peer=%d\n", nErased, peer);
|
||||
|
@ -104,7 +111,7 @@ void TxOrphanage::EraseForPeer(NodeId peer)
|
|||
|
||||
void TxOrphanage::LimitOrphans(unsigned int max_orphans)
|
||||
{
|
||||
AssertLockHeld(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
|
||||
unsigned int nEvicted = 0;
|
||||
static int64_t nNextSweep;
|
||||
|
@ -118,7 +125,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans)
|
|||
{
|
||||
std::map<uint256, OrphanTx>::iterator maybeErase = iter++;
|
||||
if (maybeErase->second.nTimeExpire <= nNow) {
|
||||
nErased += EraseTx(maybeErase->second.tx->GetHash());
|
||||
nErased += _EraseTx(maybeErase->second.tx->GetHash());
|
||||
} else {
|
||||
nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
|
||||
}
|
||||
|
@ -132,15 +139,19 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans)
|
|||
{
|
||||
// Evict a random orphan:
|
||||
size_t randompos = rng.randrange(m_orphan_list.size());
|
||||
EraseTx(m_orphan_list[randompos]->first);
|
||||
_EraseTx(m_orphan_list[randompos]->first);
|
||||
++nEvicted;
|
||||
}
|
||||
if (nEvicted > 0) LogPrint(BCLog::MEMPOOL, "orphanage overflow, removed %u tx\n", nEvicted);
|
||||
}
|
||||
|
||||
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>& orphan_work_set) const
|
||||
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, NodeId peer)
|
||||
{
|
||||
AssertLockHeld(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
|
||||
// Get this peer's work set, emplacing an empty set it didn't exist
|
||||
std::set<uint256>& orphan_work_set = m_peer_work_set.try_emplace(peer).first->second;
|
||||
|
||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
||||
const auto it_by_prev = m_outpoint_to_orphan_it.find(COutPoint(tx.GetHash(), i));
|
||||
if (it_by_prev != m_outpoint_to_orphan_it.end()) {
|
||||
|
@ -153,7 +164,7 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>
|
|||
|
||||
bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
if (gtxid.IsWtxid()) {
|
||||
return m_wtxid_to_orphan_it.count(gtxid.GetHash());
|
||||
} else {
|
||||
|
@ -161,18 +172,32 @@ bool TxOrphanage::HaveTx(const GenTxid& gtxid) const
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<CTransactionRef, NodeId> TxOrphanage::GetTx(const uint256& txid) const
|
||||
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer, NodeId& originator, bool& more)
|
||||
{
|
||||
AssertLockHeld(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
|
||||
const auto it = m_orphans.find(txid);
|
||||
if (it == m_orphans.end()) return {nullptr, -1};
|
||||
return {it->second.tx, it->second.fromPeer};
|
||||
auto work_set_it = m_peer_work_set.find(peer);
|
||||
if (work_set_it != m_peer_work_set.end()) {
|
||||
auto& work_set = work_set_it->second;
|
||||
while (!work_set.empty()) {
|
||||
uint256 txid = *work_set.begin();
|
||||
work_set.erase(work_set.begin());
|
||||
|
||||
const auto orphan_it = m_orphans.find(txid);
|
||||
if (orphan_it != m_orphans.end()) {
|
||||
more = !work_set.empty();
|
||||
originator = orphan_it->second.fromPeer;
|
||||
return orphan_it->second.tx;
|
||||
}
|
||||
}
|
||||
}
|
||||
more = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TxOrphanage::EraseForBlock(const CBlock& block)
|
||||
{
|
||||
LOCK(g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
|
||||
std::vector<uint256> vOrphanErase;
|
||||
|
||||
|
@ -195,7 +220,7 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
|
|||
if (vOrphanErase.size()) {
|
||||
int nErased = 0;
|
||||
for (const uint256& orphanHash : vOrphanErase) {
|
||||
nErased += EraseTx(orphanHash);
|
||||
nErased += _EraseTx(orphanHash);
|
||||
}
|
||||
LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include <primitives/transaction.h>
|
||||
#include <sync.h>
|
||||
|
||||
/** Guards orphan transactions and extra txs for compact blocks */
|
||||
extern RecursiveMutex g_cs_orphans;
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
/** A class to track orphan transactions (failed on TX_MISSING_INPUTS)
|
||||
* Since we cannot distinguish orphans from bad transactions with
|
||||
|
@ -21,40 +21,46 @@ extern RecursiveMutex g_cs_orphans;
|
|||
class TxOrphanage {
|
||||
public:
|
||||
/** Add a new orphan transaction */
|
||||
bool AddTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
||||
bool AddTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Check if we already have an orphan transaction (by txid or wtxid) */
|
||||
bool HaveTx(const GenTxid& gtxid) const LOCKS_EXCLUDED(::g_cs_orphans);
|
||||
bool HaveTx(const GenTxid& gtxid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Get an orphan transaction and its originating peer
|
||||
* (Transaction ref will be nullptr if not found)
|
||||
/** Extract a transaction from a peer's work set
|
||||
* Returns nullptr and sets more to false if there are no transactions
|
||||
* to work on. Otherwise returns the transaction reference, removes
|
||||
* the transaction from the work set, and populates its arguments with
|
||||
* the originating peer, and whether there are more orphans for this peer
|
||||
* to work on after this tx.
|
||||
*/
|
||||
std::pair<CTransactionRef, NodeId> GetTx(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
||||
CTransactionRef GetTxToReconsider(NodeId peer, NodeId& originator, bool& more) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Erase an orphan by txid */
|
||||
int EraseTx(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
||||
int EraseTx(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Erase all orphans announced by a peer (eg, after that peer disconnects) */
|
||||
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
||||
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Erase all orphans included in or invalidated by a new block */
|
||||
void EraseForBlock(const CBlock& block) LOCKS_EXCLUDED(::g_cs_orphans);
|
||||
void EraseForBlock(const CBlock& block) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Limit the orphanage to the given maximum */
|
||||
void LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
||||
void LimitOrphans(unsigned int max_orphans) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Add any orphans that list a particular tx as a parent into a peer's work set
|
||||
* (ie orphans that may have found their final missing parent, and so should be reconsidered for the mempool) */
|
||||
void AddChildrenToWorkSet(const CTransaction& tx, std::set<uint256>& orphan_work_set) const EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
||||
/** Add any orphans that list a particular tx as a parent into a peer's work set */
|
||||
void AddChildrenToWorkSet(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
||||
|
||||
/** Return how many entries exist in the orphange */
|
||||
size_t Size() LOCKS_EXCLUDED(::g_cs_orphans)
|
||||
size_t Size() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
||||
{
|
||||
LOCK(::g_cs_orphans);
|
||||
LOCK(m_mutex);
|
||||
return m_orphans.size();
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Guards orphan transactions */
|
||||
mutable Mutex m_mutex;
|
||||
|
||||
struct OrphanTx {
|
||||
CTransactionRef tx;
|
||||
NodeId fromPeer;
|
||||
|
@ -64,7 +70,10 @@ protected:
|
|||
|
||||
/** Map from txid to orphan transaction record. Limited by
|
||||
* -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
|
||||
std::map<uint256, OrphanTx> m_orphans GUARDED_BY(g_cs_orphans);
|
||||
std::map<uint256, OrphanTx> m_orphans GUARDED_BY(m_mutex);
|
||||
|
||||
/** Which peer provided a parent tx of orphans that need to be reconsidered */
|
||||
std::map<NodeId, std::set<uint256>> m_peer_work_set GUARDED_BY(m_mutex);
|
||||
|
||||
using OrphanMap = decltype(m_orphans);
|
||||
|
||||
|
@ -79,14 +88,17 @@ protected:
|
|||
|
||||
/** Index from the parents' COutPoint into the m_orphans. Used
|
||||
* to remove orphan transactions from the m_orphans */
|
||||
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it GUARDED_BY(g_cs_orphans);
|
||||
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it GUARDED_BY(m_mutex);
|
||||
|
||||
/** Orphan transactions in vector for quick random eviction */
|
||||
std::vector<OrphanMap::iterator> m_orphan_list GUARDED_BY(g_cs_orphans);
|
||||
std::vector<OrphanMap::iterator> m_orphan_list GUARDED_BY(m_mutex);
|
||||
|
||||
/** Index from wtxid into the m_orphans to lookup orphan
|
||||
* transactions using their witness ids. */
|
||||
std::map<uint256, OrphanMap::iterator> m_wtxid_to_orphan_it GUARDED_BY(g_cs_orphans);
|
||||
std::map<uint256, OrphanMap::iterator> m_wtxid_to_orphan_it GUARDED_BY(m_mutex);
|
||||
|
||||
/** Erase an orphan by txid */
|
||||
int _EraseTx(const uint256& txid) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
|
||||
};
|
||||
|
||||
#endif // BITCOIN_TXORPHANAGE_H
|
||||
|
|
Loading…
Add table
Reference in a new issue