Merge bitcoin/bitcoin#30111: locks: introduce mutex for tx download, flush rejection filters once per tip change
c85accecaf[refactor] delete EraseTxNoLock, just use EraseTx (glozow)6ff84069a5remove obsoleted TxOrphanage::m_mutex (glozow)61745c7451lock m_recent_confirmed_transactions using m_tx_download_mutex (glozow)723ea0f9a5remove obsoleted hashRecentRejectsChainTip (glozow)18a4355250update recent_rejects filters on ActiveTipChange (glozow)36f170d879add ValidationInterface::ActiveTipChange (glozow)3eb1307df0guard TxRequest and rejection caches with new mutex (glozow) Pull request description: See #27463 for full project tracking. This contains the first few commits of #30110, which require some thinking about thread safety in review. - Introduce a new `m_tx_download_mutex` which guards the transaction download data structures including `m_txrequest`, the rolling bloom filters, and `m_orphanage`. Later this should become the mutex guarding `TxDownloadManager`. - `m_txrequest` doesn't need to be guarded using `cs_main` anymore - `m_recent_confirmed_transactions` doesn't need its own lock anymore - `m_orphanage` doesn't need its own lock anymore - Adds a new `ValidationInterface` event, `ActiveTipChanged`, which is a synchronous callback whenever the tip of the active chainstate changes. - Flush `m_recent_rejects` and `m_recent_rejects_reconsiderable` on `ActiveTipChanged` just once instead of checking the tip every time `AlreadyHaveTx` is called. This should speed up calls to that function (no longer comparing a block hash each time) and removes the need to lock `cs_main` every time it is called. Motivation: - These data structures need synchronization. While we are holding `m_tx_download_mutex`, these should hold: - a tx hash in `m_txrequest` is not also in `m_orphanage` - a tx hash in `m_txrequest` is not also in `m_recent_rejects` or `m_recent_confirmed_transactions` - In the future, orphan resolution tracking should also be synchronized. If a tx has an entry in the orphan resolution tracker, it is also in `m_orphanage`, and not in `m_txrequest`, etc. - Currently, `cs_main` is used to e.g. sync accesses to `m_txrequest`. We should not broaden the scope of things it locks. - Currently, we need to know the current chainstate every time we call `AlreadyHaveTx` so we can decide whether we should update it. Every call compares the current tip hash with `hashRecentRejectsChainTip`. It is more efficient to have a validation interface callback that updates the rejection filters whenever the chain tip changes. ACKs for top commit: instagibbs: reACKc85accecafdergoegge: Code review ACKc85accecaftheStack: Light code-review ACKc85accecafhebasto: ACKc85accecaf, I have reviewed the code and it looks OK. Tree-SHA512: c3bd524b5de1cafc9a10770dadb484cc479d6d4c687d80dd0f176d339fd95f73b85cb44cb3b6b464d38a52e20feda00aa2a1da5a73339e31831687e4bd0aa0c5
This commit is contained in:
7 files changed
+128
-124
No files matched your search
+81
-64
@@ -489,10 +489,12 @@ public:
|
|||||||
CTxMemPool& pool, node::Warnings& warnings, Options opts);
|
CTxMemPool& pool, node::Warnings& warnings, Options opts);
|
||||||
|
|
||||||
/** Overridden from CValidationInterface. */
|
/** Overridden from CValidationInterface. */
|
||||||
|
void ActiveTipChange(const CBlockIndex* new_tip, bool) override
|
||||||
|
EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
|
||||||
void BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override
|
void BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected) override
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
|
||||||
void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override
|
void BlockDisconnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex* pindex) override
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_recent_confirmed_transactions_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_tx_download_mutex);
|
||||||
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override
|
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
|
||||||
void BlockChecked(const CBlock& block, const BlockValidationState& state) override
|
void BlockChecked(const CBlock& block, const BlockValidationState& state) override
|
||||||
@@ -501,13 +503,13 @@ public:
|
|||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex);
|
||||||
|
|
||||||
/** Implement NetEventsInterface */
|
/** Implement NetEventsInterface */
|
||||||
void InitializeNode(const CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex);
|
void InitializeNode(const CNode& node, ServiceFlags our_services) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_tx_download_mutex);
|
||||||
void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex);
|
void FinalizeNode(const CNode& node) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_headers_presync_mutex, !m_tx_download_mutex);
|
||||||
bool HasAllDesirableServiceFlags(ServiceFlags services) const override;
|
bool HasAllDesirableServiceFlags(ServiceFlags services) const override;
|
||||||
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override
|
bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex, !m_tx_download_mutex);
|
||||||
bool SendMessages(CNode* pto) override
|
bool SendMessages(CNode* pto) override
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, g_msgproc_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_most_recent_block_mutex, g_msgproc_mutex, !m_tx_download_mutex);
|
||||||
|
|
||||||
/** Implement PeerManager */
|
/** Implement PeerManager */
|
||||||
void StartScheduledTasks(CScheduler& scheduler) override;
|
void StartScheduledTasks(CScheduler& scheduler) override;
|
||||||
@@ -526,7 +528,7 @@ public:
|
|||||||
void UnitTestMisbehaving(NodeId peer_id) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving(*Assert(GetPeerRef(peer_id)), ""); };
|
void UnitTestMisbehaving(NodeId peer_id) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex) { Misbehaving(*Assert(GetPeerRef(peer_id)), ""); };
|
||||||
void ProcessMessage(CNode& pfrom, const std::string& msg_type, DataStream& vRecv,
|
void ProcessMessage(CNode& pfrom, const std::string& msg_type, DataStream& vRecv,
|
||||||
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
|
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) override
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_recent_confirmed_transactions_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, !m_most_recent_block_mutex, !m_headers_presync_mutex, g_msgproc_mutex, !m_tx_download_mutex);
|
||||||
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
|
void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) override;
|
||||||
ServiceFlags GetDesirableServiceFlags(ServiceFlags services) const override;
|
ServiceFlags GetDesirableServiceFlags(ServiceFlags services) const override;
|
||||||
|
|
||||||
@@ -585,12 +587,12 @@ private:
|
|||||||
* Updates m_txrequest, m_recent_rejects, m_recent_rejects_reconsiderable, m_orphanage, and vExtraTxnForCompact. */
|
* Updates m_txrequest, m_recent_rejects, m_recent_rejects_reconsiderable, m_orphanage, and vExtraTxnForCompact. */
|
||||||
void ProcessInvalidTx(NodeId nodeid, const CTransactionRef& tx, const TxValidationState& result,
|
void ProcessInvalidTx(NodeId nodeid, const CTransactionRef& tx, const TxValidationState& result,
|
||||||
bool maybe_add_extra_compact_tx)
|
bool maybe_add_extra_compact_tx)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
|
||||||
|
|
||||||
/** Handle a transaction whose result was MempoolAcceptResult::ResultType::VALID.
|
/** Handle a transaction whose result was MempoolAcceptResult::ResultType::VALID.
|
||||||
* Updates m_txrequest, m_orphanage, and vExtraTxnForCompact. Also queues the tx for relay. */
|
* Updates m_txrequest, m_orphanage, and vExtraTxnForCompact. Also queues the tx for relay. */
|
||||||
void ProcessValidTx(NodeId nodeid, const CTransactionRef& tx, const std::list<CTransactionRef>& replaced_transactions)
|
void ProcessValidTx(NodeId nodeid, const CTransactionRef& tx, const std::list<CTransactionRef>& replaced_transactions)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
|
||||||
|
|
||||||
struct PackageToValidate {
|
struct PackageToValidate {
|
||||||
const Package m_txns;
|
const Package m_txns;
|
||||||
@@ -620,13 +622,13 @@ private:
|
|||||||
* individual transactions, and caches rejection for the package as a group.
|
* individual transactions, and caches rejection for the package as a group.
|
||||||
*/
|
*/
|
||||||
void ProcessPackageResult(const PackageToValidate& package_to_validate, const PackageMempoolAcceptResult& package_result)
|
void ProcessPackageResult(const PackageToValidate& package_to_validate, const PackageMempoolAcceptResult& package_result)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
|
||||||
|
|
||||||
/** Look for a child of this transaction in the orphanage to form a 1-parent-1-child package,
|
/** Look for a child of this transaction in the orphanage to form a 1-parent-1-child package,
|
||||||
* skipping any combinations that have already been tried. Return the resulting package along with
|
* skipping any combinations that have already been tried. Return the resulting package along with
|
||||||
* the senders of its respective transactions, or std::nullopt if no package is found. */
|
* the senders of its respective transactions, or std::nullopt if no package is found. */
|
||||||
std::optional<PackageToValidate> Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
|
std::optional<PackageToValidate> Find1P1CPackage(const CTransactionRef& ptx, NodeId nodeid)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, m_tx_download_mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reconsider orphan transactions after a parent has been accepted to the mempool.
|
* Reconsider orphan transactions after a parent has been accepted to the mempool.
|
||||||
@@ -640,7 +642,7 @@ private:
|
|||||||
* will be empty.
|
* will be empty.
|
||||||
*/
|
*/
|
||||||
bool ProcessOrphanTx(Peer& peer)
|
bool ProcessOrphanTx(Peer& peer)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex, g_msgproc_mutex, !m_tx_download_mutex);
|
||||||
|
|
||||||
/** Process a single headers message from a peer.
|
/** Process a single headers message from a peer.
|
||||||
*
|
*
|
||||||
@@ -722,7 +724,7 @@ private:
|
|||||||
* peer. The announcement parameters are decided in PeerManager and then
|
* peer. The announcement parameters are decided in PeerManager and then
|
||||||
* passed to TxRequestTracker. */
|
* passed to TxRequestTracker. */
|
||||||
void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
|
void AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_tx_download_mutex);
|
||||||
|
|
||||||
/** Send a message to a peer */
|
/** Send a message to a peer */
|
||||||
void PushMessage(CNode& node, CSerializedNetMsg&& msg) const { m_connman.PushMessage(&node, std::move(msg)); }
|
void PushMessage(CNode& node, CSerializedNetMsg&& msg) const { m_connman.PushMessage(&node, std::move(msg)); }
|
||||||
@@ -770,7 +772,19 @@ private:
|
|||||||
BanMan* const m_banman;
|
BanMan* const m_banman;
|
||||||
ChainstateManager& m_chainman;
|
ChainstateManager& m_chainman;
|
||||||
CTxMemPool& m_mempool;
|
CTxMemPool& m_mempool;
|
||||||
TxRequestTracker m_txrequest GUARDED_BY(::cs_main);
|
|
||||||
|
/** Synchronizes tx download including TxRequestTracker, rejection filters, and TxOrphanage.
|
||||||
|
* Lock invariants:
|
||||||
|
* - A txhash (txid or wtxid) in m_txrequest is not also in m_orphanage.
|
||||||
|
* - A txhash (txid or wtxid) in m_txrequest is not also in m_recent_rejects.
|
||||||
|
* - A txhash (txid or wtxid) in m_txrequest is not also in m_recent_rejects_reconsiderable.
|
||||||
|
* - A txhash (txid or wtxid) in m_txrequest is not also in m_recent_confirmed_transactions.
|
||||||
|
* - Each data structure's limits hold (m_orphanage max size, m_txrequest per-peer limits, etc).
|
||||||
|
*
|
||||||
|
* m_tx_download_mutex must be acquired before mempool.cs
|
||||||
|
*/
|
||||||
|
Mutex m_tx_download_mutex;
|
||||||
|
TxRequestTracker m_txrequest GUARDED_BY(m_tx_download_mutex);
|
||||||
std::unique_ptr<TxReconciliationTracker> m_txreconciliation;
|
std::unique_ptr<TxReconciliationTracker> m_txreconciliation;
|
||||||
|
|
||||||
/** The height of the best chain */
|
/** The height of the best chain */
|
||||||
@@ -847,11 +861,9 @@ private:
|
|||||||
* - m_recent_rejects
|
* - m_recent_rejects
|
||||||
* - m_recent_rejects_reconsiderable (if include_reconsiderable = true)
|
* - m_recent_rejects_reconsiderable (if include_reconsiderable = true)
|
||||||
* - m_recent_confirmed_transactions
|
* - m_recent_confirmed_transactions
|
||||||
* Also responsible for resetting m_recent_rejects and m_recent_rejects_reconsiderable if the
|
|
||||||
* chain tip has changed.
|
|
||||||
* */
|
* */
|
||||||
bool AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
|
bool AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
|
||||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex);
|
EXCLUSIVE_LOCKS_REQUIRED(m_tx_download_mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter for transactions that were recently rejected by the mempool.
|
* Filter for transactions that were recently rejected by the mempool.
|
||||||
@@ -887,10 +899,7 @@ private:
|
|||||||
*
|
*
|
||||||
* Memory used: 1.3 MB
|
* Memory used: 1.3 MB
|
||||||
*/
|
*/
|
||||||
CRollingBloomFilter m_recent_rejects GUARDED_BY(::cs_main){120'000, 0.000'001};
|
CRollingBloomFilter m_recent_rejects GUARDED_BY(m_tx_download_mutex){120'000, 0.000'001};
|
||||||
/** Block hash of chain tip the last time we reset m_recent_rejects and
|
|
||||||
* m_recent_rejects_reconsiderable. */
|
|
||||||
uint256 hashRecentRejectsChainTip GUARDED_BY(cs_main);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter for:
|
* Filter for:
|
||||||
@@ -912,7 +921,7 @@ private:
|
|||||||
*
|
*
|
||||||
* Parameters are picked to be the same as m_recent_rejects, with the same rationale.
|
* Parameters are picked to be the same as m_recent_rejects, with the same rationale.
|
||||||
*/
|
*/
|
||||||
CRollingBloomFilter m_recent_rejects_reconsiderable GUARDED_BY(::cs_main){120'000, 0.000'001};
|
CRollingBloomFilter m_recent_rejects_reconsiderable GUARDED_BY(m_tx_download_mutex){120'000, 0.000'001};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Filter for transactions that have been recently confirmed.
|
* Filter for transactions that have been recently confirmed.
|
||||||
@@ -929,8 +938,7 @@ private:
|
|||||||
* transaction per day that would be inadvertently ignored (which is the
|
* transaction per day that would be inadvertently ignored (which is the
|
||||||
* same probability that we have in the reject filter).
|
* same probability that we have in the reject filter).
|
||||||
*/
|
*/
|
||||||
Mutex m_recent_confirmed_transactions_mutex;
|
CRollingBloomFilter m_recent_confirmed_transactions GUARDED_BY(m_tx_download_mutex){48'000, 0.000'001};
|
||||||
CRollingBloomFilter m_recent_confirmed_transactions GUARDED_BY(m_recent_confirmed_transactions_mutex){48'000, 0.000'001};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For sending `inv`s to inbound peers, we use a single (exponentially
|
* For sending `inv`s to inbound peers, we use a single (exponentially
|
||||||
@@ -1067,7 +1075,7 @@ private:
|
|||||||
int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
|
int m_peers_downloading_from GUARDED_BY(cs_main) = 0;
|
||||||
|
|
||||||
/** Storage for orphan information */
|
/** Storage for orphan information */
|
||||||
TxOrphanage m_orphanage;
|
TxOrphanage m_orphanage GUARDED_BY(m_tx_download_mutex);
|
||||||
|
|
||||||
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex);
|
||||||
|
|
||||||
@@ -1630,7 +1638,8 @@ void PeerManagerImpl::PushNodeVersion(CNode& pnode, const Peer& peer)
|
|||||||
|
|
||||||
void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
|
void PeerManagerImpl::AddTxAnnouncement(const CNode& node, const GenTxid& gtxid, std::chrono::microseconds current_time)
|
||||||
{
|
{
|
||||||
AssertLockHeld(::cs_main); // For m_txrequest
|
AssertLockHeld(::cs_main); // for State
|
||||||
|
AssertLockHeld(m_tx_download_mutex); // For m_txrequest
|
||||||
NodeId nodeid = node.GetId();
|
NodeId nodeid = node.GetId();
|
||||||
if (!node.HasPermission(NetPermissionFlags::Relay) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
|
if (!node.HasPermission(NetPermissionFlags::Relay) && m_txrequest.Count(nodeid) >= MAX_PEER_TX_ANNOUNCEMENTS) {
|
||||||
// Too many queued announcements from this peer
|
// Too many queued announcements from this peer
|
||||||
@@ -1666,8 +1675,11 @@ void PeerManagerImpl::InitializeNode(const CNode& node, ServiceFlags our_service
|
|||||||
{
|
{
|
||||||
NodeId nodeid = node.GetId();
|
NodeId nodeid = node.GetId();
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main); // For m_node_states
|
||||||
m_node_states.emplace_hint(m_node_states.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(node.IsInboundConn()));
|
m_node_states.emplace_hint(m_node_states.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(node.IsInboundConn()));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
LOCK(m_tx_download_mutex);
|
||||||
assert(m_txrequest.Count(nodeid) == 0);
|
assert(m_txrequest.Count(nodeid) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1735,8 +1747,11 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_orphanage.EraseForPeer(nodeid);
|
{
|
||||||
m_txrequest.DisconnectedPeer(nodeid);
|
LOCK(m_tx_download_mutex);
|
||||||
|
m_orphanage.EraseForPeer(nodeid);
|
||||||
|
m_txrequest.DisconnectedPeer(nodeid);
|
||||||
|
}
|
||||||
if (m_txreconciliation) m_txreconciliation->ForgetPeer(nodeid);
|
if (m_txreconciliation) m_txreconciliation->ForgetPeer(nodeid);
|
||||||
m_num_preferred_download_peers -= state->fPreferredDownload;
|
m_num_preferred_download_peers -= state->fPreferredDownload;
|
||||||
m_peers_downloading_from -= (!state->vBlocksInFlight.empty());
|
m_peers_downloading_from -= (!state->vBlocksInFlight.empty());
|
||||||
@@ -1753,6 +1768,7 @@ void PeerManagerImpl::FinalizeNode(const CNode& node)
|
|||||||
assert(m_peers_downloading_from == 0);
|
assert(m_peers_downloading_from == 0);
|
||||||
assert(m_outbound_peers_with_protect_from_disconnect == 0);
|
assert(m_outbound_peers_with_protect_from_disconnect == 0);
|
||||||
assert(m_wtxid_relay_peers == 0);
|
assert(m_wtxid_relay_peers == 0);
|
||||||
|
LOCK(m_tx_download_mutex);
|
||||||
assert(m_txrequest.Size() == 0);
|
assert(m_txrequest.Size() == 0);
|
||||||
assert(m_orphanage.Size() == 0);
|
assert(m_orphanage.Size() == 0);
|
||||||
}
|
}
|
||||||
@@ -2054,6 +2070,21 @@ void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler)
|
|||||||
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
|
scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PeerManagerImpl::ActiveTipChange(const CBlockIndex* new_tip, bool is_ibd)
|
||||||
|
{
|
||||||
|
AssertLockNotHeld(m_mempool.cs);
|
||||||
|
AssertLockNotHeld(m_tx_download_mutex);
|
||||||
|
|
||||||
|
if (!is_ibd) {
|
||||||
|
LOCK(m_tx_download_mutex);
|
||||||
|
// If the chain tip has changed, previously rejected transactions might now be valid, e.g. due
|
||||||
|
// to a timelock. Reset the rejection filters to give those transactions another chance if we
|
||||||
|
// see them again.
|
||||||
|
m_recent_rejects.reset();
|
||||||
|
m_recent_rejects_reconsiderable.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evict orphan txn pool entries based on a newly connected
|
* Evict orphan txn pool entries based on a newly connected
|
||||||
* block, remember the recently confirmed transactions, and delete tracked
|
* block, remember the recently confirmed transactions, and delete tracked
|
||||||
@@ -2084,23 +2115,18 @@ void PeerManagerImpl::BlockConnected(
|
|||||||
if (role == ChainstateRole::BACKGROUND) {
|
if (role == ChainstateRole::BACKGROUND) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
LOCK(m_tx_download_mutex);
|
||||||
m_orphanage.EraseForBlock(*pblock);
|
m_orphanage.EraseForBlock(*pblock);
|
||||||
|
|
||||||
{
|
for (const auto& ptx : pblock->vtx) {
|
||||||
LOCK(m_recent_confirmed_transactions_mutex);
|
m_recent_confirmed_transactions.insert(ptx->GetHash().ToUint256());
|
||||||
for (const auto& ptx : pblock->vtx) {
|
if (ptx->HasWitness()) {
|
||||||
m_recent_confirmed_transactions.insert(ptx->GetHash().ToUint256());
|
m_recent_confirmed_transactions.insert(ptx->GetWitnessHash().ToUint256());
|
||||||
if (ptx->HasWitness()) {
|
|
||||||
m_recent_confirmed_transactions.insert(ptx->GetWitnessHash().ToUint256());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
for (const auto& ptx : pblock->vtx) {
|
||||||
LOCK(cs_main);
|
m_txrequest.ForgetTxHash(ptx->GetHash());
|
||||||
for (const auto& ptx : pblock->vtx) {
|
m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
|
||||||
m_txrequest.ForgetTxHash(ptx->GetHash());
|
|
||||||
m_txrequest.ForgetTxHash(ptx->GetWitnessHash());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2114,7 +2140,7 @@ void PeerManagerImpl::BlockDisconnected(const std::shared_ptr<const CBlock> &blo
|
|||||||
// block's worth of transactions in it, but that should be fine, since
|
// block's worth of transactions in it, but that should be fine, since
|
||||||
// presumably the most common case of relaying a confirmed transaction
|
// presumably the most common case of relaying a confirmed transaction
|
||||||
// should be just after a new block containing it is found.
|
// should be just after a new block containing it is found.
|
||||||
LOCK(m_recent_confirmed_transactions_mutex);
|
LOCK(m_tx_download_mutex);
|
||||||
m_recent_confirmed_transactions.reset();
|
m_recent_confirmed_transactions.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2254,15 +2280,7 @@ void PeerManagerImpl::BlockChecked(const CBlock& block, const BlockValidationSta
|
|||||||
|
|
||||||
bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
|
bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_reconsiderable)
|
||||||
{
|
{
|
||||||
if (m_chainman.ActiveChain().Tip()->GetBlockHash() != hashRecentRejectsChainTip) {
|
AssertLockHeld(m_tx_download_mutex);
|
||||||
// If the chain tip has changed previously rejected transactions
|
|
||||||
// might be now valid, e.g. due to a nLockTime'd tx becoming valid,
|
|
||||||
// or a double-spend. Reset the rejects filter and give those
|
|
||||||
// txs a second chance.
|
|
||||||
hashRecentRejectsChainTip = m_chainman.ActiveChain().Tip()->GetBlockHash();
|
|
||||||
m_recent_rejects.reset();
|
|
||||||
m_recent_rejects_reconsiderable.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint256& hash = gtxid.GetHash();
|
const uint256& hash = gtxid.GetHash();
|
||||||
|
|
||||||
@@ -2286,10 +2304,7 @@ bool PeerManagerImpl::AlreadyHaveTx(const GenTxid& gtxid, bool include_reconside
|
|||||||
|
|
||||||
if (include_reconsiderable && m_recent_rejects_reconsiderable.contains(hash)) return true;
|
if (include_reconsiderable && m_recent_rejects_reconsiderable.contains(hash)) return true;
|
||||||
|
|
||||||
{
|
if (m_recent_confirmed_transactions.contains(hash)) return true;
|
||||||
LOCK(m_recent_confirmed_transactions_mutex);
|
|
||||||
if (m_recent_confirmed_transactions.contains(hash)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_recent_rejects.contains(hash) || m_mempool.exists(gtxid);
|
return m_recent_rejects.contains(hash) || m_mempool.exists(gtxid);
|
||||||
}
|
}
|
||||||
@@ -3154,7 +3169,7 @@ void PeerManagerImpl::ProcessInvalidTx(NodeId nodeid, const CTransactionRef& ptx
|
|||||||
{
|
{
|
||||||
AssertLockNotHeld(m_peer_mutex);
|
AssertLockNotHeld(m_peer_mutex);
|
||||||
AssertLockHeld(g_msgproc_mutex);
|
AssertLockHeld(g_msgproc_mutex);
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(m_tx_download_mutex);
|
||||||
|
|
||||||
LogDebug(BCLog::MEMPOOLREJ, "%s (wtxid=%s) from peer=%d was not accepted: %s\n",
|
LogDebug(BCLog::MEMPOOLREJ, "%s (wtxid=%s) from peer=%d was not accepted: %s\n",
|
||||||
ptx->GetHash().ToString(),
|
ptx->GetHash().ToString(),
|
||||||
@@ -3219,7 +3234,7 @@ void PeerManagerImpl::ProcessValidTx(NodeId nodeid, const CTransactionRef& tx, c
|
|||||||
{
|
{
|
||||||
AssertLockNotHeld(m_peer_mutex);
|
AssertLockNotHeld(m_peer_mutex);
|
||||||
AssertLockHeld(g_msgproc_mutex);
|
AssertLockHeld(g_msgproc_mutex);
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(m_tx_download_mutex);
|
||||||
|
|
||||||
// As this version of the transaction was acceptable, we can forget about any requests for it.
|
// As this version of the transaction was acceptable, we can forget about any requests for it.
|
||||||
// No-op if the tx is not in txrequest.
|
// No-op if the tx is not in txrequest.
|
||||||
@@ -3247,7 +3262,7 @@ void PeerManagerImpl::ProcessPackageResult(const PackageToValidate& package_to_v
|
|||||||
{
|
{
|
||||||
AssertLockNotHeld(m_peer_mutex);
|
AssertLockNotHeld(m_peer_mutex);
|
||||||
AssertLockHeld(g_msgproc_mutex);
|
AssertLockHeld(g_msgproc_mutex);
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(m_tx_download_mutex);
|
||||||
|
|
||||||
const auto& package = package_to_validate.m_txns;
|
const auto& package = package_to_validate.m_txns;
|
||||||
const auto& senders = package_to_validate.m_senders;
|
const auto& senders = package_to_validate.m_senders;
|
||||||
@@ -3303,7 +3318,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
|
|||||||
{
|
{
|
||||||
AssertLockNotHeld(m_peer_mutex);
|
AssertLockNotHeld(m_peer_mutex);
|
||||||
AssertLockHeld(g_msgproc_mutex);
|
AssertLockHeld(g_msgproc_mutex);
|
||||||
AssertLockHeld(cs_main);
|
AssertLockHeld(m_tx_download_mutex);
|
||||||
|
|
||||||
const auto& parent_wtxid{ptx->GetWitnessHash()};
|
const auto& parent_wtxid{ptx->GetWitnessHash()};
|
||||||
|
|
||||||
@@ -3356,7 +3371,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
|
|||||||
bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
|
bool PeerManagerImpl::ProcessOrphanTx(Peer& peer)
|
||||||
{
|
{
|
||||||
AssertLockHeld(g_msgproc_mutex);
|
AssertLockHeld(g_msgproc_mutex);
|
||||||
LOCK(cs_main);
|
LOCK2(::cs_main, m_tx_download_mutex);
|
||||||
|
|
||||||
CTransactionRef porphanTx = nullptr;
|
CTransactionRef porphanTx = nullptr;
|
||||||
|
|
||||||
@@ -4173,7 +4188,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||||||
|
|
||||||
const bool reject_tx_invs{RejectIncomingTxs(pfrom)};
|
const bool reject_tx_invs{RejectIncomingTxs(pfrom)};
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK2(cs_main, m_tx_download_mutex);
|
||||||
|
|
||||||
const auto current_time{GetTime<std::chrono::microseconds>()};
|
const auto current_time{GetTime<std::chrono::microseconds>()};
|
||||||
uint256* best_block{nullptr};
|
uint256* best_block{nullptr};
|
||||||
@@ -4506,7 +4521,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||||||
const uint256& hash = peer->m_wtxid_relay ? wtxid : txid;
|
const uint256& hash = peer->m_wtxid_relay ? wtxid : txid;
|
||||||
AddKnownTx(*peer, hash);
|
AddKnownTx(*peer, hash);
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK2(cs_main, m_tx_download_mutex);
|
||||||
|
|
||||||
m_txrequest.ReceivedResponse(pfrom.GetId(), txid);
|
m_txrequest.ReceivedResponse(pfrom.GetId(), txid);
|
||||||
if (tx.HasWitness()) m_txrequest.ReceivedResponse(pfrom.GetId(), wtxid);
|
if (tx.HasWitness()) m_txrequest.ReceivedResponse(pfrom.GetId(), wtxid);
|
||||||
@@ -5263,7 +5278,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|||||||
std::vector<CInv> vInv;
|
std::vector<CInv> vInv;
|
||||||
vRecv >> vInv;
|
vRecv >> vInv;
|
||||||
if (vInv.size() <= MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
|
if (vInv.size() <= MAX_PEER_TX_ANNOUNCEMENTS + MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
|
||||||
LOCK(::cs_main);
|
LOCK(m_tx_download_mutex);
|
||||||
for (CInv &inv : vInv) {
|
for (CInv &inv : vInv) {
|
||||||
if (inv.IsGenTxMsg()) {
|
if (inv.IsGenTxMsg()) {
|
||||||
// If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as
|
// If we receive a NOTFOUND message for a tx we requested, mark the announcement for it as
|
||||||
@@ -5388,6 +5403,7 @@ bool PeerManagerImpl::ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt
|
|||||||
// by another peer that was already processed; in that case,
|
// by another peer that was already processed; in that case,
|
||||||
// the extra work may not be noticed, possibly resulting in an
|
// the extra work may not be noticed, possibly resulting in an
|
||||||
// unnecessary 100ms delay)
|
// unnecessary 100ms delay)
|
||||||
|
LOCK(m_tx_download_mutex);
|
||||||
if (m_orphanage.HaveTxToReconsider(peer->m_id)) fMoreWork = true;
|
if (m_orphanage.HaveTxToReconsider(peer->m_id)) fMoreWork = true;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg.m_type), msg.m_message_size, e.what(), typeid(e).name());
|
LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg.m_type), msg.m_message_size, e.what(), typeid(e).name());
|
||||||
@@ -6281,6 +6297,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|||||||
//
|
//
|
||||||
// Message: getdata (transactions)
|
// Message: getdata (transactions)
|
||||||
//
|
//
|
||||||
|
LOCK(m_tx_download_mutex);
|
||||||
std::vector<std::pair<NodeId, GenTxid>> expired;
|
std::vector<std::pair<NodeId, GenTxid>> expired;
|
||||||
auto requestable = m_txrequest.GetRequestable(pto->GetId(), current_time, &expired);
|
auto requestable = m_txrequest.GetRequestable(pto->GetId(), current_time, &expired);
|
||||||
for (const auto& entry : expired) {
|
for (const auto& entry : expired) {
|
||||||
|
|||||||
@@ -21,15 +21,13 @@ BOOST_FIXTURE_TEST_SUITE(orphanage_tests, TestingSetup)
|
|||||||
class TxOrphanageTest : public TxOrphanage
|
class TxOrphanageTest : public TxOrphanage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline size_t CountOrphans() const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
inline size_t CountOrphans() const
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
return m_orphans.size();
|
return m_orphans.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef RandomOrphan() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
CTransactionRef RandomOrphan()
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
std::map<Wtxid, OrphanTx>::iterator it;
|
std::map<Wtxid, OrphanTx>::iterator it;
|
||||||
it = m_orphans.lower_bound(Wtxid::FromUint256(InsecureRand256()));
|
it = m_orphans.lower_bound(Wtxid::FromUint256(InsecureRand256()));
|
||||||
if (it == m_orphans.end())
|
if (it == m_orphans.end())
|
||||||
|
|||||||
+4
-31
@@ -20,8 +20,6 @@ static constexpr auto ORPHAN_TX_EXPIRE_INTERVAL{5min};
|
|||||||
|
|
||||||
bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
const Txid& hash = tx->GetHash();
|
const Txid& hash = tx->GetHash();
|
||||||
const Wtxid& wtxid = tx->GetWitnessHash();
|
const Wtxid& wtxid = tx->GetWitnessHash();
|
||||||
if (m_orphans.count(wtxid))
|
if (m_orphans.count(wtxid))
|
||||||
@@ -55,13 +53,6 @@ bool TxOrphanage::AddTx(const CTransactionRef& tx, NodeId peer)
|
|||||||
|
|
||||||
int TxOrphanage::EraseTx(const Wtxid& wtxid)
|
int TxOrphanage::EraseTx(const Wtxid& wtxid)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
return EraseTxNoLock(wtxid);
|
|
||||||
}
|
|
||||||
|
|
||||||
int TxOrphanage::EraseTxNoLock(const Wtxid& wtxid)
|
|
||||||
{
|
|
||||||
AssertLockHeld(m_mutex);
|
|
||||||
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
|
std::map<Wtxid, OrphanTx>::iterator it = m_orphans.find(wtxid);
|
||||||
if (it == m_orphans.end())
|
if (it == m_orphans.end())
|
||||||
return 0;
|
return 0;
|
||||||
@@ -97,8 +88,6 @@ int TxOrphanage::EraseTxNoLock(const Wtxid& wtxid)
|
|||||||
|
|
||||||
void TxOrphanage::EraseForPeer(NodeId peer)
|
void TxOrphanage::EraseForPeer(NodeId peer)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
m_peer_work_set.erase(peer);
|
m_peer_work_set.erase(peer);
|
||||||
|
|
||||||
int nErased = 0;
|
int nErased = 0;
|
||||||
@@ -108,7 +97,7 @@ void TxOrphanage::EraseForPeer(NodeId peer)
|
|||||||
// increment to avoid iterator becoming invalid after erasure
|
// increment to avoid iterator becoming invalid after erasure
|
||||||
const auto& [wtxid, orphan] = *iter++;
|
const auto& [wtxid, orphan] = *iter++;
|
||||||
if (orphan.fromPeer == peer) {
|
if (orphan.fromPeer == peer) {
|
||||||
nErased += EraseTxNoLock(wtxid);
|
nErased += EraseTx(wtxid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
|
if (nErased > 0) LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) from peer=%d\n", nErased, peer);
|
||||||
@@ -116,8 +105,6 @@ void TxOrphanage::EraseForPeer(NodeId peer)
|
|||||||
|
|
||||||
void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
|
void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
unsigned int nEvicted = 0;
|
unsigned int nEvicted = 0;
|
||||||
auto nNow{Now<NodeSeconds>()};
|
auto nNow{Now<NodeSeconds>()};
|
||||||
if (m_next_sweep <= nNow) {
|
if (m_next_sweep <= nNow) {
|
||||||
@@ -129,7 +116,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
|
|||||||
{
|
{
|
||||||
std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
|
std::map<Wtxid, OrphanTx>::iterator maybeErase = iter++;
|
||||||
if (maybeErase->second.nTimeExpire <= nNow) {
|
if (maybeErase->second.nTimeExpire <= nNow) {
|
||||||
nErased += EraseTxNoLock(maybeErase->second.tx->GetWitnessHash());
|
nErased += EraseTx(maybeErase->second.tx->GetWitnessHash());
|
||||||
} else {
|
} else {
|
||||||
nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
|
nMinExpTime = std::min(maybeErase->second.nTimeExpire, nMinExpTime);
|
||||||
}
|
}
|
||||||
@@ -142,7 +129,7 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
|
|||||||
{
|
{
|
||||||
// Evict a random orphan:
|
// Evict a random orphan:
|
||||||
size_t randompos = rng.randrange(m_orphan_list.size());
|
size_t randompos = rng.randrange(m_orphan_list.size());
|
||||||
EraseTxNoLock(m_orphan_list[randompos]->second.tx->GetWitnessHash());
|
EraseTx(m_orphan_list[randompos]->second.tx->GetWitnessHash());
|
||||||
++nEvicted;
|
++nEvicted;
|
||||||
}
|
}
|
||||||
if (nEvicted > 0) LogPrint(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
|
if (nEvicted > 0) LogPrint(BCLog::TXPACKAGES, "orphanage overflow, removed %u tx\n", nEvicted);
|
||||||
@@ -150,9 +137,6 @@ void TxOrphanage::LimitOrphans(unsigned int max_orphans, FastRandomContext& rng)
|
|||||||
|
|
||||||
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
|
void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < tx.vout.size(); i++) {
|
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));
|
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()) {
|
if (it_by_prev != m_outpoint_to_orphan_it.end()) {
|
||||||
@@ -171,14 +155,11 @@ void TxOrphanage::AddChildrenToWorkSet(const CTransaction& tx)
|
|||||||
|
|
||||||
bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
|
bool TxOrphanage::HaveTx(const Wtxid& wtxid) const
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
return m_orphans.count(wtxid);
|
return m_orphans.count(wtxid);
|
||||||
}
|
}
|
||||||
|
|
||||||
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
|
CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
auto work_set_it = m_peer_work_set.find(peer);
|
auto work_set_it = m_peer_work_set.find(peer);
|
||||||
if (work_set_it != m_peer_work_set.end()) {
|
if (work_set_it != m_peer_work_set.end()) {
|
||||||
auto& work_set = work_set_it->second;
|
auto& work_set = work_set_it->second;
|
||||||
@@ -197,8 +178,6 @@ CTransactionRef TxOrphanage::GetTxToReconsider(NodeId peer)
|
|||||||
|
|
||||||
bool TxOrphanage::HaveTxToReconsider(NodeId peer)
|
bool TxOrphanage::HaveTxToReconsider(NodeId peer)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
auto work_set_it = m_peer_work_set.find(peer);
|
auto work_set_it = m_peer_work_set.find(peer);
|
||||||
if (work_set_it != m_peer_work_set.end()) {
|
if (work_set_it != m_peer_work_set.end()) {
|
||||||
auto& work_set = work_set_it->second;
|
auto& work_set = work_set_it->second;
|
||||||
@@ -209,8 +188,6 @@ bool TxOrphanage::HaveTxToReconsider(NodeId peer)
|
|||||||
|
|
||||||
void TxOrphanage::EraseForBlock(const CBlock& block)
|
void TxOrphanage::EraseForBlock(const CBlock& block)
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
std::vector<Wtxid> vOrphanErase;
|
std::vector<Wtxid> vOrphanErase;
|
||||||
|
|
||||||
for (const CTransactionRef& ptx : block.vtx) {
|
for (const CTransactionRef& ptx : block.vtx) {
|
||||||
@@ -231,7 +208,7 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
|
|||||||
if (vOrphanErase.size()) {
|
if (vOrphanErase.size()) {
|
||||||
int nErased = 0;
|
int nErased = 0;
|
||||||
for (const auto& orphanHash : vOrphanErase) {
|
for (const auto& orphanHash : vOrphanErase) {
|
||||||
nErased += EraseTxNoLock(orphanHash);
|
nErased += EraseTx(orphanHash);
|
||||||
}
|
}
|
||||||
LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", nErased);
|
LogPrint(BCLog::TXPACKAGES, "Erased %d orphan transaction(s) included or conflicted by block\n", nErased);
|
||||||
}
|
}
|
||||||
@@ -239,8 +216,6 @@ void TxOrphanage::EraseForBlock(const CBlock& block)
|
|||||||
|
|
||||||
std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
|
std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
// First construct a vector of iterators to ensure we do not return duplicates of the same tx
|
// First construct a vector of iterators to ensure we do not return duplicates of the same tx
|
||||||
// and so we can sort by nTimeExpire.
|
// and so we can sort by nTimeExpire.
|
||||||
std::vector<OrphanMap::iterator> iters;
|
std::vector<OrphanMap::iterator> iters;
|
||||||
@@ -281,8 +256,6 @@ std::vector<CTransactionRef> TxOrphanage::GetChildrenFromSamePeer(const CTransac
|
|||||||
|
|
||||||
std::vector<std::pair<CTransactionRef, NodeId>> TxOrphanage::GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const
|
std::vector<std::pair<CTransactionRef, NodeId>> TxOrphanage::GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
|
|
||||||
// First construct vector of iterators to ensure we do not return duplicates of the same tx.
|
// First construct vector of iterators to ensure we do not return duplicates of the same tx.
|
||||||
std::vector<OrphanMap::iterator> iters;
|
std::vector<OrphanMap::iterator> iters;
|
||||||
|
|
||||||
|
|||||||
+17
-24
@@ -22,55 +22,51 @@
|
|||||||
class TxOrphanage {
|
class TxOrphanage {
|
||||||
public:
|
public:
|
||||||
/** Add a new orphan transaction */
|
/** Add a new orphan transaction */
|
||||||
bool AddTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
bool AddTx(const CTransactionRef& tx, NodeId peer);
|
||||||
|
|
||||||
/** Check if we already have an orphan transaction (by wtxid only) */
|
/** Check if we already have an orphan transaction (by wtxid only) */
|
||||||
bool HaveTx(const Wtxid& wtxid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
bool HaveTx(const Wtxid& wtxid) const;
|
||||||
|
|
||||||
/** Extract a transaction from a peer's work set
|
/** Extract a transaction from a peer's work set
|
||||||
* Returns nullptr if there are no transactions to work on.
|
* Returns nullptr if there are no transactions to work on.
|
||||||
* Otherwise returns the transaction reference, and removes
|
* Otherwise returns the transaction reference, and removes
|
||||||
* it from the work set.
|
* it from the work set.
|
||||||
*/
|
*/
|
||||||
CTransactionRef GetTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
CTransactionRef GetTxToReconsider(NodeId peer);
|
||||||
|
|
||||||
/** Erase an orphan by wtxid */
|
/** Erase an orphan by wtxid */
|
||||||
int EraseTx(const Wtxid& wtxid) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
int EraseTx(const Wtxid& wtxid);
|
||||||
|
|
||||||
/** Erase all orphans announced by a peer (eg, after that peer disconnects) */
|
/** Erase all orphans announced by a peer (eg, after that peer disconnects) */
|
||||||
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
void EraseForPeer(NodeId peer);
|
||||||
|
|
||||||
/** Erase all orphans included in or invalidated by a new block */
|
/** Erase all orphans included in or invalidated by a new block */
|
||||||
void EraseForBlock(const CBlock& block) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
void EraseForBlock(const CBlock& block);
|
||||||
|
|
||||||
/** Limit the orphanage to the given maximum */
|
/** Limit the orphanage to the given maximum */
|
||||||
void LimitOrphans(unsigned int max_orphans, FastRandomContext& rng) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
void LimitOrphans(unsigned int max_orphans, FastRandomContext& rng);
|
||||||
|
|
||||||
/** Add any orphans that list a particular tx as a parent into the from peer's work set */
|
/** Add any orphans that list a particular tx as a parent into the from peer's work set */
|
||||||
void AddChildrenToWorkSet(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);;
|
void AddChildrenToWorkSet(const CTransaction& tx);
|
||||||
|
|
||||||
/** Does this peer have any work to do? */
|
/** Does this peer have any work to do? */
|
||||||
bool HaveTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);;
|
bool HaveTxToReconsider(NodeId peer);
|
||||||
|
|
||||||
/** Get all children that spend from this tx and were received from nodeid. Sorted from most
|
/** Get all children that spend from this tx and were received from nodeid. Sorted from most
|
||||||
* recent to least recent. */
|
* recent to least recent. */
|
||||||
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
std::vector<CTransactionRef> GetChildrenFromSamePeer(const CTransactionRef& parent, NodeId nodeid) const;
|
||||||
|
|
||||||
/** Get all children that spend from this tx but were not received from nodeid. Also return
|
/** Get all children that spend from this tx but were not received from nodeid. Also return
|
||||||
* which peer provided each tx. */
|
* which peer provided each tx. */
|
||||||
std::vector<std::pair<CTransactionRef, NodeId>> GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
|
std::vector<std::pair<CTransactionRef, NodeId>> GetChildrenFromDifferentPeer(const CTransactionRef& parent, NodeId nodeid) const;
|
||||||
|
|
||||||
/** Return how many entries exist in the orphange */
|
/** Return how many entries exist in the orphange */
|
||||||
size_t Size() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
|
size_t Size()
|
||||||
{
|
{
|
||||||
LOCK(m_mutex);
|
|
||||||
return m_orphans.size();
|
return m_orphans.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Guards orphan transactions */
|
|
||||||
mutable Mutex m_mutex;
|
|
||||||
|
|
||||||
struct OrphanTx {
|
struct OrphanTx {
|
||||||
CTransactionRef tx;
|
CTransactionRef tx;
|
||||||
NodeId fromPeer;
|
NodeId fromPeer;
|
||||||
@@ -80,10 +76,10 @@ protected:
|
|||||||
|
|
||||||
/** Map from wtxid to orphan transaction record. Limited by
|
/** Map from wtxid to orphan transaction record. Limited by
|
||||||
* -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
|
* -maxorphantx/DEFAULT_MAX_ORPHAN_TRANSACTIONS */
|
||||||
std::map<Wtxid, OrphanTx> m_orphans GUARDED_BY(m_mutex);
|
std::map<Wtxid, OrphanTx> m_orphans;
|
||||||
|
|
||||||
/** Which peer provided the orphans that need to be reconsidered */
|
/** Which peer provided the orphans that need to be reconsidered */
|
||||||
std::map<NodeId, std::set<Wtxid>> m_peer_work_set GUARDED_BY(m_mutex);
|
std::map<NodeId, std::set<Wtxid>> m_peer_work_set;
|
||||||
|
|
||||||
using OrphanMap = decltype(m_orphans);
|
using OrphanMap = decltype(m_orphans);
|
||||||
|
|
||||||
@@ -98,16 +94,13 @@ protected:
|
|||||||
|
|
||||||
/** Index from the parents' COutPoint into the m_orphans. Used
|
/** Index from the parents' COutPoint into the m_orphans. Used
|
||||||
* to remove orphan transactions from the m_orphans */
|
* to remove orphan transactions from the m_orphans */
|
||||||
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it GUARDED_BY(m_mutex);
|
std::map<COutPoint, std::set<OrphanMap::iterator, IteratorComparator>> m_outpoint_to_orphan_it;
|
||||||
|
|
||||||
/** Orphan transactions in vector for quick random eviction */
|
/** Orphan transactions in vector for quick random eviction */
|
||||||
std::vector<OrphanMap::iterator> m_orphan_list GUARDED_BY(m_mutex);
|
std::vector<OrphanMap::iterator> m_orphan_list;
|
||||||
|
|
||||||
/** Erase an orphan by wtxid */
|
|
||||||
int EraseTxNoLock(const Wtxid& wtxid) EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
|
|
||||||
|
|
||||||
/** Timestamp for the next scheduled sweep of expired orphans */
|
/** Timestamp for the next scheduled sweep of expired orphans */
|
||||||
NodeSeconds m_next_sweep GUARDED_BY(m_mutex){0s};
|
NodeSeconds m_next_sweep{0s};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_TXORPHANAGE_H
|
#endif // BITCOIN_TXORPHANAGE_H
|
||||||
+13
-1
@@ -3473,6 +3473,7 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
|
|||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
{
|
||||||
// Lock transaction pool for at least as long as it takes for connectTrace to be consumed
|
// Lock transaction pool for at least as long as it takes for connectTrace to be consumed
|
||||||
LOCK(MempoolMutex());
|
LOCK(MempoolMutex());
|
||||||
const bool was_in_ibd = m_chainman.IsInitialBlockDownload();
|
const bool was_in_ibd = m_chainman.IsInitialBlockDownload();
|
||||||
@@ -3549,7 +3550,12 @@ bool Chainstate::ActivateBestChain(BlockValidationState& state, std::shared_ptr<
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // release MempoolMutex
|
||||||
|
// Notify external listeners about the new tip, even if pindexFork == pindexNewTip.
|
||||||
|
if (m_chainman.m_options.signals && this == &m_chainman.ActiveChainstate()) {
|
||||||
|
m_chainman.m_options.signals->ActiveTipChange(pindexNewTip, m_chainman.IsInitialBlockDownload());
|
||||||
|
}
|
||||||
|
} // release cs_main
|
||||||
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
|
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
|
||||||
|
|
||||||
if (exited_ibd) {
|
if (exited_ibd) {
|
||||||
@@ -3768,6 +3774,12 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
|
|||||||
// distinguish user-initiated invalidateblock changes from other
|
// distinguish user-initiated invalidateblock changes from other
|
||||||
// changes.
|
// changes.
|
||||||
(void)m_chainman.GetNotifications().blockTip(GetSynchronizationState(m_chainman.IsInitialBlockDownload(), m_chainman.m_blockman.m_blockfiles_indexed), *to_mark_failed->pprev);
|
(void)m_chainman.GetNotifications().blockTip(GetSynchronizationState(m_chainman.IsInitialBlockDownload(), m_chainman.m_blockman.m_blockfiles_indexed), *to_mark_failed->pprev);
|
||||||
|
|
||||||
|
// Fire ActiveTipChange now for the current chain tip to make sure clients are notified.
|
||||||
|
// ActivateBestChain may call this as well, but not necessarily.
|
||||||
|
if (m_chainman.m_options.signals) {
|
||||||
|
m_chainman.m_options.signals->ActiveTipChange(m_chain.Tip(), m_chainman.IsInitialBlockDownload());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,6 +183,12 @@ void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlo
|
|||||||
fInitialDownload);
|
fInitialDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ValidationSignals::ActiveTipChange(const CBlockIndex *new_tip, bool is_ibd)
|
||||||
|
{
|
||||||
|
LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip->GetBlockHash().ToString(), new_tip->nHeight);
|
||||||
|
m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });
|
||||||
|
}
|
||||||
|
|
||||||
void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
|
void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
|
||||||
{
|
{
|
||||||
auto event = [tx, mempool_sequence, this] {
|
auto event = [tx, mempool_sequence, this] {
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ protected:
|
|||||||
* Called on a background thread. Only called for the active chainstate.
|
* Called on a background thread. Only called for the active chainstate.
|
||||||
*/
|
*/
|
||||||
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
|
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
|
||||||
|
/**
|
||||||
|
* Notifies listeners any time the block chain tip changes, synchronously.
|
||||||
|
*/
|
||||||
|
virtual void ActiveTipChange(const CBlockIndex* new_tip, bool is_ibd) {};
|
||||||
/**
|
/**
|
||||||
* Notifies listeners of a transaction having been added to mempool.
|
* Notifies listeners of a transaction having been added to mempool.
|
||||||
*
|
*
|
||||||
@@ -214,6 +218,7 @@ public:
|
|||||||
void SyncWithValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main);
|
void SyncWithValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main);
|
||||||
|
|
||||||
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
|
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
|
||||||
|
void ActiveTipChange(const CBlockIndex*, bool);
|
||||||
void TransactionAddedToMempool(const NewMempoolTransactionInfo&, uint64_t mempool_sequence);
|
void TransactionAddedToMempool(const NewMempoolTransactionInfo&, uint64_t mempool_sequence);
|
||||||
void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence);
|
void TransactionRemovedFromMempool(const CTransactionRef&, MemPoolRemovalReason, uint64_t mempool_sequence);
|
||||||
void MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>&, unsigned int nBlockHeight);
|
void MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>&, unsigned int nBlockHeight);
|
||||||
|
|||||||
Reference in new issue
Block a user