This commit is contained in:
Péter Szilágyi 2018-09-21 00:55:37 +00:00 committed by GitHub
commit 58df708484
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 224 additions and 95 deletions

View file

@ -154,7 +154,7 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
return nil, errBlockNumberUnsupported return nil, errBlockNumberUnsupported
} }
statedb, _ := b.blockchain.State() statedb, _ := b.blockchain.State()
val := statedb.GetState(contract, key) val, _ := statedb.GetState(contract, key)
return val[:], nil return val[:], nil
} }

View file

@ -108,8 +108,10 @@ type (
prev uint64 prev uint64
} }
storageChange struct { storageChange struct {
account *common.Address account *common.Address
key, prevalue common.Hash key common.Hash
prevValue common.Hash
prevDirty bool
} }
codeChange struct { codeChange struct {
account *common.Address account *common.Address
@ -196,7 +198,7 @@ func (ch codeChange) dirtied() *common.Address {
} }
func (ch storageChange) revert(s *StateDB) { 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 { func (ch storageChange) dirtied() *common.Address {

View file

@ -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,17 +159,22 @@ func (c *stateObject) getTrie(db Database) Trie {
return c.trie return c.trie
} }
// GetState returns a value in account storage. // GetState returns a value from account storage, and also whether the slot is
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash { // dirty in the current transaction execution context.
value, exists := self.cachedStorage[key] func (self *stateObject) GetState(db Database, key common.Hash) (common.Hash, bool) {
if exists { value, dirty := self.dirtyStorage[key]
return value if dirty {
return value, true
}
value, cached := self.originStorage[key]
if cached {
return value, false
} }
// Load from DB in case it is missing. // Load from DB in case it is missing.
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)
return common.Hash{} return common.Hash{}, false
} }
if len(enc) > 0 { if len(enc) > 0 {
_, content, _, err := rlp.Split(enc) _, content, _, err := rlp.Split(enc)
@ -178,23 +183,34 @@ 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, false
} }
// 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 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{ self.db.journal.append(storageChange{
account: &self.address, account: &self.address,
key: key, key: key,
prevalue: self.GetState(db, key), prevValue: prev,
prevDirty: dirty,
}) })
self.setState(key, value)
} }
func (self *stateObject) setState(key, value common.Hash) { func (self *stateObject) setState(key, value common.Hash, dirty bool) {
self.cachedStorage[key] = value if dirty {
self.dirtyStorage[key] = value self.dirtyStorage[key] = value
} else {
delete(self.dirtyStorage, key)
}
} }
// updateTrie writes cached storage modifications into the object's storage trie. // 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) 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 +302,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

View file

