From d3d994cedd8f696c6a51c6ed20b0946e00a017b9 Mon Sep 17 00:00:00 2001 From: Iraklis Leontiadis Date: Sun, 25 Feb 2024 14:33:12 +0200 Subject: [PATCH] Improve eth_getLogs performance for latest block (#6305) Co-authored-by: Matthias Seitz --- crates/primitives/src/chain/info.rs | 10 +++++-- crates/rpc/rpc/src/eth/filter.rs | 41 +++++++++++++++++++++------- crates/rpc/rpc/src/eth/logs_utils.rs | 8 +++--- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/crates/primitives/src/chain/info.rs b/crates/primitives/src/chain/info.rs index 2084c23274c0..fb954345b79b 100644 --- a/crates/primitives/src/chain/info.rs +++ b/crates/primitives/src/chain/info.rs @@ -1,10 +1,16 @@ -use crate::{BlockNumber, B256}; +use crate::{BlockNumHash, BlockNumber, B256}; /// Current status of the blockchain's head. -#[derive(Default, Clone, Debug, Eq, PartialEq)] +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] pub struct ChainInfo { /// The block hash of the highest fully synced block. pub best_hash: B256, /// The block number of the highest fully synced block. pub best_number: BlockNumber, } + +impl From for BlockNumHash { + fn from(value: ChainInfo) -> Self { + BlockNumHash { number: value.best_number, hash: value.best_hash } + } +} diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index f5100a0abfa1..a0d47e6a9436 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -11,13 +11,14 @@ use core::fmt; use async_trait::async_trait; use jsonrpsee::{core::RpcResult, server::IdProvider}; -use reth_primitives::{IntoRecoveredTransaction, TxHash}; +use reth_primitives::{ChainInfo, IntoRecoveredTransaction, TxHash}; use reth_provider::{BlockIdReader, BlockReader, EvmEnvProvider, ProviderError}; use reth_rpc_api::EthFilterApiServer; use reth_rpc_types::{ BlockNumHash, Filter, FilterBlockOption, FilterChanges, FilterId, FilteredParams, Log, PendingTransactionFilterKind, }; + use reth_tasks::TaskSpawner; use reth_transaction_pool::{NewSubpoolTransactionStream, PoolTransaction, TransactionPool}; use std::{ @@ -141,7 +142,7 @@ where if filter.block > best_number { // no new blocks since the last poll - return Ok(FilterChanges::Empty) + return Ok(FilterChanges::Empty); } // update filter @@ -187,10 +188,9 @@ where (start_block, best_number) } }; - let logs = self .inner - .get_logs_in_block_range(&filter, from_block_number, to_block_number) + .get_logs_in_block_range(&filter, from_block_number, to_block_number, info) .await?; Ok(FilterChanges::Logs(logs)) } @@ -211,7 +211,7 @@ where *filter.clone() } else { // Not a log filter - return Err(FilterError::FilterNotFound(id)) + return Err(FilterError::FilterNotFound(id)); } }; @@ -382,7 +382,8 @@ where .flatten(); let (from_block_number, to_block_number) = logs_utils::get_filter_block_range(from, to, start_block, info); - self.get_logs_in_block_range(&filter, from_block_number, to_block_number).await + self.get_logs_in_block_range(&filter, from_block_number, to_block_number, info) + .await } } } @@ -413,17 +414,36 @@ where filter: &Filter, from_block: u64, to_block: u64, + chain_info: ChainInfo, ) -> Result, FilterError> { trace!(target: "rpc::eth::filter", from=from_block, to=to_block, ?filter, "finding logs in range"); + let best_number = chain_info.best_number; if to_block - from_block > self.max_blocks_per_filter { - return Err(FilterError::QueryExceedsMaxBlocks(self.max_blocks_per_filter)) + return Err(FilterError::QueryExceedsMaxBlocks(self.max_blocks_per_filter)); } let mut all_logs = Vec::new(); let filter_params = FilteredParams::new(Some(filter.clone())); - // derive bloom filters from filter input + if (to_block == best_number) && (from_block == best_number) { + // only one block to check and it's the current best block which we can fetch directly + // Note: In case of a reorg, the best block's hash might have changed, hence we only + // return early of we were able to fetch the best block's receipts + if let Some(receipts) = self.eth_cache.get_receipts(chain_info.best_hash).await? { + logs_utils::append_matching_block_logs( + &mut all_logs, + &self.provider, + &filter_params, + chain_info.into(), + &receipts, + false, + )?; + } + return Ok(all_logs); + } + + // derive bloom filters from filter input, so we can check headers for matching logs let address_filter = FilteredParams::address_filter(&filter.address); let topics_filter = FilteredParams::topics_filter(&filter.topics); @@ -465,7 +485,7 @@ where if is_multi_block_range && all_logs.len() > self.max_logs_per_response { return Err(FilterError::QueryExceedsMaxResults( self.max_logs_per_response, - )) + )); } } } @@ -639,6 +659,7 @@ enum FilterKind { Block, PendingTransaction(PendingTransactionKind), } + /// Errors that can occur in the handler implementation #[derive(Debug, thiserror::Error)] pub enum FilterError { @@ -704,7 +725,7 @@ impl Iterator for BlockRangeInclusiveIter { let start = self.iter.next()?; let end = (start + self.step).min(self.end); if start > end { - return None + return None; } Some((start, end)) } diff --git a/crates/rpc/rpc/src/eth/logs_utils.rs b/crates/rpc/rpc/src/eth/logs_utils.rs index 0bc37956433c..2b8d0bd570be 100644 --- a/crates/rpc/rpc/src/eth/logs_utils.rs +++ b/crates/rpc/rpc/src/eth/logs_utils.rs @@ -167,7 +167,7 @@ mod tests { let from = 15000001u64; let to = 15000002u64; let info = ChainInfo { best_number: 15000000, ..Default::default() }; - let range = get_filter_block_range(Some(from), Some(to), info.best_number, info.clone()); + let range = get_filter_block_range(Some(from), Some(to), info.best_number, info); assert_eq!(range, (info.best_number, info.best_number)); } @@ -175,7 +175,7 @@ mod tests { fn test_log_range_from() { let from = 14000000u64; let info = ChainInfo { best_number: 15000000, ..Default::default() }; - let range = get_filter_block_range(Some(from), None, info.best_number, info.clone()); + let range = get_filter_block_range(Some(from), None, info.best_number, info); assert_eq!(range, (from, info.best_number)); } @@ -183,14 +183,14 @@ mod tests { fn test_log_range_to() { let to = 14000000u64; let info = ChainInfo { best_number: 15000000, ..Default::default() }; - let range = get_filter_block_range(None, Some(to), info.best_number, info.clone()); + let range = get_filter_block_range(None, Some(to), info.best_number, info); assert_eq!(range, (info.best_number, to)); } #[test] fn test_log_range_empty() { let info = ChainInfo { best_number: 15000000, ..Default::default() }; - let range = get_filter_block_range(None, None, info.best_number, info.clone()); + let range = get_filter_block_range(None, None, info.best_number, info); // no range given -> head assert_eq!(range, (info.best_number, info.best_number));