p2p: Allow block downloads from peers without snapshot block after assumeutxo validation
What changed, and why it matters
This change fixes a logic bug in Bitcoin Core's peer-to-peer block downloading. After a node finishes validating an 'assumeutxo' snapshot, it was still refusing to download blocks from peers whose best chain did not include the snapshot block—until the node was restarted. The fix removes that unnecessary restriction once validation is complete, allowing normal block downloads to resume. It is a network-efficiency and robustness fix rather than a direct theft-of-funds vulnerability.
Treat as a recommended bug-fix patch. Nodes using assumeutxo should upgrade or apply the patch to avoid lingering block-download restrictions after snapshot validation completes. No emergency response is indicated by the diff alone.
Security signals we found
Logic error causing overly restrictive peer selection
Post-validation state not reflected in existing guard condition
Potential for reduced block-download connectivity after assumeutxo sync
No evidence of malicious exploitation in the diff
Evidence from the diff
In PeerManagerImpl::FindNextBlocksToDownload(), the existing guard skipped peers whose best-known chain did not contain the snapshot base block whenever SnapshotBase() was non-null. Because SnapshotBase() remains set after assumeutxo background validation finishes, the guard stayed active indefinitely (until restart). The patch adds an additional condition m_chainman.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED so the restriction only applies while the snapshot is still being validated. Once validation completes, the node can again download blocks from peers on chains that diverge before the snapshot height.
Changed components
src/net_processing.cppPeerManagerImpl::FindNextBlocksToDownload()AssumeUtxo background validation state handlingInspect captured patch +5 / −3
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 51dcadad..a3707d3b 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1385,11 +1385,13 @@ void PeerManagerImpl::FindNextBlocksToDownload(const Peer& peer, unsigned int co
return;
}
- // When we sync with AssumeUtxo and discover the snapshot is not in the peer's best chain, abort:
- // We can't reorg to this chain due to missing undo data until the background sync has finished,
+ // When syncing with AssumeUtxo and the snapshot has not yet been validated,
+ // abort downloading blocks from peers that don't have the snapshot block in their best chain.
+ // We can't reorg to this chain due to missing undo data until validation completes,
// so downloading blocks from it would be futile.
const CBlockIndex* snap_base{m_chainman.CurrentChainstate().SnapshotBase()};
- if (snap_base && state->pindexBestKnownBlock->GetAncestor(snap_base->nHeight) != snap_base) {
+ if (snap_base && m_chainman.CurrentChainstate().m_assumeutxo == Assumeutxo::UNVALIDATED &&
+ state->pindexBestKnownBlock->GetAncestor(snap_base->nHeight) != snap_base) {
LogDebug(BCLog::NET, "Not downloading blocks from peer=%d, which doesn't have the snapshot block in its best chain.\n", peer.m_id);
return;
}
Why this scored 37/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.