diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 34749c96805..439ba2746db 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2955,6 +2955,13 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, Peer& peer, state, &pindexLast)}; if (!processed) { if (state.IsInvalid()) { + if (!pfrom.IsInboundConn() && state.GetResult() == BlockValidationResult::BLOCK_CACHED_INVALID) { + // Warn user if outgoing peers send us headers of blocks that we previously marked as invalid. + LogWarning("%s (received from peer=%i). " + "If this happens with all peers, consider database corruption (that -reindex may fix) " + "or a potential consensus incompatibility.", + state.GetDebugMessage(), pfrom.GetId()); + } MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received"); return; } diff --git a/src/validation.cpp b/src/validation.cpp index eafe19c767f..50732965581 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1958,18 +1958,15 @@ 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) { + 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)) { - LogWarning("Found invalid chain at least ~6 blocks longer than our best chain. Chain state database corruption likely."); + LogWarning("Found invalid chain more than 6 blocks longer than our best chain. This could be due to database corruption or consensus incompatibility with peers."); 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.")); + _("Warning: Found invalid chain more than 6 blocks longer than our best chain. This could be due to database corruption or consensus incompatibility with peers.")); } else { m_chainman.GetNotifications().warningUnset(kernel::Warning::LARGE_WORK_INVALID_CHAIN); } @@ -4244,7 +4241,8 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida *ppindex = pindex; if (pindex->nStatus & BLOCK_FAILED_MASK) { LogDebug(BCLog::VALIDATION, "%s: block %s is marked invalid\n", __func__, hash.ToString()); - return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate-invalid"); + return state.Invalid(BlockValidationResult::BLOCK_CACHED_INVALID, "duplicate-invalid", + strprintf("block %s was previously marked invalid", hash.ToString())); } return true; } @@ -4637,6 +4635,8 @@ bool Chainstate::LoadChainTip() /*verification_progress=*/m_chainman.GuessVerificationProgress(tip)); } + CheckForkWarningConditions(); + return true; } diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index 652c1e023b2..09e3c7fc0a9 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -25,8 +25,7 @@ FILE_CHARS_DISALLOWED = '/\\?%*:|"<>' if platform.system() == 'Windows' else '/' UNCONFIRMED_HASH_STRING = 'unconfirmed' LARGE_WORK_INVALID_CHAIN_WARNING = ( - "Warning: We do not appear to fully agree with our peers " # Exclamation mark removed by SanitizeString in AlertNotify - "You may need to upgrade, or other nodes may need to upgrade." + "Warning: Found invalid chain more than 6 blocks longer than our best chain. This could be due to database corruption or consensus incompatibility with peers." )