status-go/services/wallet/balance_cache.go

95 lines
2.3 KiB
Go

package wallet
import (
"context"
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
)
type balanceCache struct {
// cache maps an address to a map of a block number and the balance of this particular address
cache map[common.Address]map[*big.Int]*big.Int
requestCounter map[common.Address]uint
cacheHitsCounter map[common.Address]uint
rw sync.RWMutex
}
type BalanceCache interface {
BalanceAt(ctx context.Context, client BalanceReader, account common.Address, blockNumber *big.Int) (*big.Int, error)
}
func (b *balanceCache) readCachedBalance(account common.Address, blockNumber *big.Int) *big.Int {
b.rw.RLock()
defer b.rw.RUnlock()
return b.cache[account][blockNumber]
}
func (b *balanceCache) addBalanceToCache(account common.Address, blockNumber *big.Int, balance *big.Int) {
b.rw.Lock()
defer b.rw.Unlock()
_, exists := b.cache[account]
if !exists {
b.cache[account] = make(map[*big.Int]*big.Int)
}
b.cache[account][blockNumber] = balance
}
func (b *balanceCache) incRequestsNumber(account common.Address) {
b.rw.Lock()
defer b.rw.Unlock()
cnt, ok := b.requestCounter[account]
if !ok {
b.requestCounter[account] = 1
}
b.requestCounter[account] = cnt + 1
}
func (b *balanceCache) incCacheHitNumber(account common.Address) {
b.rw.Lock()
defer b.rw.Unlock()
cnt, ok := b.cacheHitsCounter[account]
if !ok {
b.cacheHitsCounter[account] = 1
}
b.cacheHitsCounter[account] = cnt + 1
}
func (b *balanceCache) getStats(account common.Address) (uint, uint) {
b.rw.RLock()
defer b.rw.RUnlock()
return b.requestCounter[account], b.cacheHitsCounter[account]
}
func (b *balanceCache) BalanceAt(ctx context.Context, client BalanceReader, account common.Address, blockNumber *big.Int) (*big.Int, error) {
b.incRequestsNumber(account)
cachedBalance := b.readCachedBalance(account, blockNumber)
if cachedBalance != nil {
b.incCacheHitNumber(account)
return cachedBalance, nil
}
balance, err := client.BalanceAt(ctx, account, blockNumber)
if err != nil {
return nil, err
}
b.addBalanceToCache(account, blockNumber, balance)
return balance, nil
}
func newBalanceCache() *balanceCache {
return &balanceCache{
cache: make(map[common.Address]map[*big.Int]*big.Int),
requestCounter: make(map[common.Address]uint),
cacheHitsCounter: make(map[common.Address]uint),
}
}