Skip to content

Commit

Permalink
core: print db failure in case of mismatched hash root (ethereum#26870)
Browse files Browse the repository at this point in the history
  • Loading branch information
gzliudan committed Oct 21, 2024
1 parent f9af2c4 commit ad96def
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 40 deletions.
2 changes: 1 addition & 1 deletion core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
// 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
}
Expand Down
48 changes: 13 additions & 35 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ func (s Storage) String() (str string) {
for key, value := range s {
str += fmt.Sprintf("%X : %X\n", key, value)
}

return
}

Expand All @@ -50,7 +49,6 @@ func (s Storage) Copy() Storage {
for key, value := range s {
cpy[key] = value
}

return cpy
}

Expand All @@ -66,13 +64,6 @@ type stateObject struct {
data Account
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
Expand All @@ -82,7 +73,7 @@ type stateObject struct {
fakeStorage Storage // Fake storage which constructed by caller for debugging purpose.

// 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
Expand Down Expand Up @@ -129,13 +120,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
if s.onDirty != nil {
Expand Down Expand Up @@ -163,7 +147,7 @@ func (s *stateObject) getTrie(db Database) Trie {
s.trie, err = db.OpenStorageTrie(s.addrHash, s.data.Root)
if err != nil {
s.trie, _ = db.OpenStorageTrie(s.addrHash, common.Hash{})
s.setError(fmt.Errorf("can't create storage trie: %v", err))
s.db.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
return s.trie
Expand All @@ -178,13 +162,13 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
// Load from DB in case it is missing.
enc, err := s.getTrie(db).TryGet(key[:])
if err != nil {
s.setError(err)
s.db.setError(err)
return common.Hash{}
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
s.setError(err)
s.db.setError(err)
}
value.SetBytes(content)
}
Expand All @@ -203,13 +187,13 @@ func (s *stateObject) GetState(db Database, key common.Hash) common.Hash {
// Load from DB in case it is missing.
enc, err := s.getTrie(db).TryGet(key[:])
if err != nil {
s.setError(err)
s.db.setError(err)
return common.Hash{}
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
s.setError(err)
s.db.setError(err)
}
value.SetBytes(content)
}
Expand Down Expand Up @@ -269,17 +253,21 @@ func (s *stateObject) setState(key, value common.Hash) {
}

// updateTrie writes cached storage modifications into the object's storage trie.
// 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.
// TODO: return error
// func (s *stateObject) updateTrie(db Database) (Trie, error) {
func (s *stateObject) updateTrie(db Database) Trie {
tr := s.getTrie(db)
for key, value := range s.dirtyStorage {
delete(s.dirtyStorage, key)
if (value == common.Hash{}) {
s.setError(tr.TryDelete(key[:]))
s.db.setError(tr.TryDelete(key[:]))
continue
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
s.setError(tr.TryUpdate(key[:], v))
s.db.setError(tr.TryUpdate(key[:], v))
}
return tr
}
Expand All @@ -294,9 +282,6 @@ func (s *stateObject) updateRoot(db Database) {
// This updates the trie root.
func (s *stateObject) CommitTrie(db Database) error {
s.updateTrie(db)
if s.dbErr != nil {
return s.dbErr
}
root, err := s.trie.Commit(nil)
if err == nil {
s.data.Root = root
Expand Down Expand Up @@ -377,7 +362,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
Expand Down Expand Up @@ -434,10 +419,3 @@ func (s *stateObject) Nonce() uint64 {
func (s *stateObject) Root() common.Hash {
return s.data.Root
}

// 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")
}
7 changes: 5 additions & 2 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,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.
Expand Down Expand Up @@ -135,6 +137,7 @@ func (self *StateDB) setError(err error) {
}
}

// Error returns the memorized database failure occurred earlier.
func (self *StateDB) Error() error {
return self.dbErr
}
Expand Down
2 changes: 0 additions & 2 deletions core/vm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,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
Expand Down

0 comments on commit ad96def

Please sign in to comment.