diff --git a/core/block_validator.go b/core/block_validator.go index db7ea35687..2a8df7c965 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -113,7 +113,7 @@ 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 { - return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) + return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) } return nil } diff --git a/core/state/state_object.go b/core/state/state_object.go index 5dfd3c1b64..7e34cba44a 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -43,7 +43,6 @@ func (s Storage) String() (str string) { for key, value := range s { str += fmt.Sprintf("%X : %X\n", key, value) } - return } @@ -52,7 +51,6 @@ func (s Storage) Copy() Storage { for key, value := range s { cpy[key] = value } - return cpy } @@ -68,13 +66,6 @@ type stateObject struct { data types.StateAccount db *StateDB - // 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. - dbErr error - // Write caches. trie Trie // storage trie, which becomes non-nil on first access code Code // contract bytecode, which gets set when code is loaded @@ -84,7 +75,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 @@ -123,13 +114,6 @@ func (s *stateObject) EncodeRLP(w io.Writer) error { return rlp.Encode(w, &s.data) } -// setError remembers the first non-nil error it is called with. -func (s *stateObject) setError(err error) { - if s.dbErr == nil { - s.dbErr = err - } -} - func (s *stateObject) markSuicided() { s.suicided = true } @@ -214,7 +198,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has start := time.Now() tr, err := s.getTrie(db) if err != nil { - s.setError(err) + s.db.setError(err) return common.Hash{} } enc, err = tr.TryGet(key.Bytes()) @@ -222,7 +206,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has s.db.StorageReads += time.Since(start) } if err != nil { - s.setError(err) + s.db.setError(err) return common.Hash{} } } @@ -230,7 +214,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has if len(enc) > 0 { _, content, _, err := rlp.Split(enc) if err != nil { - s.setError(err) + s.db.setError(err) } value.SetBytes(content) } @@ -296,7 +280,7 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { ) tr, err := s.getTrie(db) if err != nil { - s.setError(err) + s.db.setError(err) return nil, err } // Insert all the pending updates into the trie @@ -311,7 +295,7 @@ 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) + s.db.setError(err) return nil, err } s.db.StorageDeleted += 1 @@ -319,7 +303,7 @@ 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) + s.db.setError(err) return nil, err } s.db.StorageUpdated += 1 @@ -351,7 +335,6 @@ func (s *stateObject) updateTrie(db Database) (Trie, error) { func (s *stateObject) updateRoot(db Database) { tr, err := s.updateTrie(db) if err != nil { - s.setError(fmt.Errorf("updateRoot (%x) error: %w", s.address, err)) return } // If nothing changed, don't bother with hashing anything @@ -372,9 +355,6 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) { 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 @@ -385,7 +365,7 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) { } root, nodes := tr.Commit(false) s.data.Root = root - return nodes, err + return nodes, nil } // AddBalance adds amount to s's balance. @@ -457,7 +437,7 @@ func (s *stateObject) Code(db Database) []byte { } code, err := db.ContractCode(s.addrHash, common.BytesToHash(s.CodeHash())) if err != nil { - s.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err)) + s.db.setError(fmt.Errorf("can't load code hash %x: %v", s.CodeHash(), err)) } s.code = code return code @@ -475,7 +455,7 @@ func (s *stateObject) CodeSize(db Database) int { } size, err := db.ContractCodeSize(s.addrHash, common.BytesToHash(s.CodeHash())) if err != nil { - s.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err)) + s.db.setError(fmt.Errorf("can't load code size %x: %v", s.CodeHash(), err)) } return size } @@ -519,10 +499,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 247aef8b23..54d5040451 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -81,8 +81,10 @@ 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. Notably, this error is also shared + // by all cached state objects in case the database failure occurs + // when accessing state of accounts. dbErr error // The refund counter, also used by state transitioning. @@ -187,6 +189,7 @@ func (s *StateDB) setError(err error) { } } +// Error returns the memorized database failure occurred earlier. func (s *StateDB) Error() error { return s.dbErr } @@ -478,13 +481,11 @@ func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash) if prev == value { return } - s.journal.append(transientStorageChange{ account: &addr, key: key, prevalue: prev, }) - s.setTransientState(addr, key, value) } @@ -957,6 +958,7 @@ 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 in case any database failure occurred earlier. if s.dbErr != nil { return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.dbErr) } diff --git a/core/vm/interface.go b/core/vm/interface.go index 0ee32b1dd5..b83f78307e 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