Merge pull request #3072 from karalabe/state-storage-dirty
core/state: track dirty state entries for each object
This commit is contained in:
commit
c683e4aaa2
@ -77,7 +77,9 @@ type StateObject struct {
|
|||||||
// 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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user