From 8913b097e925e100ffb141281609da019fee8249 Mon Sep 17 00:00:00 2001 From: zjubfd <296179868@qq.com> Date: Tue, 11 Jan 2022 18:00:13 +0800 Subject: [PATCH] add unit testcase --- cmd/evm/internal/t8ntool/execution.go | 16 +- cmd/evm/runner.go | 7 +- cmd/evm/staterunner.go | 6 +- cmd/utils/flags.go | 2 +- consensus/clique/clique.go | 8 +- consensus/ethash/consensus.go | 2 +- consensus/parlia/parlia.go | 10 +- core/block_validator.go | 6 +- core/blockchain.go | 14 +- core/blockchain_test.go | 265 ++++++++++++++++++-------- core/chain_makers.go | 6 +- core/error.go | 3 + core/genesis.go | 4 +- core/state/snapshot/difflayer.go | 13 +- core/state/snapshot/disklayer.go | 2 +- core/state/snapshot/snapshot.go | 10 +- core/state/state_test.go | 6 +- core/state/statedb.go | 51 +++-- core/state/statedb_test.go | 20 +- core/state/sync_test.go | 2 +- core/state_processor.go | 6 +- eth/api_test.go | 6 +- eth/state_accessor.go | 2 +- eth/tracers/api.go | 6 +- miner/worker.go | 1 + tests/state_test.go | 9 +- tests/state_test_util.go | 8 +- tests/vm_test_util.go | 2 +- 28 files changed, 324 insertions(+), 169 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 2a9426540a..8b39d17592 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -166,7 +166,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if chainConfig.IsByzantium(vmContext.BlockNumber) { statedb.Finalise(true) } else { - root = statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)).Bytes() + stateRoot, err := statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) + if err != nil { + return nil, nil, err + } + root = stateRoot.Bytes() } // Create a new receipt for the transaction, storing the intermediate root and @@ -197,7 +201,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txIndex++ } - statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) + + _, err := statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) + if err != nil { + return nil, nil, err + } // Add mining reward? if miningReward > 0 { // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases @@ -223,7 +231,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, statedb.AddBalance(pre.Env.Coinbase, minerReward) } // Commit block - root, _, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber)) + root, _, err := statedb.Commit(chainConfig.IsEIP158(vmContext.BlockNumber), nil) if err != nil { fmt.Fprintf(os.Stderr, "Could not commit state: %v", err) return nil, nil, NewError(ErrorEVM, fmt.Errorf("could not commit state: %v", err)) @@ -252,7 +260,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB } } // Commit and re-open to start with a clean state. - root, _, _ := statedb.Commit(false) + root, _, _ := statedb.Commit(false, nil) statedb, _ = state.New(root, sdb, nil) return statedb } diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index f522233e71..bc81f37b17 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -268,8 +268,11 @@ func runCmd(ctx *cli.Context) error { output, leftOverGas, stats, err := timedExec(bench, execFunc) if ctx.GlobalBool(DumpFlag.Name) { - statedb.Commit(true) - statedb.IntermediateRoot(true) + statedb.Commit(true, nil) + _, err := statedb.IntermediateRoot(true) + if err != nil { + return err + } fmt.Println(string(statedb.Dump(false, false, true))) } diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index bfc243b471..923b104172 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -101,7 +101,11 @@ func stateTestCmd(ctx *cli.Context) error { _, state, err := test.Run(st, cfg, false) // print state root for evmlab tracing if ctx.GlobalBool(MachineFlag.Name) && state != nil { - fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", state.IntermediateRoot(false)) + root, err := state.IntermediateRoot(false) + if err != nil { + return err + } + fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", root) } if err != nil { // Test failed, mark as so and dump any state to aid debugging diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index b3daf70ed2..67aca6e731 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -129,7 +129,7 @@ var ( } PipeCommitFlag = cli.BoolFlag{ Name: "pipecommit", - Usage: "Enable pipeline commit feature", + Usage: "Enable MPT pipeline commit, it will improve syncing performance. It is an experimental feature", } RangeLimitFlag = cli.BoolFlag{ Name: "rangelimit", diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index dfec81f6ad..d4a358e893 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -550,7 +550,7 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction, uncles []*types.Header, receipts *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) { // No block rewards in PoA, so the state remains as is and uncles are dropped - header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.Root, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = types.CalcUncleHash(nil) return } @@ -560,7 +560,11 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { // No block rewards in PoA, so the state remains as is and uncles are dropped - header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + var err error + header.Root, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + if err != nil { + return nil, nil, err + } header.UncleHash = types.CalcUncleHash(nil) // Assemble and return the final block for sealing diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index c32e0ec3cf..99c0a5a9ca 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -582,7 +582,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. receipts *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) { // Accumulate any block and uncle rewards and commit the final state root accumulateRewards(chain.Config(), state, header, uncles) - header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.Root, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) return } diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 7ff9219877..e85e9d4e3c 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -769,7 +769,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * wg := sync.WaitGroup{} wg.Add(2) go func() { - rootHash = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + rootHash, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) wg.Done() }() go func() { @@ -779,7 +779,7 @@ func (p *Parlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * wg.Wait() blk.SetRoot(rootHash) // Assemble and return the final block for sealing - return blk, receipts, nil + return blk, receipts, err } // Authorize injects a private key into the consensus engine to mint new blocks @@ -1211,7 +1211,11 @@ func (p *Parlia) applyTransaction( if p.chainConfig.IsByzantium(header.Number) { state.Finalise(true) } else { - root = state.IntermediateRoot(p.chainConfig.IsEIP158(header.Number)).Bytes() + stateRoot, err := state.IntermediateRoot(p.chainConfig.IsEIP158(header.Number)) + if err != nil { + return err + } + root = stateRoot.Bytes() } *usedGas += gasUsed receipt := types.NewReceipt(root, false, *usedGas) diff --git a/core/block_validator.go b/core/block_validator.go index 10976ec0e4..5015397749 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -58,7 +58,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { return ErrKnownBlock } if v.bc.isCachedBadBlock(block) { - return errStateRootVerificationFailed + return ErrKnownBadBlock } // Header validity is known at this point, check the uncles and transactions header := block.Header() @@ -138,8 +138,8 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } if !skipHeavyVerify { validateFuns = append(validateFuns, func() error { - if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { - return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) + if root, err := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root || err != nil { + return fmt.Errorf("invalid merkle root (remote: %x local: %x), err %v", header.Root, root, err) } else { return nil } diff --git a/core/blockchain.go b/core/blockchain.go index 4484c7903e..f95d312692 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -472,6 +472,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par } go bc.untrustedDiffLayerPruneLoop() if bc.pipeCommit { + // check current block and rewind invalid one go bc.rewindInvalidHeaderBlockLoop() } return bc, nil @@ -595,8 +596,10 @@ func (bc *BlockChain) tryRewindBadBlocks() { block := bc.CurrentBlock() snaps := bc.snaps // Verified and Result is false - if snaps != nil && snaps.Snapshot(block.Root()).Verified() && !snaps.Snapshot(block.Root()).WaitVerified() { + if snaps != nil && snaps.Snapshot(block.Root()) != nil && + snaps.Snapshot(block.Root()).Verified() && !snaps.Snapshot(block.Root()).WaitVerified() { // Rewind by one block + log.Warn("current block verified failed, rewind to its parent", "height", block.NumberU64(), "hash", block.Hash()) bc.futureBlocks.Remove(block.Hash()) bc.badBlockCache.Add(block.Hash(), time.Now()) bc.reportBlock(block, nil, errStateRootVerificationFailed) @@ -1086,8 +1089,8 @@ func (bc *BlockChain) HasFastBlock(hash common.Hash, number uint64) bool { // HasState checks if state trie is fully present in the database or not. func (bc *BlockChain) HasState(hash common.Hash) bool { - if bc.snaps != nil { - // If parent snap is pending on verified, treat it as state exist + if bc.pipeCommit && bc.snaps != nil { + // If parent snap is pending on verification, treat it as state exist if s := bc.snaps.Snapshot(hash); s != nil && !s.Verified() { return true } @@ -1774,7 +1777,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types. } // Commit all cached state changes into underlying memory database. - _, diffLayer, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()), tryCommitTrieDB) + _, diffLayer, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()), bc.tryRewindBadBlocks, tryCommitTrieDB) if err != nil { return NonStatTy, err } @@ -2138,8 +2141,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er // Validate the state using the default validator substart = time.Now() if !statedb.IsLightProcessed() { - // Do the state root verification asynchronously - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, true); err != nil { + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, bc.pipeCommit); err != nil { log.Error("validate state failed", "error", err) bc.reportBlock(block, receipts, err) return it.index, err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 984e0295ec..884b33e6d1 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -43,7 +43,8 @@ import ( // So we can deterministically seed different blockchains var ( canonicalSeed = 1 - forkSeed = 2 + forkSeed1 = 2 + forkSeed2 = 3 TestTriesInMemory = 128 ) @@ -51,14 +52,18 @@ var ( // newCanonical creates a chain database, and injects a deterministic canonical // chain. Depending on the full flag, if creates either a full block chain or a // header only chain. -func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *BlockChain, error) { +func newCanonical(engine consensus.Engine, n int, full, pipeline bool) (ethdb.Database, *BlockChain, error) { var ( db = rawdb.NewMemoryDatabase() genesis = new(Genesis).MustCommit(db) ) // Initialize a fresh chain with only a genesis block - blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil) + var ops []BlockChainOption + if pipeline { + ops = append(ops, EnablePipelineCommit) + } + blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, ops...) // Create and inject the requested chain if n == 0 { return db, blockchain, nil @@ -76,9 +81,53 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B } // Test fork of length N starting from block i -func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) { +func testInvalidStateRootBlockImport(t *testing.T, blockchain *BlockChain, i, n int, pipeline bool) { // Copy old chain up to #i into a new db - db, blockchain2, err := newCanonical(ethash.NewFaker(), i, full) + db, blockchain2, err := newCanonical(ethash.NewFaker(), i, true, pipeline) + if err != nil { + t.Fatal("could not make new canonical in testFork", err) + } + defer blockchain2.Stop() + + // Assert the chains have the same header/block at #i + hash1 := blockchain.GetBlockByNumber(uint64(i)).Hash() + hash2 := blockchain2.GetBlockByNumber(uint64(i)).Hash() + if hash1 != hash2 { + t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) + } + // Extend the newly created chain + blockChainB := makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed1) + for idx, block := range blockChainB { + block.SetRoot(common.Hash{0: byte(forkSeed1), 19: byte(idx)}) + } + previousBlock := blockchain.CurrentBlock() + // Sanity check that the forked chain can be imported into the original + if _, err := blockchain.InsertChain(blockChainB); err == nil { + t.Fatalf("failed to report insert error") + } + + time.Sleep(2 * rewindBadBlockInterval) + latestBlock := blockchain.CurrentBlock() + if latestBlock.Hash() != previousBlock.Hash() || latestBlock.NumberU64() != previousBlock.NumberU64() { + t.Fatalf("rewind do not take effect") + } + db, blockchain3, err := newCanonical(ethash.NewFaker(), i, true, pipeline) + if err != nil { + t.Fatal("could not make new canonical in testFork", err) + } + defer blockchain3.Stop() + + blockChainC := makeBlockChain(blockchain3.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed2) + + if _, err := blockchain.InsertChain(blockChainC); err != nil { + t.Fatalf("failed to insert forking chain: %v", err) + } +} + +// Test fork of length N starting from block i +func testFork(t *testing.T, blockchain *BlockChain, i, n int, full, pipeline bool, comparator func(td1, td2 *big.Int)) { + // Copy old chain up to #i into a new db + db, blockchain2, err := newCanonical(ethash.NewFaker(), i, full, pipeline) if err != nil { t.Fatal("could not make new canonical in testFork", err) } @@ -102,12 +151,12 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara headerChainB []*types.Header ) if full { - blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed) + blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, ethash.NewFaker(), db, forkSeed1) if _, err := blockchain2.InsertChain(blockChainB); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } } else { - headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, ethash.NewFaker(), db, forkSeed) + headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, ethash.NewFaker(), db, forkSeed1) if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil { t.Fatalf("failed to insert forking chain: %v", err) } @@ -117,7 +166,7 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara if full { tdPre = blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()) - if err := testBlockChainImport(blockChainB, blockchain); err != nil { + if err := testBlockChainImport(blockChainB, pipeline, blockchain); err != nil { t.Fatalf("failed to import forked block chain: %v", err) } tdPost = blockchain.GetTdByHash(blockChainB[len(blockChainB)-1].Hash()) @@ -134,7 +183,7 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara // testBlockChainImport tries to process a chain of blocks, writing them into // the database if successful. -func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { +func testBlockChainImport(chain types.Blocks, pipelineCommit bool, blockchain *BlockChain) error { for _, block := range chain { // Try and process the block err := blockchain.engine.VerifyHeader(blockchain, block.Header(), true) @@ -152,12 +201,15 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { return err } statedb.SetExpectedStateRoot(block.Root()) + if pipelineCommit { + statedb.EnablePipeCommit() + } statedb, receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}) if err != nil { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, false) + err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, pipelineCommit) if err != nil { blockchain.reportBlock(block, receipts, err) return err @@ -165,7 +217,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.chainmu.Lock() rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) rawdb.WriteBlock(blockchain.db, block) - statedb.Commit(false) + statedb.Commit(false, nil) blockchain.chainmu.Unlock() } return nil @@ -188,8 +240,22 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error return nil } +func TestBlockImportVerification(t *testing.T) { + length := 5 + + // Make first chain starting from genesis + _, processor, err := newCanonical(ethash.NewFaker(), length, true, true) + if err != nil { + t.Fatalf("failed to make new canonical chain: %v", err) + } + defer processor.Stop() + // Start fork from current height + processor = EnablePipelineCommit(processor) + testInvalidStateRootBlockImport(t, processor, length, 10, true) +} + func TestLastBlock(t *testing.T) { - _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true) + _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true, false) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } @@ -206,14 +272,20 @@ func TestLastBlock(t *testing.T) { // Tests that given a starting canonical chain of a given size, it can be extended // with various length chains. -func TestExtendCanonicalHeaders(t *testing.T) { testExtendCanonical(t, false) } -func TestExtendCanonicalBlocks(t *testing.T) { testExtendCanonical(t, true) } +func TestExtendCanonicalHeaders(t *testing.T) { + testExtendCanonical(t, false, false) -func testExtendCanonical(t *testing.T, full bool) { +} +func TestExtendCanonicalBlocks(t *testing.T) { + testExtendCanonical(t, true, false) + testExtendCanonical(t, true, true) +} + +func testExtendCanonical(t *testing.T, full, pipeline bool) { length := 5 // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(ethash.NewFaker(), length, full, pipeline) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -226,22 +298,25 @@ func testExtendCanonical(t *testing.T, full bool) { } } // Start fork from current height - testFork(t, processor, length, 1, full, better) - testFork(t, processor, length, 2, full, better) - testFork(t, processor, length, 5, full, better) - testFork(t, processor, length, 10, full, better) + testFork(t, processor, length, 1, full, pipeline, better) + testFork(t, processor, length, 2, full, pipeline, better) + testFork(t, processor, length, 5, full, pipeline, better) + testFork(t, processor, length, 10, full, pipeline, better) } // Tests that given a starting canonical chain of a given size, creating shorter // forks do not take canonical ownership. -func TestShorterForkHeaders(t *testing.T) { testShorterFork(t, false) } -func TestShorterForkBlocks(t *testing.T) { testShorterFork(t, true) } +func TestShorterForkHeaders(t *testing.T) { testShorterFork(t, false, false) } +func TestShorterForkBlocks(t *testing.T) { + testShorterFork(t, true, false) + testShorterFork(t, true, true) +} -func testShorterFork(t *testing.T, full bool) { +func testShorterFork(t *testing.T, full, pipeline bool) { length := 10 // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(ethash.NewFaker(), length, full, pipeline) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -254,24 +329,30 @@ func testShorterFork(t *testing.T, full bool) { } } // Sum of numbers must be less than `length` for this to be a shorter fork - testFork(t, processor, 0, 3, full, worse) - testFork(t, processor, 0, 7, full, worse) - testFork(t, processor, 1, 1, full, worse) - testFork(t, processor, 1, 7, full, worse) - testFork(t, processor, 5, 3, full, worse) - testFork(t, processor, 5, 4, full, worse) + testFork(t, processor, 0, 3, full, pipeline, worse) + testFork(t, processor, 0, 7, full, pipeline, worse) + testFork(t, processor, 1, 1, full, pipeline, worse) + testFork(t, processor, 1, 7, full, pipeline, worse) + testFork(t, processor, 5, 3, full, pipeline, worse) + testFork(t, processor, 5, 4, full, pipeline, worse) } // Tests that given a starting canonical chain of a given size, creating longer // forks do take canonical ownership. -func TestLongerForkHeaders(t *testing.T) { testLongerFork(t, false) } -func TestLongerForkBlocks(t *testing.T) { testLongerFork(t, true) } +func TestLongerForkHeaders(t *testing.T) { + testLongerFork(t, false, false) +} +func TestLongerForkBlocks(t *testing.T) { + testLongerFork(t, true, false) + testLongerFork(t, true, true) + +} -func testLongerFork(t *testing.T, full bool) { +func testLongerFork(t *testing.T, full, pipeline bool) { length := 10 // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(ethash.NewFaker(), length, full, pipeline) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -284,24 +365,28 @@ func testLongerFork(t *testing.T, full bool) { } } // Sum of numbers must be greater than `length` for this to be a longer fork - testFork(t, processor, 0, 11, full, better) - testFork(t, processor, 0, 15, full, better) - testFork(t, processor, 1, 10, full, better) - testFork(t, processor, 1, 12, full, better) - testFork(t, processor, 5, 6, full, better) - testFork(t, processor, 5, 8, full, better) + testFork(t, processor, 0, 11, full, pipeline, better) + testFork(t, processor, 0, 15, full, pipeline, better) + testFork(t, processor, 1, 10, full, pipeline, better) + testFork(t, processor, 1, 12, full, pipeline, better) + testFork(t, processor, 5, 6, full, pipeline, better) + testFork(t, processor, 5, 8, full, pipeline, better) } // Tests that given a starting canonical chain of a given size, creating equal // forks do take canonical ownership. -func TestEqualForkHeaders(t *testing.T) { testEqualFork(t, false) } -func TestEqualForkBlocks(t *testing.T) { testEqualFork(t, true) } +func TestEqualForkHeaders(t *testing.T) { testEqualFork(t, false, false) } +func TestEqualForkBlocks(t *testing.T) { + testEqualFork(t, true, true) + testEqualFork(t, true, false) -func testEqualFork(t *testing.T, full bool) { +} + +func testEqualFork(t *testing.T, full, pipeline bool) { length := 10 // Make first chain starting from genesis - _, processor, err := newCanonical(ethash.NewFaker(), length, full) + _, processor, err := newCanonical(ethash.NewFaker(), length, full, pipeline) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -314,21 +399,24 @@ func testEqualFork(t *testing.T, full bool) { } } // Sum of numbers must be equal to `length` for this to be an equal fork - testFork(t, processor, 0, 10, full, equal) - testFork(t, processor, 1, 9, full, equal) - testFork(t, processor, 2, 8, full, equal) - testFork(t, processor, 5, 5, full, equal) - testFork(t, processor, 6, 4, full, equal) - testFork(t, processor, 9, 1, full, equal) + testFork(t, processor, 0, 10, full, pipeline, equal) + testFork(t, processor, 1, 9, full, pipeline, equal) + testFork(t, processor, 2, 8, full, pipeline, equal) + testFork(t, processor, 5, 5, full, pipeline, equal) + testFork(t, processor, 6, 4, full, pipeline, equal) + testFork(t, processor, 9, 1, full, pipeline, equal) } // Tests that chains missing links do not get accepted by the processor. -func TestBrokenHeaderChain(t *testing.T) { testBrokenChain(t, false) } -func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) } +func TestBrokenHeaderChain(t *testing.T) { testBrokenChain(t, false, false) } +func TestBrokenBlockChain(t *testing.T) { + testBrokenChain(t, true, false) + testBrokenChain(t, true, true) +} -func testBrokenChain(t *testing.T, full bool) { +func testBrokenChain(t *testing.T, full, pipeline bool) { // Make chain starting from genesis - db, blockchain, err := newCanonical(ethash.NewFaker(), 10, full) + db, blockchain, err := newCanonical(ethash.NewFaker(), 10, full, pipeline) if err != nil { t.Fatalf("failed to make new canonical chain: %v", err) } @@ -336,12 +424,12 @@ func testBrokenChain(t *testing.T, full bool) { // Create a forked chain, and try to insert with a missing link if full { - chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed)[1:] - if err := testBlockChainImport(chain, blockchain); err == nil { + chain := makeBlockChain(blockchain.CurrentBlock(), 5, ethash.NewFaker(), db, forkSeed1)[1:] + if err := testBlockChainImport(chain, pipeline, blockchain); err == nil { t.Errorf("broken block chain not reported") } } else { - chain := makeHeaderChain(blockchain.CurrentHeader(), 5, ethash.NewFaker(), db, forkSeed)[1:] + chain := makeHeaderChain(blockchain.CurrentHeader(), 5, ethash.NewFaker(), db, forkSeed1)[1:] if err := testHeaderChainImport(chain, blockchain); err == nil { t.Errorf("broken header chain not reported") } @@ -350,19 +438,25 @@ func testBrokenChain(t *testing.T, full bool) { // Tests that reorganising a long difficult chain after a short easy one // overwrites the canonical numbers and links in the database. -func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) } -func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) } +func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false, false) } +func TestReorgLongBlocks(t *testing.T) { + testReorgLong(t, true, false) + testReorgLong(t, true, true) +} -func testReorgLong(t *testing.T, full bool) { - testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280, full) +func testReorgLong(t *testing.T, full, pipeline bool) { + testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280, full, pipeline) } // Tests that reorganising a short difficult chain after a long easy one // overwrites the canonical numbers and links in the database. -func TestReorgShortHeaders(t *testing.T) { testReorgShort(t, false) } -func TestReorgShortBlocks(t *testing.T) { testReorgShort(t, true) } +func TestReorgShortHeaders(t *testing.T) { testReorgShort(t, false, false) } +func TestReorgShortBlocks(t *testing.T) { + testReorgShort(t, true, false) + testReorgShort(t, true, true) +} -func testReorgShort(t *testing.T, full bool) { +func testReorgShort(t *testing.T, full, pipeline bool) { // Create a long easy chain vs. a short heavy one. Due to difficulty adjustment // we need a fairly long chain of blocks with different difficulties for a short // one to become heavyer than a long one. The 96 is an empirical value. @@ -374,12 +468,12 @@ func testReorgShort(t *testing.T, full bool) { for i := 0; i < len(diff); i++ { diff[i] = -9 } - testReorg(t, easy, diff, 12615120, full) + testReorg(t, easy, diff, 12615120, full, pipeline) } -func testReorg(t *testing.T, first, second []int64, td int64, full bool) { +func testReorg(t *testing.T, first, second []int64, td int64, full, pipeline bool) { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, pipeline) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } @@ -445,12 +539,16 @@ func testReorg(t *testing.T, first, second []int64, td int64, full bool) { } // Tests that the insertion functions detect banned hashes. -func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) } -func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) } +func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false, false) } +func TestBadBlockHashes(t *testing.T) { + testBadHashes(t, true, true) + testBadHashes(t, true, false) + +} -func testBadHashes(t *testing.T, full bool) { +func testBadHashes(t *testing.T, full, pipeline bool) { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, pipeline) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } @@ -479,12 +577,16 @@ func testBadHashes(t *testing.T, full bool) { // Tests that bad hashes are detected on boot, and the chain rolled back to a // good state prior to the bad hash. -func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) } -func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) } +func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false, false) } +func TestReorgBadBlockHashes(t *testing.T) { + testReorgBadHashes(t, true, false) + testReorgBadHashes(t, true, true) -func testReorgBadHashes(t *testing.T, full bool) { +} + +func testReorgBadHashes(t *testing.T, full, pipeline bool) { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, pipeline) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } @@ -534,13 +636,16 @@ func testReorgBadHashes(t *testing.T, full bool) { } // Tests chain insertions in the face of one entity containing an invalid nonce. -func TestHeadersInsertNonceError(t *testing.T) { testInsertNonceError(t, false) } -func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) } +func TestHeadersInsertNonceError(t *testing.T) { testInsertNonceError(t, false, false) } +func TestBlocksInsertNonceError(t *testing.T) { + testInsertNonceError(t, true, false) + testInsertNonceError(t, true, true) +} -func testInsertNonceError(t *testing.T, full bool) { +func testInsertNonceError(t *testing.T, full, pipeline bool) { for i := 1; i < 25 && !t.Failed(); i++ { // Create a pristine chain and database - db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full) + db, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, pipeline) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } @@ -1213,7 +1318,7 @@ done: // Tests if the canonical block can be fetched from the database during chain insertion. func TestCanonicalBlockRetrieval(t *testing.T) { - _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true) + _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true, false) if err != nil { t.Fatalf("failed to create pristine chain: %v", err) } diff --git a/core/chain_makers.go b/core/chain_makers.go index d8e3ee012f..07e82edae5 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -223,7 +223,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse block, _, _ := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts) // Write state changes to db - root, _, err := statedb.Commit(config.IsEIP158(b.header.Number)) + root, _, err := statedb.Commit(config.IsEIP158(b.header.Number), nil) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } @@ -254,9 +254,9 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S } else { time = parent.Time() + 10 // block time is fixed at 10 seconds } - + root, _ := state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())) return &types.Header{ - Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())), + Root: root, ParentHash: parent.Hash(), Coinbase: parent.Coinbase(), Difficulty: engine.CalcDifficulty(chain, time, &types.Header{ diff --git a/core/error.go b/core/error.go index 1fbd0d599b..0830a699fe 100644 --- a/core/error.go +++ b/core/error.go @@ -34,6 +34,9 @@ var ( // ErrDiffLayerNotFound is returned when diff layer not found. ErrDiffLayerNotFound = errors.New("diff layer not found") + + // ErrKnownBadBlock is return when the block is a known bad block + ErrKnownBadBlock = errors.New("already known bad block") ) // List of evm-call-message pre-checking errors. All state transition messages will diff --git a/core/genesis.go b/core/genesis.go index 9303522947..8a480077c0 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -278,7 +278,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { statedb.SetState(addr, key, value) } } - root := statedb.IntermediateRoot(false) + root, _ := statedb.IntermediateRoot(false) head := &types.Header{ Number: new(big.Int).SetUint64(g.Number), Nonce: types.EncodeNonce(g.Nonce), @@ -298,7 +298,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { if g.Difficulty == nil { head.Difficulty = params.GenesisDifficulty } - statedb.Commit(false) + statedb.Commit(false, nil) statedb.Database().TrieDB().Commit(root, true, nil) return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)) diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go index f5185a0e8f..29207fd6a7 100644 --- a/core/state/snapshot/difflayer.go +++ b/core/state/snapshot/difflayer.go @@ -118,9 +118,8 @@ type diffLayer struct { storageList map[common.Hash][]common.Hash // List of storage slots for iterated retrievals, one per account. Any existing lists are sorted if non-nil storageData map[common.Hash]map[common.Hash][]byte // Keyed storage slots for direct retrieval. one per account (nil means deleted) - // the difflayer is verified when verifiedCh is nil or closed - verifiedCh chan struct{} - verifyRes bool + verifiedCh chan struct{} // the difflayer is verified when verifiedCh is nil or closed + valid bool // mark the difflayer is valid or not. diffed *bloomfilter.Filter // Bloom filter tracking all the diffed items up to the disk layer @@ -261,17 +260,17 @@ func (dl *diffLayer) Root() common.Hash { return dl.root } -// WaitVerified will wait until the diff layer been verified +// WaitVerified will wait until the diff layer been verified and return the verification result func (dl *diffLayer) WaitVerified() bool { if dl.verifiedCh == nil { return true } <-dl.verifiedCh - return dl.verifyRes + return dl.valid } -func (dl *diffLayer) MarkVerified() { - dl.verifyRes = true +func (dl *diffLayer) MarkValid() { + dl.valid = true } func (dl *diffLayer) Verified() bool { diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go index 0485e4823d..004c1c1955 100644 --- a/core/state/snapshot/disklayer.go +++ b/core/state/snapshot/disklayer.go @@ -53,7 +53,7 @@ func (dl *diskLayer) WaitVerified() bool { return true } -func (dl *diskLayer) MarkVerified() {} +func (dl *diskLayer) MarkValid() {} func (dl *diskLayer) Verified() bool { return true diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go index c09671c970..b4bb90d441 100644 --- a/core/state/snapshot/snapshot.go +++ b/core/state/snapshot/snapshot.go @@ -108,7 +108,7 @@ type Snapshot interface { Verified() bool // Store the verification result - MarkVerified() + MarkValid() // Account directly retrieves the account associated with a particular hash in // the snapshot slim data format. @@ -298,14 +298,6 @@ func (t *Tree) Snapshot(blockRoot common.Hash) Snapshot { return t.layers[blockRoot] } -// try remove the invalid difflayer -func (t *Tree) Remove(blockRoot common.Hash) { - t.lock.RLock() - defer t.lock.RUnlock() - - delete(t.layers, blockRoot) -} - // Snapshots returns all visited layers from the topmost layer with specific // root and traverses downward. The layer amount is limited by the given number. // If nodisk is set, then disk layer is excluded. diff --git a/core/state/state_test.go b/core/state/state_test.go index 77847772c6..eb0af4f768 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -54,7 +54,7 @@ func TestDump(t *testing.T) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - s.state.Commit(false) + s.state.Commit(false, nil) // check that DumpToCollector contains the state objects that are in trie got := string(s.state.Dump(false, false, true)) @@ -95,7 +95,7 @@ func TestNull(t *testing.T) { var value common.Hash s.state.SetState(address, common.Hash{}, value) - s.state.Commit(false) + s.state.Commit(false, nil) if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) { t.Errorf("expected empty current value, got %x", value) @@ -167,7 +167,7 @@ func TestSnapshot2(t *testing.T) { so0.deleted = false state.SetStateObject(so0) - root, _, _ := state.Commit(false) + root, _, _ := state.Commit(false, nil) state, _ = New(root, state.db, state.snaps) // and one with deleted == true diff --git a/core/state/statedb.go b/core/state/statedb.go index 91c9e1a210..cf4ef4545b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -140,8 +140,6 @@ type StateDB struct { SnapshotAccountReads time.Duration SnapshotStorageReads time.Duration SnapshotCommits time.Duration - - AccountIntermediate time.Duration } // New creates a new state from a given trie. @@ -174,7 +172,10 @@ func newStateDB(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, tr, err := db.OpenTrie(root) // return error when 1. failed to open trie and 2. the snap is not nil and done verification if err != nil && (sdb.snap == nil || snapVerified) { - return nil, err + if err != nil { + return nil, err + } + return nil, fmt.Errorf("no available state") } sdb.trie = tr return sdb, nil @@ -988,15 +989,18 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) { // IntermediateRoot computes the current root hash of the state trie. // It is called in between transactions to get the root hash that // goes into transaction receipts. -func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) (common.Hash, error) { if s.lightProcessed { s.StopPrefetcher() - return s.trie.Hash() + return s.trie.Hash(), nil } // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) - s.AccountsIntermediateRoot() - return s.StateIntermediateRoot() + err := s.AccountsIntermediateRoot() + if err != nil { + return common.Hash{}, err + } + return s.StateIntermediateRoot(), nil } func (s *StateDB) AccountsIntermediateRoot() error { @@ -1019,8 +1023,8 @@ func (s *StateDB) AccountsIntermediateRoot() error { // We need wait for the parent trie to commit if s.snap != nil { - if verified := s.snap.WaitVerified(); !verified { - return fmt.Errorf("verification parent snap failed") + if valid := s.snap.WaitVerified(); !valid { + return fmt.Errorf("verification on parent snap failed") } } // Although naively it makes sense to retrieve the account trie and then do @@ -1224,7 +1228,7 @@ func (s *StateDB) LightCommit() (common.Hash, *types.DiffLayer, error) { } // Only update if there's a state transition (skip empty Clique blocks) if parent := s.snap.Root(); parent != root { - // for light sync, always do sync commit + // for light commit, always do sync commit if err := s.snaps.Update(root, parent, s.snapDestructs, s.snapAccounts, s.snapStorage, nil); err != nil { log.Warn("Failed to update snapshot tree", "from", parent, "to", root, "err", err) } @@ -1259,13 +1263,23 @@ func (s *StateDB) LightCommit() (common.Hash, *types.DiffLayer, error) { } // Commit writes the state to the underlying in-memory trie database. -func (s *StateDB) Commit(deleteEmptyObjects bool, postCommitFuncs ...func() error) (common.Hash, *types.DiffLayer, error) { +func (s *StateDB) Commit(deleteEmptyObjects bool, failPostCommitFunc func(), postCommitFuncs ...func() error) (common.Hash, *types.DiffLayer, error) { if s.dbErr != nil { return common.Hash{}, nil, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } // Finalize any pending changes and merge everything into the tries if s.lightProcessed { - return s.LightCommit() + root, diff, err := s.LightCommit() + if err != nil { + return root, diff, err + } + for _, postFunc := range postCommitFuncs { + err = postFunc() + if err != nil { + return root, diff, err + } + } + return root, diff, nil } var diffLayer *types.DiffLayer var verified chan struct{} @@ -1280,9 +1294,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool, postCommitFuncs ...func() erro } s.Finalise(deleteEmptyObjects) - start := time.Now() err := s.AccountsIntermediateRoot() - s.AccountIntermediate = time.Since(start) if err != nil { return common.Hash{}, nil, err @@ -1382,9 +1394,14 @@ func (s *StateDB) Commit(deleteEmptyObjects bool, postCommitFuncs ...func() erro if s.pipeCommit { if commitErr == nil { <-snapUpdated - s.snaps.Snapshot(s.stateRoot).MarkVerified() + s.snaps.Snapshot(s.stateRoot).MarkValid() } else { - // The blockchain will do the rewind + // The blockchain will do the further rewind if write block not finish yet + if failPostCommitFunc != nil { + <-snapUpdated + failPostCommitFunc() + } + log.Error("state verification failed", "err", commitErr) } close(verified) } @@ -1466,8 +1483,6 @@ func (s *StateDB) Commit(deleteEmptyObjects bool, postCommitFuncs ...func() erro return common.Hash{}, nil, r } } - // TODO we should know what is the side effect - //s.snap, s.snapDestructs, s.snapAccounts, s.snapStorage = nil, nil, nil, nil root := s.stateRoot if s.pipeCommit { root = s.expectedRoot diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 2c0b9296ff..5a65a96115 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -54,7 +54,7 @@ func TestUpdateLeaks(t *testing.T) { } } - root := state.IntermediateRoot(false) + root, _ := state.IntermediateRoot(false) if err := state.Database().TrieDB().Commit(root, false, nil); err != nil { t.Errorf("can not commit trie %v to persistent database", root.Hex()) } @@ -102,7 +102,7 @@ func TestIntermediateLeaks(t *testing.T) { } // Commit and cross check the databases. - transRoot, _, err := transState.Commit(false) + transRoot, _, err := transState.Commit(false, nil) if err != nil { t.Fatalf("failed to commit transition state: %v", err) } @@ -110,7 +110,7 @@ func TestIntermediateLeaks(t *testing.T) { t.Errorf("can not commit trie %v to persistent database", transRoot.Hex()) } - finalRoot, _, err := finalState.Commit(false) + finalRoot, _, err := finalState.Commit(false, nil) if err != nil { t.Fatalf("failed to commit final state: %v", err) } @@ -473,7 +473,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { func TestTouchDelete(t *testing.T) { s := newStateTest() s.state.GetOrNewStateObject(common.Address{}) - root, _, _ := s.state.Commit(false) + root, _, _ := s.state.Commit(false, nil) s.state, _ = New(root, s.state.db, s.state.snaps) snapshot := s.state.Snapshot() @@ -546,7 +546,7 @@ func TestCopyCommitCopy(t *testing.T) { t.Fatalf("first copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } - copyOne.Commit(false) + copyOne.Commit(false, nil) if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("first copy post-commit balance mismatch: have %v, want %v", balance, 42) } @@ -631,7 +631,7 @@ func TestCopyCopyCommitCopy(t *testing.T) { if val := copyTwo.GetCommittedState(addr, skey); val != (common.Hash{}) { t.Fatalf("second copy pre-commit committed storage slot mismatch: have %x, want %x", val, common.Hash{}) } - copyTwo.Commit(false) + copyTwo.Commit(false, nil) if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 { t.Fatalf("second copy post-commit balance mismatch: have %v, want %v", balance, 42) } @@ -675,7 +675,7 @@ func TestDeleteCreateRevert(t *testing.T) { addr := common.BytesToAddress([]byte("so")) state.SetBalance(addr, big.NewInt(1)) - root, _, _ := state.Commit(false) + root, _, _ := state.Commit(false, nil) state, _ = New(root, state.db, state.snaps) // Simulate self-destructing in one transaction, then create-reverting in another @@ -687,7 +687,7 @@ func TestDeleteCreateRevert(t *testing.T) { state.RevertToSnapshot(id) // Commit the entire state and make sure we don't crash and have the correct state - root, _, _ = state.Commit(true) + root, _, _ = state.Commit(true, nil) state, _ = New(root, state.db, state.snaps) if state.getStateObject(addr) != nil { @@ -712,7 +712,7 @@ func TestMissingTrieNodes(t *testing.T) { a2 := common.BytesToAddress([]byte("another")) state.SetBalance(a2, big.NewInt(100)) state.SetCode(a2, []byte{1, 2, 4}) - root, _, _ = state.Commit(false) + root, _, _ = state.Commit(false, nil) t.Logf("root: %x", root) // force-flush state.Database().TrieDB().Cap(0) @@ -736,7 +736,7 @@ func TestMissingTrieNodes(t *testing.T) { } // Modify the state state.SetBalance(addr, big.NewInt(2)) - root, _, err := state.Commit(false) + root, _, err := state.Commit(false, nil) if err == nil { t.Fatalf("expected error, got root :%x", root) } diff --git a/core/state/sync_test.go b/core/state/sync_test.go index 24cae59004..83b639ee71 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -69,7 +69,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) { state.updateStateObject(obj) accounts = append(accounts, acc) } - root, _, _ := state.Commit(false) + root, _, _ := state.Commit(false, nil) // Return the generated state return db, root, accounts diff --git a/core/state_processor.go b/core/state_processor.go index b6e825b3d3..9a03ef55c9 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -457,7 +457,11 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon if config.IsByzantium(header.Number) { statedb.Finalise(true) } else { - root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() + stateRoot, err := statedb.IntermediateRoot(config.IsEIP158(header.Number)) + if err != nil { + return nil, err + } + root = stateRoot.Bytes() } *usedGas += result.UsedGas diff --git a/eth/api_test.go b/eth/api_test.go index b44eed40bc..1cbb3c18bc 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -77,8 +77,8 @@ func TestAccountRange(t *testing.T) { m[addr] = true } } - state.Commit(true) - root := state.IntermediateRoot(true) + state.Commit(true, nil) + root, _ := state.IntermediateRoot(true) trie, err := statedb.OpenTrie(root) if err != nil { @@ -134,7 +134,7 @@ func TestEmptyAccountRange(t *testing.T) { statedb = state.NewDatabase(rawdb.NewMemoryDatabase()) state, _ = state.New(common.Hash{}, statedb, nil) ) - state.Commit(true) + state.Commit(true, nil) state.IntermediateRoot(true) results := state.IteratorDump(true, true, true, (common.Hash{}).Bytes(), AccountRangeMaxResults) if bytes.Equal(results.Next, (common.Hash{}).Bytes()) { diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 6ea3161d61..ee0c3b447c 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -138,7 +138,7 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } // Finalize the state so any modifications are written to the trie - root, _, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number())) + root, _, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()), nil) if err != nil { return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w", current.NumberU64(), current.Root().Hex(), err) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index a44982b864..97c248ebf6 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -556,7 +556,11 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config } // calling IntermediateRoot will internally call Finalize on the state // so any modifications are written to the trie - roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects)) + root, err := statedb.IntermediateRoot(deleteEmptyObjects) + if err != nil { + return roots, err + } + roots = append(roots, root) } return roots, nil } diff --git a/miner/worker.go b/miner/worker.go index 2dcb75ac10..d49378463d 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -634,6 +634,7 @@ func (w *worker) resultLoop() { logs = append(logs, receipt.Logs...) } // Commit block and state to database. + task.state.SetExpectedStateRoot(block.Root()) _, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true) if err != nil { log.Error("Failed writing block to chain", "err", err) diff --git a/tests/state_test.go b/tests/state_test.go index bb80a162aa..28f8ebba24 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -75,7 +75,14 @@ func TestState(t *testing.T) { t.Run(key+"/snap", func(t *testing.T) { withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error { snaps, statedb, err := test.Run(subtest, vmconfig, true) - if _, err := snaps.Journal(statedb.IntermediateRoot(false)); err != nil { + if err != nil { + return err + } + root, err := statedb.IntermediateRoot(false) + if err != nil { + return err + } + if _, err := snaps.Journal(root); err != nil { return err } return st.checkFailure(t, name+"/snap", err) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index 77d4fd08d4..2d696eefe1 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -198,7 +198,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh } // Commit block - statedb.Commit(config.IsEIP158(block.Number())) + statedb.Commit(config.IsEIP158(block.Number()), nil) // Add 0-value mining reward. This only makes a difference in the cases // where // - the coinbase suicided, or @@ -206,8 +206,8 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh // the coinbase gets no txfee, so isn't created, and thus needs to be touched statedb.AddBalance(block.Coinbase(), new(big.Int)) // And _now_ get the state root - root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) - return snaps, statedb, root, nil + root, err := statedb.IntermediateRoot(config.IsEIP158(block.Number())) + return snaps, statedb, root, err } func (t *StateTest) gasLimit(subtest StateSubtest) uint64 { @@ -226,7 +226,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo } } // Commit and re-open to start with a clean state. - root, _, _ := statedb.Commit(false) + root, _, _ := statedb.Commit(false, nil) var snaps *snapshot.Tree if snapshotter { diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index 418cc67168..e5e649191a 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -81,7 +81,7 @@ type vmExecMarshaling struct { func (t *VMTest) Run(vmconfig vm.Config, snapshotter bool) error { snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter) if snapshotter { - preRoot := statedb.IntermediateRoot(false) + preRoot, _ := statedb.IntermediateRoot(false) defer func() { if _, err := snaps.Journal(preRoot); err != nil { panic(err)