anvil_zksync_traces/
lib.rs1use anvil_zksync_common::address_map::{self, KNOWN_ADDRESSES};
2use anvil_zksync_types::traces::{
3 CallLog, CallTrace, CallTraceArena, CallTraceNode, DecodedCallEvent, DecodedCallTrace,
4 ExecutionResult, L2L1Log, L2L1Logs, TraceMemberOrder,
5};
6use decode::CallTraceDecoder;
7use writer::TraceWriter;
8use zksync_multivm::interface::{Call, Halt, VmExecutionResultAndLogs};
9use zksync_types::H160;
10
11pub mod abi_utils;
12pub mod decode;
13pub mod format;
14pub mod identifier;
15pub mod writer;
16
17#[inline]
19fn convert_call_to_call_trace(call: &Call) -> CallTrace {
20 let label = KNOWN_ADDRESSES
21 .get(&call.to)
22 .map(|known| known.name.clone());
23
24 let execution_result = if let Some(ref revert_reason) = call.revert_reason {
26 ExecutionResult::Revert {
27 output: revert_reason.clone(),
28 }
29 } else if let Some(ref err) = call.error {
30 ExecutionResult::Halt {
31 reason: Halt::TracerCustom(err.to_string()),
32 }
33 } else {
34 ExecutionResult::Success {
35 output: call.output.clone(),
36 }
37 };
38
39 CallTrace {
40 success: !execution_result.is_failed(),
41 caller: call.from,
42 address: call.to,
43 execution_result,
44 decoded: DecodedCallTrace {
45 label,
46 ..Default::default()
47 },
48 call: call.clone(),
49 }
50}
51
52pub fn build_call_trace_arena(
54 calls: &[Call],
55 tx_result: &VmExecutionResultAndLogs,
56) -> CallTraceArena {
57 let mut arena = CallTraceArena::default();
58
59 if let Some(root_node) = arena.arena.get_mut(0) {
61 root_node.trace.execution_result = tx_result.result.clone().into();
62 }
63
64 for call in calls {
65 process_call_and_subcalls(call, 0, &mut arena, tx_result);
66 }
67 arena
68}
69
70fn process_call_and_subcalls(
72 call: &Call,
73 parent_idx: usize,
74 arena: &mut CallTraceArena,
75 tx_result: &VmExecutionResultAndLogs,
76) {
77 let logs_for_call: Vec<CallLog> = tx_result
79 .logs
80 .events
81 .iter()
82 .enumerate()
83 .filter_map(|(i, vm_event)| {
84 if vm_event.address == call.to {
85 Some(CallLog {
86 raw_log: vm_event.clone(),
87 decoded: DecodedCallEvent::default(),
88 position: i as u64,
89 })
90 } else {
91 None
92 }
93 })
94 .collect();
95
96 let l2_l1_logs_for_call: Vec<L2L1Logs> = tx_result
98 .logs
99 .user_l2_to_l1_logs
100 .iter()
101 .filter(|log| log.0.sender == call.to)
102 .map(|log| L2L1Logs {
103 raw_log: L2L1Log::User(log.clone()),
104 position: log.0.tx_number_in_block as u64,
105 })
106 .chain(
107 tx_result
108 .logs
109 .system_l2_to_l1_logs
110 .iter()
111 .filter(|log| log.0.sender == call.to)
112 .map(|log| L2L1Logs {
113 raw_log: L2L1Log::System(log.clone()),
114 position: log.0.tx_number_in_block as u64,
115 }),
116 )
117 .collect();
118
119 let call_trace = convert_call_to_call_trace(call);
120
121 let node = CallTraceNode {
122 parent: None,
123 children: Vec::new(),
124 idx: 0,
125 trace: call_trace,
126 logs: logs_for_call,
127 l2_l1_logs: l2_l1_logs_for_call,
128 ordering: Vec::new(),
129 };
130
131 let new_parent_idx = arena.add_node(Some(parent_idx), node);
132
133 for subcall in &call.calls {
135 process_call_and_subcalls(subcall, new_parent_idx, arena, tx_result);
136 }
137}
138
139pub fn render_trace_arena_inner(arena: &CallTraceArena, with_bytecodes: bool) -> String {
141 let mut w = TraceWriter::new(Vec::<u8>::new()).write_bytecodes(with_bytecodes);
142 w.write_arena(arena).expect("Failed to write traces");
143 String::from_utf8(w.into_writer()).expect("trace writer wrote invalid UTF-8")
144}
145
146pub async fn decode_trace_arena(arena: &mut CallTraceArena, decoder: &CallTraceDecoder) {
150 decoder.prefetch_signatures(&arena.arena).await;
151 decoder.populate_traces(&mut arena.arena).await;
152}
153
154pub fn filter_call_trace_arena(arena: &CallTraceArena, verbosity: u8) -> CallTraceArena {
156 let mut filtered = CallTraceArena::default();
157
158 if arena.arena.is_empty() {
159 return filtered;
160 }
161
162 let root_idx = 0;
163 let mut root_copy = arena.arena[root_idx].clone();
164 root_copy.parent = None;
165 root_copy.idx = 0;
166 root_copy.children.clear();
167 root_copy.ordering.clear();
168 filtered.arena.push(root_copy);
169
170 filter_node_recursively(
171 &arena.arena[root_idx],
172 arena,
173 &mut filtered,
174 Some(0),
175 verbosity,
176 );
177
178 for node in &mut filtered.arena {
180 rebuild_ordering(node);
181 }
182
183 filtered
184}
185
186fn filter_node_recursively(
187 orig_node: &CallTraceNode,
188 orig_arena: &CallTraceArena,
189 filtered_arena: &mut CallTraceArena,
190 parent_idx: Option<usize>,
191 verbosity: u8,
192) {
193 for &child_idx in &orig_node.children {
194 let child = &orig_arena.arena[child_idx];
195 if should_include_call(&child.trace.address, verbosity) {
196 let new_idx = filtered_arena.arena.len();
197 let mut child_copy = child.clone();
198 child_copy.idx = new_idx;
199 child_copy.parent = parent_idx;
200 child_copy.children.clear();
201 child_copy.ordering.clear();
202
203 child_copy.l2_l1_logs.retain(|log| match &log.raw_log {
205 L2L1Log::User(_) => verbosity >= 2, L2L1Log::System(_) => verbosity >= 3, });
208
209 filtered_arena.arena.push(child_copy);
210
211 if let Some(p_idx) = parent_idx {
212 filtered_arena.arena[p_idx].children.push(new_idx);
213 }
214
215 filter_node_recursively(child, orig_arena, filtered_arena, Some(new_idx), verbosity);
216 } else {
217 filter_node_recursively(child, orig_arena, filtered_arena, parent_idx, verbosity);
218 }
219 }
220}
221
222#[inline]
231fn should_include_call(address: &H160, verbosity: u8) -> bool {
232 let is_system = address_map::is_system(address);
233 let is_precompile = address_map::is_precompile(address);
234
235 match verbosity {
236 0 | 1 => false,
238 2 => !(is_system || is_precompile),
240 3 => !is_precompile,
242 4 => true,
244 _ => true,
246 }
247}
248
249fn rebuild_ordering(node: &mut CallTraceNode) {
250 node.ordering.clear();
251 for i in 0..node.logs.len() {
252 node.ordering.push(TraceMemberOrder::Log(i));
253 }
254 for i in 0..node.l2_l1_logs.len() {
255 node.ordering.push(TraceMemberOrder::L1L2Log(i));
256 }
257 for i in 0..node.children.len() {
258 node.ordering.push(TraceMemberOrder::Call(i));
259 }
260}