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.
This commit is contained in:
Glenn Willen 2023-01-24 22:03:47 -08:00
parent 8cad8cda44
commit e8475e7edd

View file

@ -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;
}
}