Skip to content

Commit f9806dc

Browse files
authored
core: fix canonical hash marker update (ethereum#24996)
* core: fix reorg * core: revert change for memory efficiency * core: revert changes
1 parent 8c0c043 commit f9806dc

File tree

2 files changed

+117
-2
lines changed

2 files changed

+117
-2
lines changed

core/blockchain.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,8 +2071,14 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
20712071
for _, tx := range types.HashDifference(deletedTxs, addedTxs) {
20722072
rawdb.DeleteTxLookupEntry(indexesBatch, tx)
20732073
}
2074-
// Delete any canonical number assignments above the new head
2075-
number := bc.CurrentBlock().NumberU64()
2074+
2075+
// Delete all hash markers that are not part of the new canonical chain.
2076+
// Because the reorg function does not handle new chain head, all hash
2077+
// markers greater than or equal to new chain head should be deleted.
2078+
number := commonBlock.NumberU64()
2079+
if len(newChain) > 1 {
2080+
number = newChain[1].NumberU64()
2081+
}
20762082
for i := number + 1; ; i++ {
20772083
hash := rawdb.ReadCanonicalHash(bc.db, i)
20782084
if hash == (common.Hash{}) {

core/blockchain_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3758,3 +3758,112 @@ func TestSetCanonical(t *testing.T) {
37583758
chain.SetCanonical(canon[TriesInMemory-1])
37593759
verify(canon[TriesInMemory-1])
37603760
}
3761+
3762+
// TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted
3763+
// correctly in case reorg is called.
3764+
func TestCanonicalHashMarker(t *testing.T) {
3765+
var cases = []struct {
3766+
forkA int
3767+
forkB int
3768+
}{
3769+
// ForkA: 10 blocks
3770+
// ForkB: 1 blocks
3771+
//
3772+
// reorged:
3773+
// markers [2, 10] should be deleted
3774+
// markers [1] should be updated
3775+
{10, 1},
3776+
3777+
// ForkA: 10 blocks
3778+
// ForkB: 2 blocks
3779+
//
3780+
// reorged:
3781+
// markers [3, 10] should be deleted
3782+
// markers [1, 2] should be updated
3783+
{10, 2},
3784+
3785+
// ForkA: 10 blocks
3786+
// ForkB: 10 blocks
3787+
//
3788+
// reorged:
3789+
// markers [1, 10] should be updated
3790+
{10, 10},
3791+
3792+
// ForkA: 10 blocks
3793+
// ForkB: 11 blocks
3794+
//
3795+
// reorged:
3796+
// markers [1, 11] should be updated
3797+
{10, 11},
3798+
}
3799+
for _, c := range cases {
3800+
var (
3801+
db = rawdb.NewMemoryDatabase()
3802+
gspec = &Genesis{
3803+
Config: params.TestChainConfig,
3804+
Alloc: GenesisAlloc{},
3805+
BaseFee: big.NewInt(params.InitialBaseFee),
3806+
}
3807+
genesis = gspec.MustCommit(db)
3808+
engine = ethash.NewFaker()
3809+
)
3810+
forkA, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkA, func(i int, gen *BlockGen) {})
3811+
forkB, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkB, func(i int, gen *BlockGen) {})
3812+
3813+
// Initialize test chain
3814+
diskdb := rawdb.NewMemoryDatabase()
3815+
gspec.MustCommit(diskdb)
3816+
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
3817+
if err != nil {
3818+
t.Fatalf("failed to create tester chain: %v", err)
3819+
}
3820+
// Insert forkA and forkB, the canonical should on forkA still
3821+
if n, err := chain.InsertChain(forkA); err != nil {
3822+
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
3823+
}
3824+
if n, err := chain.InsertChain(forkB); err != nil {
3825+
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
3826+
}
3827+
3828+
verify := func(head *types.Block) {
3829+
if chain.CurrentBlock().Hash() != head.Hash() {
3830+
t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash())
3831+
}
3832+
if chain.CurrentFastBlock().Hash() != head.Hash() {
3833+
t.Fatalf("Unexpected fast block hash, want %x, got %x", head.Hash(), chain.CurrentFastBlock().Hash())
3834+
}
3835+
if chain.CurrentHeader().Hash() != head.Hash() {
3836+
t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash())
3837+
}
3838+
if !chain.HasState(head.Root()) {
3839+
t.Fatalf("Lost block state %v %x", head.Number(), head.Hash())
3840+
}
3841+
}
3842+
3843+
// Switch canonical chain to forkB if necessary
3844+
if len(forkA) < len(forkB) {
3845+
verify(forkB[len(forkB)-1])
3846+
} else {
3847+
verify(forkA[len(forkA)-1])
3848+
chain.SetCanonical(forkB[len(forkB)-1])
3849+
verify(forkB[len(forkB)-1])
3850+
}
3851+
3852+
// Ensure all hash markers are updated correctly
3853+
for i := 0; i < len(forkB); i++ {
3854+
block := forkB[i]
3855+
hash := chain.GetCanonicalHash(block.NumberU64())
3856+
if hash != block.Hash() {
3857+
t.Fatalf("Unexpected canonical hash %d", block.NumberU64())
3858+
}
3859+
}
3860+
if c.forkA > c.forkB {
3861+
for i := uint64(c.forkB) + 1; i <= uint64(c.forkA); i++ {
3862+
hash := chain.GetCanonicalHash(i)
3863+
if hash != (common.Hash{}) {
3864+
t.Fatalf("Unexpected canonical hash %d", i)
3865+
}
3866+
}
3867+
}
3868+
}
3869+
}

0 commit comments

Comments
 (0)