diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 8b39d17592..8617452844 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -166,7 +166,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if chainConfig.IsByzantium(vmContext.BlockNumber) { statedb.Finalise(true) } else { - stateRoot, err := statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) + stateRoot := statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) if err != nil { return nil, nil, err } @@ -202,10 +202,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txIndex++ } - _, err := statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) - if err != nil { - return nil, nil, err - } + statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) // Add mining reward? if miningReward > 0 { // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases @@ -231,7 +228,9 @@ 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), nil) + statedb.Finalise(chainConfig.IsEIP158(vmContext.BlockNumber)) + statedb.AccountsIntermediateRoot() + root, _, err := statedb.Commit(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)) @@ -260,7 +259,9 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB } } // Commit and re-open to start with a clean state. - root, _, _ := statedb.Commit(false, nil) + statedb.Finalise(false) + statedb.AccountsIntermediateRoot() + root, _, _ := statedb.Commit(nil) statedb, _ = state.New(root, sdb, nil) return statedb } diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index bc81f37b17..fd18270182 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -268,11 +268,10 @@ func runCmd(ctx *cli.Context) error { output, leftOverGas, stats, err := timedExec(bench, execFunc) if ctx.GlobalBool(DumpFlag.Name) { - statedb.Commit(true, nil) - _, err := statedb.IntermediateRoot(true) - if err != nil { - return err - } + statedb.Finalise(true) + statedb.AccountsIntermediateRoot() + statedb.Commit(nil) + statedb.IntermediateRoot(true) fmt.Println(string(statedb.Dump(false, false, true))) } diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 923b104172..c373772fbe 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -101,10 +101,7 @@ 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 { - root, err := state.IntermediateRoot(false) - if err != nil { - return err - } + root := state.IntermediateRoot(false) fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%x\"}\n", root) } if err != nil { diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index d4a358e893..2c8094ca3d 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, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = types.CalcUncleHash(nil) return } @@ -561,7 +561,7 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * 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 var err error - header.Root, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) if err != nil { return nil, nil, err } diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 99c0a5a9ca..c32e0ec3cf 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, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) return } diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index e85e9d4e3c..727ac9cf69 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, err = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + rootHash = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) wg.Done() }() go func() { @@ -1211,11 +1211,7 @@ func (p *Parlia) applyTransaction( if p.chainConfig.IsByzantium(header.Number) { state.Finalise(true) } else { - stateRoot, err := state.IntermediateRoot(p.chainConfig.IsEIP158(header.Number)) - if err != nil { - return err - } - root = stateRoot.Bytes() + root = state.IntermediateRoot(p.chainConfig.IsEIP158(header.Number)).Bytes() } *usedGas += gasUsed receipt := types.NewReceipt(root, false, *usedGas) diff --git a/core/block_validator.go b/core/block_validator.go index 5015397749..b109c1e54b 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -131,18 +131,25 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil)) if receiptSha != header.ReceiptHash { return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) - } else { - return nil } + return nil }, } - if !skipHeavyVerify { + if skipHeavyVerify { + validateFuns = append(validateFuns, func() error { + if err := statedb.WaitPipeVerification(); err != nil { + return err + } + statedb.Finalise(v.config.IsEIP158(header.Number)) + statedb.AccountsIntermediateRoot() + return nil + }) + } else { validateFuns = append(validateFuns, func() error { - 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 + 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) } + return nil }) } validateRes := make(chan error, len(validateFuns)) diff --git a/core/blockchain.go b/core/blockchain.go index 18fdc173b5..6c87ffc708 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1779,7 +1779,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()), bc.tryRewindBadBlocks, tryCommitTrieDB) + _, diffLayer, err := state.Commit(bc.tryRewindBadBlocks, tryCommitTrieDB) if err != nil { return NonStatTy, err } diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 884b33e6d1..50d02e0acc 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -217,7 +217,9 @@ func testBlockChainImport(chain types.Blocks, pipelineCommit bool, blockchain *B 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, nil) + statedb.Finalise(false) + statedb.AccountsIntermediateRoot() + statedb.Commit(nil) blockchain.chainmu.Unlock() } return nil diff --git a/core/chain_makers.go b/core/chain_makers.go index 07e82edae5..0e3f9256e2 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), nil) + root, _, err := statedb.Commit(nil) if err != nil { panic(fmt.Sprintf("state write error: %v", err)) } @@ -254,7 +254,7 @@ 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())) + root := state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())) return &types.Header{ Root: root, ParentHash: parent.Hash(), diff --git a/core/genesis.go b/core/genesis.go index 8a480077c0..94bb06dd77 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, nil) + statedb.Commit(nil) statedb.Database().TrieDB().Commit(root, true, nil) return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)) diff --git a/core/state/state_test.go b/core/state/state_test.go index eb0af4f768..4be9ae8ce3 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -54,7 +54,9 @@ func TestDump(t *testing.T) { // write some of them to the trie s.state.updateStateObject(obj1) s.state.updateStateObject(obj2) - s.state.Commit(false, nil) + s.state.Finalise(false) + s.state.AccountsIntermediateRoot() + s.state.Commit(nil) // check that DumpToCollector contains the state objects that are in trie got := string(s.state.Dump(false, false, true)) @@ -95,7 +97,9 @@ func TestNull(t *testing.T) { var value common.Hash s.state.SetState(address, common.Hash{}, value) - s.state.Commit(false, nil) + s.state.Finalise(false) + s.state.AccountsIntermediateRoot() + s.state.Commit(nil) if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) { t.Errorf("expected empty current value, got %x", value) @@ -167,7 +171,9 @@ func TestSnapshot2(t *testing.T) { so0.deleted = false state.SetStateObject(so0) - root, _, _ := state.Commit(false, nil) + state.Finalise(false) + state.AccountsIntermediateRoot() + root, _, _ := state.Commit(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 9103e83ac8..216bd4fbf2 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -934,6 +934,17 @@ func (s *StateDB) GetRefund() uint64 { return s.refund } +// GetRefund returns the current value of the refund counter. +func (s *StateDB) WaitPipeVerification() error { + // We need wait for the parent trie to commit + if s.snap != nil { + if valid := s.snap.WaitAndGetVerifyRes(); !valid { + return fmt.Errorf("verification on parent snap failed") + } + } + return nil +} + // Finalise finalises the state by removing the s destructed objects and clears // the journal as well as the refunds. Finalise, however, will not push any updates // into the tries just yet. Only IntermediateRoot or Commit will do that. @@ -986,21 +997,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, error) { +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { if s.lightProcessed { s.StopPrefetcher() - return s.trie.Hash(), nil + return s.trie.Hash() } // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) - err := s.AccountsIntermediateRoot() - if err != nil { - return common.Hash{}, err - } - return s.StateIntermediateRoot(), nil + s.AccountsIntermediateRoot() + return s.StateIntermediateRoot() } -func (s *StateDB) AccountsIntermediateRoot() error { +func (s *StateDB) AccountsIntermediateRoot() { tasks := make(chan func()) finishCh := make(chan struct{}) defer close(finishCh) @@ -1018,12 +1026,6 @@ func (s *StateDB) AccountsIntermediateRoot() error { }() } - // We need wait for the parent trie to commit - if s.snap != nil { - if valid := s.snap.WaitAndGetVerifyRes(); !valid { - return fmt.Errorf("verification on parent snap failed") - } - } // Although naively it makes sense to retrieve the account trie and then do // the contract storage and account updates sequentially, that short circuits // the account prefetcher. Instead, let's process all the storage updates @@ -1055,7 +1057,6 @@ func (s *StateDB) AccountsIntermediateRoot() error { } } wg.Wait() - return nil } func (s *StateDB) StateIntermediateRoot() common.Hash { @@ -1260,7 +1261,7 @@ 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, failPostCommitFunc func(), postCommitFuncs ...func() error) (common.Hash, *types.DiffLayer, error) { +func (s *StateDB) Commit(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) } @@ -1290,13 +1291,6 @@ func (s *StateDB) Commit(deleteEmptyObjects bool, failPostCommitFunc func(), pos snapUpdated = make(chan struct{}) } - s.Finalise(deleteEmptyObjects) - err := s.AccountsIntermediateRoot() - - if err != nil { - return common.Hash{}, nil, err - } - commmitTrie := func() error { commitErr := func() error { if s.stateRoot = s.StateIntermediateRoot(); s.fullProcessed && s.expectedRoot != s.stateRoot { @@ -1448,9 +1442,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool, failPostCommitFunc func(), pos // - head layer is paired with HEAD state // - head-1 layer is paired with HEAD-1 state // - head-(n-1) layer(bottom-most diff layer) is paired with HEAD-(n-1)state - if err := s.snaps.Cap(s.expectedRoot, s.snaps.CapLimit()); err != nil { - log.Warn("Failed to cap snapshot tree", "root", s.expectedRoot, "layers", s.snaps.CapLimit(), "err", err) - } + go func() { + if err := s.snaps.Cap(s.expectedRoot, s.snaps.CapLimit()); err != nil { + log.Warn("Failed to cap snapshot tree", "root", s.expectedRoot, "layers", s.snaps.CapLimit(), "err", err) + } + }() } } return nil diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 5a65a96115..acbbf1cd2f 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,9 @@ func TestIntermediateLeaks(t *testing.T) { } // Commit and cross check the databases. - transRoot, _, err := transState.Commit(false, nil) + transState.Finalise(false) + transState.AccountsIntermediateRoot() + transRoot, _, err := transState.Commit(nil) if err != nil { t.Fatalf("failed to commit transition state: %v", err) } @@ -110,7 +112,9 @@ func TestIntermediateLeaks(t *testing.T) { t.Errorf("can not commit trie %v to persistent database", transRoot.Hex()) } - finalRoot, _, err := finalState.Commit(false, nil) + finalState.Finalise(false) + finalState.AccountsIntermediateRoot() + finalRoot, _, err := finalState.Commit(nil) if err != nil { t.Fatalf("failed to commit final state: %v", err) } @@ -473,7 +477,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, nil) + root, _, _ := s.state.Commit(nil) s.state, _ = New(root, s.state.db, s.state.snaps) snapshot := s.state.Snapshot() @@ -546,7 +550,9 @@ 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, nil) + copyOne.Finalise(false) + copyOne.AccountsIntermediateRoot() + copyOne.Commit(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 +637,10 @@ 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, nil) + + copyTwo.Finalise(false) + copyTwo.AccountsIntermediateRoot() + copyTwo.Commit(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 +684,9 @@ func TestDeleteCreateRevert(t *testing.T) { addr := common.BytesToAddress([]byte("so")) state.SetBalance(addr, big.NewInt(1)) - root, _, _ := state.Commit(false, nil) + state.Finalise(false) + state.AccountsIntermediateRoot() + root, _, _ := state.Commit(nil) state, _ = New(root, state.db, state.snaps) // Simulate self-destructing in one transaction, then create-reverting in another @@ -686,8 +697,10 @@ func TestDeleteCreateRevert(t *testing.T) { state.SetBalance(addr, big.NewInt(2)) state.RevertToSnapshot(id) + state.Finalise(true) + state.AccountsIntermediateRoot() // Commit the entire state and make sure we don't crash and have the correct state - root, _, _ = state.Commit(true, nil) + root, _, _ = state.Commit(nil) state, _ = New(root, state.db, state.snaps) if state.getStateObject(addr) != nil { @@ -712,7 +725,9 @@ 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, nil) + state.Finalise(false) + state.AccountsIntermediateRoot() + root, _, _ = state.Commit(nil) t.Logf("root: %x", root) // force-flush state.Database().TrieDB().Cap(0) @@ -736,7 +751,9 @@ func TestMissingTrieNodes(t *testing.T) { } // Modify the state state.SetBalance(addr, big.NewInt(2)) - root, _, err := state.Commit(false, nil) + state.Finalise(false) + state.AccountsIntermediateRoot() + root, _, err := state.Commit(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 83b639ee71..fe896791d3 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -69,7 +69,9 @@ func makeTestState() (Database, common.Hash, []*testAccount) { state.updateStateObject(obj) accounts = append(accounts, acc) } - root, _, _ := state.Commit(false, nil) + state.Finalise(false) + state.AccountsIntermediateRoot() + root, _, _ := state.Commit(nil) // Return the generated state return db, root, accounts diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go index 3d1b223bb0..ed60c811d2 100644 --- a/core/state/trie_prefetcher.go +++ b/core/state/trie_prefetcher.go @@ -105,7 +105,7 @@ func (p *triePrefetcher) close() { for _, fetcher := range p.fetchers { p.abortChan <- fetcher // safe to do multiple times <-fetcher.term - if metrics.Enabled { + if metrics.EnabledExpensive { if fetcher.root == p.root { p.accountLoadMeter.Mark(int64(len(fetcher.seen))) p.accountDupMeter.Mark(int64(fetcher.dups)) diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index ec4e7bf972..64dfa26e48 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -17,7 +17,6 @@ package core import ( - "runtime" "sync/atomic" "github.com/ethereum/go-ethereum/consensus" @@ -27,6 +26,8 @@ import ( "github.com/ethereum/go-ethereum/params" ) +const prefetchThread = 2 + // statePrefetcher is a basic Prefetcher, which blindly executes a block on top // of an arbitrary state with the goal of prefetching potentially useful state // data from disk before the main block processor start executing. @@ -54,25 +55,23 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c signer = types.MakeSigner(p.config, header.Number) ) transactions := block.Transactions() - threads := runtime.NumCPU() - batch := len(transactions) / (threads + 1) - if batch == 0 { - return + sortTransactions := make([][]*types.Transaction, prefetchThread) + for i := 0; i < prefetchThread; i++ { + sortTransactions[i] = make([]*types.Transaction, 0, len(transactions)/prefetchThread) + } + for idx, _ := range transactions { + threadIdx := idx % prefetchThread + sortTransactions[threadIdx] = append(sortTransactions[threadIdx], transactions[idx]) } // No need to execute the first batch, since the main processor will do it. - for i := 1; i <= threads; i++ { - start := i * batch - end := (i + 1) * batch - if i == threads { - end = len(transactions) - } - go func(start, end int) { + for i := 0; i < prefetchThread; i++ { + go func(idx int) { newStatedb := statedb.Copy() gaspool := new(GasPool).AddGas(block.GasLimit()) blockContext := NewEVMBlockContext(header, p.bc, nil) evm := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) // Iterate over and process the individual transactions - for i, tx := range transactions[start:end] { + for i, tx := range sortTransactions[idx] { // If block precaching was interrupted, abort if interrupt != nil && atomic.LoadUint32(interrupt) == 1 { return @@ -82,14 +81,13 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c if err != nil { return // Also invalid block, bail out } - newStatedb.Prepare(tx.Hash(), block.Hash(), i) + newStatedb.Prepare(tx.Hash(), header.Hash(), i) if err := precacheTransaction(msg, p.config, gaspool, newStatedb, header, evm); err != nil { return // Ugh, something went horribly wrong, bail out } } - }(start, end) + }(i) } - } // precacheTransaction attempts to apply a transaction to the given state database diff --git a/core/state_processor.go b/core/state_processor.go index 9a03ef55c9..ad185c4188 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -382,6 +382,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg gp = new(GasPool).AddGas(block.GasLimit()) ) signer := types.MakeSigner(p.bc.chainConfig, block.Number()) + statedb.TryPreload(block, signer) var receipts = make([]*types.Receipt, 0) // Mutate the block and state according to any hard-fork specs if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { @@ -457,11 +458,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainCon if config.IsByzantium(header.Number) { statedb.Finalise(true) } else { - stateRoot, err := statedb.IntermediateRoot(config.IsEIP158(header.Number)) - if err != nil { - return nil, err - } - root = stateRoot.Bytes() + root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() } *usedGas += result.UsedGas diff --git a/core/types/transaction.go b/core/types/transaction.go index 74c011544b..2c9d37b2ab 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -488,6 +488,23 @@ func (t *TransactionsByPriceAndNonce) CurrentSize() int { return len(t.heads) } +func (t *TransactionsByPriceAndNonce) Copy() *TransactionsByPriceAndNonce { + var newTransactions TransactionsByPriceAndNonce + newTransactions.signer = t.signer + newTransactions.heads = make([]*Transaction, len(t.heads)) + for idx, tx := range t.heads { + newTransactions.heads[idx] = tx + } + newTransactions.txs = make(map[common.Address]Transactions, len(t.txs)) + for account, txs := range t.txs { + newTransactions.txs[account] = make([]*Transaction, len(txs)) + for idx, tx := range txs { + newTransactions.txs[account][idx] = tx + } + } + return &newTransactions +} + // Message is a fully derived transaction and implements core.Message // // NOTE: In a future PR this will be removed. diff --git a/eth/api_test.go b/eth/api_test.go index 1cbb3c18bc..359671579b 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -77,8 +77,10 @@ func TestAccountRange(t *testing.T) { m[addr] = true } } - state.Commit(true, nil) - root, _ := state.IntermediateRoot(true) + state.Finalise(true) + state.AccountsIntermediateRoot() + state.Commit(nil) + root := state.IntermediateRoot(true) trie, err := statedb.OpenTrie(root) if err != nil { @@ -134,7 +136,7 @@ func TestEmptyAccountRange(t *testing.T) { statedb = state.NewDatabase(rawdb.NewMemoryDatabase()) state, _ = state.New(common.Hash{}, statedb, nil) ) - state.Commit(true, nil) + state.Commit(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 ee0c3b447c..24a0e776f6 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -138,7 +138,9 @@ 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()), nil) + statedb.Finalise(eth.blockchain.Config().IsEIP158(current.Number())) + statedb.AccountsIntermediateRoot() + root, _, err := statedb.Commit(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 6be15e3f2a..8ee2c22ffd 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -556,10 +556,8 @@ 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 - root, err := statedb.IntermediateRoot(deleteEmptyObjects) - if err != nil { - return nil, err - } + root := statedb.IntermediateRoot(deleteEmptyObjects) + roots = append(roots, root) } return roots, nil diff --git a/miner/worker.go b/miner/worker.go index d49378463d..fff785d837 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -135,6 +135,7 @@ type worker struct { engine consensus.Engine eth Backend chain *core.BlockChain + fetcher core.Prefetcher // Feeds pendingLogsFeed event.Feed @@ -200,6 +201,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus chainConfig: chainConfig, engine: engine, eth: eth, + fetcher: core.NewStatePrefetcher(chainConfig, eth.BlockChain(), engine), mux: mux, chain: eth.BlockChain(), isLocalBlock: isLocalBlock, @@ -995,6 +997,10 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64) // and commits new work if consensus engine is running. func (w *worker) commit(uncles []*types.Header, interval func(), update bool, start time.Time) error { s := w.current.state + err := s.WaitPipeVerification() + if err != nil { + return err + } block, receipts, err := w.engine.FinalizeAndAssemble(w.chain, types.CopyHeader(w.current.header), s, w.current.txs, uncles, w.current.receipts) if err != nil { return err diff --git a/tests/state_test.go b/tests/state_test.go index 28f8ebba24..709d874423 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -78,11 +78,7 @@ func TestState(t *testing.T) { if err != nil { return err } - root, err := statedb.IntermediateRoot(false) - if err != nil { - return err - } - if _, err := snaps.Journal(root); err != nil { + if _, err := snaps.Journal(statedb.IntermediateRoot(false)); 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 2d696eefe1..e26f0ed6fd 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -198,7 +198,9 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh } // Commit block - statedb.Commit(config.IsEIP158(block.Number()), nil) + statedb.Finalise(config.IsEIP158(block.Number())) + statedb.AccountsIntermediateRoot() + statedb.Commit(nil) // Add 0-value mining reward. This only makes a difference in the cases // where // - the coinbase suicided, or @@ -206,7 +208,7 @@ 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, err := statedb.IntermediateRoot(config.IsEIP158(block.Number())) + root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) return snaps, statedb, root, err } @@ -226,7 +228,9 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo } } // Commit and re-open to start with a clean state. - root, _, _ := statedb.Commit(false, nil) + statedb.Finalise(false) + statedb.AccountsIntermediateRoot() + root, _, _ := statedb.Commit(nil) var snaps *snapshot.Tree if snapshotter { diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index e5e649191a..418cc67168 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) diff --git a/trie/database.go b/trie/database.go index 1d2028af50..b6a3154d48 100644 --- a/trie/database.go +++ b/trie/database.go @@ -605,14 +605,16 @@ func (db *Database) Cap(limit common.StorageSize) error { // outside code doesn't see an inconsistent state (referenced data removed from // memory cache during commit but not yet in persistent storage). This is ensured // by only uncaching existing data when the database write finalizes. + db.lock.RLock() nodes, storage, start := len(db.dirties), db.dirtiesSize, time.Now() - batch := db.diskdb.NewBatch() - // db.dirtiesSize only contains the useful data in the cache, but when reporting // the total memory consumption, the maintenance metadata is also needed to be // counted. size := db.dirtiesSize + common.StorageSize((len(db.dirties)-1)*cachedNodeSize) size += db.childrenSize - common.StorageSize(len(db.dirties[common.Hash{}].children)*(common.HashLength+2)) + db.lock.RUnlock() + + batch := db.diskdb.NewBatch() // If the preimage cache got large enough, push to disk. If it's still small // leave for later to deduplicate writes. @@ -730,7 +732,9 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H batch.Reset() } // Move the trie itself into the batch, flushing if enough data is accumulated + db.lock.RLock() nodes, storage := len(db.dirties), db.dirtiesSize + db.lock.RUnlock() uncacher := &cleaner{db} if err := db.commit(node, batch, uncacher, callback); err != nil { @@ -774,10 +778,14 @@ func (db *Database) Commit(node common.Hash, report bool, callback func(common.H // commit is the private locked version of Commit. func (db *Database) commit(hash common.Hash, batch ethdb.Batch, uncacher *cleaner, callback func(common.Hash)) error { // If the node does not exist, it's a previously committed node + db.lock.RLock() node, ok := db.dirties[hash] if !ok { + db.lock.RUnlock() return nil } + db.lock.RUnlock() + var err error node.forChilds(func(child common.Hash) { if err == nil {