Improve eth_getLogs performance for latest block (#6305)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
Iraklis Leontiadis
2024-02-25 14:33:12 +02:00
committed by GitHub
parent 2a10306e3b
commit d3d994cedd
3 changed files with 43 additions and 16 deletions

View File

@@ -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<ChainInfo> for BlockNumHash {
fn from(value: ChainInfo) -> Self {
BlockNumHash { number: value.best_number, hash: value.best_hash }
}
}

View File

@@ -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<Vec<Log>, 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))
}

View File

@@ -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));