diff --git a/core/blockchain.go b/core/blockchain.go index 6d63383f45..759143c30a 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -883,10 +883,10 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [ var lastWrite uint64 -// WriteBlockWithoutState writes only the block and its metadata to the database, +// writeBlockWithoutState writes only the block and its metadata to the database, // but does not write any state. This is used to construct competing side forks // up to the point where they exceed the canonical total difficulty. -func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error) { +func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) { bc.wg.Add(1) defer bc.wg.Done() @@ -898,6 +898,26 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e return nil } +// writeKnownBlock updates the head block flag with a known block +// and introduces chain reorg if necessary. +func (bc *BlockChain) writeKnownBlock(block *types.Block) error { + bc.wg.Add(1) + defer bc.wg.Done() + + current := bc.CurrentBlock() + if block.ParentHash() != current.Hash() { + if err := bc.reorg(current, block); err != nil { + return err + } + } + // Write the positional metadata for transaction/receipt lookups. + // Preimages here is empty, ignore it. + rawdb.WriteTxLookupEntries(bc.db, block) + + bc.insert(block) + return nil +} + // WriteBlockWithState writes the block and all associated state to the database. func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) { bc.chainmu.Lock() @@ -1139,19 +1159,36 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] // 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() { + var ( + current = bc.CurrentBlock() + localTd = bc.GetTd(current.Hash(), current.NumberU64()) + externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1) // The first block can't be nil + ) + externTd = new(big.Int).Add(externTd, block.Difficulty()) + for block != nil && err == ErrKnownBlock && localTd.Cmp(externTd) >= 0 { stats.ignored++ block, err = it.next() - } - // First block is still known block, the only scenario here is: - // We did a roll-back, and we want to re-import a batch of known blocks while a part - // of known blocks are higher than current head block. - if err == ErrKnownBlock { - block, err = bc.insertKnownChain(block, it) - if bc.CurrentBlock().NumberU64() != current { - lastCanon = bc.CurrentBlock() + if block != nil { + externTd = new(big.Int).Add(externTd, block.Difficulty()) + } + } + // The remaining blocks are still known blocks, the only scenario here is: + // During the fast sync, the pivot point is already submitted but rollback + // happens. Then node resets the head full block to a lower height via `rollback` + // and leaves a few known blocks in the database. + // + // When node runs a fast sync again, it can re-import a batch of known blocks via + // `insertChain` while a part of them have higher total difficulty than current + // head full block(new pivot point). + if err == ErrKnownBlock { + for block != nil && err == ErrKnownBlock { + if err := bc.writeKnownBlock(block); err != nil { + return it.index, nil, nil, err + } + lastCanon = block + + block, err = it.next() } } // Falls through to the block import @@ -1323,37 +1360,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, [] return it.index, events, coalescedLogs, err } -// insertKnownChain inserts a batch of known blocks which are higher than current -// head block. -func (bc *BlockChain) insertKnownChain(block *types.Block, it *insertIterator) (*types.Block, error) { - var ( - externTd *big.Int - knownBlocks []*types.Block - - err = ErrKnownBlock - current = bc.CurrentBlock() - localTd = bc.GetTd(current.Hash(), current.NumberU64()) - ) - for ; block != nil && (err == ErrKnownBlock); block, err = it.next() { - if externTd == nil { - externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1) - } - externTd = new(big.Int).Add(externTd, block.Difficulty()) - - // Short circuit if the known block cannot be imported as a new - // canonical block. - if block.ParentHash() != current.Hash() || externTd.Cmp(localTd) <= 0 { - break - } - knownBlocks = append(knownBlocks, block) - localTd, current = new(big.Int).Add(localTd, block.Difficulty()), block - } - if len(knownBlocks) > 0 { - bc.insert(knownBlocks[len(knownBlocks)-1]) - } - return block, err -} - // insertSideChain is called when an import batch hits upon a pruned ancestor // error, which happens when a sidechain with a sufficiently old fork-block is // found. @@ -1401,7 +1407,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i if !bc.HasBlock(block.Hash(), block.NumberU64()) { start := time.Now() - if err := bc.WriteBlockWithoutState(block, externTd); err != nil { + if err := bc.writeBlockWithoutState(block, externTd); err != nil { return it.index, nil, nil, err } log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(), diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 59678c601a..6157c58c0b 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -1702,116 +1702,113 @@ func TestPrunedImportSide(t *testing.T) { } func TestInsertKnownHeaders(t *testing.T) { testInsertKnownChainData(t, "headers") } -func TestInsertKnownReceiptChain(t *testing.T) { testInsertKnownChainData(t, "receiptChain") } +func TestInsertKnownReceiptChain(t *testing.T) { testInsertKnownChainData(t, "receipts") } func TestInsertKnownBlocks(t *testing.T) { testInsertKnownChainData(t, "blocks") } func testInsertKnownChainData(t *testing.T, typ string) { - // Generate the original common chain segment and the two competing forks engine := ethash.NewFaker() db := rawdb.NewMemoryDatabase() genesis := new(Genesis).MustCommit(db) - blocks, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) - blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + blocks, receipts := GenerateChain(params.TestChainConfig, genesis, engine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + blocks2, receipts2 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) }) + blocks3, receipts3 := GenerateChain(params.TestChainConfig, blocks[len(blocks)-1], engine, db, 33, func(i int, b *BlockGen) { + b.SetCoinbase(common.Address{1}) + b.OffsetTime(-9) // A higher difficulty + }) // Import the shared chain and the original canonical one - diskdb := rawdb.NewMemoryDatabase() - new(Genesis).MustCommit(diskdb) + chaindb := rawdb.NewMemoryDatabase() + new(Genesis).MustCommit(chaindb) - chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil) + chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil) if err != nil { t.Fatalf("failed to create tester chain: %v", err) } + var ( + inserter func(blocks []*types.Block, receipts []types.Receipts) error + asserter func(t *testing.T, block *types.Block) + ) + headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2)) + for _, block := range blocks { + headers = append(headers, block.Header()) + } + for _, block := range blocks2 { + headers2 = append(headers2, block.Header()) + } + if typ == "headers" { + inserter = func(blocks []*types.Block, receipts []types.Receipts) error { + headers := make([]*types.Header, 0, len(blocks)) + for _, block := range blocks { + headers = append(headers, block.Header()) + } + _, err := chain.InsertHeaderChain(headers, 1) + return err + } + asserter = func(t *testing.T, block *types.Block) { + if chain.CurrentHeader().Hash() != block.Hash() { + t.Fatalf("current head header mismatch, have %v, want %v", chain.CurrentHeader().Hash().Hex(), block.Hash().Hex()) + } + } + } else if typ == "receipts" { + inserter = func(blocks []*types.Block, receipts []types.Receipts) error { + headers := make([]*types.Header, 0, len(blocks)) + for _, block := range blocks { + headers = append(headers, block.Header()) + } + _, err := chain.InsertHeaderChain(headers, 1) + if err != nil { + return err + } + _, err = chain.InsertReceiptChain(blocks, receipts) + return err + } + asserter = func(t *testing.T, block *types.Block) { + if chain.CurrentFastBlock().Hash() != block.Hash() { + t.Fatalf("current head fast block mismatch, have %v, want %v", chain.CurrentFastBlock().Hash().Hex(), block.Hash().Hex()) + } + } + } else { + inserter = func(blocks []*types.Block, receipts []types.Receipts) error { + _, err := chain.InsertChain(blocks) + return err + } + asserter = func(t *testing.T, block *types.Block) { + if chain.CurrentBlock().Hash() != block.Hash() { + t.Fatalf("current head block mismatch, have %v, want %v", chain.CurrentBlock().Hash().Hex(), block.Hash().Hex()) + } + } + } + + if err := inserter(blocks, receipts); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + + // Reimport the chain data again. All the imported + // chain data are regarded "known" data. + if err := inserter(blocks, receipts); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + asserter(t, blocks[len(blocks)-1]) + + // Import a long canonical chain with some known data as prefix. var rollback []common.Hash for i := len(blocks) / 2; i < len(blocks); i++ { rollback = append(rollback, blocks[i].Hash()) } - - if typ == "blocks" { - if _, err := chain.InsertChain(blocks); err != nil { - t.Fatalf("failed to insert chain: %v", err) - } - - // The imported blocks are all known blocks and not higher than - // head block. - if _, err := chain.InsertChain(blocks); err != nil { - t.Fatalf("failed to insert known chain: %v", err) - } - - // A part of imported blocks are known blocks. Besides a part of - // known blocks are higher than head blocks. - chain.Rollback(rollback) - if _, err := chain.InsertChain(append(blocks, blocks2...)); err != nil { - t.Fatalf("failed to insert chain with known block as prefix: %v", err) - } - if chain.CurrentBlock().Hash() != blocks2[len(blocks2)-1].Hash() { - t.Fatalf("failed to insert chain with known block as prefix, want head block %v, have head block %v", - blocks2[len(blocks2)-1].Hash().Hex(), chain.CurrentBlock().Hash().Hex()) - } - } else if typ == "headers" { - headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2)) - for _, block := range blocks { - headers = append(headers, block.Header()) - } - for _, block := range blocks2 { - headers2 = append(headers2, block.Header()) - } - if _, err := chain.InsertHeaderChain(headers, 1); err != nil { - t.Fatalf("failed to insert header chain: %v", err) - } - - // The imported headers are all known headers and not higher than - // head block. - if _, err := chain.InsertHeaderChain(headers, 1); err != nil { - t.Fatalf("failed to insert known header chain: %v", err) - } - - // A part of imported headers are known headers. Besides a part of - // known headers are higher than head headers. - chain.Rollback(rollback) - if _, err := chain.InsertHeaderChain(append(headers, headers2...), 1); err != nil { - t.Fatalf("failed to insert header chain with known headers as prefix: %v", err) - } - if chain.CurrentHeader().Hash() != headers2[len(headers2)-1].Hash() { - t.Fatalf("failed to insert header chain with known header as prefix, want head header %v, have head header %v", - headers2[len(headers2)-1].Hash().Hex(), chain.CurrentBlock().Hash().Hex()) - } - } else { - headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2)) - for _, block := range blocks { - headers = append(headers, block.Header()) - } - for _, block := range blocks2 { - headers2 = append(headers2, block.Header()) - } - if _, err := chain.InsertHeaderChain(headers, 1); err != nil { - t.Fatalf("failed to insert header chain: %v", err) - } - if _, err := chain.InsertReceiptChain(blocks, receipts); err != nil { - t.Fatalf("failed to insert receipt chain: %v", err) - } - - // The imported receipt chain are all known and not higher than - // head fast block. - if _, err := chain.InsertReceiptChain(blocks, receipts); err != nil { - t.Fatalf("failed to insert receipt chain: %v", err) - } - // A part of imported headers are known headers. Besides a part of - // known headers are higher than head headers. - chain.Rollback(rollback) - if _, err := chain.InsertHeaderChain(append(headers, headers2...), 1); err != nil { - t.Fatalf("failed to insert header chain with known headers as prefix: %v", err) - } - if _, err := chain.InsertReceiptChain(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil { - t.Fatalf("failed to insert header chain with known headers as prefix: %v", err) - } - if chain.CurrentFastBlock().Hash() != blocks2[len(blocks2)-1].Hash() { - t.Fatalf("failed to insert header chain with known header as prefix, want head header %v, have head header %v", - blocks2[len(blocks2)-1].Hash().Hex(), chain.CurrentFastBlock().Hash().Hex()) - } + chain.Rollback(rollback) + if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil { + t.Fatalf("failed to insert chain data: %v", err) } + asserter(t, blocks2[len(blocks2)-1]) + + // Import a heavier forked chain with some known data as prefix. + if err := inserter(append(blocks, blocks3...), append(receipts, receipts3...)); err != nil { + t.Fatalf("failed to insert chain data: %v", err) + } + asserter(t, blocks3[len(blocks3)-1]) } // Benchmarks large blocks with value transfers to non-existing accounts