From e8475e7eddb7b5737a8eac238bfbe3924746ca90 Mon Sep 17 00:00:00 2001 From: Glenn Willen Date: Tue, 24 Jan 2023 22:03:47 -0800 Subject: [PATCH] Fix stuck IBD when discarding unwanted headers from peers. When receiving unwanted headers from a peer, first check whether we have recent "best known block" state for that peer. If not, accept one batch of headers to refresh it. Otherwise, we can become stuck in a state where we need blocks, but we don't know which peers have those blocks available. This especially happens during startup, when we don't have this information for any peers until we start processing header messages. --- src/net_processing.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 91abd6025f..b3eb795d65 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2018,18 +2018,26 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer, uint64_t headers_ahead = pindexBestHeader->nHeight - m_chainman.ActiveHeight(); bool too_far_ahead = fTrimHeaders && (headers_ahead >= nHeaderDownloadBuffer); if (too_far_ahead) { - LogPrint(BCLog::NET, "Discarding received headers and pausing header sync from peer=%d, because we are too far ahead of block sync (%d > %d, %d new headers received)\n", pfrom.GetId(), pindexBestHeader->nHeight, m_chainman.ActiveHeight(), nCount); LOCK(cs_main); CNodeState *nodestate = State(pfrom.GetId()); - if (nodestate->fSyncStarted) { - // Cancel sync from this node, so we don't penalize it later. - // This will cause us to automatically start syncing from a different node (or restart syncing from the same node) later, - // if we still need to sync headers. - nSyncStarted--; - nodestate->fSyncStarted = false; - nodestate->m_headers_sync_timeout = 0us; + if ((nodestate->pindexBestKnownBlock == nullptr) || + (nodestate->pindexBestKnownBlock->nHeight < m_chainman.ActiveHeight())) { + // Our notion of what blocks a peer has available is based on its pindexBestKnownBlock, + // which is based on headers recieved from it. If we don't have one, or it's too old, + // then we can never get blocks from this peer until we accept headers from it first. + LogPrint(BCLog::NET, "NOT discarding headers from peer=%d, to update its block availability. (current best header %d, active chain height %d)\n", pfrom.GetId(), pindexBestHeader->nHeight, m_chainman.ActiveHeight()); + } else { + LogPrint(BCLog::NET, "Discarding received headers and pausing header sync from peer=%d, because we are too far ahead of block sync. (%d > %d)\n", pfrom.GetId(), pindexBestHeader->nHeight, m_chainman.ActiveHeight()); + if (nodestate->fSyncStarted) { + // Cancel sync from this node, so we don't penalize it later. + // This will cause us to automatically start syncing from a different node (or restart syncing from the same node) later, + // if we still need to sync headers. + nSyncStarted--; + nodestate->fSyncStarted = false; + nodestate->m_headers_sync_timeout = 0us; + } + return; } - return; } }