diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go index be96a6a7ec..e98f462380 100644 --- a/core/blockchain_reader.go +++ b/core/blockchain_reader.go @@ -17,6 +17,7 @@ package core import ( + "errors" "math/big" "github.com/ethereum/go-ethereum/common" @@ -337,7 +338,20 @@ func (bc *BlockChain) State() (*state.StateDB, error) { // StateAt returns a new mutable state based on a particular point in time. func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) { - return state.New(root, bc.stateCache, bc.snaps) + stateDb, err := state.New(root, bc.stateCache, bc.snaps) + if err != nil { + return nil, err + } + + // If there's no trie and the specified snapshot is not available, getting + // any state will by default return nil. + // Instead of that, it will be more useful to return an error to indicate + // the state is not available. + if stateDb.NoTrie() && stateDb.GetSnap() == nil { + return nil, errors.New("state is not available") + } + + return stateDb, err } // Config retrieves the chain's fork configuration. diff --git a/core/state/statedb.go b/core/state/statedb.go index 1fcf3711a1..372098fe77 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -71,7 +71,7 @@ type StateDB struct { // It will be updated when the Commit is called. originalRoot common.Hash expectedRoot common.Hash // The state root in the block header - stateRoot common.Hash + // These maps hold the state changes (including the corresponding // original value) that occurred in this **block**. accounts map[common.Hash][]byte // The mutated accounts in 'slim RLP' encoding @@ -1193,7 +1193,7 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er 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.stateRoot = s.IntermediateRoot(deleteEmptyObjects) + root := s.IntermediateRoot(deleteEmptyObjects) // Commit objects to the trie, measuring the elapsed time var ( @@ -1282,15 +1282,15 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er if s.snap != nil { start := time.Now() // Only update if there's a state transition (skip empty Clique blocks) - if parent := s.snap.Root(); parent != s.expectedRoot { - if err := s.snaps.Update(s.expectedRoot, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages); err != nil { + if parent := s.snap.Root(); parent != root { + if err := s.snaps.Update(root, parent, s.convertAccountSet(s.stateObjectsDestruct), s.accounts, s.storages); err != nil { log.Warn("Failed to update snapshot tree", "from", parent, "to", s.expectedRoot, "err", err) } // Keep 128 diff layers in the memory, persistent layer is 129th. // - head layer is paired with HEAD state // - head-1 layer is paired with HEAD-1 state // - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state - if err := s.snaps.Cap(s.expectedRoot, 128); err != nil { + if err := s.snaps.Cap(root, 128); err != nil { log.Warn("Failed to cap snapshot tree", "root", s.expectedRoot, "layers", 128, "err", err) } } @@ -1300,7 +1300,6 @@ func (s *StateDB) Commit(block uint64, deleteEmptyObjects bool) (common.Hash, er s.snap = nil } - root := s.stateRoot if root == (common.Hash{}) { root = types.EmptyRootHash } @@ -1429,6 +1428,10 @@ func (s *StateDB) NoTrie() bool { return s.noTrie } +func (s *StateDB) GetSnap() snapshot.Snapshot { + return s.snap +} + // Mark that the block is processed by diff layer func (s *StateDB) SetExpectedStateRoot(root common.Hash) { s.expectedRoot = root diff --git a/tests/state_test_util.go b/tests/state_test_util.go index ecf61ffe06..32dc316220 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -299,10 +299,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh statedb.AddBalance(block.Coinbase(), new(big.Int)) // Commit state mutations into database. - root := statedb.IntermediateRoot(config.IsEIP158(block.Number())) - statedb.SetExpectedStateRoot(root) - - root, _ = statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number())) + root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number())) return triedb, snaps, statedb, root, err } @@ -329,10 +326,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo } } // Commit and re-open to start with a clean state. - root := statedb.IntermediateRoot(false) - statedb.SetExpectedStateRoot(root) - - root, _ = statedb.Commit(0, false) + root, _ := statedb.Commit(0, false) var snaps *snapshot.Tree if snapshotter {