From 9cb7bd75e5becdb1d825b1da8bf05f223e32d534 Mon Sep 17 00:00:00 2001 From: simlecode <69969590+simlecode@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:55:10 +0800 Subject: [PATCH] feat: ETH RPC: Use Block Cache for EthGetBlockByHash --- app/submodule/eth/eth_api.go | 54 ++++++++++++++++++++++++++++++++++-- pkg/config/config.go | 8 ++++++ 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/app/submodule/eth/eth_api.go b/app/submodule/eth/eth_api.go index 9f6fcea255..9a8442158d 100644 --- a/app/submodule/eth/eth_api.go +++ b/app/submodule/eth/eth_api.go @@ -31,6 +31,7 @@ import ( types2 "github.com/filecoin-project/venus/venus-shared/actors/types" v1 "github.com/filecoin-project/venus/venus-shared/api/chain/v1" "github.com/filecoin-project/venus/venus-shared/types" + "github.com/hashicorp/golang-lru/arc/v2" "github.com/ipfs/go-cid" logging "github.com/ipfs/go-log/v2" cbg "github.com/whyrusleeping/cbor-gen" @@ -76,6 +77,20 @@ func newEthAPI(em *EthSubModule) (*ethAPI, error) { } } + cfg := em.cfg.FevmConfig + if cfg.EthBlkCacheSize > 0 { + var err error + a.EthBlkCache, err = arc.NewARC[cid.Cid, *types.EthBlock](cfg.EthBlkCacheSize) + if err != nil { + return nil, fmt.Errorf("failed to create block cache: %w", err) + } + + a.EthBlkTxCache, err = arc.NewARC[cid.Cid, *types.EthBlock](cfg.EthBlkCacheSize) + if err != nil { + return nil, fmt.Errorf("failed to create block transaction cache: %w", err) + } + } + return a, nil } @@ -84,6 +99,9 @@ type ethAPI struct { chain v1.IChain mpool v1.IMessagePool ethTxHashManager *ethTxHashManager + + EthBlkCache *arc.ARCCache[cid.Cid, *types.EthBlock] // caches blocks by their CID but blocks only have the transaction hashes + EthBlkTxCache *arc.ARCCache[cid.Cid, *types.EthBlock] // caches blocks along with full transaction payload by their CID } func (a *ethAPI) start(ctx context.Context) error { @@ -178,11 +196,41 @@ func (a *ethAPI) EthGetBlockTransactionCountByHash(ctx context.Context, blkHash } func (a *ethAPI) EthGetBlockByHash(ctx context.Context, blkHash types.EthHash, fullTxInfo bool) (types.EthBlock, error) { - ts, err := a.em.chainModule.ChainReader.GetTipSetByCid(ctx, blkHash.ToCid()) + cache := a.EthBlkCache + if fullTxInfo { + cache = a.EthBlkTxCache + } + + // Attempt to retrieve the Ethereum block from cache + cid := blkHash.ToCid() + if cache != nil { + if ethBlock, found := cache.Get(cid); found { + if ethBlock != nil { + return *ethBlock, nil + } + // Log and remove the nil entry from cache + log.Errorw("nil value in eth block cache", "hash", blkHash.String()) + cache.Remove(cid) + } + } + + // Fetch the tipset using the block hash + ts, err := a.em.chainModule.ChainReader.GetTipSetByCid(ctx, cid) if err != nil { - return types.EthBlock{}, fmt.Errorf("error loading tipset %s: %w", ts, err) + return types.EthBlock{}, fmt.Errorf("failed to load tipset by CID %s: %w", cid, err) + } + + // Generate an Ethereum block from the Filecoin tipset + blk, err := newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.em.chainModule.MessageStore, a.em.chainModule.Stmgr) + if err != nil { + return types.EthBlock{}, fmt.Errorf("failed to create Ethereum block from Filecoin tipset: %w", err) + } + + // Add the newly created block to the cache and return + if cache != nil { + cache.Add(cid, &blk) } - return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.em.chainModule.MessageStore, a.em.chainModule.Stmgr) + return blk, nil } func (a *ethAPI) parseBlkParam(ctx context.Context, blkParam string, strict bool) (tipset *types.TipSet, err error) { diff --git a/pkg/config/config.go b/pkg/config/config.go index 876789f2eb..1d7c4e2545 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -487,6 +487,13 @@ type FevmConfig struct { // Set to 0 to keep all mappings EthTxHashMappingLifetimeDays int `json:"ethTxHashMappingLifetimeDays"` + // EthBlkCacheSize specifies the size of the cache used for caching Ethereum blocks. + // This cache enhances the performance of the eth_getBlockByHash RPC call by minimizing the need to access chain state for + // recently requested blocks that are already cached. + // The default size of the cache is 500 blocks. + // Note: Setting this value to 0 disables the cache. + EthBlkCacheSize int + Event EventConfig `json:"event"` } @@ -502,6 +509,7 @@ func newFevmConfig() *FevmConfig { return &FevmConfig{ EnableEthRPC: false, EthTxHashMappingLifetimeDays: 0, + EthBlkCacheSize: 500, Event: EventConfig{ DisableRealTimeFilterAPI: false, DisableHistoricFilterAPI: false,