diff --git a/turbo/jsonrpc/erigon_receipts.go b/turbo/jsonrpc/erigon_receipts.go index 17a3f99b23e..d6222ccf26f 100644 --- a/turbo/jsonrpc/erigon_receipts.go +++ b/turbo/jsonrpc/erigon_receipts.go @@ -29,11 +29,6 @@ func (api *ErigonImpl) GetLogsByHash(ctx context.Context, hash common.Hash) ([][ } defer tx.Rollback() - chainConfig, err := api.chainConfig(ctx, tx) - if err != nil { - return nil, err - } - block, err := api.blockByHashWithSenders(ctx, tx, hash) if err != nil { return nil, err @@ -41,7 +36,7 @@ func (api *ErigonImpl) GetLogsByHash(ctx context.Context, hash common.Hash) ([][ if block == nil { return nil, nil } - receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs()) + receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs()) if err != nil { return nil, fmt.Errorf("getReceipts error: %w", err) } @@ -426,7 +421,7 @@ func (api *ErigonImpl) GetBlockReceiptsByBlockHash(ctx context.Context, cannonic if err != nil { return nil, err } - receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs()) + receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs()) if err != nil { return nil, fmt.Errorf("getReceipts error: %w", err) } diff --git a/turbo/jsonrpc/eth_api.go b/turbo/jsonrpc/eth_api.go index 8a93ea5dc4e..b6b2b38c93c 100644 --- a/turbo/jsonrpc/eth_api.go +++ b/turbo/jsonrpc/eth_api.go @@ -109,8 +109,11 @@ type EthAPI interface { } type BaseAPI struct { - stateCache kvcache.Cache // thread-safe - blocksLRU *lru.Cache[common.Hash, *types.Block] // thread-safe + // all caches are thread-safe + stateCache kvcache.Cache + blocksLRU *lru.Cache[common.Hash, *types.Block] + receiptsCache *lru.Cache[common.Hash, []*types.Receipt] + filters *rpchelper.Filters _chainConfig atomic.Pointer[chain.Config] _genesis atomic.Pointer[types.Block] @@ -127,16 +130,36 @@ type BaseAPI struct { } func NewBaseApi(f *rpchelper.Filters, stateCache kvcache.Cache, blockReader services.FullBlockReader, agg *libstate.Aggregator, singleNodeMode bool, evmCallTimeout time.Duration, engine consensus.EngineReader, dirs datadir.Dirs) *BaseAPI { - blocksLRUSize := 128 // ~32Mb + var ( + blocksLRUSize = 128 // ~32Mb + receiptsCacheLimit = 32 + ) + // if RPCDaemon deployed as independent process: increase cache sizes if !singleNodeMode { - blocksLRUSize = 512 + blocksLRUSize *= 5 + receiptsCacheLimit *= 5 } blocksLRU, err := lru.New[common.Hash, *types.Block](blocksLRUSize) if err != nil { panic(err) } + receiptsCache, err := lru.New[common.Hash, []*types.Receipt](receiptsCacheLimit) + if err != nil { + panic(err) + } - return &BaseAPI{filters: f, stateCache: stateCache, blocksLRU: blocksLRU, _blockReader: blockReader, _txnReader: blockReader, _agg: agg, evmCallTimeout: evmCallTimeout, _engine: engine, dirs: dirs} + return &BaseAPI{ + filters: f, + stateCache: stateCache, + blocksLRU: blocksLRU, + receiptsCache: receiptsCache, + _blockReader: blockReader, + _txnReader: blockReader, + _agg: agg, + evmCallTimeout: evmCallTimeout, + _engine: engine, + dirs: dirs, + } } func (api *BaseAPI) chainConfig(ctx context.Context, tx kv.Tx) (*chain.Config, error) { diff --git a/turbo/jsonrpc/eth_receipts.go b/turbo/jsonrpc/eth_receipts.go index 2d56ae05d4d..ff93a1c6740 100644 --- a/turbo/jsonrpc/eth_receipts.go +++ b/turbo/jsonrpc/eth_receipts.go @@ -34,11 +34,22 @@ import ( "github.com/ledgerwatch/erigon/turbo/transactions" ) -func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, chainConfig *chain.Config, block *types.Block, senders []common.Address) (types.Receipts, error) { - if cached := rawdb.ReadReceipts(tx, block, senders); cached != nil { - return cached, nil +// getReceipts - checking in-mem cache, or else fallback to db, or else fallback to re-exec of block to re-gen receipts +func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, block *types.Block, senders []common.Address) (types.Receipts, error) { + if receipts, ok := api.receiptsCache.Get(block.Hash()); ok { + return receipts, nil } + + if receipts := rawdb.ReadReceipts(tx, block, senders); receipts != nil { + api.receiptsCache.Add(block.Hash(), receipts) + return receipts, nil + } + engine := api.engine() + chainConfig, err := api.chainConfig(ctx, tx) + if err != nil { + return nil, err + } _, _, _, ibs, _, err := transactions.ComputeTxEnv(ctx, engine, block, chainConfig, api._blockReader, tx, 0, api.historyV3(tx)) if err != nil { @@ -71,6 +82,7 @@ func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, chainConfig *chai receipts[i] = receipt } + api.receiptsCache.Add(block.Hash(), receipts) return receipts, nil } @@ -86,11 +98,10 @@ func (api *APIImpl) GetLogs(ctx context.Context, crit filters.FilterCriteria) (t defer tx.Rollback() if crit.BlockHash != nil { - block, err := api._blockReader.BlockByHash(ctx, tx, *crit.BlockHash) + block, err := api.blockByHashWithSenders(ctx, tx, *crit.BlockHash) if err != nil { return nil, err } - if block == nil { return nil, fmt.Errorf("block not found: %x", *crit.BlockHash) } @@ -648,7 +659,7 @@ func (api *APIImpl) GetTransactionReceipt(ctx context.Context, txnHash common.Ha borTx = types.NewBorTransaction() } } - receipts, err := api.getReceipts(ctx, tx, cc, block, block.Body().SendersFromTxs()) + receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs()) if err != nil { return nil, fmt.Errorf("getReceipts error: %w", err) } @@ -694,7 +705,7 @@ func (api *APIImpl) GetBlockReceipts(ctx context.Context, numberOrHash rpc.Block if err != nil { return nil, err } - receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs()) + receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs()) if err != nil { return nil, fmt.Errorf("getReceipts error: %w", err) } diff --git a/turbo/jsonrpc/eth_system.go b/turbo/jsonrpc/eth_system.go index d4dd9c5f792..549750b70d7 100644 --- a/turbo/jsonrpc/eth_system.go +++ b/turbo/jsonrpc/eth_system.go @@ -111,12 +111,7 @@ func (api *APIImpl) GasPrice(ctx context.Context) (*hexutil.Big, error) { return nil, err } defer tx.Rollback() - cc, err := api.chainConfig(ctx, tx) - if err != nil { - return nil, err - } - - oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, cc, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache) + oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache) tipcap, err := oracle.SuggestTipCap(ctx) gasResult := big.NewInt(0) @@ -138,11 +133,7 @@ func (api *APIImpl) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, err return nil, err } defer tx.Rollback() - cc, err := api.chainConfig(ctx, tx) - if err != nil { - return nil, err - } - oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, cc, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache) + oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache) tipcap, err := oracle.SuggestTipCap(ctx) if err != nil { return nil, err @@ -163,11 +154,7 @@ func (api *APIImpl) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, return nil, err } defer tx.Rollback() - cc, err := api.chainConfig(ctx, tx) - if err != nil { - return nil, err - } - oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, cc, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache) + oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache) oldest, reward, baseFee, gasUsed, err := oracle.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles) if err != nil { @@ -197,12 +184,11 @@ func (api *APIImpl) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, type GasPriceOracleBackend struct { tx kv.Tx - cc *chain.Config baseApi *BaseAPI } -func NewGasPriceOracleBackend(tx kv.Tx, cc *chain.Config, baseApi *BaseAPI) *GasPriceOracleBackend { - return &GasPriceOracleBackend{tx: tx, cc: cc, baseApi: baseApi} +func NewGasPriceOracleBackend(tx kv.Tx, baseApi *BaseAPI) *GasPriceOracleBackend { + return &GasPriceOracleBackend{tx: tx, baseApi: baseApi} } func (b *GasPriceOracleBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) { @@ -219,10 +205,11 @@ func (b *GasPriceOracleBackend) BlockByNumber(ctx context.Context, number rpc.Bl return b.baseApi.blockByRPCNumber(ctx, number, b.tx) } func (b *GasPriceOracleBackend) ChainConfig() *chain.Config { - return b.cc + cc, _ := b.baseApi.chainConfig(context.Background(), b.tx) + return cc } func (b *GasPriceOracleBackend) GetReceipts(ctx context.Context, block *types.Block) (types.Receipts, error) { - return rawdb.ReadReceipts(b.tx, block, nil), nil + return b.baseApi.getReceipts(ctx, b.tx, block, nil) } func (b *GasPriceOracleBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) { return nil, nil diff --git a/turbo/jsonrpc/graphql_api.go b/turbo/jsonrpc/graphql_api.go index 41e1d0b14ee..8562f67a1be 100644 --- a/turbo/jsonrpc/graphql_api.go +++ b/turbo/jsonrpc/graphql_api.go @@ -73,7 +73,7 @@ func (api *GraphQLAPIImpl) GetBlockDetails(ctx context.Context, blockNumber rpc. return nil, err } - receipts, err := api.getReceipts(ctx, tx, chainConfig, block, senders) + receipts, err := api.getReceipts(ctx, tx, block, senders) if err != nil { return nil, fmt.Errorf("getReceipts error: %w", err) } @@ -107,8 +107,14 @@ func (api *GraphQLAPIImpl) getBlockWithSenders(ctx context.Context, number rpc.B return nil, nil, err } - block, senders, err := api._blockReader.BlockWithSenders(ctx, tx, blockHash, blockHeight) - return block, senders, err + block, err := api.blockWithSenders(ctx, tx, blockHash, blockHeight) + if err != nil { + return nil, nil, err + } + if block == nil { + return nil, nil, nil + } + return block, block.Body().SendersFromTxs(), nil } func (api *GraphQLAPIImpl) delegateGetBlockByNumber(tx kv.Tx, b *types.Block, number rpc.BlockNumber, inclTx bool) (map[string]interface{}, error) { diff --git a/turbo/jsonrpc/otterscan_api.go b/turbo/jsonrpc/otterscan_api.go index d3a8320e1be..3ee50aae2da 100644 --- a/turbo/jsonrpc/otterscan_api.go +++ b/turbo/jsonrpc/otterscan_api.go @@ -573,8 +573,14 @@ func (api *OtterscanAPIImpl) getBlockWithSenders(ctx context.Context, number rpc return nil, nil, err } - block, senders, err := api._blockReader.BlockWithSenders(ctx, tx, hash, n) - return block, senders, err + block, err := api.blockWithSenders(ctx, tx, hash, n) + if err != nil { + return nil, nil, err + } + if block == nil { + return nil, nil, nil + } + return block, block.Body().SendersFromTxs(), nil } func (api *OtterscanAPIImpl) GetBlockTransactions(ctx context.Context, number rpc.BlockNumber, pageNumber uint8, pageSize uint8) (map[string]interface{}, error) { @@ -603,7 +609,7 @@ func (api *OtterscanAPIImpl) GetBlockTransactions(ctx context.Context, number rp } // Receipts - receipts, err := api.getReceipts(ctx, tx, chainConfig, b, senders) + receipts, err := api.getReceipts(ctx, tx, b, senders) if err != nil { return nil, fmt.Errorf("getReceipts error: %v", err) } diff --git a/turbo/jsonrpc/otterscan_block_details.go b/turbo/jsonrpc/otterscan_block_details.go index f410d44c15c..c44bdb277f2 100644 --- a/turbo/jsonrpc/otterscan_block_details.go +++ b/turbo/jsonrpc/otterscan_block_details.go @@ -44,7 +44,7 @@ func (api *OtterscanAPIImpl) GetBlockDetailsByHash(ctx context.Context, hash com if blockNumber == nil { return nil, fmt.Errorf("couldn't find block number for hash %v", hash.Bytes()) } - b, senders, err := api._blockReader.BlockWithSenders(ctx, tx, hash, *blockNumber) + b, err := api.blockWithSenders(ctx, tx, hash, *blockNumber) if err != nil { return nil, err } @@ -53,7 +53,7 @@ func (api *OtterscanAPIImpl) GetBlockDetailsByHash(ctx context.Context, hash com } number := rpc.BlockNumber(b.Number().Int64()) - return api.getBlockDetailsImpl(ctx, tx, b, number, senders) + return api.getBlockDetailsImpl(ctx, tx, b, number, b.Body().SendersFromTxs()) } func (api *OtterscanAPIImpl) getBlockDetailsImpl(ctx context.Context, tx kv.Tx, b *types.Block, number rpc.BlockNumber, senders []common.Address) (map[string]interface{}, error) { @@ -70,7 +70,7 @@ func (api *OtterscanAPIImpl) getBlockDetailsImpl(ctx context.Context, tx kv.Tx, if err != nil { return nil, err } - receipts, err := api.getReceipts(ctx, tx, chainConfig, b, senders) + receipts, err := api.getReceipts(ctx, tx, b, senders) if err != nil { return nil, fmt.Errorf("getReceipts error: %v", err) } diff --git a/turbo/jsonrpc/otterscan_search_trace.go b/turbo/jsonrpc/otterscan_search_trace.go index 816b7b2813c..38ec3698f91 100644 --- a/turbo/jsonrpc/otterscan_search_trace.go +++ b/turbo/jsonrpc/otterscan_search_trace.go @@ -10,7 +10,6 @@ import ( "github.com/ledgerwatch/erigon-lib/common" "github.com/ledgerwatch/erigon-lib/kv" "github.com/ledgerwatch/erigon/core" - "github.com/ledgerwatch/erigon/core/rawdb" "github.com/ledgerwatch/erigon/core/state" "github.com/ledgerwatch/erigon/core/types" "github.com/ledgerwatch/erigon/core/vm" @@ -48,7 +47,7 @@ func (api *OtterscanAPIImpl) traceBlock(dbtx kv.Tx, ctx context.Context, blockNu return false, nil, err } - block, senders, err := api._blockReader.BlockWithSenders(ctx, dbtx, blockHash, blockNum) + block, err := api.blockWithSenders(ctx, dbtx, blockHash, blockNum) if err != nil { return false, nil, err } @@ -74,7 +73,10 @@ func (api *OtterscanAPIImpl) traceBlock(dbtx kv.Tx, ctx context.Context, blockNu } engine := api.engine() - blockReceipts := rawdb.ReadReceipts(dbtx, block, senders) + blockReceipts, err := api.getReceipts(ctx, dbtx, block, block.Body().SendersFromTxs()) + if err != nil { + return false, nil, err + } header := block.Header() rules := chainConfig.Rules(block.NumberU64(), header.Time) found := false diff --git a/turbo/jsonrpc/otterscan_transaction_by_sender_and_nonce.go b/turbo/jsonrpc/otterscan_transaction_by_sender_and_nonce.go index 86348ae06c9..321aec8464b 100644 --- a/turbo/jsonrpc/otterscan_transaction_by_sender_and_nonce.go +++ b/turbo/jsonrpc/otterscan_transaction_by_sender_and_nonce.go @@ -276,10 +276,11 @@ func (api *OtterscanAPIImpl) findNonce(ctx context.Context, tx kv.Tx, addr commo if err != nil { return false, common.Hash{}, err } - block, senders, err := api._blockReader.BlockWithSenders(ctx, tx, hash, blockNum) + block, err := api.blockWithSenders(ctx, tx, hash, blockNum) if err != nil { return false, common.Hash{}, err } + senders := block.Body().SendersFromTxs() txs := block.Transactions() for i, s := range senders { diff --git a/turbo/jsonrpc/overlay_api.go b/turbo/jsonrpc/overlay_api.go index 67ec8017211..ca551435bba 100644 --- a/turbo/jsonrpc/overlay_api.go +++ b/turbo/jsonrpc/overlay_api.go @@ -496,7 +496,7 @@ func (api *OverlayAPIImpl) replayBlock(ctx context.Context, blockNum uint64, sta gp := new(core.GasPool).AddGas(math.MaxUint64).AddBlobGas(math.MaxUint64) vmConfig := vm.Config{Debug: false} evm = vm.NewEVM(blockCtx, evmtypes.TxContext{}, statedb, chainConfig, vmConfig) - receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs()) + receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs()) if err != nil { return nil, err } @@ -566,7 +566,7 @@ func (api *OverlayAPIImpl) replayBlock(ctx context.Context, blockNum uint64, sta func getBeginEnd(ctx context.Context, tx kv.Tx, api *OverlayAPIImpl, crit filters.FilterCriteria) (uint64, uint64, error) { var begin, end uint64 if crit.BlockHash != nil { - block, err := api._blockReader.BlockByHash(ctx, tx, *crit.BlockHash) + block, err := api.blockByHashWithSenders(ctx, tx, *crit.BlockHash) if err != nil { return 0, 0, err }