mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
blockchain: more tests for sidechain import, fixes #19105
This commit is contained in:
parent
4f85c2b88b
commit
92420baba8
2 changed files with 104 additions and 7 deletions
|
|
@ -588,6 +588,13 @@ func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCanon returns whether the given hash/number is part of the canon chain,
|
||||||
|
// irrespective of whether the state has been pruned or not
|
||||||
|
func (bc *BlockChain) IsCanon(hash common.Hash, number uint64) bool {
|
||||||
|
canonHash := rawdb.ReadCanonicalHash(bc.db, number)
|
||||||
|
return canonHash != (common.Hash{}) && canonHash == hash
|
||||||
|
}
|
||||||
|
|
||||||
// HasBlockAndState checks if a block and associated state trie is fully present
|
// HasBlockAndState checks if a block and associated state trie is fully present
|
||||||
// in the database or not, caching it if present.
|
// in the database or not, caching it if present.
|
||||||
func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
|
func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
|
||||||
|
|
@ -1084,21 +1091,33 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
if len(chain) == 0 {
|
if len(chain) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
// Remove already known canon-blocks
|
||||||
|
var (
|
||||||
|
start = 0
|
||||||
|
block, prev *types.Block
|
||||||
|
)
|
||||||
|
for start, block = range chain {
|
||||||
|
if !bc.IsCanon(block.Hash(), block.NumberU64()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
// Do a sanity check that the provided chain is actually ordered and linked
|
// Do a sanity check that the provided chain is actually ordered and linked
|
||||||
for i := 1; i < len(chain); i++ {
|
for i := start + 1; i < len(chain); i++ {
|
||||||
if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() {
|
block = chain[i]
|
||||||
|
prev = chain[i-1]
|
||||||
|
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
|
||||||
// Chain broke ancestry, log a message (programming error) and skip insertion
|
// Chain broke ancestry, log a message (programming error) and skip insertion
|
||||||
log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),
|
log.Error("Non contiguous block insert", "number", block.Number(), "hash", block.Hash(),
|
||||||
"parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())
|
"parent", block.ParentHash(), "prevnumber", prev.Number(), "prevhash", prev.Hash())
|
||||||
|
|
||||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),
|
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, prev.NumberU64(),
|
||||||
chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
|
prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pre-checks passed, start the full block imports
|
// Pre-checks passed, start the full block imports
|
||||||
bc.wg.Add(1)
|
bc.wg.Add(1)
|
||||||
bc.chainmu.Lock()
|
bc.chainmu.Lock()
|
||||||
n, events, logs, err := bc.insertChain(chain, true)
|
n, events, logs, err := bc.insertChain(chain[start:], true)
|
||||||
bc.chainmu.Unlock()
|
bc.chainmu.Unlock()
|
||||||
bc.wg.Done()
|
bc.wg.Done()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1538,3 +1538,81 @@ func TestLowDiffLongChain(t *testing.T) {
|
||||||
header = chain.GetHeader(header.ParentHash, number-1)
|
header = chain.GetHeader(header.ParentHash, number-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that importing a sidechain (S), where
|
||||||
|
// - S is sidechain, containing blocks [Sn...Sm]
|
||||||
|
// - C is canon chain, containing blocks [G..Cn..Cm]
|
||||||
|
// - A common ancestor is placed at prune-point + blocksBetweenCommonAncestorAndPruneblock
|
||||||
|
// - The sidechain S is prepended with numCanonBlocksInSidechain blocks from the canon chain
|
||||||
|
func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommonAncestorAndPruneblock int) {
|
||||||
|
|
||||||
|
// Generate a canonical chain to act as the main dataset
|
||||||
|
engine := ethash.NewFaker()
|
||||||
|
db := ethdb.NewMemDatabase()
|
||||||
|
genesis := new(Genesis).MustCommit(db)
|
||||||
|
|
||||||
|
// Generate and import the canonical chain
|
||||||
|
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, nil)
|
||||||
|
diskdb := ethdb.NewMemDatabase()
|
||||||
|
new(Genesis).MustCommit(diskdb)
|
||||||
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPrunedIndex := len(blocks) - triesInMemory - 1
|
||||||
|
lastPrunedBlock := blocks[lastPrunedIndex]
|
||||||
|
firstNonPrunedBlock := blocks[len(blocks)-triesInMemory]
|
||||||
|
|
||||||
|
// Verify pruning of lastPrunedBlock
|
||||||
|
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
||||||
|
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
|
||||||
|
}
|
||||||
|
// Verify firstNonPrunedBlock is not pruned
|
||||||
|
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
|
||||||
|
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
|
||||||
|
}
|
||||||
|
// Generate the sidechain
|
||||||
|
// First block should be a known block, block after should be a pruned block. So
|
||||||
|
// canon(pruned), side, side...
|
||||||
|
|
||||||
|
// Generate fork chain, make it longer than canon
|
||||||
|
parent := blocks[lastPrunedIndex+blocksBetweenCommonAncestorAndPruneblock]
|
||||||
|
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 2*triesInMemory, func(i int, b *BlockGen) {
|
||||||
|
b.SetCoinbase(common.Address{2})
|
||||||
|
})
|
||||||
|
// Prepend the parent(s)
|
||||||
|
var sidechain []*types.Block
|
||||||
|
for i := numCanonBlocksInSidechain; i > 0; i-- {
|
||||||
|
sidechain = append(sidechain, blocks[len(blocks)-i])
|
||||||
|
}
|
||||||
|
sidechain = append(sidechain, fork...)
|
||||||
|
_, err = chain.InsertChain(sidechain)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Got error, %v", err)
|
||||||
|
}
|
||||||
|
head := chain.CurrentBlock()
|
||||||
|
if got := fork[len(fork)-1].Hash(); got != head.Hash() {
|
||||||
|
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that importing a sidechain (S), where
|
||||||
|
// - S is sidechain, containing blocks [Sn...Sm]
|
||||||
|
// - C is canon chain, containing blocks [G..Cn..Cm]
|
||||||
|
// - The common ancestor Cc is pruned
|
||||||
|
// - The first block in S: Sn, is == Cn
|
||||||
|
// That is: the sidechain for import contains some blocks already present in canon chain.
|
||||||
|
// So the blocks are
|
||||||
|
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
||||||
|
// ^ ^ ^ pruned
|
||||||
|
func TestPrunedImportSide(t *testing.T) {
|
||||||
|
testSideImport(t, 3, 3)
|
||||||
|
testSideImport(t, 3, -3)
|
||||||
|
testSideImport(t, 10, 0)
|
||||||
|
testSideImport(t, 1, 10)
|
||||||
|
testSideImport(t, 1, -10)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue