Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: print db failure in case of mismatched hash root #26870

Merged
merged 3 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
48 changes: 12 additions & 36 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ func (s Storage) String() (str string) {
for key, value := range s {
str += fmt.Sprintf("%X : %X\n", key, value)
}

return
}

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

return cpy
}

Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -214,23 +198,23 @@ 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())
if metrics.EnabledExpensive {
s.db.StorageReads += time.Since(start)
}
if err != nil {
s.setError(err)
s.db.setError(err)
return common.Hash{}
}
}
var value 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 @@ -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
Expand All @@ -311,15 +295,15 @@ 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
} else {
// 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
Expand Down Expand Up @@ -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))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This operation is removed, since the returned error is already memorized in s.updateTrie function.

return
}
// If nothing changed, don't bother with hashing anything
Expand All @@ -372,8 +355,8 @@ func (s *stateObject) commitTrie(db Database) (*trie.NodeSet, error) {
if err != nil {
return nil, err
}
if s.dbErr != nil {
return nil, s.dbErr
if s.db.dbErr != nil {
rjl493456442 marked this conversation as resolved.
Show resolved Hide resolved
return nil, s.db.dbErr
}
// If nothing changed, don't bother with committing anything
if tr == nil {
Expand All @@ -385,7 +368,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.
Expand Down Expand Up @@ -457,7 +440,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 All @@ -475,7 +458,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
}
Expand Down Expand Up @@ -519,10 +502,3 @@ func (s *stateObject) Balance() *big.Int {
func (s *stateObject) Nonce() uint64 {
return s.data.Nonce
}

rjl493456442 marked this conversation as resolved.
Show resolved Hide resolved
// 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")
}
10 changes: 6 additions & 4 deletions core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -187,6 +189,7 @@ func (s *StateDB) setError(err error) {
}
}

// Error returns the memorized database failure occurs earlier.
func (s *StateDB) Error() error {
return s.dbErr
}
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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 occurs earlier.
rjl493456442 marked this conversation as resolved.
Show resolved Hide resolved
if s.dbErr != nil {
return common.Hash{}, fmt.Errorf("commit aborted due to earlier error: %v", s.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 @@ -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
Expand Down