p2p: Add warning message when receiving headers for blocks cached as invalid
What changed, and why it matters
This commit only adds a clearer warning message in the log when Bitcoin Core receives block headers for blocks it has already marked as invalid. It does not change how blocks are validated, accepted, or rejected. The goal is to help users diagnose database corruption or consensus incompatibility that can cause an endless 'headers sync' loop, not to fix a security vulnerability.
No security action required; treat as a normal usability/diagnostic improvement. Users seeing the new warning should investigate local database corruption or consensus incompatibility as suggested.
Security signals we found
Log-only diagnostic change
No validation or consensus rule modified
No memory safety, cryptographic, or permission change
Helps detect local database corruption / consensus divergence
Evidence from the diff
The patch adds a LogWarning() in PeerManagerImpl::ProcessHeadersMessage() when an outbound peer sends a header whose validation state is BLOCK_CACHED_INVALID. It also enriches the debug message in ChainstateManager::AcceptBlockHeader() to include the block hash. No validation logic, punishment logic, or network behavior is altered.
Changed components
src/net_processing.cppsrc/validation.cppInspect captured patch +9 / −1
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 9cab2461..c539f4a5 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 73b3e91d..e1d6babf 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -4244,7 +4244,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;
}
Why this scored 20/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.