foundry_evm_traces/identifier/
local.rs
1use super::{AddressIdentity, TraceIdentifier};
2use alloy_json_abi::JsonAbi;
3use alloy_primitives::Address;
4use foundry_common::contracts::{bytecode_diff_score, ContractsByArtifact};
5use foundry_compilers::ArtifactId;
6use std::borrow::Cow;
7
8pub struct LocalTraceIdentifier<'a> {
10 known_contracts: &'a ContractsByArtifact,
12 ordered_ids: Vec<(&'a ArtifactId, usize)>,
14}
15
16impl<'a> LocalTraceIdentifier<'a> {
17 #[inline]
19 pub fn new(known_contracts: &'a ContractsByArtifact) -> Self {
20 let mut ordered_ids = known_contracts
21 .iter()
22 .filter_map(|(id, contract)| Some((id, contract.deployed_bytecode()?)))
23 .map(|(id, bytecode)| (id, bytecode.len()))
24 .collect::<Vec<_>>();
25 ordered_ids.sort_by_key(|(_, len)| *len);
26 Self { known_contracts, ordered_ids }
27 }
28
29 #[inline]
31 pub fn contracts(&self) -> &'a ContractsByArtifact {
32 self.known_contracts
33 }
34
35 pub fn identify_code(
37 &self,
38 runtime_code: &[u8],
39 creation_code: &[u8],
40 ) -> Option<(&'a ArtifactId, &'a JsonAbi)> {
41 let len = runtime_code.len();
42
43 let mut min_score = f64::MAX;
44 let mut min_score_id = None;
45
46 let mut check = |id, is_creation, min_score: &mut f64| {
47 let contract = self.known_contracts.get(id)?;
48 let (contract_bytecode, current_bytecode) = if is_creation {
50 (contract.bytecode(), creation_code)
51 } else {
52 (contract.deployed_bytecode(), runtime_code)
53 };
54
55 if let Some(bytecode) = contract_bytecode {
56 let score = bytecode_diff_score(bytecode, current_bytecode);
57 if score == 0.0 {
58 trace!(target: "evm::traces", "found exact match");
59 return Some((id, &contract.abi));
60 }
61 if score < *min_score {
62 *min_score = score;
63 min_score_id = Some((id, &contract.abi));
64 }
65 }
66 None
67 };
68
69 let max_len = (len * 11) / 10;
71
72 let same_length_idx = self.find_index(len);
74 for idx in same_length_idx..self.ordered_ids.len() {
75 let (id, len) = self.ordered_ids[idx];
76 if len > max_len {
77 break;
78 }
79 if let found @ Some(_) = check(id, true, &mut min_score) {
80 return found;
81 }
82 }
83
84 let min_len = (len * 9) / 10;
86 let idx = self.find_index(min_len);
87 for i in idx..same_length_idx {
88 let (id, _) = self.ordered_ids[i];
89 if let found @ Some(_) = check(id, true, &mut min_score) {
90 return found;
91 }
92 }
93
94 if min_score >= 0.85 {
96 for (artifact, _) in &self.ordered_ids {
97 if let found @ Some(_) = check(artifact, false, &mut min_score) {
98 return found;
99 }
100 }
101 }
102
103 trace!(target: "evm::traces", %min_score, "no exact match found");
104
105 if min_score < 0.85 {
108 min_score_id
109 } else {
110 None
111 }
112 }
113
114 fn find_index(&self, len: usize) -> usize {
117 let (Ok(mut idx) | Err(mut idx)) =
118 self.ordered_ids.binary_search_by_key(&len, |(_, probe)| *probe);
119
120 while idx > 0 && self.ordered_ids[idx - 1].1 == len {
122 idx -= 1;
123 }
124
125 idx
126 }
127}
128
129impl TraceIdentifier for LocalTraceIdentifier<'_> {
130 fn identify_addresses<'a, A>(&mut self, addresses: A) -> Vec<AddressIdentity<'_>>
131 where
132 A: Iterator<Item = (&'a Address, Option<&'a [u8]>, Option<&'a [u8]>)>,
133 {
134 trace!(target: "evm::traces", "identify {:?} addresses", addresses.size_hint().1);
135
136 addresses
137 .filter_map(|(address, runtime_code, creation_code)| {
138 let _span = trace_span!(target: "evm::traces", "identify", %address).entered();
139
140 trace!(target: "evm::traces", "identifying");
141 let (id, abi) = self.identify_code(runtime_code?, creation_code?)?;
142 trace!(target: "evm::traces", id=%id.identifier(), "identified");
143
144 Some(AddressIdentity {
145 address: *address,
146 contract: Some(id.identifier()),
147 label: Some(id.name.clone()),
148 abi: Some(Cow::Borrowed(abi)),
149 artifact_id: Some(id.clone()),
150 })
151 })
152 .collect()
153 }
154}