diff --git a/core/block_validator.go b/core/block_validator.go index e713342d9c62..d185f908bad5 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -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 } diff --git a/core/state/state_object.go b/core/state/state_object.go index 99763db267e2..d36d3b8f0f8d 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -41,7 +41,6 @@ func (s Storage) String() (str string) { for key, value := range s { str += fmt.Sprintf("%X : %X\n", key, value) } - return } @@ -50,7 +49,6 @@ func (s Storage) Copy() Storage { for key, value := range s { cpy[key] = value } - return cpy } @@ -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 @@ -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 @@ -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 { @@ -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 @@ -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) } @@ -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) } @@ -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 } @@ -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 @@ -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 @@ -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") -} diff --git a/core/state/statedb.go b/core/state/statedb.go index 37bcc85c5a35..ef262c41779f 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -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. @@ -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 } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 98a737706eb7..855b2fad230d 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -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 { diff --git a/core/vm/interface.go b/core/vm/interface.go index 5c695ffed703..b88b5dfad140 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -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 diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 46f3dc959d39..d692aa99b21e 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -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() @@ -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{