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

[WIP] core: print db failure in case of mismatched hash root #684

Open
wants to merge 2 commits into
base: dev-upgrade
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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 @@ -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
28 changes: 15 additions & 13 deletions core/vm/gas_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,19 +163,21 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
return params.NetSstoreDirtyGas, nil
}

// 0. If *gasleft* is less than or equal to 2300, fail the current call.
// 1. If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
// 2. If current value does not equal new value:
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context):
// 2.1.1. If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
// 2.1.2. Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
// 2.2.1. If original value is not 0:
// 2.2.1.1. If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset):
// 2.2.2.1. If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
// 2.2.2.2. Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
// Here come the EIP2200 rules:
//
// (0.) If *gasleft* is less than or equal to 2300, fail the current call.
// (1.) If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
// (2.) If current value does not equal new value:
// (2.1.) If original value equals current value (this storage slot has not been changed by the current execution context):
// (2.1.1.) If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
// (2.1.2.) Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
// (2.2.) If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
// (2.2.1.) If original value is not 0:
// (2.2.1.1.) If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
// (2.2.1.2.) If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
// (2.2.2.) If original value equals new value (this storage slot is reset):
// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
// If we fail the minimum gas availability invariant, fail (0)
if contract.Gas <= params.SstoreSentryGasEIP2200 {
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
8 changes: 4 additions & 4 deletions core/vm/jump_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ func newBerlinInstructionSet() JumpTable {
return validate(instructionSet)
}

// newIstanbulInstructionSet returns the frontier, homestead
// byzantium, contantinople and petersburg instructions.
// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
// constantinople, istanbul and petersburg instructions.
func newIstanbulInstructionSet() JumpTable {
instructionSet := newConstantinopleInstructionSet()

Expand All @@ -132,8 +132,8 @@ func newIstanbulInstructionSet() JumpTable {
return validate(instructionSet)
}

// newConstantinopleInstructionSet returns the frontier, homestead
// byzantium and contantinople instructions.
// newConstantinopleInstructionSet returns the frontier, homestead,
// byzantium and constantinople instructions.
func newConstantinopleInstructionSet() JumpTable {
instructionSet := newByzantiumInstructionSet()
instructionSet[SHL] = &operation{
Expand Down