From b0fe3d755e863e059b453225cedffe5921a0064d Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sat, 30 Sep 2023 12:35:11 +0200 Subject: [PATCH] feat: impl trace_filter --- crates/rpc/rpc/src/trace.rs | 68 ++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/crates/rpc/rpc/src/trace.rs b/crates/rpc/rpc/src/trace.rs index c3b3e7212c8a1..f38fc0e57e765 100644 --- a/crates/rpc/rpc/src/trace.rs +++ b/crates/rpc/rpc/src/trace.rs @@ -5,7 +5,6 @@ use crate::{ utils::recover_raw_transaction, EthTransactions, }, - result::internal_rpc_err, TracingCallGuard, }; use async_trait::async_trait; @@ -249,12 +248,16 @@ where } } - /// Returns all traces for the given transaction hash + /// Returns all transaction traces that match the given filter. + /// + /// This is similar to [Self::trace_block] but only returns traces for transactions that match + /// the filter. pub async fn trace_filter( &self, filter: TraceFilter, ) -> EthResult> { - let TraceFilter { from_block, to_block, from_address, to_address, after, count } = filter; + let matcher = filter.matcher(); + let TraceFilter { from_block, to_block, after: _after, count: _count, .. } = filter; let start = from_block.unwrap_or(0); let end = if let Some(to_block) = to_block { to_block @@ -265,15 +268,64 @@ where // ensure that the range is not too large, since we need to fetch all blocks in the range let distance = end.saturating_sub(start); if distance > 100 { - return Err( - EthApiError::InvalidParams("Block range too large; currently limited to 100 blocks".to_string()) - ) + return Err(EthApiError::InvalidParams( + "Block range too large; currently limited to 100 blocks".to_string(), + )) + } + + // fetch all blocks in that range + let blocks = self.provider().block_range(start..=end)?; + + // find relevant blocks to trace + let mut target_blocks = Vec::new(); + for block in blocks { + let mut transaction_indices = HashSet::new(); + for (tx_idx, tx) in block.body.iter().enumerate() { + let from = tx.recover_signer().ok_or(BlockError::InvalidSignature)?; + let to = tx.to(); + if matcher.matches(from, to) { + transaction_indices.insert(tx_idx as u64); + } + } + if !transaction_indices.is_empty() { + target_blocks.push((block.number, transaction_indices)); + } } - self.provider().block_ran + // TODO: this could be optimized to only trace the block until the highest matching index in + // that block + + // trace all relevant blocks + let mut block_traces = Vec::with_capacity(target_blocks.len()); + for (num, indices) in target_blocks { + let traces = self.trace_block_with( + num.into(), + TracingInspectorConfig::default_parity(), + move |tx_info, inspector, res, _, _| { + if let Some(idx) = tx_info.index { + if !indices.contains(&idx) { + // only record traces for relevant transactions + return Ok(None) + } + } + let traces = inspector + .with_transaction_gas_used(res.gas_used()) + .into_parity_builder() + .into_localized_transaction_traces(tx_info); + Ok(Some(traces)) + }, + ); + block_traces.push(traces); + } + let block_traces = futures::future::try_join_all(block_traces).await?; + let all_traces = block_traces + .into_iter() + .flatten() + .flat_map(|traces| traces.into_iter().flatten().flat_map(|traces| traces.into_iter())) + .collect(); - todo!() + Ok(all_traces) } /// Returns all traces for the given transaction hash