Merge pull request #17383 from holiman/eip1283

Eip1283
This commit is contained in:
Péter Szilágyi 2018-09-21 14:16:18 +03:00 committed by GitHub
commit ee92bc537f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 195 additions and 146 deletions

@ -77,7 +77,7 @@ type stateObject struct {
trie Trie // storage trie, which becomes non-nil on first access trie Trie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded 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 dirtyStorage Storage // Storage entries that need to be flushed to disk
// Cache flags. // Cache flags.
@ -115,7 +115,7 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
address: address, address: address,
addrHash: crypto.Keccak256Hash(address[:]), addrHash: crypto.Keccak256Hash(address[:]),
data: data, data: data,
cachedStorage: make(Storage), originStorage: make(Storage),
dirtyStorage: make(Storage), dirtyStorage: make(Storage),
} }
} }
@ -159,13 +159,25 @@ func (c *stateObject) getTrie(db Database) Trie {
return c.trie return c.trie
} }
// GetState returns a value in account storage. // GetState retrieves a value from the account storage trie.
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash { func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
value, exists := self.cachedStorage[key] // If we have a dirty value for this state entry, return it
if exists { value, dirty := self.dirtyStorage[key]
if dirty {
return value return value
} }
// Load from DB in case it is missing. // Otherwise return the entry's original value
return self.GetCommittedState(db, key)
}
// GetCommittedState retrieves a value from the committed account storage trie.
func (self *stateObject) GetCommittedState(db Database, key common.Hash) common.Hash {
// If we have the original value cached, return that
value, cached := self.originStorage[key]
if cached {
return value
}
// Otherwise load the value from the database
enc, err := self.getTrie(db).TryGet(key[:]) enc, err := self.getTrie(db).TryGet(key[:])
if err != nil { if err != nil {
self.setError(err) self.setError(err)
@ -178,22 +190,27 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
} }
value.SetBytes(content) value.SetBytes(content)
} }
self.cachedStorage[key] = value self.originStorage[key] = value
return value return value
} }
// SetState updates a value in account storage. // SetState updates a value in account storage.
func (self *stateObject) SetState(db Database, key, value common.Hash) { func (self *stateObject) SetState(db Database, key, value common.Hash) {
// If the new value is the same as old, don't set
prev := self.GetState(db, key)
if prev == value {
return
}
// New value is different, update and journal the change
self.db.journal.append(storageChange{ self.db.journal.append(storageChange{
account: &self.address, account: &self.address,
key: key, key: key,
prevalue: self.GetState(db, key), prevalue: prev,
}) })
self.setState(key, value) self.setState(key, value)
} }
func (self *stateObject) setState(key, value common.Hash) { func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value self.dirtyStorage[key] = value
} }
@ -202,6 +219,13 @@ func (self *stateObject) updateTrie(db Database) Trie {
tr := self.getTrie(db) tr := self.getTrie(db)
for key, value := range self.dirtyStorage { for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key) delete(self.dirtyStorage, key)
// Skip noop changes, persist actual changes
if value == self.originStorage[key] {
continue
}
self.originStorage[key] = value
if (value == common.Hash{}) { if (value == common.Hash{}) {
self.setError(tr.TryDelete(key[:])) self.setError(tr.TryDelete(key[:]))
continue continue
@ -279,7 +303,7 @@ func (self *stateObject) deepCopy(db *StateDB) *stateObject {
} }
stateObject.code = self.code stateObject.code = self.code
stateObject.dirtyStorage = self.dirtyStorage.Copy() stateObject.dirtyStorage = self.dirtyStorage.Copy()
stateObject.cachedStorage = self.dirtyStorage.Copy() stateObject.originStorage = self.originStorage.Copy()
stateObject.suicided = self.suicided stateObject.suicided = self.suicided
stateObject.dirtyCode = self.dirtyCode stateObject.dirtyCode = self.dirtyCode
stateObject.deleted = self.deleted stateObject.deleted = self.deleted

@ -96,11 +96,15 @@ func (s *StateSuite) TestNull(c *checker.C) {
s.state.CreateAccount(address) s.state.CreateAccount(address)
//value := common.FromHex("0x823140710bf13990e4500136726d8b55") //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
var value common.Hash var value common.Hash
s.state.SetState(address, common.Hash{}, value) s.state.SetState(address, common.Hash{}, value)
s.state.Commit(false) s.state.Commit(false)
value = s.state.GetState(address, common.Hash{})
if value != (common.Hash{}) { if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty hash. got %x", value) c.Errorf("expected empty current value, got %x", value)
}
if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
c.Errorf("expected empty committed value, got %x", value)
} }
} }
@ -110,20 +114,24 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
data1 := common.BytesToHash([]byte{42}) data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43}) data2 := common.BytesToHash([]byte{43})
// snapshot the genesis state
genesis := s.state.Snapshot()
// set initial state object value // set initial state object value
s.state.SetState(stateobjaddr, storageaddr, data1) s.state.SetState(stateobjaddr, storageaddr, data1)
// get snapshot of current state
snapshot := s.state.Snapshot() 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) s.state.SetState(stateobjaddr, storageaddr, data2)
// restore snapshot
s.state.RevertToSnapshot(snapshot) s.state.RevertToSnapshot(snapshot)
// get state storage value c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, data1)
res := s.state.GetState(stateobjaddr, storageaddr) c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
c.Assert(data1, checker.DeepEquals, res) // revert up to the genesis state and ensure correct content
s.state.RevertToSnapshot(genesis)
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
c.Assert(s.state.GetCommittedState(stateobjaddr, storageaddr), checker.DeepEquals, common.Hash{})
} }
func (s *StateSuite) TestSnapshotEmpty(c *checker.C) { func (s *StateSuite) TestSnapshotEmpty(c *checker.C) {
@ -208,24 +216,30 @@ func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code) t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
} }
if len(so1.cachedStorage) != len(so0.cachedStorage) { if len(so1.dirtyStorage) != len(so0.dirtyStorage) {
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage)) t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage))
} }
for k, v := range so1.cachedStorage { for k, v := range so1.dirtyStorage {
if so0.cachedStorage[k] != v { if so0.dirtyStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v) t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v)
} }
} }
for k, v := range so0.cachedStorage { for k, v := range so0.dirtyStorage {
if so1.cachedStorage[k] != v { if so1.dirtyStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want none.", k, v) t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v)
} }
} }
if len(so1.originStorage) != len(so0.originStorage) {
if so0.suicided != so1.suicided { t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage))
t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided)
} }
if so0.deleted != so1.deleted { for k, v := range so1.originStorage {
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted) 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.originStorage {
if so1.originStorage[k] != v {
t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v)
}
} }
} }

