Skip to content

Commit

Permalink
consortium-v2: add unit test for FindAncientHeader
Browse files Browse the repository at this point in the history
We remove the hash check in FindAncientHeader to make it easier to write unit
test. This is fine because the parents are guaranteed to be ordered and linked
by the check when InsertChain. This is a preparation commit for refactoring this
function.
  • Loading branch information
minh-bq committed Nov 14, 2024
1 parent 8e06b07 commit 925ef3b
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 2 deletions.
3 changes: 1 addition & 2 deletions consensus/consortium/v2/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,7 @@ func FindAncientHeader(header *types.Header, ite uint64, chain consensus.ChainHe
index := sort.Search(len(candidateParents), func(i int) bool {
return candidateParents[i].Number.Uint64() >= parentHeight
})
if index < len(candidateParents) && candidateParents[index].Number.Uint64() == parentHeight &&
candidateParents[index].Hash() == parentHash {
if index < len(candidateParents) && candidateParents[index].Number.Uint64() == parentHeight {
ancient = candidateParents[index]
found = true
}
Expand Down
93 changes: 93 additions & 0 deletions consensus/consortium/v2/snapshot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package v2

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)

type mockChainReader struct {
headerMapping map[common.Hash]*types.Header
}

func (chainReader *mockChainReader) Config() *params.ChainConfig { return nil }
func (chainReader *mockChainReader) CurrentHeader() *types.Header { return nil }
func (chainReader *mockChainReader) GetHeader(hash common.Hash, number uint64) *types.Header {
return chainReader.headerMapping[hash]
}
func (chainReader *mockChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil }
func (chainReader *mockChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil }
func (chainReader *mockChainReader) DB() ethdb.Database { return nil }
func (chainReader *mockChainReader) StateCache() state.Database { return nil }
func (chainReader *mockChainReader) OpEvents() []*vm.PublishEvent { return nil }

func TestFindCheckpointHeader(t *testing.T) {
// Case 1: checkpoint header is at block 5 (in parent list)
parents := make([]*types.Header, 10)
for i := range parents {
parents[i] = &types.Header{Number: big.NewInt(int64(i)), Coinbase: common.BigToAddress(big.NewInt(int64(i)))}
}

currentHeader := &types.Header{Number: big.NewInt(10)}
checkpointHeader := FindAncientHeader(currentHeader, currentHeader.Number.Uint64()-5, nil, parents)
if checkpointHeader.Number.Cmp(big.NewInt(5)) != 0 && checkpointHeader.Coinbase != common.BigToAddress(big.NewInt(5)) {
t.Fatalf("Expect checkpoint header number: %d, got: %d", 5, checkpointHeader.Number.Int64())
}

// Case 2: checkpoint header is at 5 (lower than parent list)
// parent list ranges from [10, 20)
for i := range parents {
parents[i] = &types.Header{Number: big.NewInt(int64(i + 10)), ParentHash: common.BigToHash(big.NewInt(int64(i + 10 - 1)))}
}
mockChain := mockChainReader{
headerMapping: make(map[common.Hash]*types.Header),
}
// create mock chain 1
for i := 5; i < 10; i++ {
mockChain.headerMapping[common.BigToHash(big.NewInt(int64(100+i)))] = &types.Header{
Number: big.NewInt(int64(i)),
ParentHash: common.BigToHash(big.NewInt(int64(100 + i - 1))),
}
}

// create mock chain 2
for i := 5; i < 10; i++ {
mockChain.headerMapping[common.BigToHash(big.NewInt(int64(i)))] = &types.Header{
Number: big.NewInt(int64(i)),
ParentHash: common.BigToHash(big.NewInt(int64(i - 1))),
}
}

currentHeader = &types.Header{ParentHash: common.BigToHash(big.NewInt(19)), Number: big.NewInt(20)}
// Must traverse and get the correct header in chain 2
checkpointHeader = FindAncientHeader(currentHeader, currentHeader.Number.Uint64()-5, &mockChain, parents)
if checkpointHeader == nil {
t.Fatal("Failed to find checkpoint header")
}
if checkpointHeader.Number.Cmp(big.NewInt(5)) != 0 && checkpointHeader.ParentHash != common.BigToHash(big.NewInt(int64(4))) {
t.Fatalf("Expect checkpoint header number %d, parent hash: %s, got number: %d, parent hash: %s",
5, common.BigToHash(big.NewInt(int64(4))),
checkpointHeader.Number.Int64(), checkpointHeader.ParentHash,
)
}

// Case 3: find checkpoint header with nil parent list
currentHeader = &types.Header{Number: big.NewInt(10), ParentHash: common.BigToHash(big.NewInt(109))}
checkpointHeader = FindAncientHeader(currentHeader, currentHeader.Number.Uint64()-5, &mockChain, nil)
// Must traverse and get the correct header in chain 1
if checkpointHeader == nil {
t.Fatal("Failed to find checkpoint header")
}
if checkpointHeader.Number.Cmp(big.NewInt(5)) != 0 && checkpointHeader.ParentHash != common.BigToHash(big.NewInt(int64(104))) {
t.Fatalf("Expect checkpoint header number %d, parent hash: %s, got number: %d, parent hash: %s",
5, common.BigToHash(big.NewInt(int64(104))),
checkpointHeader.Number.Int64(), checkpointHeader.ParentHash,
)
}
}

0 comments on commit 925ef3b

Please sign in to comment.