github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/evm/keeper/keeper.go (about)

     1  package keeper
     2  
     3  import (
     4  	"encoding/binary"
     5  	"math/big"
     6  	"sync"
     7  
     8  	"github.com/VictoriaMetrics/fastcache"
     9  	ethcmn "github.com/ethereum/go-ethereum/common"
    10  	"github.com/ethereum/go-ethereum/common/prque"
    11  	ethstate "github.com/ethereum/go-ethereum/core/state"
    12  	ethtypes "github.com/ethereum/go-ethereum/core/types"
    13  	lru "github.com/hashicorp/golang-lru"
    14  
    15  	app "github.com/fibonacci-chain/fbc/app/types"
    16  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    17  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store"
    18  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/mpt"
    19  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    20  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/auth"
    21  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
    22  	tmtypes "github.com/fibonacci-chain/fbc/libs/tendermint/types"
    23  	"github.com/fibonacci-chain/fbc/x/evm/types"
    24  	"github.com/fibonacci-chain/fbc/x/evm/watcher"
    25  	"github.com/fibonacci-chain/fbc/x/params"
    26  )
    27  
    28  const (
    29  	heightCacheLimit = 1024
    30  	hashCacheLimit   = 1024
    31  )
    32  
    33  // Keeper wraps the CommitStateDB, allowing us to pass in SDK context while adhering
    34  // to the StateDB interface.
    35  type Keeper struct {
    36  	// Amino codec
    37  	cdc *codec.Codec
    38  	// Store key required for the EVM Prefix KVStore. It is required by:
    39  	// - storing Account's Storage State
    40  	// - storing Account's Code
    41  	// - storing transaction Logs
    42  	// - storing block height -> bloom filter map. Needed for the Web3 API.
    43  	// - storing block hash -> block height map. Needed for the Web3 API.
    44  	storeKey sdk.StoreKey
    45  
    46  	// Account Keeper for fetching accounts
    47  	accountKeeper types.AccountKeeper
    48  	paramSpace    types.Subspace
    49  	supplyKeeper  types.SupplyKeeper
    50  	bankKeeper    types.BankKeeper
    51  	govKeeper     GovKeeper
    52  	stakingKeeper types.StakingKeeper
    53  
    54  	// Transaction counter in a block. Used on StateSB's Prepare function.
    55  	// It is reset to 0 every block on BeginBlock so there's no point in storing the counter
    56  	// on the KVStore or adding it as a field on the EVM genesis state.
    57  	TxCount int
    58  	Bloom   *big.Int
    59  	Bhash   ethcmn.Hash
    60  	LogSize uint
    61  	Ada     types.DbAdapter
    62  
    63  	LogsManages *LogsManager
    64  
    65  	// add inner block data
    66  	innerBlockData BlockInnerData
    67  
    68  	db          ethstate.Database
    69  	rootTrie    ethstate.Trie
    70  	rootHash    ethcmn.Hash
    71  	startHeight uint64
    72  	triegc      *prque.Prque
    73  	stateCache  *fastcache.Cache
    74  	cmLock      sync.Mutex
    75  
    76  	EvmStateDb     *types.CommitStateDB
    77  	UpdatedAccount []ethcmn.Address
    78  
    79  	// cache chain config
    80  	cci *chainConfigInfo
    81  
    82  	hooks   types.EvmHooks
    83  	logger  log.Logger
    84  	Watcher *watcher.Watcher
    85  
    86  	heightCache *lru.Cache // Cache for the most recent block heights
    87  	hashCache   *lru.Cache // Cache for the most recent block hash
    88  }
    89  
    90  type chainConfigInfo struct {
    91  	// chainConfig cached chain config
    92  	// nil means invalid the cache, we should cache it again.
    93  	cc *types.ChainConfig
    94  
    95  	// gasReduced: cached chain config reduces gas costs.
    96  	// when use cached chain config, we restore the gas cost(gasReduced)
    97  	gasReduced sdk.Gas
    98  }
    99  
   100  // NewKeeper generates new evm module keeper
   101  func NewKeeper(
   102  	cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace params.Subspace, ak types.AccountKeeper, sk types.SupplyKeeper, bk types.BankKeeper, stk types.StakingKeeper,
   103  	logger log.Logger) *Keeper {
   104  	// set KeyTable if it has not already been set
   105  	if !paramSpace.HasKeyTable() {
   106  		paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
   107  	}
   108  
   109  	err := initInnerDB()
   110  	if err != nil {
   111  		panic(err)
   112  	}
   113  
   114  	if enable := types.GetEnableBloomFilter(); enable {
   115  		db := types.BloomDb()
   116  		types.InitIndexer(db)
   117  	}
   118  	logger = logger.With("module", types.ModuleName)
   119  	heightCache, _ := lru.New(heightCacheLimit)
   120  	hashCache, _ := lru.New(hashCacheLimit)
   121  	// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
   122  	k := &Keeper{
   123  		cdc:           cdc,
   124  		storeKey:      storeKey,
   125  		accountKeeper: ak,
   126  		paramSpace:    paramSpace,
   127  		supplyKeeper:  sk,
   128  		bankKeeper:    bk,
   129  		stakingKeeper: stk,
   130  		TxCount:       0,
   131  		Bloom:         big.NewInt(0),
   132  		LogSize:       0,
   133  		Ada:           types.DefaultPrefixDb{},
   134  
   135  		innerBlockData: defaultBlockInnerData(),
   136  
   137  		db:             mpt.InstanceOfMptStore(),
   138  		triegc:         prque.New(nil),
   139  		UpdatedAccount: make([]ethcmn.Address, 0),
   140  		cci:            &chainConfigInfo{},
   141  		LogsManages:    NewLogManager(),
   142  		logger:         logger,
   143  		Watcher:        watcher.NewWatcher(logger),
   144  		heightCache:    heightCache,
   145  		hashCache:      hashCache,
   146  	}
   147  	k.Watcher.SetWatchDataManager()
   148  	ak.SetObserverKeeper(k)
   149  
   150  	k.OpenTrie()
   151  	k.EvmStateDb = types.NewCommitStateDB(k.GenerateCSDBParams())
   152  	return k
   153  }
   154  
   155  // NewKeeper generates new evm module keeper
   156  func NewSimulateKeeper(
   157  	cdc *codec.Codec, storeKey sdk.StoreKey, paramSpace types.Subspace, ak types.AccountKeeper, sk types.SupplyKeeper, bk types.BankKeeper, stk types.StakingKeeper, ada types.DbAdapter,
   158  	logger log.Logger) *Keeper {
   159  	heightCache, _ := lru.New(heightCacheLimit)
   160  	hashCache, _ := lru.New(hashCacheLimit)
   161  	// NOTE: we pass in the parameter space to the CommitStateDB in order to use custom denominations for the EVM operations
   162  	k := &Keeper{
   163  		cdc:           cdc,
   164  		storeKey:      storeKey,
   165  		accountKeeper: ak,
   166  		paramSpace:    paramSpace,
   167  		supplyKeeper:  sk,
   168  		bankKeeper:    bk,
   169  		stakingKeeper: stk,
   170  		TxCount:       0,
   171  		Bloom:         big.NewInt(0),
   172  		LogSize:       0,
   173  		Watcher:       watcher.NewWatcher(nil),
   174  		Ada:           ada,
   175  
   176  		db: mpt.InstanceOfMptStore(),
   177  		// Optimize memory usage. No need to initialize this variable when simulate tx.
   178  		// triegc:         prque.New(nil),
   179  		UpdatedAccount: make([]ethcmn.Address, 0),
   180  		cci:            &chainConfigInfo{},
   181  		heightCache:    heightCache,
   182  		hashCache:      hashCache,
   183  	}
   184  
   185  	k.OpenTrie()
   186  	k.EvmStateDb = types.NewCommitStateDB(k.GenerateCSDBParams())
   187  
   188  	return k
   189  }
   190  
   191  // Warning, you need to use pointer object here, for you need to update UpdatedAccount var
   192  func (k *Keeper) OnAccountUpdated(acc auth.Account) {
   193  	if _, ok := acc.(*app.EthAccount); ok {
   194  		k.Watcher.DeleteAccount(acc.GetAddress())
   195  	}
   196  
   197  	k.UpdatedAccount = append(k.UpdatedAccount, ethcmn.BytesToAddress(acc.GetAddress().Bytes()))
   198  }
   199  
   200  // Logger returns a module-specific logger.
   201  func (k *Keeper) GenerateCSDBParams() types.CommitStateDBParams {
   202  	return types.CommitStateDBParams{
   203  		StoreKey:      k.storeKey,
   204  		ParamSpace:    k.paramSpace,
   205  		AccountKeeper: k.accountKeeper,
   206  		SupplyKeeper:  k.supplyKeeper,
   207  		BankKeeper:    k.bankKeeper,
   208  		Ada:           k.Ada,
   209  		Cdc:           k.cdc,
   210  		DB:            k.db,
   211  		Trie:          k.rootTrie,
   212  		RootHash:      k.rootHash,
   213  	}
   214  }
   215  
   216  // GeneratePureCSDBParams generates an instance of csdb params ONLY for store setter and getter
   217  func (k Keeper) GeneratePureCSDBParams() types.CommitStateDBParams {
   218  	return types.CommitStateDBParams{
   219  		StoreKey:   k.storeKey,
   220  		ParamSpace: k.paramSpace,
   221  		Ada:        k.Ada,
   222  		Cdc:        k.cdc,
   223  
   224  		DB:       k.db,
   225  		Trie:     k.rootTrie,
   226  		RootHash: k.rootHash,
   227  	}
   228  }
   229  
   230  // Logger returns a module-specific logger.
   231  func (k Keeper) Logger() log.Logger {
   232  	return k.logger
   233  }
   234  
   235  func (k Keeper) GetStoreKey() store.StoreKey {
   236  	return k.storeKey
   237  }
   238  
   239  // ----------------------------------------------------------------------------
   240  // Block hash mapping functions
   241  // Required by Web3 API.
   242  //  TODO: remove once tendermint support block queries by hash.
   243  // ----------------------------------------------------------------------------
   244  
   245  // GetBlockHeight gets block height from block consensus hash
   246  func (k Keeper) GetBlockHeight(ctx sdk.Context, hash ethcmn.Hash) (int64, bool) {
   247  	if cached, ok := k.heightCache.Get(hash.Hex()); ok {
   248  		height := cached.(int64)
   249  		return height, true
   250  	}
   251  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   252  		return k.getBlockHashInDiskDB(hash.Bytes())
   253  	}
   254  
   255  	store := k.Ada.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBlockHash)
   256  	bz := store.Get(hash.Bytes())
   257  	if len(bz) == 0 {
   258  		return 0, false
   259  	}
   260  
   261  	height := binary.BigEndian.Uint64(bz)
   262  	return int64(height), true
   263  }
   264  
   265  // SetBlockHeight sets the mapping from block consensus hash to block height
   266  func (k Keeper) SetBlockHeight(ctx sdk.Context, hash []byte, height int64) {
   267  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   268  		k.setBlockHashInDiskDB(hash, height)
   269  		return
   270  	}
   271  
   272  	store := k.Ada.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBlockHash)
   273  	bz := sdk.Uint64ToBigEndian(uint64(height))
   274  	store.Set(hash, bz)
   275  	if mpt.TrieWriteAhead {
   276  		k.setBlockHashInDiskDB(hash, height)
   277  	}
   278  }
   279  
   280  // IterateBlockHash iterates all over the block hash in every height
   281  func (k Keeper) IterateBlockHash(ctx sdk.Context, fn func(key []byte, value []byte) (stop bool)) {
   282  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   283  		k.iterateBlockHashInDiskDB(fn)
   284  		return
   285  	}
   286  
   287  	iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.KeyPrefixBlockHash)
   288  	defer iterator.Close()
   289  	for ; iterator.Valid(); iterator.Next() {
   290  		if stop := fn(iterator.Key(), iterator.Value()); stop {
   291  			break
   292  		}
   293  	}
   294  }
   295  
   296  // ----------------------------------------------------------------------------
   297  // Epoch Height -> hash mapping functions
   298  // Required by EVM context's GetHashFunc
   299  // ----------------------------------------------------------------------------
   300  
   301  // GetHeightHash returns the block header hash associated with a given block height and chain epoch number.
   302  func (k Keeper) GetHeightHash(ctx sdk.Context, height uint64) ethcmn.Hash {
   303  	if cached, ok := k.hashCache.Get(int64(height)); ok {
   304  		hash := cached.(string)
   305  		return ethcmn.HexToHash(hash)
   306  	}
   307  	return types.CreateEmptyCommitStateDB(k.GenerateCSDBParams(), ctx).GetHeightHash(height)
   308  }
   309  
   310  // SetHeightHash sets the block header hash associated with a given height.
   311  func (k Keeper) SetHeightHash(ctx sdk.Context, height uint64, hash ethcmn.Hash) {
   312  	types.CreateEmptyCommitStateDB(k.GenerateCSDBParams(), ctx).SetHeightHash(height, hash)
   313  }
   314  
   315  // ----------------------------------------------------------------------------
   316  // Block bloom bits mapping functions
   317  // Required by Web3 API.
   318  // ----------------------------------------------------------------------------
   319  
   320  // GetBlockBloom gets bloombits from block height
   321  func (k Keeper) GetBlockBloom(ctx sdk.Context, height int64) ethtypes.Bloom {
   322  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   323  		return k.getBlockBloomInDiskDB(height)
   324  	}
   325  
   326  	store := k.Ada.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBloom)
   327  	has := store.Has(types.BloomKey(height))
   328  	if !has {
   329  		return ethtypes.Bloom{}
   330  	}
   331  	bz := store.Get(types.BloomKey(height))
   332  	return ethtypes.BytesToBloom(bz)
   333  }
   334  
   335  // SetBlockBloom sets the mapping from block height to bloom bits
   336  func (k Keeper) SetBlockBloom(ctx sdk.Context, height int64, bloom ethtypes.Bloom) {
   337  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   338  		k.setBlockBloomInDiskDB(height, bloom)
   339  		return
   340  	}
   341  
   342  	store := k.Ada.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixBloom)
   343  	store.Set(types.BloomKey(height), bloom.Bytes())
   344  	if mpt.TrieWriteAhead {
   345  		k.setBlockBloomInDiskDB(height, bloom)
   346  	}
   347  }
   348  
   349  // IterateBlockBloom iterates all over the bloom value in every height
   350  func (k Keeper) IterateBlockBloom(ctx sdk.Context, fn func(key []byte, value []byte) (stop bool)) {
   351  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   352  		k.iterateBlockBloomInDiskDB(fn)
   353  		return
   354  	}
   355  
   356  	iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.KeyPrefixBloom)
   357  	defer iterator.Close()
   358  	for ; iterator.Valid(); iterator.Next() {
   359  		if stop := fn(iterator.Key(), iterator.Value()); stop {
   360  			break
   361  		}
   362  	}
   363  }
   364  
   365  // GetAccountStorage return state storage associated with an account
   366  func (k Keeper) GetAccountStorage(ctx sdk.Context, address ethcmn.Address) (types.Storage, error) {
   367  	storage := types.Storage{}
   368  	csdb := types.CreateEmptyCommitStateDB(k.GenerateCSDBParams(), ctx)
   369  	err := csdb.ForEachStorage(address, func(key, value ethcmn.Hash) bool {
   370  		storage = append(storage, types.NewState(key, value))
   371  		return false
   372  	})
   373  	if err != nil {
   374  		return types.Storage{}, err
   375  	}
   376  
   377  	return storage, nil
   378  }
   379  
   380  // getChainConfig get raw chain config and unmarshal it
   381  func (k *Keeper) getChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
   382  	// if keeper has cached the chain config, return immediately
   383  
   384  	var store types.StoreProxy
   385  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   386  		store = k.Ada.NewStore(k.paramSpace.CustomKVStore(ctx), types.KeyPrefixChainConfig)
   387  	} else {
   388  		store = k.Ada.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixChainConfig)
   389  	}
   390  
   391  	// get from an empty key that's already prefixed by KeyPrefixChainConfig
   392  	bz := store.Get([]byte{})
   393  	if len(bz) == 0 {
   394  		return types.ChainConfig{}, false
   395  	}
   396  
   397  	var config types.ChainConfig
   398  	// first 4 bytes are type prefix
   399  	// bz len must > 4; otherwise, MustUnmarshalBinaryBare will panic
   400  	if err := config.UnmarshalFromAmino(k.cdc, bz[4:]); err != nil {
   401  		k.cdc.MustUnmarshalBinaryBare(bz, &config)
   402  	}
   403  
   404  	return config, true
   405  }
   406  
   407  // GetChainConfig gets chain config, the result if from cached result, or
   408  // it gains chain config and gas costs from getChainConfig, then
   409  // cache the chain config and gas costs.
   410  func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) {
   411  	// if keeper has cached the chain config, return immediately, and increase gas costs.
   412  	if k.cci.cc != nil {
   413  		ctx.GasMeter().ConsumeGas(k.cci.gasReduced, "cached chain config recover")
   414  		return *k.cci.cc, true
   415  	}
   416  
   417  	gasStart := ctx.GasMeter().GasConsumed()
   418  	chainConfig, found := k.getChainConfig(ctx)
   419  	gasStop := ctx.GasMeter().GasConsumed()
   420  
   421  	// only cache chain config result when we found it, or try to found again.
   422  	if found {
   423  		k.cci.cc = &chainConfig
   424  		k.cci.gasReduced = gasStop - gasStart
   425  	}
   426  
   427  	return chainConfig, found
   428  }
   429  
   430  // SetChainConfig sets the mapping from block consensus hash to block height
   431  func (k *Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) {
   432  	var store types.StoreProxy
   433  	if tmtypes.HigherThanMars(ctx.BlockHeight()) {
   434  		store = k.Ada.NewStore(k.paramSpace.CustomKVStore(ctx), types.KeyPrefixChainConfig)
   435  	} else {
   436  		store = k.Ada.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixChainConfig)
   437  	}
   438  
   439  	bz := k.cdc.MustMarshalBinaryBare(config)
   440  	// get to an empty key that's already prefixed by KeyPrefixChainConfig
   441  	store.Set([]byte{}, bz)
   442  
   443  	// invalid the chainConfig
   444  	k.cci.cc = nil
   445  }
   446  
   447  // SetGovKeeper sets keeper of gov
   448  func (k *Keeper) SetGovKeeper(gk GovKeeper) {
   449  	k.govKeeper = gk
   450  }
   451  
   452  var commitStateDBPool = &sync.Pool{
   453  	New: func() interface{} {
   454  		return &types.CommitStateDB{GuFactor: types.DefaultGuFactor}
   455  	},
   456  }
   457  
   458  // checks whether the address is blocked
   459  func (k *Keeper) IsAddressBlocked(ctx sdk.Context, addr sdk.AccAddress) bool {
   460  	csdb := commitStateDBPool.Get().(*types.CommitStateDB)
   461  	defer commitStateDBPool.Put(csdb)
   462  	types.ResetCommitStateDB(csdb, k.GenerateCSDBParams(), &ctx)
   463  
   464  	// csdb := types.CreateEmptyCommitStateDB(k.GenerateCSDBParams(), ctx)
   465  	return k.GetParams(ctx).EnableContractBlockedList && csdb.IsContractInBlockedList(addr.Bytes())
   466  }
   467  
   468  func (k *Keeper) IsContractInBlockedList(ctx sdk.Context, addr sdk.AccAddress) bool {
   469  	csdb := types.CreateEmptyCommitStateDB(k.GenerateCSDBParams(), ctx)
   470  	return csdb.IsContractInBlockedList(addr.Bytes())
   471  }
   472  
   473  func (k *Keeper) SetObserverKeeper(infuraKeeper watcher.InfuraKeeper) {
   474  	k.Watcher.InfuraKeeper = infuraKeeper
   475  }
   476  
   477  // SetHooks sets the hooks for the EVM module
   478  // It should be called only once during initialization, it panics if called more than once.
   479  func (k *Keeper) SetHooks(hooks types.EvmHooks) *Keeper {
   480  	if k.hooks != nil {
   481  		panic("cannot set evm hooks twice")
   482  	}
   483  	k.hooks = hooks
   484  
   485  	return k
   486  }
   487  
   488  // ResetHooks resets the hooks for the EVM module
   489  func (k *Keeper) ResetHooks() *Keeper {
   490  	k.hooks = nil
   491  
   492  	return k
   493  }
   494  
   495  // GetHooks gets the hooks for the EVM module
   496  func (k *Keeper) GetHooks() types.EvmHooks {
   497  	return k.hooks
   498  }
   499  
   500  // CallEvmHooks delegate the call to the hooks. If no hook has been registered, this function returns with a `nil` error
   501  func (k *Keeper) CallEvmHooks(ctx sdk.Context, st *types.StateTransition, receipt *ethtypes.Receipt) error {
   502  	if k.hooks == nil {
   503  		return nil
   504  	}
   505  	return k.hooks.PostTxProcessing(ctx, st, receipt)
   506  }
   507  
   508  // Add latest block height and hash to lru cache
   509  func (k *Keeper) AddHeightHashToCache(height int64, hash string) {
   510  	k.heightCache.Add(hash, height)
   511  	k.hashCache.Add(height, hash)
   512  }