From 89f150cf6e6abf89aa7edcaa3eb35335f129060f Mon Sep 17 00:00:00 2001 From: Karl Date: Wed, 29 Oct 2025 23:58:37 +0800 Subject: [PATCH 1/7] resolve eth_getLogs --- crates/rpc/rpc/src/eth/filter.rs | 126 +++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 25 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 01b6a94158f..c250c8a7317 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -3,7 +3,7 @@ use alloy_consensus::BlockHeader; use alloy_primitives::{Sealable, TxHash}; use alloy_rpc_types_eth::{ - BlockNumHash, Filter, FilterBlockOption, FilterChanges, FilterId, Log, + BlockNumHash, BlockNumberOrTag, Filter, FilterBlockOption, FilterChanges, FilterId, Log, PendingTransactionFilterKind, }; use async_trait::async_trait; @@ -487,32 +487,108 @@ where Ok(all_logs) } FilterBlockOption::Range { from_block, to_block } => { - // compute the range - let info = self.provider().chain_info()?; - - // we start at the most recent block if unset in filter - let start_block = info.best_number; - let from = from_block - .map(|num| self.provider().convert_block_number(num)) - .transpose()? - .flatten(); - let to = to_block - .map(|num| self.provider().convert_block_number(num)) - .transpose()? - .flatten(); - - if let Some(f) = from && - f > info.best_number - { - // start block higher than local head, can return empty - return Ok(Vec::new()); - } + // Check if either from_block or to_block is pending + let has_pending = from_block + .is_some_and(|b| matches!(b, BlockNumberOrTag::Pending)) || + to_block.is_some_and(|b| matches!(b, BlockNumberOrTag::Pending)); + + if has_pending { + // Handle pending blocks separately + let mut all_logs = Vec::new(); + + // If pending block is requested, try to get it from provider + if let Ok(Some((block, receipts))) = + self.provider().pending_block_and_receipts() + { + let block_num_hash = BlockNumHash::new(block.number(), block.hash()); + let block_timestamp = block.timestamp(); + let block_arc = Arc::new(block); + let receipts_arc = Arc::new(receipts); + + // Use existing append_matching_block_logs function to process pending logs + append_matching_block_logs( + &mut all_logs, + ProviderOrBlock::::Block(block_arc), + &filter, + block_num_hash, + &receipts_arc, + false, // removed = false for pending blocks + block_timestamp, + )?; + } - let (from_block_number, to_block_number) = - logs_utils::get_filter_block_range(from, to, start_block, info); + // If the range includes both pending and non-pending blocks, handle the + // non-pending part + let non_pending_from = if matches!(from_block, Some(BlockNumberOrTag::Pending)) + { + None + } else { + from_block + }; + let non_pending_to = if matches!(to_block, Some(BlockNumberOrTag::Pending)) { + None + } else { + to_block + }; + + if non_pending_from.is_some() || non_pending_to.is_some() { + // Process non-pending range using the original logic + let info = self.provider().chain_info()?; + let start_block = info.best_number; + let from = non_pending_from + .map(|num| self.provider().convert_block_number(num)) + .transpose()? + .flatten(); + let to = non_pending_to + .map(|num| self.provider().convert_block_number(num)) + .transpose()? + .flatten(); - self.get_logs_in_block_range(filter, from_block_number, to_block_number, limits) - .await + if let Some(f) = from && + f <= info.best_number + { + let (from_block_number, to_block_number) = + logs_utils::get_filter_block_range(from, to, start_block, info); + + let mut non_pending_logs = self + .get_logs_in_block_range( + filter, + from_block_number, + to_block_number, + limits, + ) + .await?; + all_logs.append(&mut non_pending_logs); + } + } + + Ok(all_logs) + } else { + // Original logic for non-pending blocks + let info = self.provider().chain_info()?; + let start_block = info.best_number; + let from = from_block + .map(|num| self.provider().convert_block_number(num)) + .transpose()? + .flatten(); + let to = to_block + .map(|num| self.provider().convert_block_number(num)) + .transpose()? + .flatten(); + + if let Some(f) = from && + f > info.best_number + { + // start block higher than local head, can return empty + return Ok(Vec::new()); + } + + 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, limits) + .await + } } } } From 90b4e2592cf2e6eac5e9577d99fcc2ecd622f0c1 Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 30 Oct 2025 09:45:35 +0800 Subject: [PATCH 2/7] apply mattsse suggestions --- crates/rpc/rpc/src/eth/filter.rs | 179 +++++++++++++++---------------- 1 file changed, 86 insertions(+), 93 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index c250c8a7317..100e57749b8 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -1,6 +1,7 @@ //! `eth_` `Filter` RPC handler implementation use alloy_consensus::BlockHeader; +use alloy_eips::BlockId; use alloy_primitives::{Sealable, TxHash}; use alloy_rpc_types_eth::{ BlockNumHash, BlockNumberOrTag, Filter, FilterBlockOption, FilterChanges, FilterId, Log, @@ -17,6 +18,7 @@ use jsonrpsee::{core::RpcResult, server::IdProvider}; use reth_errors::ProviderError; use reth_primitives_traits::{NodePrimitives, SealedHeader}; use reth_rpc_eth_api::{ + helpers::{EthBlocks, LoadBlock, LoadReceipt}, EngineEthFilter, EthApiTypes, EthFilterApiServer, FullEthApiTypes, QueryLimits, RpcConvert, RpcNodeCoreExt, RpcTransaction, }; @@ -48,7 +50,12 @@ use tracing::{debug, error, trace}; impl EngineEthFilter for EthFilter where - Eth: FullEthApiTypes + RpcNodeCoreExt + 'static, + Eth: FullEthApiTypes + + RpcNodeCoreExt + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, { /// Returns logs matching given filter object, no query limits fn logs( @@ -193,7 +200,12 @@ where impl EthFilter where - Eth: FullEthApiTypes + RpcNodeCoreExt + 'static, + Eth: FullEthApiTypes + + RpcNodeCoreExt + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, { /// Access the underlying provider. fn provider(&self) -> &Eth::Provider { @@ -315,7 +327,7 @@ where #[async_trait] impl EthFilterApiServer> for EthFilter where - Eth: FullEthApiTypes + RpcNodeCoreExt + 'static, + Eth: FullEthApiTypes + RpcNodeCoreExt + LoadBlock + LoadReceipt + EthBlocks + 'static, { /// Handler for `eth_newFilter` async fn new_filter(&self, filter: Filter) -> RpcResult { @@ -434,6 +446,9 @@ impl EthFilterInner where Eth: RpcNodeCoreExt + EthApiTypes + + LoadBlock + + LoadReceipt + + EthBlocks + 'static, { /// Access the underlying provider. @@ -487,25 +502,18 @@ where Ok(all_logs) } FilterBlockOption::Range { from_block, to_block } => { - // Check if either from_block or to_block is pending - let has_pending = from_block - .is_some_and(|b| matches!(b, BlockNumberOrTag::Pending)) || - to_block.is_some_and(|b| matches!(b, BlockNumberOrTag::Pending)); - - if has_pending { - // Handle pending blocks separately + // Handle special case where from block is pending + if from_block.is_some_and(|b| matches!(b, BlockNumberOrTag::Pending)) { let mut all_logs = Vec::new(); - // If pending block is requested, try to get it from provider - if let Ok(Some((block, receipts))) = - self.provider().pending_block_and_receipts() + // Try to get pending block and receipts + if let Ok(Some((block_arc, receipts_arc))) = + self.eth_api.load_block_and_receipts(BlockId::pending()).await { - let block_num_hash = BlockNumHash::new(block.number(), block.hash()); - let block_timestamp = block.timestamp(); - let block_arc = Arc::new(block); - let receipts_arc = Arc::new(receipts); + let block_num_hash = + BlockNumHash::new(block_arc.number(), block_arc.hash()); + let block_timestamp = block_arc.timestamp(); - // Use existing append_matching_block_logs function to process pending logs append_matching_block_logs( &mut all_logs, ProviderOrBlock::::Block(block_arc), @@ -517,78 +525,33 @@ where )?; } - // If the range includes both pending and non-pending blocks, handle the - // non-pending part - let non_pending_from = if matches!(from_block, Some(BlockNumberOrTag::Pending)) - { - None - } else { - from_block - }; - let non_pending_to = if matches!(to_block, Some(BlockNumberOrTag::Pending)) { - None - } else { - to_block - }; - - if non_pending_from.is_some() || non_pending_to.is_some() { - // Process non-pending range using the original logic - let info = self.provider().chain_info()?; - let start_block = info.best_number; - let from = non_pending_from - .map(|num| self.provider().convert_block_number(num)) - .transpose()? - .flatten(); - let to = non_pending_to - .map(|num| self.provider().convert_block_number(num)) - .transpose()? - .flatten(); - - if let Some(f) = from && - f <= info.best_number - { - let (from_block_number, to_block_number) = - logs_utils::get_filter_block_range(from, to, start_block, info); - - let mut non_pending_logs = self - .get_logs_in_block_range( - filter, - from_block_number, - to_block_number, - limits, - ) - .await?; - all_logs.append(&mut non_pending_logs); - } - } + return Ok(all_logs); + } - Ok(all_logs) - } else { - // Original logic for non-pending blocks - let info = self.provider().chain_info()?; - let start_block = info.best_number; - let from = from_block - .map(|num| self.provider().convert_block_number(num)) - .transpose()? - .flatten(); - let to = to_block - .map(|num| self.provider().convert_block_number(num)) - .transpose()? - .flatten(); - - if let Some(f) = from && - f > info.best_number - { - // start block higher than local head, can return empty - return Ok(Vec::new()); - } + // Original logic for non-pending ranges + let info = self.provider().chain_info()?; + let start_block = info.best_number; + let from = from_block + .map(|num| self.provider().convert_block_number(num)) + .transpose()? + .flatten(); + let to = to_block + .map(|num| self.provider().convert_block_number(num)) + .transpose()? + .flatten(); + + if let Some(f) = from && + f > info.best_number + { + // start block higher than local head, can return empty + return Ok(Vec::new()); + } - let (from_block_number, to_block_number) = - logs_utils::get_filter_block_range(from, to, start_block, info); + 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, limits) - .await - } + self.get_logs_in_block_range(filter, from_block_number, to_block_number, limits) + .await } } } @@ -988,7 +951,12 @@ where /// Represents different modes for processing block ranges when filtering logs enum RangeMode< - Eth: RpcNodeCoreExt + EthApiTypes + 'static, + Eth: RpcNodeCoreExt + + EthApiTypes + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, > { /// Use cache-based processing for recent blocks Cached(CachedMode), @@ -997,7 +965,12 @@ enum RangeMode< } impl< - Eth: RpcNodeCoreExt + EthApiTypes + 'static, + Eth: RpcNodeCoreExt + + EthApiTypes + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, > RangeMode { /// Creates a new `RangeMode`. @@ -1069,14 +1042,24 @@ impl< /// Mode for processing blocks using cache optimization for recent blocks struct CachedMode< - Eth: RpcNodeCoreExt + EthApiTypes + 'static, + Eth: RpcNodeCoreExt + + EthApiTypes + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, > { filter_inner: Arc>, headers_iter: std::vec::IntoIter::Header>>, } impl< - Eth: RpcNodeCoreExt + EthApiTypes + 'static, + Eth: RpcNodeCoreExt + + EthApiTypes + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, > CachedMode { async fn next(&mut self) -> Result>, EthFilterError> { @@ -1103,7 +1086,12 @@ type ReceiptFetchFuture

= /// Mode for processing blocks using range queries for older blocks struct RangeBlockMode< - Eth: RpcNodeCoreExt + EthApiTypes + 'static, + Eth: RpcNodeCoreExt + + EthApiTypes + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, > { filter_inner: Arc>, iter: Peekable::Header>>>, @@ -1114,7 +1102,12 @@ struct RangeBlockMode< } impl< - Eth: RpcNodeCoreExt + EthApiTypes + 'static, + Eth: RpcNodeCoreExt + + EthApiTypes + + LoadBlock + + LoadReceipt + + EthBlocks + + 'static, > RangeBlockMode { async fn next(&mut self) -> Result>, EthFilterError> { From 672faac8d8e8052c269fdd92646e95110537ccbd Mon Sep 17 00:00:00 2001 From: Karl Date: Thu, 30 Oct 2025 10:03:53 +0800 Subject: [PATCH 3/7] clean up --- crates/rpc/rpc/src/eth/filter.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 100e57749b8..a6857bce082 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -18,7 +18,7 @@ use jsonrpsee::{core::RpcResult, server::IdProvider}; use reth_errors::ProviderError; use reth_primitives_traits::{NodePrimitives, SealedHeader}; use reth_rpc_eth_api::{ - helpers::{EthBlocks, LoadBlock, LoadReceipt}, + helpers::{EthBlocks, LoadReceipt}, EngineEthFilter, EthApiTypes, EthFilterApiServer, FullEthApiTypes, QueryLimits, RpcConvert, RpcNodeCoreExt, RpcTransaction, }; @@ -52,7 +52,6 @@ impl EngineEthFilter for EthFilter where Eth: FullEthApiTypes + RpcNodeCoreExt - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -202,7 +201,6 @@ impl EthFilter where Eth: FullEthApiTypes + RpcNodeCoreExt - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -327,7 +325,7 @@ where #[async_trait] impl EthFilterApiServer> for EthFilter where - Eth: FullEthApiTypes + RpcNodeCoreExt + LoadBlock + LoadReceipt + EthBlocks + 'static, + Eth: FullEthApiTypes + RpcNodeCoreExt + LoadReceipt + EthBlocks + 'static, { /// Handler for `eth_newFilter` async fn new_filter(&self, filter: Filter) -> RpcResult { @@ -446,7 +444,6 @@ impl EthFilterInner where Eth: RpcNodeCoreExt + EthApiTypes - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -953,7 +950,6 @@ where enum RangeMode< Eth: RpcNodeCoreExt + EthApiTypes - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -967,7 +963,6 @@ enum RangeMode< impl< Eth: RpcNodeCoreExt + EthApiTypes - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -1044,7 +1039,6 @@ impl< struct CachedMode< Eth: RpcNodeCoreExt + EthApiTypes - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -1056,7 +1050,6 @@ struct CachedMode< impl< Eth: RpcNodeCoreExt + EthApiTypes - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -1088,7 +1081,6 @@ type ReceiptFetchFuture

= struct RangeBlockMode< Eth: RpcNodeCoreExt + EthApiTypes - + LoadBlock + LoadReceipt + EthBlocks + 'static, @@ -1104,7 +1096,6 @@ struct RangeBlockMode< impl< Eth: RpcNodeCoreExt + EthApiTypes - + LoadBlock + LoadReceipt + EthBlocks + 'static, From 7cd71413180959b4faa82ba226a65a3e191b74b2 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Mon, 3 Nov 2025 11:16:49 +0100 Subject: [PATCH 4/7] feat: specialize pending tag getlogs --- crates/optimism/rpc/src/eth/pending_block.rs | 48 ++++++++++++------- .../rpc-eth-api/src/helpers/pending_block.rs | 20 ++++++++ crates/rpc/rpc/src/eth/filter.rs | 23 ++++----- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/crates/optimism/rpc/src/eth/pending_block.rs b/crates/optimism/rpc/src/eth/pending_block.rs index 151668f4039..cae1ff59ca1 100644 --- a/crates/optimism/rpc/src/eth/pending_block.rs +++ b/crates/optimism/rpc/src/eth/pending_block.rs @@ -38,7 +38,28 @@ where self.inner.eth_api.pending_block_kind() } - /// Returns the locally built pending block + /// Returns a [`StateProviderBox`] on a mem-pool built pending block overlaying latest. + async fn local_pending_state(&self) -> Result, Self::Error> + where + Self: SpawnBlocking, + { + let Ok(Some(pending_block)) = self.pending_flashblock().await else { + return Ok(None); + }; + + let latest_historical = self + .provider() + .history_by_block_hash(pending_block.block().parent_hash()) + .map_err(Self::Error::from_eth_err)?; + + let state = BlockState::from(pending_block); + + Ok(Some(Box::new(state.state_provider(latest_historical)) as StateProviderBox)) + } + + /// Returns the locally built pending block. + /// + /// This falls back to the latest block as its op convention. async fn local_pending_block( &self, ) -> Result>, Self::Error> { @@ -65,22 +86,13 @@ where Ok(Some(BlockAndReceipts { block: Arc::new(block), receipts: Arc::new(receipts) })) } - /// Returns a [`StateProviderBox`] on a mem-pool built pending block overlaying latest. - async fn local_pending_state(&self) -> Result, Self::Error> - where - Self: SpawnBlocking, - { - let Ok(Some(pending_block)) = self.pending_flashblock().await else { - return Ok(None); - }; - - let latest_historical = self - .provider() - .history_by_block_hash(pending_block.block().parent_hash()) - .map_err(Self::Error::from_eth_err)?; - - let state = BlockState::from(pending_block); - - Ok(Some(Box::new(state.state_provider(latest_historical)) as StateProviderBox)) + /// Exclusively returns the pending flashblock if it exits. + async fn local_pending_block_exclusive( + &self, + ) -> Result>, Self::Error> { + if let Ok(Some(pending)) = self.pending_flashblock().await { + return Ok(Some(pending.into_block_and_receipts())); + } + Ok(None) } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index 1dda44d090e..a186ba519a1 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -109,6 +109,8 @@ pub trait LoadPendingBlock: } /// Returns a [`StateProviderBox`] on a mem-pool built pending block overlaying latest. + /// + /// If no local pending block is available, this falls back to the latest state. fn local_pending_state( &self, ) -> impl Future, Self::Error>> + Send @@ -190,6 +192,8 @@ pub trait LoadPendingBlock: } /// Returns the locally built pending block + /// + /// Implementers can fallback to the latest block if no pending is available. fn local_pending_block( &self, ) -> impl Future>, Self::Error>> + Send @@ -216,6 +220,22 @@ pub trait LoadPendingBlock: } } + /// Exclusively returns the local pending block. + /// + /// Implementers must not fallback to latest block and return `None`, if not pending block is + /// available. This is intended to be used to explicitly target the pending block (not + /// latest) + fn local_pending_block_exclusive( + &self, + ) -> impl Future>, Self::Error>> + Send + where + Self: SpawnBlocking, + Self::Pool: + TransactionPool>>, + { + async move { self.local_pending_block().await } + } + /// Builds a pending block using the configured provider and pool. /// /// If the origin is the actual pending block, the block is built with withdrawals. diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index a6857bce082..5d9aa525e36 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -1,10 +1,9 @@ //! `eth_` `Filter` RPC handler implementation use alloy_consensus::BlockHeader; -use alloy_eips::BlockId; use alloy_primitives::{Sealable, TxHash}; use alloy_rpc_types_eth::{ - BlockNumHash, BlockNumberOrTag, Filter, FilterBlockOption, FilterChanges, FilterId, Log, + BlockNumHash, Filter, FilterBlockOption, FilterChanges, FilterId, Log, PendingTransactionFilterKind, }; use async_trait::async_trait; @@ -366,8 +365,6 @@ where } }; - //let filter = FilterKind::PendingTransaction(transaction_kind); - // Install the filter and propagate any errors self.inner.install_filter(transaction_kind).await } @@ -500,25 +497,23 @@ where } FilterBlockOption::Range { from_block, to_block } => { // Handle special case where from block is pending - if from_block.is_some_and(|b| matches!(b, BlockNumberOrTag::Pending)) { + if from_block.is_some_and(|b| b.is_pending()) { let mut all_logs = Vec::new(); // Try to get pending block and receipts - if let Ok(Some((block_arc, receipts_arc))) = - self.eth_api.load_block_and_receipts(BlockId::pending()).await + if let Ok(Some(pending_block)) = + self.eth_api.local_pending_block_exclusive().await { - let block_num_hash = - BlockNumHash::new(block_arc.number(), block_arc.hash()); - let block_timestamp = block_arc.timestamp(); - + let timestamp = pending_block.block.timestamp(); + let block_num_hash = pending_block.block.num_hash(); append_matching_block_logs( &mut all_logs, - ProviderOrBlock::::Block(block_arc), + ProviderOrBlock::::Block(pending_block.block), &filter, block_num_hash, - &receipts_arc, + &pending_block.receipts, false, // removed = false for pending blocks - block_timestamp, + timestamp, )?; } From 4ce4127d5c0c562d3eb1799304cf2f148600bd9b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 4 Nov 2025 11:31:45 +0100 Subject: [PATCH 5/7] check pending height --- .../rpc-eth-api/src/helpers/pending_block.rs | 20 -------- crates/rpc/rpc/src/eth/filter.rs | 50 ++++++++++++------- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index a186ba519a1..1dda44d090e 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -109,8 +109,6 @@ pub trait LoadPendingBlock: } /// Returns a [`StateProviderBox`] on a mem-pool built pending block overlaying latest. - /// - /// If no local pending block is available, this falls back to the latest state. fn local_pending_state( &self, ) -> impl Future, Self::Error>> + Send @@ -192,8 +190,6 @@ pub trait LoadPendingBlock: } /// Returns the locally built pending block - /// - /// Implementers can fallback to the latest block if no pending is available. fn local_pending_block( &self, ) -> impl Future>, Self::Error>> + Send @@ -220,22 +216,6 @@ pub trait LoadPendingBlock: } } - /// Exclusively returns the local pending block. - /// - /// Implementers must not fallback to latest block and return `None`, if not pending block is - /// available. This is intended to be used to explicitly target the pending block (not - /// latest) - fn local_pending_block_exclusive( - &self, - ) -> impl Future>, Self::Error>> + Send - where - Self: SpawnBlocking, - Self::Pool: - TransactionPool>>, - { - async move { self.local_pending_block().await } - } - /// Builds a pending block using the configured provider and pool. /// /// If the origin is the actual pending block, the block is built with withdrawals. diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 5d9aa525e36..c740b400572 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -1,6 +1,7 @@ //! `eth_` `Filter` RPC handler implementation use alloy_consensus::BlockHeader; +use alloy_eips::BlockNumberOrTag; use alloy_primitives::{Sealable, TxHash}; use alloy_rpc_types_eth::{ BlockNumHash, Filter, FilterBlockOption, FilterChanges, FilterId, Log, @@ -498,29 +499,40 @@ where FilterBlockOption::Range { from_block, to_block } => { // Handle special case where from block is pending if from_block.is_some_and(|b| b.is_pending()) { - let mut all_logs = Vec::new(); - - // Try to get pending block and receipts - if let Ok(Some(pending_block)) = - self.eth_api.local_pending_block_exclusive().await - { - let timestamp = pending_block.block.timestamp(); - let block_num_hash = pending_block.block.num_hash(); - append_matching_block_logs( - &mut all_logs, - ProviderOrBlock::::Block(pending_block.block), - &filter, - block_num_hash, - &pending_block.receipts, - false, // removed = false for pending blocks - timestamp, - )?; + let to_block = to_block.unwrap_or(BlockNumberOrTag::Pending); + if !(to_block.is_pending() || to_block.is_number()) { + // always empty range + return Ok(Vec::new()); } + // Try to get pending block and receipts + if let Ok(Some(pending_block)) = self.eth_api.local_pending_block().await { + if let BlockNumberOrTag::Number(to_block) = to_block { + if to_block < pending_block.block.number() { + // this block range is empty based on the user input + return Ok(Vec::new()); + } + } - return Ok(all_logs); + let info = self.provider().chain_info()?; + if pending_block.block.number() > info.best_number { + // only consider the pending block if it is ahead of the chain + let mut all_logs = Vec::new(); + let timestamp = pending_block.block.timestamp(); + let block_num_hash = pending_block.block.num_hash(); + append_matching_block_logs( + &mut all_logs, + ProviderOrBlock::::Block(pending_block.block), + &filter, + block_num_hash, + &pending_block.receipts, + false, // removed = false for pending blocks + timestamp, + )?; + return Ok(all_logs); + } + } } - // Original logic for non-pending ranges let info = self.provider().chain_info()?; let start_block = info.best_number; let from = from_block From 06643b4b7300da5c2e51688b76de6cb7378dfd4b Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 4 Nov 2025 11:34:22 +0100 Subject: [PATCH 6/7] clippy --- crates/rpc/rpc/src/eth/filter.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index c740b400572..a9dadab5461 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -506,12 +506,11 @@ where } // Try to get pending block and receipts if let Ok(Some(pending_block)) = self.eth_api.local_pending_block().await { - if let BlockNumberOrTag::Number(to_block) = to_block { - if to_block < pending_block.block.number() { + if let BlockNumberOrTag::Number(to_block) = to_block + && to_block < pending_block.block.number() { // this block range is empty based on the user input return Ok(Vec::new()); } - } let info = self.provider().chain_info()?; if pending_block.block.number() > info.best_number { From 20c8ffd2b9f222103c7102acb130c803583fdc9a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Tue, 4 Nov 2025 11:37:07 +0100 Subject: [PATCH 7/7] touchups --- crates/optimism/rpc/src/eth/pending_block.rs | 39 +++++--------------- crates/rpc/rpc/src/eth/filter.rs | 11 +++--- 2 files changed, 16 insertions(+), 34 deletions(-) diff --git a/crates/optimism/rpc/src/eth/pending_block.rs b/crates/optimism/rpc/src/eth/pending_block.rs index cae1ff59ca1..88bf2496592 100644 --- a/crates/optimism/rpc/src/eth/pending_block.rs +++ b/crates/optimism/rpc/src/eth/pending_block.rs @@ -6,16 +6,13 @@ use alloy_eips::BlockNumberOrTag; use reth_chain_state::BlockState; use reth_rpc_eth_api::{ helpers::{pending_block::PendingEnvBuilder, LoadPendingBlock, SpawnBlocking}, - FromEvmError, RpcConvert, RpcNodeCore, + FromEvmError, RpcConvert, RpcNodeCore, RpcNodeCoreExt, }; use reth_rpc_eth_types::{ block::BlockAndReceipts, builder::config::PendingBlockKind, error::FromEthApiError, EthApiError, PendingBlock, }; -use reth_storage_api::{ - BlockReader, BlockReaderIdExt, ReceiptProvider, StateProviderBox, StateProviderFactory, -}; -use std::sync::Arc; +use reth_storage_api::{BlockReaderIdExt, StateProviderBox, StateProviderFactory}; impl LoadPendingBlock for OpEthApi where @@ -57,9 +54,7 @@ where Ok(Some(Box::new(state.state_provider(latest_historical)) as StateProviderBox)) } - /// Returns the locally built pending block. - /// - /// This falls back to the latest block as its op convention. + /// Returns the locally built pending block async fn local_pending_block( &self, ) -> Result>, Self::Error> { @@ -72,27 +67,13 @@ where .provider() .latest_header()? .ok_or(EthApiError::HeaderNotFound(BlockNumberOrTag::Latest.into()))?; - let block_id = latest.hash().into(); - let block = self - .provider() - .recovered_block(block_id, Default::default())? - .ok_or(EthApiError::HeaderNotFound(block_id.into()))?; - - let receipts = self - .provider() - .receipts_by_block(block_id)? - .ok_or(EthApiError::ReceiptsNotFound(block_id.into()))?; - - Ok(Some(BlockAndReceipts { block: Arc::new(block), receipts: Arc::new(receipts) })) - } - /// Exclusively returns the pending flashblock if it exits. - async fn local_pending_block_exclusive( - &self, - ) -> Result>, Self::Error> { - if let Ok(Some(pending)) = self.pending_flashblock().await { - return Ok(Some(pending.into_block_and_receipts())); - } - Ok(None) + let latest = self + .cache() + .get_block_and_receipts(latest.hash()) + .await + .map_err(Self::Error::from_eth_err)? + .map(|(block, receipts)| BlockAndReceipts { block, receipts }); + Ok(latest) } } diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index a9dadab5461..22b14d7a174 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -506,11 +506,12 @@ where } // Try to get pending block and receipts if let Ok(Some(pending_block)) = self.eth_api.local_pending_block().await { - if let BlockNumberOrTag::Number(to_block) = to_block - && to_block < pending_block.block.number() { - // this block range is empty based on the user input - return Ok(Vec::new()); - } + if let BlockNumberOrTag::Number(to_block) = to_block && + to_block < pending_block.block.number() + { + // this block range is empty based on the user input + return Ok(Vec::new()); + } let info = self.provider().chain_info()?; if pending_block.block.number() > info.best_number {