core, params: polish net gas metering PR a bit
This commit is contained in:
parent
caa2c23a38
commit
5d921fa3a0
|
@ -77,9 +77,9 @@ 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
|
||||||
originalValue Storage // Map of original storage values, at the beginning of current call context
|
|
||||||
// Cache flags.
|
// Cache flags.
|
||||||
// When an object is marked suicided it will be delete from the trie
|
// When an object is marked suicided it will be delete from the trie
|
||||||
// during the "update" phase of the state transition.
|
// during the "update" phase of the state transition.
|
||||||
|
@ -115,9 +115,8 @@ 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),
|
||||||
originalValue: make(Storage),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,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)
|
||||||
|
@ -179,37 +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
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetOriginalStateValue returns the state value that is currently in the Trie, that is, ignoring any
|
|
||||||
// changes that have been made but not yet written to trie.
|
|
||||||
func (self *stateObject) GetOriginalStateValue(db Database, key common.Hash) common.Hash{
|
|
||||||
if original, exist:= self.originalValue[key]; exist {
|
|
||||||
// original value has been set, return it
|
|
||||||
return original
|
|
||||||
}
|
|
||||||
return self.GetState(db, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
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: prev,
|
prevalue: prev,
|
||||||
})
|
})
|
||||||
if _, isSet := self.originalValue[key]; !isSet {
|
|
||||||
// original value has not been set, so set it now
|
|
||||||
self.originalValue[key] = 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,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
|
||||||
|
@ -226,10 +234,6 @@ func (self *stateObject) updateTrie(db Database) Trie {
|
||||||
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
|
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
|
||||||
self.setError(tr.TryUpdate(key[:], v))
|
self.setError(tr.TryUpdate(key[:], v))
|
||||||
}
|
}
|
||||||
// Clean the map containing 'original' value of storage entries
|
|
||||||
for k, _ := range self.originalValue {
|
|
||||||
delete(self.originalValue, k)
|
|
||||||
}
|
|
||||||
return tr
|
return tr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,8 +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.originalValue = self.originalValue.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -247,18 +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{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetStateOriginal(addr common.Address, bhash common.Hash) 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)
|
stateObject := self.getStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.GetOriginalStateValue(self.db, bhash)
|
return stateObject.GetCommittedState(self.db, hash)
|
||||||
}
|
}
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
@ -454,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 {
|
||||||
|
|
|
@ -116,79 +116,70 @@ 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 (
|
|
||||||
y, x = stack.Back(1), stack.Back(0)
|
|
||||||
val = 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// gasSStoreEip1283 calculates SSTORE gas cost according to EIP-1283
|
|
||||||
func gasSStoreEip1283(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)
|
||||||
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||||
)
|
)
|
||||||
//1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
// The legacy gas metering only takes into consideration the current state
|
||||||
//2. If current value does not equal new value
|
if !evm.chainRules.IsConstantinople {
|
||||||
// 2.1 If original value equals current value (this storage slot has not been changed by the current execution context)
|
// This checks for 3 scenario's and calculates gas accordingly:
|
||||||
// 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.
|
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||||
// 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. From a non-zero value address to a zero-value address (DELETE)
|
||||||
// 2.2.1 If original value is not 0
|
// 3. From a non-zero to a non-zero (CHANGE)
|
||||||
// 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.
|
switch {
|
||||||
// 2.2.1.2 If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
|
||||||
// 2.2.2 If original value equals new value (this storage slot is reset)
|
return params.SstoreSetGas, nil
|
||||||
// 2.2.2.1 If original value is 0, add 19800 gas to refund counter.
|
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
|
||||||
// 2.2.2.2 Otherwise, add 4800 gas to refund counter.
|
evm.StateDB.AddRefund(params.SstoreRefundGas)
|
||||||
new := common.BigToHash(y)
|
return params.SstoreClearGas, nil
|
||||||
if current == new {
|
default: // non 0 => non 0 (or 0 => 0)
|
||||||
// 1. current == new
|
return params.SstoreResetGas, nil
|
||||||
return 200, nil
|
|
||||||
}
|
|
||||||
original := evm.StateDB.GetStateOriginal(contract.Address(), common.BigToHash(x))
|
|
||||||
// 2
|
|
||||||
if original == current { // 2.1
|
|
||||||
if original == (common.Hash{}) { // 2.1.1
|
|
||||||
return 20000, nil
|
|
||||||
}
|
|
||||||
// 2.1.2
|
|
||||||
if new == (common.Hash{}) {
|
|
||||||
evm.StateDB.AddRefund(15000)
|
|
||||||
}
|
|
||||||
return 5000, nil
|
|
||||||
}
|
|
||||||
// 2.2
|
|
||||||
if original != (common.Hash{}) { // 2.2.1
|
|
||||||
if current == (common.Hash{}) { // 2.2.1.1
|
|
||||||
evm.StateDB.SubRefund(15000)
|
|
||||||
} else {
|
|
||||||
// 2.2.1.2
|
|
||||||
evm.StateDB.AddRefund(15000)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if original == new { // 2.2.2
|
// The new gas metering is based on net gas costs (EIP-1283):
|
||||||
if original == (common.Hash{}) {
|
//
|
||||||
evm.StateDB.AddRefund(19800)
|
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||||
} else {
|
// 2. If current value does not equal new value
|
||||||
evm.StateDB.AddRefund(4800)
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 200, nil
|
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 {
|
||||||
|
|
|
@ -43,8 +43,8 @@ type StateDB interface {
|
||||||
SubRefund(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
|
||||||
GetStateOriginal(common.Address, common.Hash) common.Hash
|
|
||||||
SetState(common.Address, common.Hash, common.Hash)
|
SetState(common.Address, common.Hash, common.Hash)
|
||||||
|
|
||||||
Suicide(common.Address) bool
|
Suicide(common.Address) bool
|
||||||
|
|
|
@ -95,14 +95,6 @@ func newConstantinopleInstructionSet() [256]operation {
|
||||||
writes: true,
|
writes: true,
|
||||||
returns: true,
|
returns: true,
|
||||||
}
|
}
|
||||||
instructionSet[SSTORE] = operation{
|
|
||||||
execute: opSstore,
|
|
||||||
gasCost: gasSStoreEip1283,
|
|
||||||
validateStack: makeStackFunc(2, 0),
|
|
||||||
valid: true,
|
|
||||||
writes: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
return instructionSet
|
return instructionSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,72 +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) SubRefund(uint64) {}
|
|
||||||
func (NoopStateDB) GetRefund() uint64 { return 0 }
|
|
||||||
func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
|
|
||||||
func (NoopStateDB) GetStateOriginal(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) {}
|
|
|
@ -335,7 +335,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.
|
||||||
|
@ -344,5 +344,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.
|
||||||
|
|
Loading…
Reference in New Issue