diff --git a/indexer/indexer.go b/indexer/indexer.go index 9813f0f..2c891c2 100644 --- a/indexer/indexer.go +++ b/indexer/indexer.go @@ -29,8 +29,8 @@ type EVMIndexer interface { // tx TxByHash(ctx context.Context, hash common.Hash) (*rpctypes.RPCTransaction, error) - TxByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (*rpctypes.RPCTransaction, error) IterateBlockTxs(ctx context.Context, blockHeight uint64, cb func(tx *rpctypes.RPCTransaction) (bool, error)) error + TxHashByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (common.Hash, error) // tx receipt TxReceiptByHash(ctx context.Context, hash common.Hash) (*coretypes.Receipt, error) @@ -38,7 +38,6 @@ type EVMIndexer interface { // block BlockHashToNumber(ctx context.Context, hash common.Hash) (uint64, error) - BlockHeaderByHash(ctx context.Context, hash common.Hash) (*coretypes.Header, error) BlockHeaderByNumber(ctx context.Context, number uint64) (*coretypes.Header, error) // cosmos tx hash diff --git a/indexer/reader.go b/indexer/reader.go index e7f7793..a95e04c 100644 --- a/indexer/reader.go +++ b/indexer/reader.go @@ -9,16 +9,6 @@ import ( rpctypes "github.com/initia-labs/minievm/jsonrpc/types" ) -// BlockHeaderByHash implements EVMIndexer. -func (e *EVMIndexerImpl) BlockHeaderByHash(ctx context.Context, hash common.Hash) (*coretypes.Header, error) { - blockNumber, err := e.BlockHashToNumberMap.Get(ctx, hash.Bytes()) - if err != nil { - return nil, err - } - - return e.BlockHeaderByNumber(ctx, blockNumber) -} - // BlockHeaderByNumber implements EVMIndexer. func (e *EVMIndexerImpl) BlockHeaderByNumber(ctx context.Context, blockNumber uint64) (*coretypes.Header, error) { blockHeader, err := e.BlockHeaderMap.Get(ctx, blockNumber) @@ -29,15 +19,14 @@ func (e *EVMIndexerImpl) BlockHeaderByNumber(ctx context.Context, blockNumber ui return &blockHeader, nil } -// TxByBlockAndIndex implements EVMIndexer. -func (e *EVMIndexerImpl) TxByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (*rpctypes.RPCTransaction, error) { +// TxHashByBlockAndIndex implements EVMIndexer. +func (e *EVMIndexerImpl) TxHashByBlockAndIndex(ctx context.Context, blockHeight uint64, index uint64) (common.Hash, error) { txHashBz, err := e.BlockAndIndexToTxHashMap.Get(ctx, collections.Join(blockHeight, index)) if err != nil { - return nil, err + return common.Hash{}, err } - txHash := common.BytesToHash(txHashBz) - return e.TxByHash(ctx, txHash) + return common.BytesToHash(txHashBz), nil } // TxByHash implements EVMIndexer. diff --git a/jsonrpc/backend/backend.go b/jsonrpc/backend/backend.go index c5933dd..f9c7cff 100644 --- a/jsonrpc/backend/backend.go +++ b/jsonrpc/backend/backend.go @@ -5,6 +5,9 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/lru" + coretypes "github.com/ethereum/go-ethereum/core/types" lrucache "github.com/hashicorp/golang-lru/v2" "cosmossdk.io/log" @@ -13,6 +16,7 @@ import ( "github.com/initia-labs/minievm/app" "github.com/initia-labs/minievm/jsonrpc/config" + rpctypes "github.com/initia-labs/minievm/jsonrpc/types" ) type JSONRPCBackend struct { @@ -20,7 +24,16 @@ type JSONRPCBackend struct { logger log.Logger queuedTxs *lrucache.Cache[string, []byte] - historyCache *lrucache.Cache[cacheKey, processedFees] + historyCache *lru.Cache[cacheKey, processedFees] + + headerCache *lru.Cache[uint64, *coretypes.Header] + blockTxsCache *lru.Cache[uint64, []*rpctypes.RPCTransaction] + blockRceiptsCache *lru.Cache[uint64, []*coretypes.Receipt] + blockHashCache *lru.Cache[common.Hash, uint64] + + txLookupCache *lru.Cache[common.Hash, *rpctypes.RPCTransaction] + receiptsCache *lru.Cache[common.Hash, *coretypes.Receipt] + logsCache *lru.Cache[uint64, []*coretypes.Log] mut sync.Mutex // mutex for accMuts accMuts map[string]*AccMut @@ -32,6 +45,13 @@ type JSONRPCBackend struct { cfg config.JSONRPCConfig } +const ( + feeHistoryCacheSize = 2048 + blockCacheLimit = 256 + receiptsCacheLimit = 32 + txLookupCacheLimit = 1024 +) + // NewJSONRPCBackend creates a new JSONRPCBackend instance func NewJSONRPCBackend( app *app.MinitiaApp, @@ -40,11 +60,14 @@ func NewJSONRPCBackend( clientCtx client.Context, cfg config.JSONRPCConfig, ) (*JSONRPCBackend, error) { - queuedTxs, err := lrucache.New[string, []byte](cfg.QueuedTransactionCap) - if err != nil { - return nil, err + if cfg.QueuedTransactionCap == 0 { + cfg.QueuedTransactionCap = config.DefaultQueuedTransactionCap + } + if cfg.LogCacheSize == 0 { + cfg.LogCacheSize = config.DefaultLogCacheSize } - historyCache, err := lrucache.New[cacheKey, processedFees](2048) + + queuedTxs, err := lrucache.New[string, []byte](cfg.QueuedTransactionCap) if err != nil { return nil, err } @@ -55,7 +78,16 @@ func NewJSONRPCBackend( logger: logger, queuedTxs: queuedTxs, - historyCache: historyCache, + historyCache: lru.NewCache[cacheKey, processedFees](feeHistoryCacheSize), + + headerCache: lru.NewCache[uint64, *coretypes.Header](blockCacheLimit), + blockTxsCache: lru.NewCache[uint64, []*rpctypes.RPCTransaction](blockCacheLimit), + blockRceiptsCache: lru.NewCache[uint64, []*coretypes.Receipt](blockCacheLimit), + blockHashCache: lru.NewCache[common.Hash, uint64](blockCacheLimit), + + txLookupCache: lru.NewCache[common.Hash, *rpctypes.RPCTransaction](txLookupCacheLimit), + receiptsCache: lru.NewCache[common.Hash, *coretypes.Receipt](receiptsCacheLimit), + logsCache: lru.NewCache[uint64, []*coretypes.Log](cfg.LogCacheSize), accMuts: make(map[string]*AccMut), diff --git a/jsonrpc/backend/block.go b/jsonrpc/backend/block.go index c792df9..46bc3b7 100644 --- a/jsonrpc/backend/block.go +++ b/jsonrpc/backend/block.go @@ -23,12 +23,7 @@ func (b *JSONRPCBackend) BlockNumber() (hexutil.Uint64, error) { func (b *JSONRPCBackend) resolveBlockNrOrHash(blockNrOrHash rpc.BlockNumberOrHash) (uint64, error) { if blockHash, ok := blockNrOrHash.Hash(); ok { - queryCtx, err := b.getQueryCtx() - if err != nil { - return 0, err - } - - return b.app.EVMIndexer().BlockHashToNumber(queryCtx, blockHash) + return b.blockNumberByHash(blockHash) } else if blockNumber, ok := blockNrOrHash.Number(); !ok || blockNumber < 0 { num, err := b.BlockNumber() if err != nil { @@ -63,6 +58,9 @@ func (b *JSONRPCBackend) GetHeaderByNumber(ethBlockNum rpc.BlockNumber) (*corety if err != nil { return nil, err } + if header, ok := b.headerCache.Get(blockNumber); ok { + return header, nil + } queryCtx, err := b.getQueryCtx() if err != nil { @@ -77,24 +75,21 @@ func (b *JSONRPCBackend) GetHeaderByNumber(ethBlockNum rpc.BlockNumber) (*corety return nil, err } + // cache the header + _ = b.headerCache.Add(blockNumber, header) return header, nil } func (b *JSONRPCBackend) GetHeaderByHash(hash common.Hash) (*coretypes.Header, error) { - queryCtx, err := b.getQueryCtx() - if err != nil { - return nil, err - } - - header, err := b.app.EVMIndexer().BlockHeaderByHash(queryCtx, hash) + blockNumber, err := b.resolveBlockNrOrHash(rpc.BlockNumberOrHash{BlockHash: &hash}) if err != nil && errors.Is(err, collections.ErrNotFound) { return nil, nil } else if err != nil { - b.logger.Error("failed to get block header by hash", "err", err) + b.logger.Error("failed to get block number by hash", "err", err) return nil, err } - return header, nil + return b.GetHeaderByNumber(rpc.BlockNumber(blockNumber)) } func (b *JSONRPCBackend) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { @@ -103,17 +98,13 @@ func (b *JSONRPCBackend) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bo return nil, err } - queryCtx, err := b.getQueryCtx() + header, err := b.GetHeaderByNumber(ethBlockNum) if err != nil { + b.logger.Error("failed to get block header by number", "err", err) return nil, err } - - header, err := b.app.EVMIndexer().BlockHeaderByNumber(queryCtx, blockNumber) - if err != nil && errors.Is(err, collections.ErrNotFound) { + if header == nil { return nil, nil - } else if err != nil { - b.logger.Error("failed to get block header by number", "err", err) - return nil, err } txs, err := b.getBlockTransactions(blockNumber) @@ -125,17 +116,33 @@ func (b *JSONRPCBackend) GetBlockByNumber(ethBlockNum rpc.BlockNumber, fullTx bo } func (b *JSONRPCBackend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) { + blockNumber, err := b.resolveBlockNrOrHash(rpc.BlockNumberOrHash{BlockHash: &hash}) + if err != nil && errors.Is(err, collections.ErrNotFound) { + return nil, nil + } else if err != nil { + b.logger.Error("failed to get block number by hash", "err", err) + return nil, err + } + return b.GetBlockByNumber(rpc.BlockNumber(blockNumber), fullTx) +} + +func (b *JSONRPCBackend) blockNumberByHash(hash common.Hash) (uint64, error) { + if number, ok := b.blockHashCache.Get(hash); ok { + return number, nil + } + queryCtx, err := b.getQueryCtx() if err != nil { - return nil, err + return 0, err } - blockNumber, err := b.app.EVMIndexer().BlockHashToNumber(queryCtx, hash) + number, err := b.app.EVMIndexer().BlockHashToNumber(queryCtx, hash) if err != nil { - return nil, err + return 0, err } - return b.GetBlockByNumber(rpc.BlockNumber(blockNumber), fullTx) + _ = b.blockHashCache.Add(hash, number) + return number, nil } func formatBlock(header *coretypes.Header, txs []*rpctypes.RPCTransaction, fullTx bool) map[string]interface{} { diff --git a/jsonrpc/backend/feehistory.go b/jsonrpc/backend/feehistory.go index ee25fff..86868e4 100644 --- a/jsonrpc/backend/feehistory.go +++ b/jsonrpc/backend/feehistory.go @@ -128,7 +128,7 @@ func (b *JSONRPCBackend) FeeHistory(blocks uint64, unresolvedLastBlock rpc.Block } else { fees.header, fees.err = b.GetHeaderByNumber(rpc.BlockNumber(blockNumber)) if len(rewardPercentiles) != 0 && fees.err == nil { - fees.receipts, fees.err = b.getBLockReceipts(blockNumber) + fees.receipts, fees.err = b.getBlockReceipts(blockNumber) if fees.err == nil { var txs []*rpctypes.RPCTransaction txs, fees.err = b.getBlockTransactions(blockNumber) diff --git a/jsonrpc/backend/filters.go b/jsonrpc/backend/filters.go index 98e2d53..cd3b178 100644 --- a/jsonrpc/backend/filters.go +++ b/jsonrpc/backend/filters.go @@ -2,38 +2,37 @@ package backend import ( coretypes "github.com/ethereum/go-ethereum/core/types" - - rpctypes "github.com/initia-labs/minievm/jsonrpc/types" + "github.com/ethereum/go-ethereum/rpc" ) // GetLogsByHeight returns all the logs from all the ethereum transactions in a block. func (b *JSONRPCBackend) GetLogsByHeight(height uint64) ([]*coretypes.Log, error) { - blockLogs := []*coretypes.Log{} + if blockLogs, ok := b.logsCache.Get(height); ok { + return blockLogs, nil + } - queryCtx, err := b.getQueryCtx() + blockHeader, err := b.GetHeaderByNumber(rpc.BlockNumber(height)) if err != nil { return nil, err + } else if blockHeader == nil { + return nil, nil } - blockHeader, err := b.app.EVMIndexer().BlockHeaderByNumber(queryCtx, height) + txs, err := b.getBlockTransactions(height) if err != nil { return nil, err } - - txs := []*rpctypes.RPCTransaction{} - err = b.app.EVMIndexer().IterateBlockTxs(queryCtx, height, func(tx *rpctypes.RPCTransaction) (bool, error) { - txs = append(txs, tx) - return false, nil - }) + receipts, err := b.getBlockReceipts(height) if err != nil { return nil, err } + if len(txs) != len(receipts) { + return nil, NewInternalError("number of transactions and receipts do not match") + } - for _, tx := range txs { - receipt, err := b.app.EVMIndexer().TxReceiptByHash(queryCtx, tx.Hash) - if err != nil { - return nil, err - } + blockLogs := []*coretypes.Log{} + for i, tx := range txs { + receipt := receipts[i] logs := receipt.Logs for idx, log := range logs { log.BlockHash = blockHeader.Hash() @@ -45,5 +44,7 @@ func (b *JSONRPCBackend) GetLogsByHeight(height uint64) ([]*coretypes.Log, error blockLogs = append(blockLogs, logs...) } + // cache the logs + _ = b.logsCache.Add(height, blockLogs) return blockLogs, nil } diff --git a/jsonrpc/backend/tx.go b/jsonrpc/backend/tx.go index e7d4bcc..516dd88 100644 --- a/jsonrpc/backend/tx.go +++ b/jsonrpc/backend/tx.go @@ -152,12 +152,7 @@ func (b *JSONRPCBackend) GetTransactionCount(address common.Address, blockNrOrHa var blockNumber rpc.BlockNumber if blockHash, ok := blockNrOrHash.Hash(); ok { - queryCtx, err := b.getQueryCtx() - if err != nil { - return nil, err - } - - blockNumberU64, err := b.app.EVMIndexer().BlockHashToNumber(queryCtx, blockHash) + blockNumberU64, err := b.blockNumberByHash(blockHash) if err != nil && errors.Is(err, collections.ErrNotFound) { return nil, nil } else if err != nil { @@ -196,25 +191,18 @@ func (b *JSONRPCBackend) GetTransactionCount(address common.Address, blockNrOrHa // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (b *JSONRPCBackend) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { - queryCtx, err := b.getQueryCtx() + receipt, err := b.getReceipt(hash) if err != nil { return nil, err - } - - tx, err := b.app.EVMIndexer().TxByHash(queryCtx, hash) - if err != nil && errors.Is(err, collections.ErrNotFound) { + } else if receipt == nil { return nil, nil - } else if err != nil { - b.logger.Error("failed to get transaction by hash", "err", err) - return nil, NewTxIndexingError() } - receipt, err := b.app.EVMIndexer().TxReceiptByHash(queryCtx, hash) - if err != nil && errors.Is(err, collections.ErrNotFound) { + tx, err := b.getTransaction(hash) + if err != nil { + return nil, err + } else if tx == nil { return nil, nil - } else if err != nil { - b.logger.Error("failed to get transaction receipt by hash", "err", err) - return nil, NewTxIndexingError() } return marshalReceipt(receipt, tx), nil @@ -222,12 +210,7 @@ func (b *JSONRPCBackend) GetTransactionReceipt(hash common.Hash) (map[string]int // GetTransactionByBlockHashAndIndex returns the transaction at the given block hash and index. func (b *JSONRPCBackend) GetTransactionByBlockHashAndIndex(hash common.Hash, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) { - queryCtx, err := b.getQueryCtx() - if err != nil { - return nil, err - } - - number, err := b.app.EVMIndexer().BlockHashToNumber(queryCtx, hash) + blockNumber, err := b.resolveBlockNrOrHash(rpc.BlockNumberOrHash{BlockHash: &hash}) if err != nil && errors.Is(err, collections.ErrNotFound) { return nil, nil } else if err != nil { @@ -235,26 +218,25 @@ func (b *JSONRPCBackend) GetTransactionByBlockHashAndIndex(hash common.Hash, idx return nil, err } - rpcTx, err := b.app.EVMIndexer().TxByBlockAndIndex(queryCtx, number, uint64(idx)) - if err != nil && errors.Is(err, collections.ErrNotFound) { - return nil, nil - } else if err != nil { - b.logger.Error("failed to get transaction by block and index", "err", err) - return nil, err - } - - return rpcTx, nil + return b.GetTransactionByBlockNumberAndIndex(rpc.BlockNumber(blockNumber), idx) } // GetTransactionByBlockNumberAndIndex returns the transaction at the given block number and index. func (b *JSONRPCBackend) GetTransactionByBlockNumberAndIndex(blockNum rpc.BlockNumber, idx hexutil.Uint) (*rpctypes.RPCTransaction, error) { + blockNumber, err := b.resolveBlockNr(blockNum) + if err != nil { + return nil, err + } + if txs, ok := b.blockTxsCache.Get(blockNumber); ok { + return txs[idx], nil + } + queryCtx, err := b.getQueryCtx() if err != nil { return nil, err } - number := uint64(blockNum.Int64()) - rpcTx, err := b.app.EVMIndexer().TxByBlockAndIndex(queryCtx, number, uint64(idx)) + txhash, err := b.app.EVMIndexer().TxHashByBlockAndIndex(queryCtx, blockNumber, uint64(idx)) if err != nil && errors.Is(err, collections.ErrNotFound) { return nil, nil } else if err != nil { @@ -262,7 +244,7 @@ func (b *JSONRPCBackend) GetTransactionByBlockNumberAndIndex(blockNum rpc.BlockN return nil, err } - return rpcTx, nil + return b.getTransaction(txhash) } // GetBlockTransactionCountByHash returns the number of transactions in a block from a block matching the given block hash. @@ -293,6 +275,10 @@ func (b *JSONRPCBackend) GetBlockTransactionCountByNumber(blockNum rpc.BlockNumb // GetRawTransactionByHash returns the bytes of the transaction for the given hash. func (b *JSONRPCBackend) GetRawTransactionByHash(hash common.Hash) (hexutil.Bytes, error) { + if tx, ok := b.txLookupCache.Get(hash); ok { + return tx.ToTransaction().MarshalBinary() + } + queryCtx, err := b.getQueryCtx() if err != nil { return nil, err @@ -308,6 +294,7 @@ func (b *JSONRPCBackend) GetRawTransactionByHash(hash common.Hash) (hexutil.Byte return nil, nil } + _ = b.txLookupCache.Add(hash, rpcTx) return rpcTx.ToTransaction().MarshalBinary() } @@ -367,68 +354,127 @@ func (b *JSONRPCBackend) PendingTransactions() ([]*rpctypes.RPCTransaction, erro return result, nil } -func (b *JSONRPCBackend) getBlockTransactions(blockNumber uint64) ([]*rpctypes.RPCTransaction, error) { - queryCtx, err := b.getQueryCtx() +func (b *JSONRPCBackend) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + blockNumber, err := b.resolveBlockNrOrHash(blockNrOrHash) + if err != nil && errors.Is(err, collections.ErrNotFound) { + return nil, nil + } else if err != nil { + b.logger.Error("failed to get block number by hash", "err", err) + return nil, err + } + + txs, err := b.getBlockTransactions(blockNumber) if err != nil { return nil, err } - txs := []*rpctypes.RPCTransaction{} - err = b.app.EVMIndexer().IterateBlockTxs(queryCtx, blockNumber, func(tx *rpctypes.RPCTransaction) (bool, error) { - txs = append(txs, tx) - return false, nil - }) + receipts, err := b.getBlockReceipts(blockNumber) if err != nil { return nil, err } - return txs, nil + if len(txs) != len(receipts) { + return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts)) + } + + result := make([]map[string]interface{}, len(receipts)) + for i, receipt := range receipts { + result[i] = marshalReceipt(receipt, txs[i]) + } + + return result, nil } -func (b *JSONRPCBackend) getBLockReceipts(blockNumber uint64) ([]*coretypes.Receipt, error) { +func (b *JSONRPCBackend) getTransaction(hash common.Hash) (*rpctypes.RPCTransaction, error) { + if tx, ok := b.txLookupCache.Get(hash); ok { + return tx, nil + } + queryCtx, err := b.getQueryCtx() if err != nil { return nil, err } - recepts := []*coretypes.Receipt{} - err = b.app.EVMIndexer().IterateBlockTxRecepts(queryCtx, blockNumber, func(recept *coretypes.Receipt) (bool, error) { - recepts = append(recepts, recept) - return false, nil - }) - if err != nil { - return nil, err + tx, err := b.app.EVMIndexer().TxByHash(queryCtx, hash) + if err != nil && errors.Is(err, collections.ErrNotFound) { + return nil, nil + } else if err != nil { + b.logger.Error("failed to get transaction by hash", "err", err) + return nil, NewTxIndexingError() } - return recepts, nil + _ = b.txLookupCache.Add(hash, tx) + return tx, nil } -func (b *JSONRPCBackend) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { - blockNumber, err := b.resolveBlockNrOrHash(blockNrOrHash) +func (b *JSONRPCBackend) getReceipt(hash common.Hash) (*coretypes.Receipt, error) { + if receipt, ok := b.receiptsCache.Get(hash); ok { + return receipt, nil + } + + queryCtx, err := b.getQueryCtx() if err != nil { return nil, err } - txs, err := b.getBlockTransactions(blockNumber) + receipt, err := b.app.EVMIndexer().TxReceiptByHash(queryCtx, hash) + if err != nil && errors.Is(err, collections.ErrNotFound) { + return nil, nil + } else if err != nil { + b.logger.Error("failed to get transaction receipt by hash", "err", err) + return nil, NewTxIndexingError() + } + + _ = b.receiptsCache.Add(hash, receipt) + return receipt, nil +} + +func (b *JSONRPCBackend) getBlockTransactions(blockNumber uint64) ([]*rpctypes.RPCTransaction, error) { + if txs, ok := b.blockTxsCache.Get(blockNumber); ok { + return txs, nil + } + + queryCtx, err := b.getQueryCtx() if err != nil { return nil, err } - receipts, err := b.getBLockReceipts(blockNumber) + txs := []*rpctypes.RPCTransaction{} + err = b.app.EVMIndexer().IterateBlockTxs(queryCtx, blockNumber, func(tx *rpctypes.RPCTransaction) (bool, error) { + txs = append(txs, tx) + return false, nil + }) if err != nil { return nil, err } - if len(txs) != len(receipts) { - return nil, fmt.Errorf("receipts length mismatch: %d vs %d", len(txs), len(receipts)) + // cache the transactions + _ = b.blockTxsCache.Add(blockNumber, txs) + return txs, nil +} + +func (b *JSONRPCBackend) getBlockReceipts(blockNumber uint64) ([]*coretypes.Receipt, error) { + if recepts, ok := b.blockRceiptsCache.Get(blockNumber); ok { + return recepts, nil } - result := make([]map[string]interface{}, len(receipts)) - for i, receipt := range receipts { - result[i] = marshalReceipt(receipt, txs[i]) + queryCtx, err := b.getQueryCtx() + if err != nil { + return nil, err } - return result, nil + recepts := []*coretypes.Receipt{} + err = b.app.EVMIndexer().IterateBlockTxRecepts(queryCtx, blockNumber, func(recept *coretypes.Receipt) (bool, error) { + recepts = append(recepts, recept) + return false, nil + }) + if err != nil { + return nil, err + } + + // cache the receipts + _ = b.blockRceiptsCache.Add(blockNumber, recepts) + return recepts, nil } // marshalReceipt marshals a transaction receipt into a JSON object. diff --git a/jsonrpc/config/config.go b/jsonrpc/config/config.go index d363439..a4f00f2 100644 --- a/jsonrpc/config/config.go +++ b/jsonrpc/config/config.go @@ -28,8 +28,6 @@ const ( DefaultLogsCap int32 = 100 // DefaultFilterCap is the default global cap for total number of filters that can be created. DefaultFilterCap int32 = 200 - // DefaultBlockRangeCap is the default max block range allowed for `eth_getLogs` query. - DefaultBlockRangeCap int32 = 100 // DefaultAddress defines the default HTTP server to listen on. DefaultAddress = "127.0.0.1:8545" // DefaultAddressWS defines the default WebSocket server address to bind to. @@ -46,6 +44,8 @@ const ( DefaultFeeHistoryMaxBlocks = 1024 // DefaultFilterTimeout is the default filter timeout, how long filters stay active. DefaultFilterTimeout = 5 * time.Minute + // DefaultLogCacheSize is the maximum number of cached blocks. + DefaultLogCacheSize = 32 ) var ( @@ -71,6 +71,7 @@ const ( flagJSONRPCFeeHistoryMaxHeaders = "json-rpc.fee-history-max-headers" flagJSONRPCFeeHistoryMaxBlocks = "json-rpc.fee-history-max-blocks" flagJSONRPCFilterTimeout = "json-rpc.filter-timeout" + flagJSONRPCLogCacheSize = "json-rpc.log-cache-size" ) // JSONRPCConfig defines configuration for the EVM RPC server. @@ -106,6 +107,8 @@ type JSONRPCConfig struct { FeeHistoryMaxBlocks int `mapstructure:"fee-history-max-blocks"` // FilterTimeout is a duration how long filters stay active (default: 5min) FilterTimeout time.Duration `mapstructure:"filter-timeout"` + // LogCacheSize is the maximum number of cached blocks. + LogCacheSize int `mapstructure:"log-cache-size"` } // DefaultJSONRPCConfig returns a default configuration for the EVM RPC server. @@ -132,6 +135,8 @@ func DefaultJSONRPCConfig() JSONRPCConfig { FeeHistoryMaxBlocks: DefaultFeeHistoryMaxBlocks, FilterTimeout: DefaultFilterTimeout, + + LogCacheSize: DefaultLogCacheSize, } } @@ -152,6 +157,7 @@ func AddConfigFlags(startCmd *cobra.Command) { startCmd.Flags().Int(flagJSONRPCFeeHistoryMaxHeaders, DefaultFeeHistoryMaxHeaders, "Maximum number of headers used to lookup the fee history") startCmd.Flags().Int(flagJSONRPCFeeHistoryMaxBlocks, DefaultFeeHistoryMaxBlocks, "Maximum number of blocks used to lookup the fee history") startCmd.Flags().Duration(flagJSONRPCFilterTimeout, DefaultFilterTimeout, "Duration how long filters stay active") + startCmd.Flags().Int(flagJSONRPCLogCacheSize, DefaultLogCacheSize, "Maximum number of cached blocks for the log filter") } // GetConfig load config values from the app options @@ -172,6 +178,7 @@ func GetConfig(appOpts servertypes.AppOptions) JSONRPCConfig { FeeHistoryMaxHeaders: cast.ToInt(appOpts.Get(flagJSONRPCFeeHistoryMaxHeaders)), FeeHistoryMaxBlocks: cast.ToInt(appOpts.Get(flagJSONRPCFeeHistoryMaxBlocks)), FilterTimeout: cast.ToDuration(appOpts.Get(flagJSONRPCFilterTimeout)), + LogCacheSize: cast.ToInt(appOpts.Get(flagJSONRPCLogCacheSize)), } } @@ -230,4 +237,7 @@ fee-history-max-blocks = {{ .JSONRPCConfig.FeeHistoryMaxBlocks }} # FilterTimeout is a duration how long filters stay active (default: 5min) filter-timeout = "{{ .JSONRPCConfig.FilterTimeout }}" + +# LogCacheSize is the maximum number of cached blocks for the log filter. +log-cache-size = {{ .JSONRPCConfig.LogCacheSize }} ` diff --git a/jsonrpc/namespaces/eth/filters/filter.go b/jsonrpc/namespaces/eth/filters/filter.go index 0db7e6c..5168f7f 100644 --- a/jsonrpc/namespaces/eth/filters/filter.go +++ b/jsonrpc/namespaces/eth/filters/filter.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "math/big" + "slices" "cosmossdk.io/log" @@ -126,11 +127,7 @@ func (f *Filter) Logs(ctx context.Context) ([]*coretypes.Log, error) { case log := <-logChan: logs = append(logs, log) case err := <-errChan: - if err != nil { - // if an error occurs during extraction, we do return the extracted data - return logs, err - } - return logs, nil + return logs, err } } } @@ -213,7 +210,7 @@ func filterLogs(logs []*coretypes.Log, fromBlock, toBlock *big.Int, addresses [] if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { return false } - if len(addresses) > 0 && !includes(addresses, log.Address) { + if len(addresses) > 0 && !slices.Contains(addresses, log.Address) { return false } // If the to filtered topics is greater than the amount of topics in logs, skip. @@ -224,7 +221,7 @@ func filterLogs(logs []*coretypes.Log, fromBlock, toBlock *big.Int, addresses [] if len(sub) == 0 { continue // empty rule set == wildcard } - if !includes(sub, log.Topics[i]) { + if !slices.Contains(sub, log.Topics[i]) { return false } } @@ -239,16 +236,6 @@ func filterLogs(logs []*coretypes.Log, fromBlock, toBlock *big.Int, addresses [] return ret } -// includes returns true if the element is present in the list. -func includes[T comparable](things []T, element T) bool { - for _, thing := range things { - if thing == element { - return true - } - } - return false -} - func bloomFilter(bloom coretypes.Bloom, addresses []common.Address, topics [][]common.Hash) bool { if len(addresses) > 0 { var included bool