@ -169,11 +169,22 @@ func (self *StateDB) Preimages() map[common.Hash][]byte {
return self.preimages return self.preimages
} }
// AddRefund adds gas to the refund counter
func (self *StateDB) AddRefund(gas uint64) { func (self *StateDB) AddRefund(gas uint64) {
self.journal.append(refundChange{prev: self.refund}) self.journal.append(refundChange{prev: self.refund})
self.refund += gas self.refund += gas
} }
// SubRefund removes gas from the refund counter.
// This method will panic if the refund counter goes below zero
func (self *StateDB) SubRefund(gas uint64) {
self.journal.append(refundChange{prev: self.refund})
if gas > self.refund {
panic("Refund counter below zero")
}
self.refund -= gas
}
// Exist reports whether the given account address exists in the state. // Exist reports whether the given account address exists in the state.
// Notably this also returns true for suicided accounts. // Notably this also returns true for suicided accounts.
func (self *StateDB) Exist(addr common.Address) bool { func (self *StateDB) Exist(addr common.Address) bool {
@ -236,10 +247,20 @@ func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
return common.BytesToHash(stateObject.CodeHash()) return common.BytesToHash(stateObject.CodeHash())
} }
func (self *StateDB) GetState(addr common.Address, bhash common.Hash) common.Hash { // GetState retrieves a value from the given account's storage trie.
func (self *StateDB) GetState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr) stateObject := self.getStateObject(addr)
if stateObject != nil { if stateObject != nil {
return stateObject.GetState(self.db, bhash) return stateObject.GetState(self.db, hash)
}
return common.Hash{}
}
// GetCommittedState retrieves a value from the given account's committed storage trie.
func (self *StateDB) GetCommittedState(addr common.Address, hash common.Hash) common.Hash {
stateObject := self.getStateObject(addr)
if stateObject != nil {
return stateObject.GetCommittedState(self.db, hash)
} }
return common.Hash{} return common.Hash{}
} }
@ -435,19 +456,14 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
if so == nil { if so == nil {
return 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)) it := trie.NewIterator(so.getTrie(db.db).NodeIterator(nil))
for it.Next() { for it.Next() {
// ignore cached values
key := common.BytesToHash(db.trie.GetKey(it.Key)) key := common.BytesToHash(db.trie.GetKey(it.Key))
if _, ok := so.cachedStorage[key]; !ok { if value, dirty := so.dirtyStorage[key]; dirty {
cb(key, common.BytesToHash(it.Value)) cb(key, value)
continue
} }
cb(key, common.BytesToHash(it.Value))
} }
} }

