diff --git a/state/block_cache.go b/state/block_cache.go index 4da8e7ce7..9af3b4edc 100644 --- a/state/block_cache.go +++ b/state/block_cache.go @@ -1,6 +1,7 @@ package state import ( + "bytes" "sort" ac "github.com/tendermint/tendermint/account" @@ -25,7 +26,7 @@ type BlockCache struct { db dbm.DB backend *State accounts map[string]accountInfo - storages map[Tuple256]Word256 + storages map[Tuple256]storageInfo } func NewBlockCache(backend *State) *BlockCache { @@ -33,7 +34,7 @@ func NewBlockCache(backend *State) *BlockCache { db: backend.DB, backend: backend, accounts: make(map[string]accountInfo), - storages: make(map[Tuple256]Word256), + storages: make(map[Tuple256]storageInfo), } } @@ -45,15 +46,14 @@ func (cache *BlockCache) State() *State { // BlockCache.account func (cache *BlockCache) GetAccount(addr []byte) *ac.Account { - acc, storage, removed := unpack(cache.accounts[string(addr)]) + acc, _, removed, _ := cache.accounts[string(addr)].unpack() if removed { return nil } else if acc != nil { return acc } else { acc = cache.backend.GetAccount(addr) - storage = makeStorage(cache.db, acc.StorageRoot) - cache.accounts[string(addr)] = accountInfo{acc, storage, false} + cache.accounts[string(addr)] = accountInfo{acc, nil, false, false} return acc } } @@ -61,22 +61,22 @@ func (cache *BlockCache) GetAccount(addr []byte) *ac.Account { func (cache *BlockCache) UpdateAccount(acc *ac.Account) { addr := acc.Address // SANITY CHECK - _, storage, removed := unpack(cache.accounts[string(addr)]) + _, storage, removed, _ := cache.accounts[string(addr)].unpack() if removed { panic("UpdateAccount on a removed account") } // SANITY CHECK END - cache.accounts[string(addr)] = accountInfo{acc, storage, false} + cache.accounts[string(addr)] = accountInfo{acc, storage, false, true} } func (cache *BlockCache) RemoveAccount(addr []byte) { // SANITY CHECK - _, _, removed := unpack(cache.accounts[string(addr)]) + _, _, removed, _ := cache.accounts[string(addr)].unpack() if removed { panic("RemoveAccount on a removed account") } // SANITY CHECK END - cache.accounts[string(addr)] = accountInfo{nil, nil, true} + cache.accounts[string(addr)] = accountInfo{nil, nil, true, false} } // BlockCache.account @@ -85,16 +85,20 @@ func (cache *BlockCache) RemoveAccount(addr []byte) { func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) { // Check cache - value, ok := cache.storages[Tuple256{addr, key}] + info, ok := cache.storages[Tuple256{addr, key}] if ok { - return value + return info.value } // Get or load storage - _, storage, removed := unpack(cache.accounts[string(addr.Prefix(20))]) + acc, storage, removed, dirty := cache.accounts[string(addr.Prefix(20))].unpack() if removed { panic("GetStorage() on removed account") } + if storage == nil { + storage = makeStorage(cache.db, acc.StorageRoot) + cache.accounts[string(addr.Prefix(20))] = accountInfo{acc, storage, false, dirty} + } // Load and set cache _, val_ := storage.Get(key.Bytes()) @@ -102,17 +106,17 @@ func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) { if val_ != nil { value = RightPadWord256(val_.([]byte)) } - cache.storages[Tuple256{addr, key}] = value + cache.storages[Tuple256{addr, key}] = storageInfo{value, false} return value } // NOTE: Set value to zero to removed from the trie. func (cache *BlockCache) SetStorage(addr Word256, key Word256, value Word256) { - _, _, removed := unpack(cache.accounts[string(addr.Prefix(20))]) + _, _, removed, _ := cache.accounts[string(addr.Prefix(20))].unpack() if removed { panic("SetStorage() on a removed account") } - cache.storages[Tuple256{addr, key}] = value + cache.storages[Tuple256{addr, key}] = storageInfo{value, true} } // BlockCache.storage @@ -140,7 +144,7 @@ func (cache *BlockCache) Sync() { for _, storageKey := range storageKeys { addr, key := Tuple256Split(storageKey) if addr != curAddr || curAcc == nil { - acc, storage, removed := unpack(cache.accounts[string(addr.Prefix(20))]) + acc, storage, removed, _ := cache.accounts[string(addr.Prefix(20))].unpack() curAddr = addr curAcc = acc curAccRemoved = removed @@ -149,7 +153,10 @@ func (cache *BlockCache) Sync() { if curAccRemoved { continue } - value := cache.storages[storageKey] + value, dirty := cache.storages[storageKey].unpack() + if !dirty { + continue + } if value.IsZero() { curStorage.Remove(key.Bytes()) } else { @@ -166,7 +173,7 @@ func (cache *BlockCache) Sync() { // Update or delete accounts. for _, addrStr := range addrStrs { - acc, storage, removed := unpack(cache.accounts[addrStr]) + acc, storage, removed, dirty := cache.accounts[addrStr].unpack() if removed { removed := cache.backend.RemoveAccount(acc.Address) if !removed { @@ -176,8 +183,16 @@ func (cache *BlockCache) Sync() { if acc == nil { panic(Fmt("Account should not be nil for addr: %X", acc.Address)) } - acc.StorageRoot = storage.Save() - cache.backend.UpdateAccount(acc) + if storage != nil { + newStorageRoot := storage.Save() + if !bytes.Equal(newStorageRoot, acc.StorageRoot) { + acc.StorageRoot = newStorageRoot + dirty = true + } + } + if dirty { + cache.backend.UpdateAccount(acc) + } } } @@ -189,8 +204,18 @@ type accountInfo struct { account *ac.Account storage merkle.Tree removed bool + dirty bool +} + +func (accInfo accountInfo) unpack() (*ac.Account, merkle.Tree, bool, bool) { + return accInfo.account, accInfo.storage, accInfo.removed, accInfo.dirty +} + +type storageInfo struct { + value Word256 + dirty bool } -func unpack(accInfo accountInfo) (*ac.Account, merkle.Tree, bool) { - return accInfo.account, accInfo.storage, accInfo.removed +func (stjInfo storageInfo) unpack() (Word256, bool) { + return stjInfo.value, stjInfo.dirty } diff --git a/state/tx_cache.go b/state/tx_cache.go index 1e7fd7139..d4188db3c 100644 --- a/state/tx_cache.go +++ b/state/tx_cache.go @@ -94,10 +94,8 @@ func (cache *TxCache) GetStorage(addr Word256, key Word256) Word256 { return value } - // Load and set cache - value = cache.backend.GetStorage(addr, key) - cache.storages[Tuple256{addr, key}] = value - return value + // Load from backend + return cache.backend.GetStorage(addr, key) } // NOTE: Set value to zero to removed from the trie.