From 0024bb018df3b699a993faeca98c3b2f753dffa9 Mon Sep 17 00:00:00 2001 From: Gustavo Figueiredo Date: Wed, 29 Apr 2026 10:13:52 +0100 Subject: [PATCH 1/3] fix(rpc): narrow getLogs retry range --- crates/rpc/rpc/src/eth/filter.rs | 95 ++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 591fd95af2e..18432038d53 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -737,18 +737,24 @@ where is_multi_block_range && all_logs.len() > max_logs_per_response { + let (retry_from_block, retry_to_block) = if num_hash.number == from_block { + (from_block, from_block) + } else { + (from_block, num_hash.number - 1) + }; + debug!( target: "rpc::eth::filter", logs_found = all_logs.len(), max_logs_per_response, - from_block, - to_block = num_hash.number, + from_block = retry_from_block, + to_block = retry_to_block, "Query exceeded max logs per response limit" ); return Err(EthFilterError::QueryExceedsMaxResults { max_logs: max_logs_per_response, - from_block, - to_block: num_hash.number, + from_block: retry_from_block, + to_block: retry_to_block, }); } } @@ -1816,6 +1822,87 @@ mod tests { assert!(result.is_none()); } + #[tokio::test] + async fn test_log_limit_retry_range_excludes_overflow_block() { + let provider = MockEthProvider::default(); + + use alloy_consensus::TxLegacy; + use reth_db_api::models::StoredBlockBodyIndices; + use reth_ethereum_primitives::{TransactionSigned, TxType}; + + let tx_inner = TxLegacy { + chain_id: Some(1), + nonce: 0, + gas_price: 21_000, + gas_limit: 21_000, + to: alloy_primitives::TxKind::Call(alloy_primitives::Address::ZERO), + value: alloy_primitives::U256::ZERO, + input: alloy_primitives::Bytes::new(), + }; + let signature = alloy_primitives::Signature::test_signature(); + let tx = TransactionSigned::new_unhashed(tx_inner.into(), signature); + + let mock_log = alloy_primitives::Log { + address: alloy_primitives::Address::ZERO, + data: alloy_primitives::LogData::new_unchecked(vec![], alloy_primitives::Bytes::new()), + }; + + let receipt = reth_ethereum_primitives::Receipt { + tx_type: TxType::Legacy, + cumulative_gas_used: 21_000, + logs: vec![mock_log], + success: true, + }; + + let mut prev_hash = alloy_primitives::B256::default(); + for (idx, block_number) in (100u64..=102).enumerate() { + let header = alloy_consensus::Header { + number: block_number, + parent_hash: prev_hash, + logs_bloom: alloy_primitives::Bloom::from([1u8; 256]), + ..Default::default() + }; + let hash = header.hash_slow(); + prev_hash = hash; + + let block = reth_ethereum_primitives::Block { + header, + body: reth_ethereum_primitives::BlockBody { + transactions: vec![tx.clone()], + ..Default::default() + }, + }; + provider.add_block(hash, block); + provider.add_receipts(block_number, vec![receipt.clone()]); + provider.add_block_body_indices( + block_number, + StoredBlockBodyIndices { first_tx_num: idx as u64, tx_count: 1 }, + ); + } + + let eth_api = build_test_eth_api(provider); + let eth_filter = EthFilter::new(eth_api, EthFilterConfig::default(), Runtime::test()); + let err = eth_filter + .inner + .clone() + .get_logs_in_block_range( + Filter::default(), + 100, + 102, + QueryLimits { max_blocks_per_filter: None, max_logs_per_response: Some(2) }, + ) + .await + .expect_err("range should exceed max logs"); + + let EthFilterError::QueryExceedsMaxResults { max_logs, from_block, to_block } = err else { + panic!("unexpected error: {err:?}"); + }; + + assert_eq!(max_logs, 2); + assert_eq!(from_block, 100); + assert_eq!(to_block, 101); + } + #[tokio::test] async fn test_non_consecutive_headers_after_bloom_filter() { let provider = MockEthProvider::default(); From 13c0e00fd62b79d969f98182f825e38a9e9e4d9e Mon Sep 17 00:00:00 2001 From: Gustavo Figueiredo Date: Wed, 29 Apr 2026 10:36:17 +0100 Subject: [PATCH 2/3] chore(rpc): simplify getlogs retry range --- crates/rpc/rpc/src/eth/filter.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index 18432038d53..f2b7c1aa718 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -737,23 +737,23 @@ where is_multi_block_range && all_logs.len() > max_logs_per_response { - let (retry_from_block, retry_to_block) = if num_hash.number == from_block { - (from_block, from_block) + let retry_to_block = if num_hash.number == from_block { + from_block } else { - (from_block, num_hash.number - 1) + num_hash.number - 1 }; debug!( target: "rpc::eth::filter", logs_found = all_logs.len(), max_logs_per_response, - from_block = retry_from_block, + from_block, to_block = retry_to_block, "Query exceeded max logs per response limit" ); return Err(EthFilterError::QueryExceedsMaxResults { max_logs: max_logs_per_response, - from_block: retry_from_block, + from_block, to_block: retry_to_block, }); } From 82ab2d8493566ee543188fa0f95aaf5a4ad48cf6 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 29 Apr 2026 11:46:00 +0200 Subject: [PATCH 3/3] chore: apply rustfmt --- crates/rpc/rpc/src/eth/filter.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/rpc/rpc/src/eth/filter.rs b/crates/rpc/rpc/src/eth/filter.rs index f2b7c1aa718..fa1067506bb 100644 --- a/crates/rpc/rpc/src/eth/filter.rs +++ b/crates/rpc/rpc/src/eth/filter.rs @@ -737,11 +737,8 @@ where is_multi_block_range && all_logs.len() > max_logs_per_response { - let retry_to_block = if num_hash.number == from_block { - from_block - } else { - num_hash.number - 1 - }; + let retry_to_block = + if num_hash.number == from_block { from_block } else { num_hash.number - 1 }; debug!( target: "rpc::eth::filter",