Skip to content

Commit

Permalink
core: fix canonical hash marker update (ethereum#24996)
Browse files Browse the repository at this point in the history
* core: fix reorg

* core: revert change for memory efficiency

* core: revert changes
  • Loading branch information
rjl493456442 authored and cp-wjhan committed Jun 2, 2023
1 parent 2c0649f commit 9903f08
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 2 deletions.
10 changes: 8 additions & 2 deletions core/blockchain.go
Expand Up @@ -2095,8 +2095,14 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
for _, tx := range types.HashDifference(deletedTxs, addedTxs) {
rawdb.DeleteTxLookupEntry(indexesBatch, tx)
}
// Delete any canonical number assignments above the new head
number := bc.CurrentBlock().NumberU64()

// Delete all hash markers that are not part of the new canonical chain.
// Because the reorg function does not handle new chain head, all hash
// markers greater than or equal to new chain head should be deleted.
number := commonBlock.NumberU64()
if len(newChain) > 1 {
number = newChain[1].NumberU64()
}
for i := number + 1; ; i++ {
hash := rawdb.ReadCanonicalHash(bc.db, i)
if hash == (common.Hash{}) {
Expand Down
109 changes: 109 additions & 0 deletions core/blockchain_test.go
Expand Up @@ -3770,3 +3770,112 @@ func TestSetCanonical(t *testing.T) {
chain.SetCanonical(canon[DefaultCacheConfig.TriesInMemory-1])
verify(canon[DefaultCacheConfig.TriesInMemory-1])
}

// TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted
// correctly in case reorg is called.
func TestCanonicalHashMarker(t *testing.T) {
var cases = []struct {
forkA int
forkB int
}{
// ForkA: 10 blocks
// ForkB: 1 blocks
//
// reorged:
// markers [2, 10] should be deleted
// markers [1] should be updated
{10, 1},

// ForkA: 10 blocks
// ForkB: 2 blocks
//
// reorged:
// markers [3, 10] should be deleted
// markers [1, 2] should be updated
{10, 2},

// ForkA: 10 blocks
// ForkB: 10 blocks
//
// reorged:
// markers [1, 10] should be updated
{10, 10},

// ForkA: 10 blocks
// ForkB: 11 blocks
//
// reorged:
// markers [1, 11] should be updated
{10, 11},
}
for _, c := range cases {
var (
db = rawdb.NewMemoryDatabase()
gspec = &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{},
BaseFee: big.NewInt(params.InitialBaseFee),
}
genesis = gspec.MustCommit(db)
engine = ethash.NewFaker()
)
forkA, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkA, func(i int, gen *BlockGen) {})
forkB, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, c.forkB, func(i int, gen *BlockGen) {})

// Initialize test chain
diskdb := rawdb.NewMemoryDatabase()
gspec.MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
// Insert forkA and forkB, the canonical should on forkA still
if n, err := chain.InsertChain(forkA); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
if n, err := chain.InsertChain(forkB); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}

verify := func(head *types.Block) {
if chain.CurrentBlock().Hash() != head.Hash() {
t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash())
}
if chain.CurrentFastBlock().Hash() != head.Hash() {
t.Fatalf("Unexpected fast block hash, want %x, got %x", head.Hash(), chain.CurrentFastBlock().Hash())
}
if chain.CurrentHeader().Hash() != head.Hash() {
t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash())
}
if !chain.HasState(head.Root()) {
t.Fatalf("Lost block state %v %x", head.Number(), head.Hash())
}
}

// Switch canonical chain to forkB if necessary
if len(forkA) < len(forkB) {
verify(forkB[len(forkB)-1])
} else {
verify(forkA[len(forkA)-1])
chain.SetCanonical(forkB[len(forkB)-1])
verify(forkB[len(forkB)-1])
}

// Ensure all hash markers are updated correctly
for i := 0; i < len(forkB); i++ {
block := forkB[i]
hash := chain.GetCanonicalHash(block.NumberU64())
if hash != block.Hash() {
t.Fatalf("Unexpected canonical hash %d", block.NumberU64())
}
}
if c.forkA > c.forkB {
for i := uint64(c.forkB) + 1; i <= uint64(c.forkA); i++ {
hash := chain.GetCanonicalHash(i)
if hash != (common.Hash{}) {
t.Fatalf("Unexpected canonical hash %d", i)
}
}
}
}
}

0 comments on commit 9903f08

Please sign in to comment.