From 2400fdfd91dbbe00a6fb1a8585075c37c77ee99e Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Mon, 6 Oct 2025 16:07:19 -0400 Subject: [PATCH 1/2] p2p: Add warning message when receiving headers for blocks cached as invalid Currently, if database corruption leads to a block being marked as invalid incorrectly, we can get stuck in an infinite headerssync loop with no indication what went wrong or how to fix it. With the added log message, users will receive an explicit warning after each failed headerssync attempt. --- src/net_processing.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 37778322151..073a4343350 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2956,6 +2956,10 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer, state, &pindexLast)}; if (!processed) { if (state.IsInvalid()) { + if (state.GetResult() == BlockValidationResult::BLOCK_CACHED_INVALID) { + LogWarning("Received header for a block previously marked as invalid from peer=%d. " + "If this happens with all peers, database corruption is likely and -reindex may be necessary to recover.", pfrom.GetId()); + } MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received"); return; } From 6ae9af6b98598d356d46b65dcfeadda4f8290d2e Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Mon, 6 Oct 2025 15:50:31 -0400 Subject: [PATCH 2/2] validation: call CheckForkWarningConditions during IBD, and at startup The existing IBD check was added at a time when CheckForkWarningConditions did also sophisticated fork detection that could lead to false positives during IBD (55ed3f14751206fc87f0cbf8cb4e223efacef338). The fork detection logic doesn't exist anymore (since fa62304c9760f0de9838e56150008816e7a9bacb), so the IBD check is no longer necessary. Displaying the log at startup will help node operators diagnose the problem better. --- src/validation.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 3f77955da62..90174a442e5 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2026,15 +2026,13 @@ void Chainstate::CheckForkWarningConditions() { AssertLockHeld(cs_main); - // Before we get past initial download, we cannot reliably alert about forks - // (we assume we don't get stuck on a fork before finishing our initial sync) - // Also not applicable to the background chainstate - if (m_chainman.IsInitialBlockDownload() || this->GetRole() == ChainstateRole::BACKGROUND) { + // Not applicable to the background chainstate + if (this->GetRole() == ChainstateRole::BACKGROUND) { return; } if (m_chainman.m_best_invalid && m_chainman.m_best_invalid->nChainWork > m_chain.Tip()->nChainWork + (GetBlockProof(*m_chain.Tip()) * 6)) { - LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); + LogWarning("Found invalid chain at least ~6 blocks longer than our best chain. Chain state database corruption likely."); m_chainman.GetNotifications().warningSet( kernel::Warning::LARGE_WORK_INVALID_CHAIN, _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.")); @@ -4677,6 +4675,8 @@ bool Chainstate::LoadChainTip() /*verification_progress=*/m_chainman.GuessVerificationProgress(tip)); } + CheckForkWarningConditions(); + return true; }