@ -381,11 +381,11 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr)) checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
// Check storage. // Check storage.
if obj := state.getStateObject(addr); obj != nil { if obj := state.getStateObject(addr); obj != nil {
state.ForEachStorage(addr, func(key, val common.Hash) bool { state.ForEachStorage(addr, func(key, value common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key)) return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
}) })
checkstate.ForEachStorage(addr, func(key, checkval common.Hash) bool { checkstate.ForEachStorage(addr, func(key, value common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval) return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
}) })
} }
if err != nil { if err != nil {

@ -117,24 +117,69 @@ 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) { func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var ( var (
y, x = stack.Back(1), stack.Back(0) y, x = stack.Back(1), stack.Back(0)
val = evm.StateDB.GetState(contract.Address(), common.BigToHash(x)) current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
) )
// This checks for 3 scenario's and calculates gas accordingly // The legacy gas metering only takes into consideration the current state
// 1. From a zero-value address to a non-zero value (NEW VALUE) if !evm.chainRules.IsConstantinople {
// 2. From a non-zero value address to a zero-value address (DELETE) // This checks for 3 scenario's and calculates gas accordingly:
// 3. From a non-zero to a non-zero (CHANGE) //
if val == (common.Hash{}) && y.Sign() != 0 { // 1. From a zero-value address to a non-zero value (NEW VALUE)
// 0 => non 0 // 2. From a non-zero value address to a zero-value address (DELETE)
return params.SstoreSetGas, nil // 3. From a non-zero to a non-zero (CHANGE)
} else if val != (common.Hash{}) && y.Sign() == 0 { switch {
// non 0 => 0 case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
evm.StateDB.AddRefund(params.SstoreRefundGas) return params.SstoreSetGas, nil
return params.SstoreClearGas, nil case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
} else { evm.StateDB.AddRefund(params.SstoreRefundGas)
// non 0 => non 0 (or 0 => 0) return params.SstoreClearGas, nil
return params.SstoreResetGas, nil default: // non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil
}
} }
// The new gas metering is based on net gas costs (EIP-1283):
//
// 1. If current value equals new value (this is a no-op), 200 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, 20000 gas is deducted.
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 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), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 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 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
value := common.BigToHash(y)
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(params.NetSstoreClearRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
evm.StateDB.AddRefund(params.NetSstoreResetClearRefund)
} else { // reset to original existing slot (2.2.2.2)
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
}
}
return params.NetSstoreDirtyGas, nil
} }
func makeGasLog(n uint64) gasFunc { func makeGasLog(n uint64) gasFunc {

@ -40,8 +40,10 @@ type StateDB interface {
GetCodeSize(common.Address) int GetCodeSize(common.Address) int
AddRefund(uint64) AddRefund(uint64)
SubRefund(uint64)
GetRefund() uint64 GetRefund() uint64
GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash) SetState(common.Address, common.Hash, common.Hash)

@ -41,11 +41,6 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
func (d *dummyContractRef) SetNonce(uint64) {} func (d *dummyContractRef) SetNonce(uint64) {}
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) } func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
type dummyStateDB struct {
NoopStateDB
ref *dummyContractRef
}
func TestStoreCapture(t *testing.T) { func TestStoreCapture(t *testing.T) {
var ( var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{}) env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})

