core/state: track dirty state entries for each object

This commit is contained in:
Péter Szilágyi 2016-10-03 10:48:01 +03:00
parent d4b55fc5b2
commit b7159818f9
2 changed files with 24 additions and 18 deletions

View File

@ -75,9 +75,11 @@ type StateObject struct {
dbErr error dbErr error
// Write caches. // Write caches.
trie *trie.SecureTrie // storage trie, which becomes non-nil on first access trie *trie.SecureTrie // 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
storage Storage // Cached storage (flushed when updated)
cachedStorage Storage // Storage entry cache to avoid duplicate reads
dirtyStorage Storage // Storage entries that need to be flushed to disk
// Cache flags. // Cache flags.
// When an object is marked for deletion it will be delete from the trie // When an object is marked for deletion it will be delete from the trie
@ -105,7 +107,7 @@ func NewObject(address common.Address, data Account, onDirty func(addr common.Ad
if data.CodeHash == nil { if data.CodeHash == nil {
data.CodeHash = emptyCodeHash data.CodeHash = emptyCodeHash
} }
return &StateObject{address: address, data: data, storage: make(Storage), onDirty: onDirty} return &StateObject{address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty}
} }
// EncodeRLP implements rlp.Encoder. // EncodeRLP implements rlp.Encoder.
@ -145,7 +147,7 @@ func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
// GetState returns a value in account storage. // GetState returns a value in account storage.
func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash { func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash {
value, exists := self.storage[key] value, exists := self.cachedStorage[key]
if exists { if exists {
return value return value
} }
@ -155,14 +157,16 @@ func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash
rlp.DecodeBytes(tr.Get(key[:]), &ret) rlp.DecodeBytes(tr.Get(key[:]), &ret)
value = common.BytesToHash(ret) value = common.BytesToHash(ret)
if (value != common.Hash{}) { if (value != common.Hash{}) {
self.storage[key] = value self.cachedStorage[key] = value
} }
return value return value
} }
// SetState updates a value in account storage. // SetState updates a value in account storage.
func (self *StateObject) SetState(key, value common.Hash) { func (self *StateObject) SetState(key, value common.Hash) {
self.storage[key] = value self.cachedStorage[key] = value
self.dirtyStorage[key] = value
if self.onDirty != nil { if self.onDirty != nil {
self.onDirty(self.Address()) self.onDirty(self.Address())
self.onDirty = nil self.onDirty = nil
@ -172,7 +176,8 @@ func (self *StateObject) SetState(key, value common.Hash) {
// updateTrie writes cached storage modifications into the object's storage trie. // updateTrie writes cached storage modifications into the object's storage trie.
func (self *StateObject) updateTrie(db trie.Database) { func (self *StateObject) updateTrie(db trie.Database) {
tr := self.getTrie(db) tr := self.getTrie(db)
for key, value := range self.storage { for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key)
if (value == common.Hash{}) { if (value == common.Hash{}) {
tr.Delete(key[:]) tr.Delete(key[:])
continue continue
@ -241,7 +246,8 @@ func (self *StateObject) Copy(db trie.Database, onDirty func(addr common.Address
stateObject := NewObject(self.address, self.data, onDirty) stateObject := NewObject(self.address, self.data, onDirty)
stateObject.trie = self.trie stateObject.trie = self.trie
stateObject.code = self.code stateObject.code = self.code
stateObject.storage = self.storage.Copy() stateObject.dirtyStorage = self.dirtyStorage.Copy()
stateObject.cachedStorage = self.dirtyStorage.Copy()
stateObject.remove = self.remove stateObject.remove = self.remove
stateObject.dirtyCode = self.dirtyCode stateObject.dirtyCode = self.dirtyCode
stateObject.deleted = self.deleted stateObject.deleted = self.deleted
@ -312,7 +318,7 @@ func (self *StateObject) Value() *big.Int {
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) { func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
// When iterating over the storage check the cache first // When iterating over the storage check the cache first
for h, value := range self.storage { for h, value := range self.cachedStorage {
cb(h, value) cb(h, value)
} }
@ -320,7 +326,7 @@ func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
for it.Next() { for it.Next() {
// ignore cached values // ignore cached values
key := common.BytesToHash(self.trie.GetKey(it.Key)) key := common.BytesToHash(self.trie.GetKey(it.Key))
if _, ok := self.storage[key]; !ok { if _, ok := self.cachedStorage[key]; !ok {
cb(key, common.BytesToHash(it.Value)) cb(key, common.BytesToHash(it.Value))
} }
} }

View File

@ -208,16 +208,16 @@ 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.storage) != len(so0.storage) { if len(so1.cachedStorage) != len(so0.cachedStorage) {
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.storage), len(so0.storage)) t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage))
} }
for k, v := range so1.storage { for k, v := range so1.cachedStorage {
if so0.storage[k] != v { if so0.cachedStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.storage[k], v) t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v)
} }
} }
for k, v := range so0.storage { for k, v := range so0.cachedStorage {
if so1.storage[k] != v { if so1.cachedStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want none.", k, v) t.Errorf("Storage key %x mismatch: have %v, want none.", k, v)
} }
} }