github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/types/cache.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	ethcmn "github.com/ethereum/go-ethereum/common"
     9  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/types"
    10  	"github.com/fibonacci-chain/fbc/libs/tendermint/crypto"
    11  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
    12  	"github.com/spf13/viper"
    13  )
    14  
    15  var (
    16  	maxAccInMap        = 100000
    17  	deleteAccCount     = 10000
    18  	maxStorageInMap    = 10000000
    19  	deleteStorageCount = 1000000
    20  
    21  	FlagMultiCache         = "multi-cache"
    22  	MaxAccInMultiCache     = "multi-cache-acc"
    23  	MaxStorageInMultiCache = "multi-cache-storage"
    24  	UseCache               bool
    25  )
    26  
    27  type Account interface {
    28  	Copy() Account
    29  	GetAddress() AccAddress
    30  	SetAddress(AccAddress) error
    31  	GetPubKey() crypto.PubKey
    32  	SetPubKey(crypto.PubKey) error
    33  	GetAccountNumber() uint64
    34  	SetAccountNumber(uint64) error
    35  	GetSequence() uint64
    36  	SetSequence(uint64) error
    37  	GetCoins() Coins
    38  	SetCoins(Coins) error
    39  	SpendableCoins(blockTime time.Time) Coins
    40  	String() string
    41  }
    42  
    43  type ModuleAccount interface {
    44  	Account
    45  
    46  	GetName() string
    47  	GetPermissions() []string
    48  	HasPermission(string) bool
    49  }
    50  
    51  type storageWithCache struct {
    52  	value []byte
    53  	dirty bool
    54  }
    55  
    56  type accountWithCache struct {
    57  	acc     Account
    58  	gas     uint64
    59  	isDirty bool
    60  }
    61  
    62  type codeWithCache struct {
    63  	code    []byte
    64  	isDirty bool
    65  }
    66  
    67  type Cache struct {
    68  	useCache  bool
    69  	parent    *Cache
    70  	gasConfig types.GasConfig
    71  
    72  	storageMap map[ethcmn.Address]map[ethcmn.Hash]*storageWithCache
    73  	accMap     map[ethcmn.Address]*accountWithCache
    74  	codeMap    map[ethcmn.Hash]*codeWithCache
    75  
    76  	accMutex sync.RWMutex
    77  }
    78  
    79  func initCacheParam() {
    80  	UseCache = viper.GetBool(FlagMultiCache)
    81  
    82  	if data := viper.GetInt(MaxAccInMultiCache); data != 0 {
    83  		maxAccInMap = data
    84  		deleteAccCount = maxAccInMap / 10
    85  	}
    86  
    87  	if data := viper.GetInt(MaxStorageInMultiCache); data != 0 {
    88  		maxStorageInMap = data
    89  		deleteStorageCount = maxStorageInMap / 10
    90  	}
    91  }
    92  
    93  func NewChainCache() *Cache {
    94  	initCacheParam()
    95  	return NewCache(nil, UseCache)
    96  }
    97  
    98  func (c *Cache) Clear() {
    99  	if c == nil {
   100  		return
   101  	}
   102  
   103  	c.storageMap = make(map[ethcmn.Address]map[ethcmn.Hash]*storageWithCache)
   104  	c.codeMap = make(map[ethcmn.Hash]*codeWithCache)
   105  	c.accMap = make(map[ethcmn.Address]*accountWithCache)
   106  	c.useCache = false
   107  }
   108  
   109  func NewCache(parent *Cache, useCache bool) *Cache {
   110  	return &Cache{
   111  		useCache: useCache,
   112  		parent:   parent,
   113  
   114  		storageMap: make(map[ethcmn.Address]map[ethcmn.Hash]*storageWithCache),
   115  		accMap:     make(map[ethcmn.Address]*accountWithCache),
   116  		codeMap:    make(map[ethcmn.Hash]*codeWithCache),
   117  		gasConfig:  types.KVGasConfig(),
   118  	}
   119  
   120  }
   121  
   122  func (c *Cache) skip() bool {
   123  	if c == nil || !c.useCache {
   124  		return true
   125  	}
   126  	return false
   127  }
   128  
   129  func (c *Cache) IsEnabled() bool {
   130  	return !c.skip()
   131  }
   132  
   133  func (c *Cache) DisableCache() {
   134  	c.useCache = false
   135  }
   136  
   137  func (c *Cache) UpdateAccount(addr AccAddress, acc Account, lenBytes int, isDirty bool) {
   138  	if c.skip() {
   139  		return
   140  	}
   141  	ethAddr := ethcmn.BytesToAddress(addr.Bytes())
   142  	accWithCache := &accountWithCache{
   143  		acc:     acc,
   144  		isDirty: isDirty,
   145  		gas:     types.Gas(lenBytes)*c.gasConfig.ReadCostPerByte + c.gasConfig.ReadCostFlat,
   146  	}
   147  	c.accMutex.Lock()
   148  	c.accMap[ethAddr] = accWithCache
   149  	c.accMutex.Unlock()
   150  }
   151  
   152  func (c *Cache) UpdateStorage(addr ethcmn.Address, key ethcmn.Hash, value []byte, isDirty bool) {
   153  	if c.skip() {
   154  		return
   155  	}
   156  
   157  	if _, ok := c.storageMap[addr]; !ok {
   158  		c.storageMap[addr] = make(map[ethcmn.Hash]*storageWithCache, 0)
   159  	}
   160  	c.storageMap[addr][key] = &storageWithCache{
   161  		value: value,
   162  		dirty: isDirty,
   163  	}
   164  }
   165  
   166  func (c *Cache) UpdateCode(key []byte, value []byte, isdirty bool) {
   167  	if c.skip() {
   168  		return
   169  	}
   170  	hash := ethcmn.BytesToHash(key)
   171  	c.codeMap[hash] = &codeWithCache{
   172  		code:    value,
   173  		isDirty: isdirty,
   174  	}
   175  }
   176  
   177  func (c *Cache) GetAccount(addr ethcmn.Address) (Account, uint64, bool) {
   178  	if c.skip() {
   179  		return nil, 0, false
   180  	}
   181  
   182  	c.accMutex.RLock()
   183  	data, ok := c.accMap[addr]
   184  	c.accMutex.RUnlock()
   185  	if ok {
   186  		return data.acc, data.gas, ok
   187  	}
   188  
   189  	if c.parent != nil {
   190  		acc, gas, ok := c.parent.GetAccount(addr)
   191  		return acc, gas, ok
   192  	}
   193  	return nil, 0, false
   194  }
   195  
   196  func (c *Cache) GetStorage(addr ethcmn.Address, key ethcmn.Hash) ([]byte, bool) {
   197  	if c.skip() {
   198  		return nil, false
   199  	}
   200  	if _, hasAddr := c.storageMap[addr]; hasAddr {
   201  		data, hasKey := c.storageMap[addr][key]
   202  		if hasKey {
   203  			return data.value, hasKey
   204  		}
   205  	}
   206  
   207  	if c.parent != nil {
   208  		return c.parent.GetStorage(addr, key)
   209  	}
   210  	return nil, false
   211  }
   212  
   213  func (c *Cache) GetCode(key []byte) ([]byte, bool) {
   214  	if c.skip() {
   215  		return nil, false
   216  	}
   217  
   218  	hash := ethcmn.BytesToHash(key)
   219  	if data, ok := c.codeMap[hash]; ok {
   220  		return data.code, ok
   221  	}
   222  
   223  	if c.parent != nil {
   224  		return c.parent.GetCode(hash.Bytes())
   225  	}
   226  	return nil, false
   227  }
   228  
   229  func (c *Cache) Write(updateDirty bool) {
   230  	if c.skip() {
   231  		return
   232  	}
   233  
   234  	if c.parent == nil {
   235  		return
   236  	}
   237  
   238  	c.writeStorage(updateDirty)
   239  	c.writeAcc(updateDirty)
   240  	c.writeCode(updateDirty)
   241  }
   242  
   243  func (c *Cache) writeStorage(updateDirty bool) {
   244  	for addr, storages := range c.storageMap {
   245  		if _, ok := c.parent.storageMap[addr]; !ok {
   246  			c.parent.storageMap[addr] = make(map[ethcmn.Hash]*storageWithCache, 0)
   247  		}
   248  
   249  		for key, v := range storages {
   250  			if needWriteToParent(updateDirty, v.dirty) {
   251  				c.parent.storageMap[addr][key] = v
   252  			}
   253  		}
   254  	}
   255  	c.storageMap = make(map[ethcmn.Address]map[ethcmn.Hash]*storageWithCache)
   256  }
   257  
   258  func (c *Cache) setAcc(addr ethcmn.Address, v *accountWithCache) {
   259  	c.accMutex.Lock()
   260  	c.accMap[addr] = v
   261  	c.accMutex.Unlock()
   262  }
   263  
   264  func (c *Cache) writeAcc(updateDirty bool) {
   265  	c.accMutex.RLock()
   266  	for addr, v := range c.accMap {
   267  		if needWriteToParent(updateDirty, v.isDirty) {
   268  			c.parent.setAcc(addr, v)
   269  		}
   270  	}
   271  	c.accMutex.RUnlock()
   272  
   273  	c.accMutex.Lock()
   274  	for k := range c.accMap {
   275  		delete(c.accMap, k)
   276  	}
   277  	c.accMutex.Unlock()
   278  }
   279  
   280  func (c *Cache) writeCode(updateDirty bool) {
   281  	for hash, v := range c.codeMap {
   282  		if needWriteToParent(updateDirty, v.isDirty) {
   283  			c.parent.codeMap[hash] = v
   284  		}
   285  	}
   286  	c.codeMap = make(map[ethcmn.Hash]*codeWithCache)
   287  }
   288  
   289  func needWriteToParent(updateDirty bool, dirty bool) bool {
   290  	// not dirty
   291  	if !dirty {
   292  		return true
   293  	}
   294  
   295  	// dirty
   296  	if updateDirty {
   297  		return true
   298  	}
   299  	return false
   300  }
   301  
   302  func (c *Cache) storageSize() int {
   303  	lenStorage := 0
   304  	for _, v := range c.storageMap {
   305  		lenStorage += len(v)
   306  	}
   307  	return lenStorage
   308  }
   309  
   310  func (c *Cache) TryDelete(logger log.Logger, height int64) {
   311  	if c.skip() {
   312  		return
   313  	}
   314  	if height%1000 == 0 {
   315  		c.logInfo(logger, "null")
   316  	}
   317  
   318  	lenStorage := c.storageSize()
   319  	if c.lenOfAccMap() < maxAccInMap && lenStorage < maxStorageInMap {
   320  		return
   321  	}
   322  
   323  	deleteMsg := ""
   324  	lenOfAcc := c.lenOfAccMap()
   325  	if lenOfAcc >= maxAccInMap {
   326  		deleteMsg += fmt.Sprintf("Acc:Deleted Before:%d", lenOfAcc)
   327  		cnt := 0
   328  		c.accMutex.Lock()
   329  		for key := range c.accMap {
   330  			delete(c.accMap, key)
   331  			cnt++
   332  			if cnt > deleteAccCount {
   333  				break
   334  			}
   335  		}
   336  		c.accMutex.Unlock()
   337  	}
   338  
   339  	if lenStorage >= maxStorageInMap {
   340  		deleteMsg += fmt.Sprintf("Storage:Deleted Before:len(contract):%d, len(storage):%d", len(c.storageMap), lenStorage)
   341  		cnt := 0
   342  		for key, value := range c.storageMap {
   343  			cnt += len(value)
   344  			delete(c.storageMap, key)
   345  			if cnt > deleteStorageCount {
   346  				break
   347  			}
   348  		}
   349  	}
   350  	if deleteMsg != "" {
   351  		c.logInfo(logger, deleteMsg)
   352  	}
   353  }
   354  
   355  func (c *Cache) logInfo(logger log.Logger, deleteMsg string) {
   356  	nowStats := fmt.Sprintf("len(acc):%d len(contracts):%d len(storage):%d", c.lenOfAccMap(), len(c.storageMap), c.storageSize())
   357  	logger.Info("MultiCache", "deleteMsg", deleteMsg, "nowStats", nowStats)
   358  }
   359  
   360  func (c *Cache) lenOfAccMap() (l int) {
   361  	c.accMutex.RLock()
   362  	l = len(c.accMap)
   363  	c.accMutex.RUnlock()
   364  	return
   365  }