Release cs_main between individual private tx re-attempts
What changed, and why it matters
This commit changes how Bitcoin Core holds an internal lock (cs_main) when retrying private transaction broadcasts. Previously, the lock was held for the entire batch of stale transactions; now it is released and re-acquired between each individual transaction. The stated goal is to make the node more responsive when hundreds or thousands of transactions are being revalidated in quick succession. This is a performance and liveness improvement, not a fix for a clear security vulnerability.
Treat as a routine performance/liveness improvement. No immediate security action is indicated. Reviewers may want to confirm that releasing cs_main between iterations does not introduce race conditions with mempool or chain state, though the per-transaction locking appears consistent with the existing ProcessTransaction usage.
Security signals we found
Lock scope reduction (cs_main) in network processing code
Performance/liveness improvement for large rebroadcast queues
No explicit security claim in commit message or diff
Evidence from the diff
In PeerManagerImpl::ReattemptPrivateBroadcast, the LOCK(cs_main) scope was moved from outside the for-loop to inside it. This means cs_main is no longer held continuously across all stale private-broadcast transactions. Each transaction is revalidated under a separate, short lock acquisition. The change reduces lock contention and improves responsiveness during large rebroadcast queues. There is no direct evidence in the diff or commit message of a security bug being fixed.
Changed components
src/net_processing.cppPeerManagerImpl::ReattemptPrivateBroadcastprivate transaction broadcast revalidationInspect captured patch +2 / −1
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 346ad36e..e7b5d4eb 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1660,8 +1660,9 @@ void PeerManagerImpl::ReattemptPrivateBroadcast(CScheduler& scheduler)
size_t num_for_rebroadcast{0};
const auto stale_txs = m_tx_for_private_broadcast.GetStale();
if (!stale_txs.empty()) {
- LOCK(cs_main);
for (const auto& stale_tx : stale_txs) {
+ // Only hold lock per single submission
+ LOCK(cs_main);
auto mempool_acceptable = m_chainman.ProcessTransaction(stale_tx, /*test_accept=*/true);
if (mempool_acceptable.m_result_type == MempoolAcceptResult::ResultType::VALID) {
LogDebug(BCLog::PRIVBROADCAST,
Why this scored 23/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.