diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index fa8828f61bc6..a5852912e73b 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -154,7 +154,7 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres return nil, errBlockNumberUnsupported } statedb, _ := b.blockchain.State() - val := statedb.GetState(contract, key) + val, _ := statedb.GetState(contract, key) return val[:], nil } diff --git a/core/state/journal.go b/core/state/journal.go index a03ca57dbc64..c61805d3bf04 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -108,8 +108,10 @@ type ( prev uint64 } storageChange struct { - account *common.Address - key, prevalue common.Hash + account *common.Address + key common.Hash + prevValue common.Hash + prevDirty bool } codeChange struct { account *common.Address @@ -196,7 +198,7 @@ func (ch codeChange) dirtied() *common.Address { } func (ch storageChange) revert(s *StateDB) { - s.getStateObject(*ch.account).setState(ch.key, ch.prevalue) + s.getStateObject(*ch.account).setState(ch.key, ch.prevValue, ch.prevDirty) } func (ch storageChange) dirtied() *common.Address { diff --git a/core/state/state_object.go b/core/state/state_object.go index 091d24184af5..f4f1e9804e6d 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -77,7 +77,7 @@ type stateObject struct { trie Trie // storage trie, which becomes non-nil on first access code Code // contract bytecode, which gets set when code is loaded - cachedStorage Storage // Storage entry cache to avoid duplicate reads + originStorage Storage // Storage cache of original entries to dedup rewrites dirtyStorage Storage // Storage entries that need to be flushed to disk // Cache flags. @@ -115,7 +115,7 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject { address: address, addrHash: crypto.Keccak256Hash(address[:]), data: data, - cachedStorage: make(Storage), + originStorage: make(Storage), dirtyStorage: make(Storage), } } @@ -159,17 +159,22 @@ func (c *stateObject) getTrie(db Database) Trie { return c.trie } -// GetState returns a value in account storage. -func (self *stateObject) GetState(db Database, key common.Hash) common.Hash { - value, exists := self.cachedStorage[key] - if exists { - return value +// GetState returns a value from account storage, and also whether the slot is +// dirty in the current transaction execution context. +func (self *stateObject) GetState(db Database, key common.Hash) (common.Hash, bool) { + value, dirty := self.dirtyStorage[key] + if dirty { + return value, true + } + value, cached := self.originStorage[key] + if cached { + return value, false } // Load from DB in case it is missing. enc, err := self.getTrie(db).TryGet(key[:]) if err != nil { self.setError(err) - return common.Hash{} + return common.Hash{}, false } if len(enc) > 0 { _, content, _, err := rlp.Split(enc) @@ -178,23 +183,34 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash { } value.SetBytes(content) } - self.cachedStorage[key] = value - return value + self.originStorage[key] = value + return value, false } // SetState updates a value in account storage. func (self *stateObject) SetState(db Database, key, value common.Hash) { + // If the new value is the same as old, don't set and don't mark dirty + prev, dirty := self.GetState(db, key) + if prev == value { + return + } + // New value is different, update and journal the change + self.setState(key, value, true) + self.db.journal.append(storageChange{ - account: &self.address, - key: key, - prevalue: self.GetState(db, key), + account: &self.address, + key: key, + prevValue: prev, + prevDirty: dirty, }) - self.setState(key, value) } -func (self *stateObject) setState(key, value common.Hash) { - self.cachedStorage[key] = value - self.dirtyStorage[key] = value +func (self *stateObject) setState(key, value common.Hash, dirty bool) { + if dirty { + self.dirtyStorage[key] = value + } else { + delete(self.dirtyStorage, key) + } } // updateTrie writes cached storage modifications into the object's storage trie. @@ -202,6 +218,13 @@ func (self *stateObject) updateTrie(db Database) Trie { tr := self.getTrie(db) for key, value := range self.dirtyStorage { delete(self.dirtyStorage, key) + + // Skip noop changes, persist actual changes + if value == self.originStorage[key] { + continue + } + self.originStorage[key] = value + if (value == common.Hash{}) { self.setError(tr.TryDelete(key[:])) continue @@ -279,7 +302,7 @@ func (self *stateObject) deepCopy(db *StateDB) *stateObject { } stateObject.code = self.code stateObject.dirtyStorage = self.dirtyStorage.Copy() - stateObject.cachedStorage = self.dirtyStorage.Copy() + stateObject.originStorage = self.originStorage.Copy() stateObject.suicided = self.suicided stateObject.dirtyCode = self.dirtyCode stateObject.deleted = self.deleted diff --git a/core/state/state_test.go b/core/state/state_test.go index 123559ea9bef..b330b7186565 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -96,12 +96,17 @@ func (s *StateSuite) TestNull(c *checker.C) { s.state.CreateAccount(address) //value := common.FromHex("0x823140710bf13990e4500136726d8b55") var value common.Hash + s.state.SetState(address, common.Hash{}, value) s.state.Commit(false) - value = s.state.GetState(address, common.Hash{}) + + value, dirty := s.state.GetState(address, common.Hash{}) if value != (common.Hash{}) { c.Errorf("expected empty hash. got %x", value) } + if dirty { + c.Errorf("expected non-dirty, got dirty") + } } func (s *StateSuite) TestSnapshot(c *checker.C) { @@ -110,20 +115,27 @@ func (s *StateSuite) TestSnapshot(c *checker.C) { data1 := common.BytesToHash([]byte{42}) data2 := common.BytesToHash([]byte{43}) - // set initial state object value + // snapshot the genesis state + genesis := s.state.Snapshot() + + // set initial state object value and snapshot it s.state.SetState(stateobjaddr, storageaddr, data1) - // get snapshot of current state snapshot := s.state.Snapshot() - // set new state object value + // set a new state object value, revert it and ensure correct content s.state.SetState(stateobjaddr, storageaddr, data2) - // restore snapshot s.state.RevertToSnapshot(snapshot) - // get state storage value - res := s.state.GetState(stateobjaddr, storageaddr) + value, dirty := s.state.GetState(stateobjaddr, storageaddr) + c.Assert(data1, checker.DeepEquals, value) + c.Assert(true, checker.DeepEquals, dirty) + + // revert up to the genesis state and ensure correct content + s.state.RevertToSnapshot(genesis) - c.Assert(data1, checker.DeepEquals, res) + value, dirty = s.state.GetState(stateobjaddr, storageaddr) + c.Assert(common.Hash{}, checker.DeepEquals, value) + c.Assert(false, checker.DeepEquals, dirty) } func (s *StateSuite) TestSnapshotEmpty(c *checker.C) { @@ -208,17 +220,30 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) { t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) } - if len(so1.cachedStorage) != len(so0.cachedStorage) { - t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage)) + if len(so1.dirtyStorage) != len(so0.dirtyStorage) { + t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage)) + } + for k, v := range so1.dirtyStorage { + if so0.dirtyStorage[k] != v { + t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v) + } + } + for k, v := range so0.dirtyStorage { + if so1.dirtyStorage[k] != v { + t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v) + } + } + if len(so1.originStorage) != len(so0.originStorage) { + t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage)) } - for k, v := range so1.cachedStorage { - if so0.cachedStorage[k] != v { - t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v) + for k, v := range so1.originStorage { + if so0.originStorage[k] != v { + t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v) } } - for k, v := range so0.cachedStorage { - if so1.cachedStorage[k] != v { - t.Errorf("Storage key %x mismatch: have %v, want none.", k, v) + for k, v := range so0.originStorage { + if so1.originStorage[k] != v { + t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v) } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 92d394ae328f..ea92b4368a8b 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/trie" ) @@ -236,12 +237,12 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash { return common.BytesToHash(stateObject.CodeHash()) } -func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash { +func (self *StateDB) GetState(addr common.Address, bhash common.Hash) (common.Hash, bool) { stateObject := self.getStateObject(addr) if stateObject != nil { return stateObject.GetState(self.db, bhash) } - return common.Hash{} + return common.Hash{}, false } // Database retrieves the low level database supporting the lower level trie ops. @@ -430,24 +431,19 @@ func (self *StateDB) CreateAccount(addr common.Address) { } } -func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash) bool) { +func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common.Hash, dirty bool) bool) { so := db.getStateObject(addr) if so == nil { return } - - // When iterating over the storage check the cache first - for h, value := range so.cachedStorage { - cb(h, value) - } - it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil)) for it.Next() { - // ignore cached values key := common.BytesToHash(db.trie.GetKey(it.Key)) - if _, ok := so.cachedStorage[key]; !ok { - cb(key, common.BytesToHash(it.Value)) + if value, dirty := so.dirtyStorage[key]; dirty { + cb(key, value, true) + continue } + cb(key, common.BytesToHash(it.Value), false) } } @@ -524,9 +520,36 @@ func (self *StateDB) RevertToSnapshot(revid int) { self.validRevisions = self.validRevisions[:idx] } -// GetRefund returns the current value of the refund counter. -func (self *StateDB) GetRefund() uint64 { - return self.refund +// GetRefund returns the current value of the refund counter and any out-of-band +// refunds at the end of the transaction. +func (self *StateDB) GetRefund(netSstoreMeter bool) uint64 { + // If legacy gas metering is used, return the current refund counter + refund := self.refund + if !netSstoreMeter { + return refund + } + // If we're already using the Constantinople net sstore gas metering, refund noops + for addr := range self.journal.dirties { + object := self.stateObjects[addr] + for key, value := range object.dirtyStorage { + // If the slot didn't change in the end, refund most gas + if value == object.originStorage[key] { + if value == (common.Hash{}) { + refund += params.NetSstoreRefundNoopZero + } else { + refund += params.NetSstoreRefundNoopNonZero + } + continue + } + // If the slot did change, but was set to zero, refund + if value == (common.Hash{}) { + refund += params.NetSstoreRefund + continue + } + // Sorry, no refund for this slot + } + } + return refund } // Finalise finalises the state by removing the self destructed objects diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index e2b349de86c1..e6835ff31c2d 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -381,11 +381,19 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) // Check storage. if obj := state.getStateObject(addr); obj != nil { - state.ForEachStorage(addr, func(key, val common.Hash) bool { - return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key)) + state.ForEachStorage(addr, func(key, value common.Hash, dirty bool) bool { + v, d := checkstate.GetState(addr, key) + if ok := checkeq("GetState("+key.Hex()+").value", value, v); ok { + return ok + } + return checkeq("GetState("+key.Hex()+").dirty", dirty, d) }) - checkstate.ForEachStorage(addr, func(key, checkval common.Hash) bool { - return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval) + checkstate.ForEachStorage(addr, func(key, value common.Hash, dirty bool) bool { + v, d := state.GetState(addr, key) + if ok := checkeq("GetState("+key.Hex()+").value", v, value); ok { + return ok + } + return checkeq("GetState("+key.Hex()+").dirty", d, dirty) }) } if err != nil { @@ -393,9 +401,11 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error { } } - if state.GetRefund() != checkstate.GetRefund() { - return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", - state.GetRefund(), checkstate.GetRefund()) + for _, netGasMetering := range []bool{false, true} { + if state.GetRefund(netGasMetering) != checkstate.GetRefund(netGasMetering) { + return fmt.Errorf("got GetRefund(%v) == %d, want GetRefund(%v) == %d", + netGasMetering, state.GetRefund(netGasMetering), netGasMetering, checkstate.GetRefund(netGasMetering)) + } } if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) { return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", diff --git a/core/state_transition.go b/core/state_transition.go index fda081b7d113..6c98226893bb 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -229,9 +229,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo func (st *StateTransition) refundGas() { // Apply refund counter, capped to half of the used gas. - refund := st.gasUsed() / 2 - if refund > st.state.GetRefund() { - refund = st.state.GetRefund() + netSstoreMeter := st.evm.ChainConfig().IsConstantinople(st.evm.BlockNumber) + + refund := st.state.GetRefund(netSstoreMeter) + if refund > st.gasUsed()/2 { + refund = st.gasUsed() / 2 } st.gas += refund diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index f9eea319e121..988eaeb521fa 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -117,23 +117,51 @@ func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack * func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( - y, x = stack.Back(1), stack.Back(0) - val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) + y, x = stack.Back(1), stack.Back(0) + value, dirty = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) ) - // This checks for 3 scenario's and calculates gas accordingly - // 1. From a zero-value address to a non-zero value (NEW VALUE) - // 2. From a non-zero value address to a zero-value address (DELETE) - // 3. From a non-zero to a non-zero (CHANGE) - if val == (common.Hash{}) && y.Sign() != 0 { - // 0 => non 0 - return params.SstoreSetGas, nil - } else if val != (common.Hash{}) && y.Sign() == 0 { - // non 0 => 0 - evm.StateDB.AddRefund(params.SstoreRefundGas) - return params.SstoreClearGas, nil - } else { - // non 0 => non 0 (or 0 => 0) - return params.SstoreResetGas, nil + // The legacy gas metering only takes into consideration the current state + if !evm.chainRules.IsConstantinople { + // This checks for 3 scenario's and calculates gas accordingly + // 1. From a zero-value address to a non-zero value (NEW VALUE) + // 2. From a non-zero value address to a zero-value address (DELETE) + // 3. From a non-zero to a non-zero (CHANGE) + switch { + case value == (common.Hash{}) && y.Sign() != 0: // 0 => non 0 + return params.SstoreSetGas, nil + case value != (common.Hash{}) && y.Sign() == 0: // non 0 => 0 + evm.StateDB.AddRefund(params.SstoreRefundGas) + return params.SstoreClearGas, nil + default: // non 0 => non 0 (or 0 => 0) + return params.SstoreResetGas, nil + } + } + // The new gas metering is based on net gas costs (EIP-1087) + // + // 0. A ‘dirty map’ for each transaction is maintained, tracking all storage + // slots in all contracts that have been modified in the current transaction. + // The dirty map is scoped in the same manner as updates to storage, meaning + // that changes to the dirty map in a call that later reverts are not retained. + // 1. When a storage slot is written to with the value it already contains, 200 + // gas is deducted, but is not explicitly marked dirty. + // 2. When a storage slot’s value is changed for the first time, the slot is + // marked as dirty. If the slot was previously set to 0, 20000 gas is deducted; + // otherwise, 5000 gas is deducted. + // 2. When a storage slot that is already in the dirty map is written to, 200 + // gas is deducted. + // 3. At the end of the transaction, for each slot in the dirty map: + // * If the slot was 0 before the transaction and is 0 now, refund 19800 gas. + // * If the slot was nonzero before the transaction and its value has not changed, refund 4800 gas. + // * If the slot was nonzero before the transaction and is 0 now, refund 10000 gas. + switch { + case value == common.BigToHash(y): // noop + return params.NetSstoreNoopGas, nil + case !dirty && y.Sign() != 0 && value == (common.Hash{}): // clean(0) => anything + return params.NetSstoreCleanInitgas, nil + case !dirty && y.Sign() != 0: // clean(non 0) => anything + return params.NetSstoreCleanUpdateGas, nil + default: // dirty(anything) => anything + return params.NetSstoreDirtyGas, nil } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index b7742e6551ed..4393ab7356a0 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -604,7 +604,7 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { loc := stack.peek() - val := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc)) + val, _ := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc)) loc.SetBytes(val.Bytes()) return nil, nil } diff --git a/core/vm/interface.go b/core/vm/interface.go index 1ef91cf1d613..a93b4647bc01 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -40,9 +40,9 @@ type StateDB interface { GetCodeSize(common.Address) int AddRefund(uint64) - GetRefund() uint64 + GetRefund(bool) uint64 - GetState(common.Address, common.Hash) common.Hash + GetState(common.Address, common.Hash) (common.Hash, bool) SetState(common.Address, common.Hash, common.Hash) Suicide(common.Address) bool @@ -61,7 +61,7 @@ type StateDB interface { AddLog(*types.Log) AddPreimage(common.Hash, []byte) - ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) + ForEachStorage(common.Address, func(common.Hash, common.Hash, bool) bool) } // CallContext provides a basic interface for the EVM calling conventions. The EVM EVM diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 1e9202424a20..31abf0eeb6ae 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -133,7 +133,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err in.intPool = nil }() } - // Increment the call depth which is restricted to 1024 in.evm.depth++ defer func() { in.evm.depth-- }() diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go index feb57e060172..d534c9226a76 100644 --- a/eth/tracers/tracer.go +++ b/eth/tracers/tracer.go @@ -214,7 +214,7 @@ func (dw *dbWrapper) pushObject(vm *duktape.Context) { hash := popSlice(ctx) addr := popSlice(ctx) - state := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash)) + state, _ := dw.db.GetState(common.BytesToAddress(addr), common.BytesToHash(hash)) ptr := ctx.PushFixedBuffer(len(state)) copy(makeSlice(ptr, uint(len(state))), state[:]) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a465a59046f8..e56133f140ed 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -597,7 +597,7 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A if state == nil || err != nil { return nil, err } - res := state.GetState(address, common.HexToHash(key)) + res, _ := state.GetState(address, common.HexToHash(key)) return res[:], state.Error() } diff --git a/params/config.go b/params/config.go index b9e9bb8d6ebd..f33ec7a64f78 100644 --- a/params/config.go +++ b/params/config.go @@ -334,7 +334,7 @@ func (err *ConfigCompatError) Error() string { type Rules struct { ChainID *big.Int IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool - IsByzantium bool + IsByzantium, IsConstantinople bool } // Rules ensures c's ChainID is not nil. @@ -343,5 +343,12 @@ func (c *ChainConfig) Rules(num *big.Int) Rules { if chainID == nil { chainID = new(big.Int) } - return Rules{ChainID: new(big.Int).Set(chainID), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num), IsByzantium: c.IsByzantium(num)} + return Rules{ + ChainID: new(big.Int).Set(chainID), + IsHomestead: c.IsHomestead(num), + IsEIP150: c.IsEIP150(num), + IsEIP155: c.IsEIP155(num), + IsEIP158: c.IsEIP158(num), + IsByzantium: c.IsByzantium(num), + IsConstantinople: c.IsConstantinople(num)} } diff --git a/params/protocol_params.go b/params/protocol_params.go index 46624ac9a24d..783f61903d64 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -36,15 +36,25 @@ const ( TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. TxDataZeroGas uint64 = 4 // Per byte of data attached to a transaction that equals zero. NOTE: Not payable on data of calls between transactions. QuadCoeffDiv uint64 = 512 // Divisor for the quadratic particle of the memory cost equation. - SstoreSetGas uint64 = 20000 // Once per SLOAD operation. LogDataGas uint64 = 8 // Per byte in a LOG* operation's data. CallStipend uint64 = 2300 // Free gas given at beginning of call. - Sha3Gas uint64 = 30 // Once per SHA3 operation. - Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. - SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. - SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. - SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. + Sha3Gas uint64 = 30 // Once per SHA3 operation. + Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. + + SstoreSetGas uint64 = 20000 // Once per SSTORE operation. + SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. + SstoreClearGas uint64 = 5000 // Once per SSTORE operation if the zeroness doesn't change. + SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. + + NetSstoreNoopGas uint64 = 200 // Once per SSTORE operation if the value doesn't change. + NetSstoreDirtyGas uint64 = 200 // Once per SSTORE operation from dirty. + NetSstoreCleanInitgas uint64 = 20000 // Once per SSTORE operation from clean zero. + NetSstoreCleanUpdateGas uint64 = 5000 // Once per SSTORE operation from clean non-zero. + NetSstoreRefundNoopZero uint64 = 19800 // Once per dirty slot if it was changed from zero to non-zero and back to zero. + NetSstoreRefundNoopNonZero uint64 = 4800 // Once per dirty slot if it was changed from non-zero to anything and back to the orignal. + NetSstoreRefund uint64 = 10000 // Once per dirty slot if it was changed from non-zero to zero. + JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes to zero. EpochDuration uint64 = 30000 // Duration between proof-of-work epochs. CallGas uint64 = 40 // Once per CALL operation & message call transaction. diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go index cb81c5b94e39..e4b07c8f3369 100644 --- a/tests/vm_test_util.go +++ b/tests/vm_test_util.go @@ -100,7 +100,7 @@ func (t *VMTest) Run(vmconfig vm.Config) error { } for addr, account := range t.json.Post { for k, wantV := range account.Storage { - if haveV := statedb.GetState(addr, k); haveV != wantV { + if haveV, _ := statedb.GetState(addr, k); haveV != wantV { return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV) } }