diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 2c68659945eb..de9b19da4804 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -200,7 +200,9 @@ 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() + // the ignored error will eventually be captured by commit. + hash, _ := statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) + root = hash.Bytes() } // Create a new receipt for the transaction, storing the intermediate root and @@ -231,7 +233,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, txIndex++ } - statedb.IntermediateRoot(chainConfig.IsEIP158(vmContext.BlockNumber)) // Add mining reward? (-1 means rewards are disabled) if miningReward >= 0 { // Add mining reward. The mining reward may be `0`, which only makes a difference in the cases diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go index 3a010da9f2dc..48f9759c2fc1 100644 --- a/cmd/evm/runner.go +++ b/cmd/evm/runner.go @@ -274,8 +274,11 @@ func runCmd(ctx *cli.Context) error { output, leftOverGas, stats, err := timedExec(bench, execFunc) if ctx.Bool(DumpFlag.Name) { - statedb.Commit(true) - statedb.IntermediateRoot(true) + _, err := statedb.Commit(true) + if err != nil { + fmt.Println("failed to commit state changes: ", err) + os.Exit(1) + } fmt.Println(string(statedb.Dump(nil))) } diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go index 5eba25c725a3..c0ec881640bd 100644 --- a/cmd/evm/staterunner.go +++ b/cmd/evm/staterunner.go @@ -102,10 +102,21 @@ func stateTestCmd(ctx *cli.Context) error { _, s, err := test.Run(st, cfg, false) // print state root for evmlab tracing if s != nil { - root := s.IntermediateRoot(false) - result.Root = &root - if ctx.Bool(MachineFlag.Name) { - fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) + root, err := s.IntermediateRoot(false) + if err != nil { + // Test failed(corrupted state shouldn't actually + // occur during tests though), mark as so and dump + // any state to aid debugging. + result.Pass, result.Error = false, err.Error() + if ctx.Bool(DumpFlag.Name) && s != nil { + dump := s.RawDump(nil) + result.State = &dump + } + } else { + result.Root = &root + if ctx.Bool(MachineFlag.Name) { + fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root) + } } } if err != nil { diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index eb5aa58ca887..4ce3cdfe03aa 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -341,9 +341,6 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types. amount = amount.Mul(amount, big.NewInt(params.GWei)) state.AddBalance(w.Address, amount) } - // The block reward is no longer handled here. It's done by the - // external consensus engine. - header.Root = state.IntermediateRoot(true) } // FinalizeAndAssemble implements consensus.Engine, setting the final state and @@ -367,6 +364,15 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea } // Finalize and assemble the block. beacon.Finalize(chain, header, state, txs, uncles, withdrawals) + + // Assign the final state root to header. + root, err := state.IntermediateRoot(true) + if err != nil { + return nil, err + } + header.Root = root + + // Assemble and return the final block. return types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)), nil } diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 4706bbac1ca9..ca877df32c85 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -569,8 +569,6 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header // rewards given. func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { // 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.UncleHash = types.CalcUncleHash(nil) } // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, @@ -579,11 +577,17 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * if len(withdrawals) > 0 { return nil, errors.New("clique does not support withdrawals") } - - // Finalize block + // Finalize block. c.Finalize(chain, header, state, txs, uncles, nil) - // Assemble and return the final block for sealing + // Assign the final state root to header. + root, err := state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + if err != nil { + return nil, err + } + header.Root = root + + // Assemble and return the final block for sealing. return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil } diff --git a/consensus/consensus.go b/consensus/consensus.go index 190d5ae12c8a..007d6afbec89 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -84,16 +84,16 @@ type Engine interface { // rules of a particular engine. The changes are executed inline. Prepare(chain ChainHeaderReader, header *types.Header) error - // Finalize runs any post-transaction state modifications (e.g. block rewards) - // but does not assemble the block. + // Finalize runs any post-transaction state modifications (e.g. block rewards + // or process withdrawals) but does not assemble the block. // - // Note: The block header and state database might be updated to reflect any - // consensus rules that happen at finalization (e.g. block rewards). + // Note: The state database might be updated to reflect any consensus rules + // that happen at finalization. Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block - // rewards) and assembles the final block. + // rewards or process withdrawals) and assembles the final block. // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index da29e16597b6..5e9396da346c 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -603,7 +603,6 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { // 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)) } // FinalizeAndAssemble implements consensus.Engine, accumulating the block and @@ -612,10 +611,17 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea if len(withdrawals) > 0 { return nil, errors.New("ethash does not support withdrawals") } - - // Finalize block + // Finalize block. ethash.Finalize(chain, header, state, txs, uncles, nil) - // Header seems complete, assemble into a block and return + + // Assign the final state root to header. + root, err := state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + if err != nil { + return nil, err + } + header.Root = root + + // Header seems complete, assemble into a block and return. return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil } diff --git a/core/block_validator.go b/core/block_validator.go index 3704158c11b3..7c41cedb43cd 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -109,7 +109,11 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } // Validate the state root against the received state root and throw // an error if they don't match. - if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { + root, err := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)) + if err != nil { + return fmt.Errorf("failed to derive state root %v", err) + } + if header.Root != root { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } return nil diff --git a/core/chain_makers.go b/core/chain_makers.go index 3518929f8e71..9c53d33e503b 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -352,8 +352,12 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S } else { time = parent.Time() + 10 // block time is fixed at 10 seconds } + root, err := state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())) + if err != nil { + panic(err) // error shouldn't occur in tests. + } header := &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/state/state_object.go b/core/state/state_object.go index 1550926d3a62..9524d6adbfc4 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -45,7 +45,6 @@ func (s Storage) String() (str string) { for key, value := range s { str += fmt.Sprintf("%X : %X\n", key, value) } - return } @@ -54,7 +53,6 @@ func (s Storage) Copy() Storage { for key, value := range s { cpy[key] = value } - return cpy } @@ -73,8 +71,12 @@ type stateObject struct { // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs - // during a database read is memoized here and will eventually be returned - // by StateDB.Commit. + // during a database read is memoized here and will eventually be + // returned by StateDB.Commit. Specially, this error is used to + // represent a failed operation of: + // - storage trie read + // - contract code read + // - trie node decode dbErr error // Write caches. @@ -86,7 +88,7 @@ type stateObject struct { dirtyStorage Storage // Storage entries that have been modified in the current transaction execution // Cache flags. - // When an object is marked suicided it will be delete from the trie + // When an object is marked suicided it will be deleted from the trie // during the "update" phase of the state transition. dirtyCode bool // true if the code was updated suicided bool @@ -282,6 +284,10 @@ func (s *stateObject) finalise(prefetch bool) { // It will return nil if the trie has not been loaded and no changes have been // made. An error will be returned if the trie can't be loaded/updated correctly. func (s *stateObject) updateTrie(db Database) (Trie, error) { + // Short circuit if any the previous database failure is memorized. + if s.dbErr != nil { + return nil, s.dbErr + } // Make sure all dirty slots are finalized into the pending storage area s.finalise(false) // Don't prefetch anymore, pull directly if need be if len(s.pendingStorage) == 0 { @@ -298,7 +304,6 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { ) tr, err := s.getTrie(db) if err != nil { - s.setError(err) return nil, err } // Insert all the pending updates into the trie @@ -313,7 +318,6 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { var v []byte if (value == common.Hash{}) { if err := tr.TryDelete(key[:]); err != nil { - s.setError(err) return nil, err } s.db.StorageDeleted += 1 @@ -321,7 +325,6 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { // Encoding []byte cannot fail, ok to ignore the error. v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:])) if err := tr.TryUpdate(key[:], v); err != nil { - s.setError(err) return nil, err } s.db.StorageUpdated += 1 @@ -348,35 +351,35 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { return tr, nil } -// UpdateRoot sets the trie root to the current root hash of. An error -// will be returned if trie root hash is not computed correctly. -func (s *stateObject) updateRoot(db Database) { +// updateRoot sets the trie root to the current root hash. An error +// will be returned if case any error occurs because of failed database +// reads. +func (s *stateObject) updateRoot(db Database) error { tr, err := s.updateTrie(db) if err != nil { - s.setError(fmt.Errorf("updateRoot (%x) error: %w", s.address, err)) - return + return err } // If nothing changed, don't bother with hashing anything if tr == nil { - return + return nil } // Track the amount of time wasted on hashing the storage trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now()) } s.data.Root = tr.Hash() + return nil } // commitTrie submits the storage changes into the storage trie and re-computes -// the root. Besides, all trie changes will be collected in a nodeset and returned. +// the root. Storage trie changes will be wrapped in nodeset and be returned. +// The error will be non-nil if any error occurs during trie commit operation +// or memorized in stateObject because of failed database reads. func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) { tr, err := s.updateTrie(db) if err != nil { return nil, err } - if s.dbErr != nil { - return nil, s.dbErr - } // If nothing changed, don't bother with committing anything if tr == nil { return nil, nil @@ -437,6 +440,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject { stateObject.suicided = s.suicided stateObject.dirtyCode = s.dirtyCode stateObject.deleted = s.deleted + stateObject.dbErr = s.dbErr return stateObject } @@ -521,10 +525,3 @@ func (s *stateObject) Balance() *big.Int { func (s *stateObject) Nonce() uint64 { return s.data.Nonce } - -// Value is never called, but must be present to allow stateObject to be used -// as a vm.Account interface that also satisfies the vm.ContractRef -// interface. Interfaces are awesome. -func (s *stateObject) Value() *big.Int { - panic("Value on stateObject should never be called") -} diff --git a/core/state/statedb.go b/core/state/statedb.go index 3f4bec2392dc..cd48cdbfbe5a 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -86,8 +86,9 @@ type StateDB struct { // DB error. // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs - // during a database read is memoized here and will eventually be returned - // by StateDB.Commit. + // during a database read is memoized here and will eventually be + // returned by StateDB.Commit. Specially, this error is used to + // represent a failed state trie read. dbErr error // The refund counter, also used by state transitioning. @@ -509,7 +510,7 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common // // updateStateObject writes the given object to the trie. -func (s *StateDB) updateStateObject(obj *stateObject) { +func (s *StateDB) updateStateObject(obj *stateObject) error { // Track the amount of time wasted on updating the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) @@ -517,9 +518,8 @@ func (s *StateDB) updateStateObject(obj *stateObject) { // Encode the account and update the account trie addr := obj.Address() if err := s.trie.TryUpdateAccount(addr, &obj.data); err != nil { - s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err)) + return err } - // If state snapshotting is active, cache the data til commit. Note, this // update mechanism is not symmetric to the deletion, because whereas it is // enough to track account updates at commit time, deletions need tracking @@ -527,19 +527,17 @@ func (s *StateDB) updateStateObject(obj *stateObject) { if s.snap != nil { s.snapAccounts[obj.addrHash] = snapshot.SlimAccountRLP(obj.data.Nonce, obj.data.Balance, obj.data.Root, obj.data.CodeHash) } + return nil } // deleteStateObject removes the given object from the state trie. -func (s *StateDB) deleteStateObject(obj *stateObject) { +func (s *StateDB) deleteStateObject(obj *stateObject) error { // Track the amount of time wasted on deleting the account from the trie if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountUpdates += time.Since(start) }(time.Now()) } // Delete the account from the trie - addr := obj.Address() - if err := s.trie.TryDeleteAccount(addr); err != nil { - s.setError(fmt.Errorf("deleteStateObject (%x) error: %v", addr[:], err)) - } + return s.trie.TryDeleteAccount(obj.Address()) } // getStateObject retrieves a state object given by the address, returning nil if @@ -709,6 +707,7 @@ func (s *StateDB) Copy() *StateDB { stateObjectsPending: make(map[common.Address]struct{}, len(s.stateObjectsPending)), stateObjectsDirty: make(map[common.Address]struct{}, len(s.journal.dirties)), stateObjectsDestruct: make(map[common.Address]struct{}, len(s.stateObjectsDestruct)), + dbErr: s.dbErr, refund: s.refund, logs: make(map[common.Hash][]*types.Log, len(s.logs)), logSize: s.logSize, @@ -883,8 +882,14 @@ 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 { +// goes into transaction receipts. An error will be returned in case +// any failed state read occurs, usually happen because of corrupted +// database. +func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) (common.Hash, error) { + // Short circuit because of the memorized database failures. + if s.dbErr != nil { + return common.Hash{}, fmt.Errorf("hash aborted due to earlier error: %v", s.dbErr) + } // Finalise all the dirty storage states and write them into the tries s.Finalise(deleteEmptyObjects) @@ -909,24 +914,30 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // to pull useful data from disk. for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; !obj.deleted { - obj.updateRoot(s.db) + if err := obj.updateRoot(s.db); err != nil { + return common.Hash{}, err + } } } // Now we're about to start to write changes to the trie. The trie is so far // _untouched_. We can check with the prefetcher, if it can give us a trie // which has the same root, but also has some content loaded into it. if prefetcher != nil { - if trie := prefetcher.trie(common.Hash{}, s.originalRoot); trie != nil { - s.trie = trie + if tr := prefetcher.trie(common.Hash{}, s.originalRoot); tr != nil { + s.trie = tr } } usedAddrs := make([][]byte, 0, len(s.stateObjectsPending)) for addr := range s.stateObjectsPending { if obj := s.stateObjects[addr]; obj.deleted { - s.deleteStateObject(obj) + if err := s.deleteStateObject(obj); err != nil { + return common.Hash{}, err + } s.AccountDeleted += 1 } else { - s.updateStateObject(obj) + if err := s.updateStateObject(obj); err != nil { + return common.Hash{}, err + } s.AccountUpdated += 1 } usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure @@ -941,7 +952,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { if metrics.EnabledExpensive { defer func(start time.Time) { s.AccountHashes += time.Since(start) }(time.Now()) } - return s.trie.Hash() + return s.trie.Hash(), nil } // SetTxContext sets the current transaction hash and index which are @@ -962,12 +973,14 @@ func (s *StateDB) clearJournalAndRefund() { // Commit writes the state to the underlying in-memory trie database. func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) { + // Short circuit because of the memorized database failures. if s.dbErr != nil { return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } // Finalize any pending changes and merge everything into the tries - s.IntermediateRoot(deleteEmptyObjects) - + if _, err := s.IntermediateRoot(deleteEmptyObjects); err != nil { + return common.Hash{}, err + } // Commit objects to the trie, measuring the elapsed time var ( accountTrieNodesUpdated int diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 8aa59e3ee592..664454f7c17d 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); err != nil { t.Errorf("can not commit trie %v to persistent database", root.Hex()) } diff --git a/core/state_processor.go b/core/state_processor.go index 163ea0a0200a..cbcf5684188b 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -113,7 +113,11 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool if config.IsByzantium(blockNumber) { statedb.Finalise(true) } else { - root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes() + hash, err := statedb.IntermediateRoot(config.IsEIP158(blockNumber)) + if err != nil { + return nil, err + } + root = hash.Bytes() } *usedGas += result.UsedGas diff --git a/core/vm/interface.go b/core/vm/interface.go index 0ee32b1dd510..b83f78307eb7 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -76,8 +76,6 @@ type StateDB interface { AddLog(*types.Log) AddPreimage(common.Hash, []byte) - - ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error } // CallContext provides a basic interface for the EVM calling conventions. The EVM diff --git a/eth/api_test.go b/eth/api_test.go index fca17f12171b..0ae7c27bafa5 100644 --- a/eth/api_test.go +++ b/eth/api_test.go @@ -85,7 +85,7 @@ func TestAccountRange(t *testing.T) { } } state.Commit(true) - root := state.IntermediateRoot(true) + root, _ := state.IntermediateRoot(true) trie, err := statedb.OpenTrie(root) if err != nil { diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 5a34d9d4a6ab..355deba39750 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -570,7 +570,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 nil, err // abort execution because of corrupted database + } + roots = append(roots, root) } return roots, nil } diff --git a/tests/state_test.go b/tests/state_test.go index 7dd2f678c683..64e677cb1cef 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -84,7 +84,11 @@ func TestState(t *testing.T) { withTrace(t, test.gasLimit(subtest), func(vmconfig vm.Config) error { snaps, statedb, err := test.Run(subtest, vmconfig, true) if snaps != nil && statedb != nil { - if _, err := snaps.Journal(statedb.IntermediateRoot(false)); err != nil { + root, err := statedb.IntermediateRoot(false) + if err != nil { + return err + } + if _, err := snaps.Journal(root); err != nil { return err } } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index b2e87fb004b9..2528c174edc3 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -273,9 +273,10 @@ 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)) // Commit block - statedb.Commit(config.IsEIP158(block.Number())) - // And _now_ get the state root - root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) + root, err := statedb.Commit(config.IsEIP158(block.Number())) + if err != nil { + return nil, nil, common.Hash{}, err + } return snaps, statedb, root, err }