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 }