diff --git a/core/blockchain.go b/core/blockchain.go index 1079834c56..4c0f3078bc 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1093,16 +1093,10 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { } // 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 - for i := start + 1; i < len(chain); i++ { + for i := 1; i < len(chain); i++ { block = chain[i] prev = chain[i-1] if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() { @@ -1117,7 +1111,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { // Pre-checks passed, start the full block imports bc.wg.Add(1) bc.chainmu.Lock() - n, events, logs, err := bc.insertChain(chain[start:], true) + n, events, logs, err := bc.insertChain(chain, true) bc.chainmu.Unlock() bc.wg.Done() @@ -1165,6 +1159,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] it := newInsertIterator(chain, results, bc.Validator()) block, err := it.next() + + // Left-trim all the known blocks + if err == ErrKnownBlock { + // First block (and state) is known + // 1. We did a roll-back, and should now do a re-import + // 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot + // from the canonical chain, which has not been verified. + // Skip all known blocks that are behind us + current := bc.CurrentBlock().NumberU64() + for block != nil && err == ErrKnownBlock && current >= block.NumberU64() { + stats.ignored++ + block, err = it.next() + } + // Falls through to the block import + } + switch { // First block is pruned, insert as sidechain and reorg only if TD grows enough case err == consensus.ErrPrunedAncestor: @@ -1184,20 +1194,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] // If there are any still remaining, mark as ignored return it.index, events, coalescedLogs, err - // First block (and state) is known - // 1. We did a roll-back, and should now do a re-import - // 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot - // from the canonical chain, which has not been verified. - case err == ErrKnownBlock: - // Skip all known blocks that behind us - current := bc.CurrentBlock().NumberU64() - - for block != nil && err == ErrKnownBlock && current >= block.NumberU64() { - stats.ignored++ - block, err = it.next() - } - // Falls through to the block import - // Some other error occurred, abort case err != nil: stats.ignored += len(it.chain) @@ -1323,6 +1319,10 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() { // Check the canonical state root for that number if number := block.NumberU64(); current.NumberU64() >= number { + if canonical := bc.GetBlockByNumber(number); 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 + continue + } if canonical := bc.GetBlockByNumber(number); canonical != nil && canonical.Root() == block.Root() { // This is most likely a shadow-state attack. When a fork is imported into the // database, and it eventually reaches a block height which is not pruned, we diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 6e364ef2bd..ff821f3242 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1580,14 +1580,15 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon // canon(pruned), side, side... // Generate fork chain, make it longer than canon - parent := blocks[lastPrunedIndex+blocksBetweenCommonAncestorAndPruneblock] + parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock + parent := blocks[parentIndex] 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, blocks[parentIndex+1-i]) } sidechain = append(sidechain, fork...) _, err = chain.InsertChain(sidechain) @@ -1610,6 +1611,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon // [ Cn, Cn+1, Cc, Sn+3 ... Sm] // ^ ^ ^ pruned func TestPrunedImportSide(t *testing.T) { + //glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false))) + //glogger.Verbosity(3) + //log.Root().SetHandler(log.Handler(glogger)) testSideImport(t, 3, 3) testSideImport(t, 3, -3) testSideImport(t, 10, 0)