validation: always validate and retain dynafed header block_height
What changed, and why it matters
This patch fixes a bug in the Elements blockchain where dynamic-federation (dynafed) block headers could contain an incorrect block height and still be accepted, and where stored header records could be rebuilt with a different hash than the original accepted header. The fix ensures dynafed headers always validate and preserve their block height, even when an older compatibility option is turned off. It is a consistency and integrity fix rather than a change to consensus rules.
Review and merge the patch. After deployment, nodes should re-evaluate or reconsider any dynafed headers accepted while the legacy option was off, and operators should ensure the fix is deployed before any dynafed chain reorganizations that could expose the hash-mismatch issue. No emergency action is indicated from the diff alone.
Security signals we found
Header hash mismatch between accepted and reconstructed dynafed headers when legacy option disabled
Missing height validation for dynafed headers when -con_blockheightinheader option is off
Potential for accepting dynafed headers with incorrect block_height
Fix explicitly notes this does not change consensus rules
Evidence from the diff
The commit corrects two places where block_height was only handled when g_con_blockheightinheader was enabled: CBlockIndex/CDiskBlockIndex::GetBlockHeader reconstruction and ContextualCheckBlockHeader validation. Because dynafed headers always serialize block_height into CBlockHeader identity/hash regardless of the legacy option, the previous conditional behavior allowed a dynafed header with a mismatched height to pass validation, and caused reconstructed headers to hash differently from the accepted header when the option was disabled. The patch adds is_dynafed_block()/!m_dynafed_params.IsNull() conditions so dynafed headers are always validated and reconstructed with block_height. No consensus rule is changed because the height was already part of every dynafed header’s hash.
Changed components
src/chain.h (CBlockIndex::GetBlockHeader, CDiskBlockIndex::GetBlockHeader)src/validation.cpp (ContextualCheckBlockHeader)Inspect captured patch +12 / −3
### src/chain.h
@@ -315,7 +315,10 @@ class CBlockIndex
block.hashPrevBlock = pprev->GetBlockHash();
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
- if (g_con_blockheightinheader) {
+ // Dynafed headers always serialize block_height as part of their
+ // identity (see CBlockHeader::Serialize), so it must be reconstructed
+ // regardless of the legacy -con_blockheightinheader option.
+ if (g_con_blockheightinheader || is_dynafed_block()) {
block.block_height = nHeight;
}
block.nBits = nBits;
@@ -540,7 +543,10 @@ class CDiskBlockIndex : public CBlockIndex
block.hashPrevBlock = hashPrev;
block.hashMerkleRoot = hashMerkleRoot;
block.nTime = nTime;
- if (g_con_blockheightinheader) {
+ // Dynafed headers always serialize block_height as part of their
+ // identity (see CBlockHeader::Serialize), so it must be reconstructed
+ // regardless of the legacy -con_blockheightinheader option.
+ if (g_con_blockheightinheader || is_dynafed_block()) {
block.block_height = nHeight;
}
block.nBits = nBits;
### src/validation.cpp
@@ -4845,7 +4845,10 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
// Check height in header against prev
- if (g_con_blockheightinheader && (uint32_t)nHeight != block.block_height) {
+ // Dynafed headers always serialize block_height as part of their identity
+ // (see CBlockHeader::Serialize), so the height must be validated even when
+ // the legacy -con_blockheightinheader option is disabled.
+ if ((g_con_blockheightinheader || !block.m_dynafed_params.IsNull()) && (uint32_t)nHeight != block.block_height) {
LogPrintf("ERROR: %s: block height in header is incorrect (got %d, expected %d)\n", __func__, block.block_height, nHeight);
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-header-height");
}Why this scored 48/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.