From 4ed4b9ac32f94e66412f879f0c2b15191a6f10b1 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 20 Aug 2019 14:33:50 +0200 Subject: [PATCH] blockchain: fix flaw in block import --- core/blockchain.go | 5 +++++ core/blockchain_test.go | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/core/blockchain.go b/core/blockchain.go index 59be355895..361fa82434 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1742,6 +1742,11 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i canonical := bc.GetBlockByNumber(number) if canonical != nil && canonical.Hash() == block.Hash() { // Not a sidechain block, this is a re-import of a canon block which has it's state pruned + + // Collect the TD of the block. Since we know it's a canon one, + // we can get it directly, and not (like further below) use + // the parent and then add the block on top + externTd = bc.GetTd(block.Hash(), block.NumberU64()) continue } if canonical != nil && canonical.Root() == block.Root() { diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 27115af52d..b79117edb9 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -2241,3 +2241,48 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) { } benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn) } + +// Tests that importing a some old blocks, where the first one is before the +// pruning point. +// This internally leads to a sidechain import, since the blocks trigger an +// ErrPrunedAncestor error. +func TestSideImportPrunedBlocks(t *testing.T) { + + // Generate a canonical chain to act as the main dataset + engine := ethash.NewFaker() + db := rawdb.NewMemoryDatabase() + genesis := new(Genesis).MustCommit(db) + + // Generate and import the canonical chain + blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*TriesInMemory, nil) + diskdb := rawdb.NewMemoryDatabase() + 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] + + // Verify pruning of lastPrunedBlock + if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) { + t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64()) + } + + firstNonPrunedBlock := blocks[len(blocks)-TriesInMemory] + // Verify firstNonPrunedBlock is not pruned + if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) { + t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64()) + } + + // Now re-import some old blocks + blockToReimport := blocks[5 : len(blocks)-TriesInMemory] + _, err = chain.InsertChain(blockToReimport) + if err != nil { + t.Errorf("Got error, %v", err) + } +}