bitxhub/internal/ledger/account_cache.go

142 lines
3.0 KiB
Go

package ledger
import (
"bytes"
"strings"
"sync"
)
type AccountCache struct {
innerAccounts map[string]*innerAccount
states map[string]map[string][]byte
codes map[string][]byte
rwLock sync.RWMutex
}
func NewAccountCache() *AccountCache {
return &AccountCache{
innerAccounts: make(map[string]*innerAccount),
states: make(map[string]map[string][]byte),
codes: make(map[string][]byte),
rwLock: sync.RWMutex{},
}
}
func (ac *AccountCache) add(accounts map[string]*Account) {
ac.rwLock.Lock()
defer ac.rwLock.Unlock()
for addr, account := range accounts {
ac.innerAccounts[addr] = account.dirtyAccount
if len(account.dirtyState) != 0 {
stateMap, ok := ac.states[addr]
if !ok {
stateMap = make(map[string][]byte)
ac.states[addr] = stateMap
}
for key, val := range account.dirtyState {
stateMap[key] = val
}
}
if !bytes.Equal(account.originCode, account.dirtyCode) {
ac.codes[addr] = account.dirtyCode
}
}
}
func (ac *AccountCache) remove(accounts map[string]*Account) {
ac.rwLock.Lock()
defer ac.rwLock.Unlock()
for addr, account := range accounts {
if innerAccount, ok := ac.innerAccounts[addr]; ok {
if !innerAccountChanged(innerAccount, account.dirtyAccount) {
delete(ac.innerAccounts, addr)
}
}
if len(account.dirtyState) != 0 {
if stateMap, ok := ac.states[addr]; ok {
for key, val := range account.dirtyState {
if v, ok := stateMap[key]; ok {
if bytes.Equal(v, val) {
delete(stateMap, key)
}
}
}
if len(stateMap) == 0 {
delete(ac.states, addr)
}
}
}
if !bytes.Equal(account.dirtyCode, account.originCode) {
if code, ok := ac.codes[addr]; ok {
if bytes.Equal(code, account.dirtyCode) {
delete(ac.codes, addr)
}
}
}
}
}
func (ac *AccountCache) getInnerAccount(addr string) (*innerAccount, bool) {
ac.rwLock.RLock()
defer ac.rwLock.RUnlock()
if innerAccount, ok := ac.innerAccounts[addr]; ok {
return innerAccount, true
}
return nil, false
}
func (ac *AccountCache) getState(addr string, key string) ([]byte, bool) {
ac.rwLock.RLock()
defer ac.rwLock.RUnlock()
if stateMap, ok := ac.states[addr]; ok {
if val, ok := stateMap[key]; ok {
return val, true
}
}
return nil, false
}
func (ac *AccountCache) getCode(addr string) ([]byte, bool) {
ac.rwLock.RLock()
defer ac.rwLock.RUnlock()
if code, ok := ac.codes[addr]; ok {
return code, true
}
return nil, false
}
func (ac *AccountCache) query(addr string, prefix string) map[string][]byte {
ac.rwLock.RLock()
defer ac.rwLock.RUnlock()
ret := make(map[string][]byte)
if stateMap, ok := ac.states[addr]; ok {
for key, val := range stateMap {
if strings.HasPrefix(key, prefix) {
ret[key] = val
}
}
}
return ret
}
func (ac *AccountCache) clear() {
ac.rwLock.Lock()
defer ac.rwLock.Unlock()
ac.innerAccounts = make(map[string]*innerAccount)
ac.states = make(map[string]map[string][]byte)
ac.codes = make(map[string][]byte)
}