From 32fea008d876dbd443b8265cb4fe3f3d07d2d620 Mon Sep 17 00:00:00 2001 From: shhhh Date: Wed, 31 Dec 2025 11:32:44 +0530 Subject: [PATCH] core/blockchain.go: cleanup finalized block on rewind in setHeadBeyondRoot (#33486) Fix #33390 `setHeadBeyondRoot` was failing to invalidate finalized blocks because it compared against the original head instead of the rewound root. This fix updates the comparison to use the post-rewind block number, preventing the node from reporting a finalized block that no longer exists. Also added relevant test cases for it. --- core/blockchain.go | 5 +++-- core/blockchain_test.go | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 858eceb630..c7647ee7b4 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1105,11 +1105,12 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha bc.txLookupCache.Purge() // Clear safe block, finalized block if needed - if safe := bc.CurrentSafeBlock(); safe != nil && head < safe.Number.Uint64() { + headBlock := bc.CurrentBlock() + if safe := bc.CurrentSafeBlock(); safe != nil && headBlock.Number.Uint64() < safe.Number.Uint64() { log.Warn("SetHead invalidated safe block") bc.SetSafe(nil) } - if finalized := bc.CurrentFinalBlock(); finalized != nil && head < finalized.Number.Uint64() { + if finalized := bc.CurrentFinalBlock(); finalized != nil && headBlock.Number.Uint64() < finalized.Number.Uint64() { log.Error("SetHead invalidated finalized block") bc.SetFinalized(nil) } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 3e3053d9bf..73ffce93fb 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -4515,3 +4515,45 @@ func TestGetCanonicalReceipt(t *testing.T) { } } } + +// TestSetHeadBeyondRootFinalizedBug tests the issue where the finalized block +// is not cleared when rewinding past it using setHeadBeyondRoot. +func TestSetHeadBeyondRootFinalizedBug(t *testing.T) { + // Create a clean blockchain with 100 blocks using PathScheme (PBSS) + _, _, blockchain, err := newCanonical(ethash.NewFaker(), 100, true, rawdb.PathScheme) + if err != nil { + t.Fatalf("failed to create pristine chain: %v", err) + } + defer blockchain.Stop() + + // Set the "Finalized" marker to the current Head (Block 100) + headBlock := blockchain.CurrentBlock() + if headBlock.Number.Uint64() != 100 { + t.Fatalf("Setup failed: expected head 100, got %d", headBlock.Number.Uint64()) + } + blockchain.SetFinalized(headBlock) + + // Verify setup + if blockchain.CurrentFinalBlock().Number.Uint64() != 100 { + t.Fatalf("Setup failed: Finalized block should be 100") + } + targetBlock := blockchain.GetBlockByNumber(50) + + // Call setHeadBeyondRoot with: + // head = 100 + // repair = true + if _, err := blockchain.setHeadBeyondRoot(100, 0, targetBlock.Root(), true); err != nil { + t.Fatalf("Failed to rewind: %v", err) + } + + currentFinal := blockchain.CurrentFinalBlock() + currentHead := blockchain.CurrentBlock().Number.Uint64() + + // The previous finalized block (100) is now invalid because we rewound to 50. + // The function should have cleared the finalized marker (set to nil). + if currentFinal != nil && currentFinal.Number.Uint64() > currentHead { + t.Errorf("Chain Head: %d , Finalized Block: %d , Finalized block was >= head block.", + currentHead, + currentFinal.Number.Uint64()) + } +}