Skip to content

Commit

Permalink
core, params: polish net gas metering PR a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
karalabe committed Sep 18, 2018
1 parent caa2c23 commit 5d921fa
Show file tree
Hide file tree
Showing 11 changed files with 165 additions and 226 deletions.
63 changes: 33 additions & 30 deletions core/state/state_object.go
Expand Up @@ -77,9 +77,9 @@ 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
originalValue Storage // Map of original storage values, at the beginning of current call context

// Cache flags.
// When an object is marked suicided it will be delete from the trie
// during the "update" phase of the state transition.
Expand Down Expand Up @@ -115,9 +115,8 @@ 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),
originalValue: make(Storage),
}
}

Expand Down Expand Up @@ -160,13 +159,25 @@ func (c *stateObject) getTrie(db Database) 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 {
value, exists := self.cachedStorage[key]
if exists {
// If we have a dirty value for this state entry, return it
value, dirty := self.dirtyStorage[key]
if dirty {
return value
}
// 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
}
// Load from DB in case it is missing.
// Otherwise load the value from the database
enc, err := self.getTrie(db).TryGet(key[:])
if err != nil {
self.setError(err)
Expand All @@ -179,37 +190,27 @@ func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
}
value.SetBytes(content)
}
self.cachedStorage[key] = value
self.originStorage[key] = 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.
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{
account: &self.address,
key: key,
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)
}

func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value
}

Expand All @@ -218,6 +219,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
Expand All @@ -226,10 +234,6 @@ func (self *stateObject) updateTrie(db Database) Trie {
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
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
}

Expand Down Expand Up @@ -299,8 +303,7 @@ func (self *stateObject) deepCopy(db *StateDB) *stateObject {
}
stateObject.code = self.code
stateObject.dirtyStorage = self.dirtyStorage.Copy()
stateObject.cachedStorage = self.dirtyStorage.Copy()
stateObject.originalValue = self.originalValue.Copy()
stateObject.originStorage = self.originStorage.Copy()
stateObject.suicided = self.suicided
stateObject.dirtyCode = self.dirtyCode
stateObject.deleted = self.deleted
Expand Down
58 changes: 36 additions & 22 deletions core/state/state_test.go
Expand Up @@ -96,11 +96,15 @@ 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{})
if value != (common.Hash{}) {
c.Errorf("expected empty hash. got %x", value)

if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
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)
}
}

Expand All @@ -110,20 +114,24 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
data1 := common.BytesToHash([]byte{42})
data2 := common.BytesToHash([]byte{43})

// snapshot the genesis state
genesis := s.state.Snapshot()

// set initial state object value
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)
c.Assert(s.state.GetState(stateobjaddr, storageaddr), checker.DeepEquals, data1)
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) {
Expand Down Expand Up @@ -208,24 +216,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.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.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.cachedStorage {
if so1.cachedStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want none.", 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 so0.suicided != so1.suicided {
t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided)
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)
}
}
if so0.deleted != so1.deleted {
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
for k, v := range so0.originStorage {
if so1.originStorage[k] != v {
t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v)
}
}
}
23 changes: 10 additions & 13 deletions core/state/statedb.go
Expand Up @@ -247,18 +247,20 @@ 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 {
// 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)
if stateObject != nil {
return stateObject.GetState(self.db, bhash)
return stateObject.GetState(self.db, 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)
if stateObject != nil {
return stateObject.GetOriginalStateValue(self.db, bhash)
return stateObject.GetCommittedState(self.db, hash)
}
return common.Hash{}
}
Expand Down Expand Up @@ -454,19 +456,14 @@ func (db *StateDB) ForEachStorage(addr common.Address, cb func(key, value common
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)
continue
}
cb(key, common.BytesToHash(it.Value))
}
}

Expand Down
8 changes: 4 additions & 4 deletions core/state/statedb_test.go
Expand Up @@ -381,11 +381,11 @@ 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) bool {
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
})
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) bool {
return checkeq("GetState("+key.Hex()+")", checkstate.GetState(addr, key), value)
})
}
if err != nil {
Expand Down

0 comments on commit 5d921fa

Please sign in to comment.