@ -96,12 +96,17 @@ 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{})
value, dirty := s.state.GetState(address, common.Hash{})
if value != (common.Hash{}) { if value != (common.Hash{}) {
c.Errorf("expected empty hash. got %x", value) c.Errorf("expected empty hash. got %x", value)
} }
if dirty {
c.Errorf("expected non-dirty, got dirty")
}
} }
func (s *StateSuite) TestSnapshot(c *checker.C) { func (s *StateSuite) TestSnapshot(c *checker.C) {
@ -110,20 +115,27 @@ 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})
// 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) 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 value, dirty := s.state.GetState(stateobjaddr, storageaddr)
res := s.state.GetState(stateobjaddr, storageaddr) c.Assert(data1, checker.DeepEquals, value)
c.Assert(true, checker.DeepEquals, dirty)
c.Assert(data1, checker.DeepEquals, res) // revert up to the genesis state and ensure correct content
s.state.RevertToSnapshot(genesis)
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) { 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) 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) {
t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage))
}
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.originStorage {
if so1.originStorage[k] != v {
t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v)
} }
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
@ -236,12 +237,12 @@ 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 { func (self *StateDB) GetState(addr common.Address, bhash common.Hash) (common.Hash, bool) {
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, bhash)
} }
return common.Hash{} return common.Hash{}, false
} }
// Database retrieves the low level database supporting the lower level trie ops. // 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) so := db.getStateObject(addr)
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, true)
continue
} }
cb(key, common.BytesToHash(it.Value), false)
} }
} }
@ -527,9 +523,36 @@ func (self *StateDB) RevertToSnapshot(revid int) {
self.validRevisions = self.validRevisions[:idx] self.validRevisions = self.validRevisions[:idx]
} }
// GetRefund returns the current value of the refund counter. // GetRefund returns the current value of the refund counter and any out-of-band
func (self *StateDB) GetRefund() uint64 { // refunds at the end of the transaction.
return self.refund 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 // Finalise finalises the state by removing the self destructed objects

View file

@ -381,11 +381,19 @@ 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, dirty bool) bool {
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key)) 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 { checkstate.ForEachStorage(addr, func(key, value common.Hash, dirty bool) bool {
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval) 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 { if err != nil {
@ -393,9 +401,11 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
} }
} }
if state.GetRefund() != checkstate.GetRefund() { for _, netGasMetering := range []bool{false, true} {
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d", if state.GetRefund(netGasMetering) != checkstate.GetRefund(netGasMetering) {
state.GetRefund(), checkstate.GetRefund()) 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{})) { if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v", return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",

View file

@ -229,9 +229,11 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
func (st *StateTransition) refundGas() { func (st *StateTransition) refundGas() {
// Apply refund counter, capped to half of the used gas. // Apply refund counter, capped to half of the used gas.
refund := st.gasUsed() / 2 netSstoreMeter := st.evm.ChainConfig().IsConstantinople(st.evm.BlockNumber)
if refund > st.state.GetRefund() {
refund = st.state.GetRefund() refund := st.state.GetRefund(netSstoreMeter)
if refund > st.gasUsed()/2 {
refund = st.gasUsed() / 2
} }
st.gas += refund st.gas += refund

View file

@ -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) { 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)) value, dirty = 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) // 1. From a zero-value address to a non-zero value (NEW VALUE)
if val == (common.Hash{}) && y.Sign() != 0 { // 2. From a non-zero value address to a zero-value address (DELETE)
// 0 => non 0 // 3. From a non-zero to a non-zero (CHANGE)
return params.SstoreSetGas, nil switch {
} else if val != (common.Hash{}) && y.Sign() == 0 { case value == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
// non 0 => 0 return params.SstoreSetGas, nil
evm.StateDB.AddRefund(params.SstoreRefundGas) case value != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
return params.SstoreClearGas, nil evm.StateDB.AddRefund(params.SstoreRefundGas)
} else { return params.SstoreClearGas, nil
// non 0 => non 0 (or 0 => 0) default: // non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil 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 slots 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
} }
} }

View file

@ -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) { func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := stack.peek() 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()) loc.SetBytes(val.Bytes())
return nil, nil return nil, nil
} }

View file

@ -40,9 +40,9 @@ type StateDB interface {
GetCodeSize(common.Address) int GetCodeSize(common.Address) int
AddRefund(uint64) 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) SetState(common.Address, common.Hash, common.Hash)
Suicide(common.Address) bool Suicide(common.Address) bool
@ -61,7 +61,7 @@ type StateDB interface {
AddLog(*types.Log) AddLog(*types.Log)
AddPreimage(common.Hash, []byte) 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 // CallContext provides a basic interface for the EVM calling conventions. The EVM

View file

@ -134,7 +134,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
in.intPool = nil in.intPool = nil
}() }()
} }
// Increment the call depth which is restricted to 1024 // Increment the call depth which is restricted to 1024
in.evm.depth++ in.evm.depth++
defer func() { in.evm.depth-- }() defer func() { in.evm.depth-- }()

View file

@ -214,7 +214,7 @@ func (dw *dbWrapper) pushObject(vm *duktape.Context) {
hash := popSlice(ctx) hash := popSlice(ctx)
addr := 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)) ptr := ctx.PushFixedBuffer(len(state))
copy(makeSlice(ptr, uint(len(state))), state[:]) copy(makeSlice(ptr, uint(len(state))), state[:])

View file

@ -597,7 +597,7 @@ func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.A
if state == nil || err != nil { if state == nil || err != nil {
return nil, err return nil, err
} }
res := state.GetState(address, common.HexToHash(key)) res, _ := state.GetState(address, common.HexToHash(key))
return res[:], state.Error() return res[:], state.Error()
} }

View file

@ -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,12 @@ 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)}
} }

View file

@ -32,15 +32,25 @@ 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 SSTORE 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.
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. 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.

View file

@ -100,7 +100,7 @@ func (t *VMTest) Run(vmconfig vm.Config) error {
} }
for addr, account := range t.json.Post { for addr, account := range t.json.Post {
for k, wantV := range account.Storage { 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) return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV)
} }
} }