Skip to content

Commit a6f7af3

Browse files
committed
refactor: optimize trace identifiers
1 parent 2e90e35 commit a6f7af3

File tree

7 files changed

+58
-76
lines changed

7 files changed

+58
-76
lines changed

crates/evm/src/trace/decoder.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,27 @@ use std::collections::{BTreeMap, HashMap};
2020

2121
/// Build a new [CallTraceDecoder].
2222
#[derive(Default)]
23+
#[must_use = "builders do nothing unless you call `build` on them"]
2324
pub struct CallTraceDecoderBuilder {
2425
decoder: CallTraceDecoder,
2526
}
2627

2728
impl CallTraceDecoderBuilder {
29+
/// Create a new builder.
30+
#[inline]
2831
pub fn new() -> Self {
2932
Self { decoder: CallTraceDecoder::new().clone() }
3033
}
3134

3235
/// Add known labels to the decoder.
36+
#[inline]
3337
pub fn with_labels(mut self, labels: impl IntoIterator<Item = (Address, String)>) -> Self {
3438
self.decoder.labels.extend(labels);
3539
self
3640
}
3741

3842
/// Add known events to the decoder.
43+
#[inline]
3944
pub fn with_events(mut self, events: impl IntoIterator<Item = Event>) -> Self {
4045
for event in events {
4146
self.decoder
@@ -48,12 +53,14 @@ impl CallTraceDecoderBuilder {
4853
}
4954

5055
/// Sets the verbosity level of the decoder.
56+
#[inline]
5157
pub fn with_verbosity(mut self, level: u8) -> Self {
5258
self.decoder.verbosity = level;
5359
self
5460
}
5561

5662
/// Build the decoder.
63+
#[inline]
5764
pub fn build(self) -> CallTraceDecoder {
5865
self.decoder
5966
}
@@ -176,15 +183,10 @@ impl CallTraceDecoder {
176183
///
177184
/// Unknown contracts are contracts that either lack a label or an ABI.
178185
pub fn identify(&mut self, trace: &CallTraceArena, identifier: &mut impl TraceIdentifier) {
179-
let unidentified_addresses = trace
180-
.addresses()
181-
.into_iter()
182-
.filter(|(address, _)| {
183-
!self.labels.contains_key(address) || !self.contracts.contains_key(address)
184-
})
185-
.collect();
186-
187-
identifier.identify_addresses(unidentified_addresses).iter().for_each(|identity| {
186+
let unidentified_addresses = trace.addresses().into_iter().filter(|&(address, _)| {
187+
!self.labels.contains_key(address) || !self.contracts.contains_key(address)
188+
});
189+
for identity in identifier.identify_addresses(unidentified_addresses) {
188190
let address = identity.address;
189191

190192
if let Some(contract) = &identity.contract {
@@ -216,11 +218,11 @@ impl CallTraceDecoder {
216218

217219
self.receive_contracts.entry(address).or_insert(abi.receive);
218220
}
219-
});
221+
}
220222
}
221223

222224
pub async fn decode(&self, traces: &mut CallTraceArena) {
223-
for node in traces.arena.iter_mut() {
225+
for node in &mut traces.arena {
224226
// Set contract name
225227
if let Some(contract) = self.contracts.get(&node.trace.address).cloned() {
226228
node.trace.contract = Some(contract);

crates/evm/src/trace/identifier/etherscan.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ impl EtherscanIdentifier {
100100
}
101101

102102
impl TraceIdentifier for EtherscanIdentifier {
103-
fn identify_addresses(
104-
&mut self,
105-
addresses: Vec<(&Address, Option<&[u8]>)>,
106-
) -> Vec<AddressIdentity> {
107-
trace!(target: "etherscanidentifier", "identify {} addresses", addresses.len());
103+
fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec<AddressIdentity>
104+
where
105+
A: Iterator<Item = (&'a Address, Option<&'a [u8]>)>,
106+
{
107+
trace!(target: "etherscanidentifier", "identify {:?} addresses", addresses.size_hint().1);
108108

109109
let Some(client) = self.client.clone() else {
110110
// no client was configured

crates/evm/src/trace/identifier/local.rs

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,40 @@
11
use super::{AddressIdentity, TraceIdentifier};
2-
use ethers::{
3-
abi::{Abi, Address, Event},
4-
prelude::ArtifactId,
5-
};
2+
use ethers::abi::{Address, Event};
63
use foundry_common::contracts::{diff_score, ContractsByArtifact};
7-
use itertools::Itertools;
84
use ordered_float::OrderedFloat;
9-
use std::{borrow::Cow, collections::BTreeMap};
5+
use std::borrow::Cow;
106

117
/// A trace identifier that tries to identify addresses using local contracts.
12-
pub struct LocalTraceIdentifier {
13-
local_contracts: BTreeMap<Vec<u8>, (ArtifactId, Abi)>,
8+
pub struct LocalTraceIdentifier<'a> {
9+
known_contracts: &'a ContractsByArtifact,
1410
}
1511

16-
impl LocalTraceIdentifier {
17-
pub fn new(known_contracts: &ContractsByArtifact) -> Self {
18-
Self {
19-
local_contracts: known_contracts
20-
.iter()
21-
.map(|(id, (abi, runtime_code))| (runtime_code.clone(), (id.clone(), abi.clone())))
22-
.collect(),
23-
}
12+
impl<'a> LocalTraceIdentifier<'a> {
13+
pub fn new(known_contracts: &'a ContractsByArtifact) -> Self {
14+
Self { known_contracts }
2415
}
2516

2617
/// Get all the events of the local contracts.
2718
pub fn events(&self) -> impl Iterator<Item = &Event> {
28-
self.local_contracts.iter().flat_map(|(_, (_, abi))| abi.events())
19+
self.known_contracts.iter().flat_map(|(_, (abi, _))| abi.events())
2920
}
3021
}
3122

32-
impl TraceIdentifier for LocalTraceIdentifier {
33-
fn identify_addresses(
34-
&mut self,
35-
addresses: Vec<(&Address, Option<&[u8]>)>,
36-
) -> Vec<AddressIdentity> {
23+
impl TraceIdentifier for LocalTraceIdentifier<'_> {
24+
fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec<AddressIdentity>
25+
where
26+
A: Iterator<Item = (&'a Address, Option<&'a [u8]>)>,
27+
{
3728
addresses
38-
.into_iter()
3929
.filter_map(|(address, code)| {
4030
let code = code?;
41-
let (_, (_, (id, abi))) = self
42-
.local_contracts
31+
let (_, (id, abi)) = self
32+
.known_contracts
4333
.iter()
44-
.filter_map(|entry| {
45-
let score = diff_score(entry.0, code);
46-
if score < 0.1 {
47-
Some((OrderedFloat(score), entry))
48-
} else {
49-
None
50-
}
34+
.map(|(id, (abi, known_code))| {
35+
(OrderedFloat(diff_score(known_code, code)), (id, abi))
5136
})
52-
.sorted_by_key(|(score, _)| *score)
53-
.next()?;
37+
.min_by_key(|(score, _)| *score)?;
5438

5539
Some(AddressIdentity {
5640
address: *address,

crates/evm/src/trace/identifier/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ pub struct AddressIdentity<'a> {
3333
pub trait TraceIdentifier {
3434
// TODO: Update docs
3535
/// Attempts to identify an address in one or more call traces.
36-
#[allow(clippy::type_complexity)]
37-
fn identify_addresses(
38-
&mut self,
39-
addresses: Vec<(&Address, Option<&[u8]>)>,
40-
) -> Vec<AddressIdentity>;
36+
fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec<AddressIdentity>
37+
where
38+
A: Iterator<Item = (&'a Address, Option<&'a [u8]>)>;
4139
}

crates/evm/src/trace/mod.rs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -584,26 +584,23 @@ pub fn load_contracts(
584584
traces: Traces,
585585
known_contracts: Option<&ContractsByArtifact>,
586586
) -> ContractsByAddress {
587-
if let Some(contracts) = known_contracts {
588-
let mut local_identifier = LocalTraceIdentifier::new(contracts);
589-
let mut decoder = CallTraceDecoderBuilder::new().build();
590-
for (_, trace) in &traces {
591-
decoder.identify(trace, &mut local_identifier);
592-
}
593-
594-
decoder
595-
.contracts
596-
.iter()
597-
.filter_map(|(addr, name)| {
598-
if let Ok(Some((_, (abi, _)))) = contracts.find_by_name_or_identifier(name) {
599-
return Some((*addr, (name.clone(), abi.clone())))
600-
}
601-
None
602-
})
603-
.collect()
604-
} else {
605-
BTreeMap::new()
587+
let Some(contracts) = known_contracts else { return BTreeMap::new() };
588+
let mut local_identifier = LocalTraceIdentifier::new(contracts);
589+
let mut decoder = CallTraceDecoderBuilder::new().build();
590+
for (_, trace) in &traces {
591+
decoder.identify(trace, &mut local_identifier);
606592
}
593+
594+
decoder
595+
.contracts
596+
.iter()
597+
.filter_map(|(addr, name)| {
598+
if let Ok(Some((_, (abi, _)))) = contracts.find_by_name_or_identifier(name) {
599+
return Some((*addr, (name.clone(), abi.clone())))
600+
}
601+
None
602+
})
603+
.collect()
607604
}
608605

609606
/// creates the memory data in 32byte chunks

crates/forge/bin/cmd/script/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl ScriptArgs {
237237

238238
let mut local_identifier = LocalTraceIdentifier::new(known_contracts);
239239
let mut decoder = CallTraceDecoderBuilder::new()
240-
.with_labels(result.labeled_addresses.clone())
240+
.with_labels(result.labeled_addresses.iter().map(|(a, s)| (*a, s.clone())))
241241
.with_verbosity(verbosity)
242242
.build();
243243

crates/forge/bin/cmd/test/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -540,7 +540,8 @@ async fn test(
540540
Ok(TestOutcome::new(results, allow_failure))
541541
} else {
542542
// Set up identifiers
543-
let mut local_identifier = LocalTraceIdentifier::new(&runner.known_contracts);
543+
let known_contracts = runner.known_contracts.clone();
544+
let mut local_identifier = LocalTraceIdentifier::new(&known_contracts);
544545
let remote_chain_id = runner.evm_opts.get_remote_chain_id();
545546
// Do not re-query etherscan for contracts that you've already queried today.
546547
let mut etherscan_identifier = EtherscanIdentifier::new(&config, remote_chain_id)?;

0 commit comments

Comments
 (0)