@ -1,70 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package vm
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool {
return true
}
func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {}
type NoopEVMCallContext struct{}
func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil
}
func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
return nil, nil
}
func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
return nil, common.Address{}, nil
}
func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
return nil, nil
}
type NoopStateDB struct{}
func (NoopStateDB) CreateAccount(common.Address) {}
func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }
func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 }
func (NoopStateDB) SetNonce(common.Address, uint64) {}
func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} }
func (NoopStateDB) GetCode(common.Address) []byte { return nil }
func (NoopStateDB) SetCode(common.Address, []byte) {}
func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
func (NoopStateDB) AddRefund(uint64) {}
func (NoopStateDB) GetRefund() uint64 { return 0 }
func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
func (NoopStateDB) Suicide(common.Address) bool { return false }
func (NoopStateDB) HasSuicided(common.Address) bool { return false }
func (NoopStateDB) Exist(common.Address) bool { return false }
func (NoopStateDB) Empty(common.Address) bool { return false }
func (NoopStateDB) RevertToSnapshot(int) {}
func (NoopStateDB) Snapshot() int { return 0 }
func (NoopStateDB) AddLog(*types.Log) {}
func (NoopStateDB) AddPreimage(common.Hash, []byte) {}
func (NoopStateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) {}

@ -383,7 +383,7 @@ func (err *ConfigCompatError) Error() string {
type Rules struct { type Rules struct {
ChainID *big.Int ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium bool IsByzantium, IsConstantinople bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -392,5 +392,13 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
if chainID == nil { if chainID == nil {
chainID = new(big.Int) 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),
}
} }

@ -32,15 +32,26 @@ const (
TxGasContractCreation uint64 = 53000 // Per transaction that creates a contract. NOTE: Not payable on data of calls between transactions. 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. 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. 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. LogDataGas uint64 = 8 // Per byte in a LOG* operation's data.
CallStipend uint64 = 2300 // Free gas given at beginning of call. CallStipend uint64 = 2300 // Free gas given at beginning of call.
Sha3Gas uint64 = 30 // Once per SHA3 operation. Sha3Gas uint64 = 30 // Once per SHA3 operation.
Sha3WordGas uint64 = 6 // Once per word of the SHA3 operation's data. 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. SstoreSetGas uint64 = 20000 // Once per SLOAD operation.
SstoreRefundGas uint64 = 15000 // Once per SSTORE operation if the zeroness changes to zero. 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.
NetSstoreInitGas uint64 = 20000 // Once per SSTORE operation from clean zero.
NetSstoreCleanGas uint64 = 5000 // Once per SSTORE operation from clean non-zero.
NetSstoreDirtyGas uint64 = 200 // Once per SSTORE operation from dirty.
NetSstoreClearRefund uint64 = 15000 // Once per SSTORE operation for clearing an originally existing storage slot
NetSstoreResetRefund uint64 = 4800 // Once per SSTORE operation for resetting to the original non-zero value
NetSstoreResetClearRefund uint64 = 19800 // Once per SSTORE operation for resetting to the original zero value
JumpdestGas uint64 = 1 // Refunded gas, once per SSTORE operation if the zeroness changes 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. EpochDuration uint64 = 30000 // Duration between proof-of-work epochs.
CallGas uint64 = 40 // Once per CALL operation & message call transaction. CallGas uint64 = 40 // Once per CALL operation & message call transaction.

@ -41,6 +41,10 @@ func TestState(t *testing.T) {
st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) { st.walk(t, stateTestDir, func(t *testing.T, name string, test *StateTest) {
for _, subtest := range test.Subtests() { for _, subtest := range test.Subtests() {
subtest := subtest subtest := subtest
if subtest.Fork == "Constantinople" {
// Skipping constantinople due to net sstore gas changes affecting all tests
continue
}
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index) key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
name := name + "/" + key name := name + "/" + key
t.Run(key, func(t *testing.T) { t.Run(key, func(t *testing.T) {