1use crate::eth::macros::node_info;
4use alloy_primitives::{Address, B256, Log, LogData, U256};
5use alloy_sol_types::SolValue;
6use foundry_evm::{
7 call_inspectors,
8 decode::decode_console_logs,
9 inspectors::{LogCollector, TracingInspector},
10 traces::{
11 CallTraceDecoder, CallTraceNode, SparsedTraceArena, TracingInspectorConfig,
12 render_trace_arena_inner,
13 },
14};
15use revm::{
16 Inspector,
17 context::{ContextTr, JournalTr},
18 inspector::JournalExt,
19 interpreter::{
20 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, CreateScheme,
21 Interpreter, interpreter::EthInterpreter,
22 },
23};
24use revm_inspectors::transfer::{TRANSFER_EVENT_TOPIC, TRANSFER_LOG_EMITTER, TransferInspector};
25use std::sync::Arc;
26
27#[derive(Clone, Debug, Default)]
29pub struct AnvilInspector {
30 pub tracer: Option<TracingInspector>,
32 pub log_collector: Option<LogCollector>,
34 pub transfer: Option<TransferInspector>,
36 simulation_logs: Option<SimulationLogCollector>,
38}
39
40#[derive(Clone, Debug)]
41struct SimulationLog {
42 log: Log,
43 index: u64,
44 canonical: bool,
45}
46
47#[derive(Clone, Debug, Default)]
49struct SimulationLogCollector {
50 logs: Vec<SimulationLog>,
51 checkpoints: Vec<usize>,
52 next_index: u64,
53 trace_transfers: bool,
54 journal_log_count: usize,
55}
56
57impl SimulationLogCollector {
58 fn push_log(&mut self, log: Log, canonical: bool) {
59 self.logs.push(SimulationLog { log, index: self.next_index, canonical });
60 self.next_index += 1;
61 }
62
63 fn push_canonical_log(&mut self, log: Log, journal_log_count: usize) {
64 self.push_log(log, true);
65 self.journal_log_count = journal_log_count;
66 }
67
68 fn sync_journal_logs(&mut self, logs: &[Log]) {
69 self.journal_log_count = self.journal_log_count.min(logs.len());
70 for log in &logs[self.journal_log_count..] {
71 self.push_log(log.clone(), true);
72 }
73 self.journal_log_count = logs.len();
74 }
75
76 fn push_transfer(&mut self, from: Address, to: Address, value: U256) {
77 if !self.trace_transfers || value.is_zero() {
78 return;
79 }
80 self.push_log(
81 Log {
82 address: TRANSFER_LOG_EMITTER,
83 data: LogData::new_unchecked(
84 vec![
85 TRANSFER_EVENT_TOPIC,
86 B256::from_slice(&from.abi_encode()),
87 B256::from_slice(&to.abi_encode()),
88 ],
89 value.abi_encode().into(),
90 ),
91 },
92 false,
93 );
94 }
95
96 fn frame_start(&mut self) {
97 self.checkpoints.push(self.logs.len());
98 }
99
100 fn frame_end(&mut self, success: bool, journal_log_count: usize) {
101 let checkpoint = self.checkpoints.pop().expect("execution frame checkpoint exists");
102 if !success {
103 self.logs.truncate(checkpoint);
104 }
105 self.journal_log_count = journal_log_count;
106 }
107
108 fn append_remaining_canonical_logs(&mut self, canonical_logs: &[Log]) {
109 let mut canonical_logs = canonical_logs.iter();
110 for collected in self.logs.iter().filter(|log| log.canonical) {
111 let canonical =
112 canonical_logs.next().expect("collected canonical log exists in result");
113 assert_eq!(&collected.log, canonical, "collected canonical logs preserve ordering");
114 }
115 for log in canonical_logs {
116 self.push_log(log.clone(), true);
117 }
118 }
119}
120
121#[derive(Clone, Debug)]
123pub struct InspectorTxConfig {
124 pub print_traces: bool,
126 pub print_logs: bool,
128 pub enable_steps_tracing: bool,
130 pub call_trace_decoder: Arc<CallTraceDecoder>,
132}
133
134impl AnvilInspector {
135 pub fn finish_transaction(&mut self, config: &InspectorTxConfig) -> Vec<CallTraceNode> {
139 if config.print_traces {
141 self.print_traces(config.call_trace_decoder.clone());
142 }
143 self.print_logs();
144
145 let traces = self.tracer.take().map(|t| t.into_traces().into_nodes()).unwrap_or_default();
146
147 let tracing_config = if config.enable_steps_tracing {
149 TracingInspectorConfig::all().with_state_diffs()
150 } else {
151 TracingInspectorConfig::all().set_steps(false)
152 };
153 self.tracer = Some(TracingInspector::new(tracing_config));
154
155 if config.print_logs {
157 self.log_collector = Some(LogCollector::Capture { logs: Vec::new() });
158 }
159
160 traces
161 }
162
163 pub fn print_logs(&self) {
167 if let Some(LogCollector::Capture { logs }) = &self.log_collector {
168 print_logs(logs);
169 }
170 }
171
172 pub fn into_print_traces(mut self, decoder: Arc<CallTraceDecoder>) {
174 if let Some(a) = self.tracer.take() {
175 print_traces(a, decoder);
176 }
177 }
178
179 pub fn print_traces(&self, decoder: Arc<CallTraceDecoder>) {
182 if let Some(a) = self.tracer.clone() {
183 print_traces(a, decoder);
184 }
185 }
186
187 pub fn with_tracing(mut self) -> Self {
189 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().set_steps(false)));
190 self
191 }
192
193 pub fn with_tracing_config(mut self, config: TracingInspectorConfig) -> Self {
195 self.tracer = Some(TracingInspector::new(config));
196 self
197 }
198
199 pub fn with_steps_tracing(mut self) -> Self {
201 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
202 self
203 }
204
205 pub fn with_log_collector(mut self) -> Self {
207 self.log_collector = Some(LogCollector::Capture { logs: Vec::new() });
208 self
209 }
210
211 pub fn with_transfers(mut self) -> Self {
213 self.transfer = Some(TransferInspector::new(false).with_logs(true));
214 self
215 }
216
217 pub fn with_simulation_logs(mut self, trace_transfers: bool) -> Self {
219 self.simulation_logs =
220 Some(SimulationLogCollector { trace_transfers, ..Default::default() });
221 self
222 }
223
224 pub fn take_simulation_logs(
226 &mut self,
227 canonical_logs: &[Log],
228 success: bool,
229 ) -> Option<(Vec<(u64, Log)>, u64)> {
230 self.simulation_logs.take().map(|mut collector| {
231 if success {
232 collector.append_remaining_canonical_logs(canonical_logs);
233 } else {
234 collector.logs.clear();
237 }
238 (
239 collector.logs.into_iter().map(|log| (log.index, log.log)).collect(),
240 collector.next_index,
241 )
242 })
243 }
244
245 pub fn with_trace_printer(mut self) -> Self {
247 self.tracer = Some(TracingInspector::new(TracingInspectorConfig::all().with_state_diffs()));
248 self
249 }
250}
251
252fn print_traces(tracer: TracingInspector, decoder: Arc<CallTraceDecoder>) {
260 let arena = tokio::task::block_in_place(move || {
261 tokio::runtime::Handle::current().block_on(async move {
262 let mut arena = tracer.into_traces();
263 decoder.populate_traces(arena.nodes_mut()).await;
264 arena
265 })
266 });
267
268 let traces =
269 SparsedTraceArena { arena, ignored: Default::default(), diagnostics: Default::default() };
270 let trace = render_trace_arena_inner(&traces, false, true);
271 node_info!(Traces = %format!("\n{}", trace));
272}
273
274impl<CTX> Inspector<CTX, EthInterpreter> for AnvilInspector
275where
276 CTX: ContextTr<Journal: JournalExt>,
277{
278 fn initialize_interp(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
279 if let Some(collector) = &mut self.simulation_logs {
280 collector.sync_journal_logs(ecx.journal().logs());
281 }
282 call_inspectors!([&mut self.tracer], |inspector| {
283 inspector.initialize_interp(interp, ecx);
284 });
285 }
286
287 fn step(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
288 if let Some(collector) = &mut self.simulation_logs {
289 collector.sync_journal_logs(ecx.journal().logs());
290 }
291 call_inspectors!([&mut self.tracer], |inspector| {
292 inspector.step(interp, ecx);
293 });
294 }
295
296 fn step_end(&mut self, interp: &mut Interpreter, ecx: &mut CTX) {
297 call_inspectors!([&mut self.tracer], |inspector| {
298 inspector.step_end(interp, ecx);
299 });
300 if let Some(collector) = &mut self.simulation_logs {
301 collector.sync_journal_logs(ecx.journal().logs());
302 }
303 }
304
305 #[allow(clippy::redundant_clone)]
306 fn log(&mut self, ecx: &mut CTX, log: Log) {
307 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
308 inspector.log(ecx, log.clone());
309 });
310 if let Some(collector) = &mut self.simulation_logs {
311 collector.push_canonical_log(log, ecx.journal().logs().len());
312 }
313 }
314
315 #[allow(clippy::redundant_clone)]
316 fn log_full(&mut self, interp: &mut Interpreter, ecx: &mut CTX, log: Log) {
317 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
318 inspector.log_full(interp, ecx, log.clone());
319 });
320 if let Some(collector) = &mut self.simulation_logs {
321 collector.push_canonical_log(log, ecx.journal().logs().len());
322 }
323 }
324
325 fn call(&mut self, ecx: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
326 if let Some(collector) = &mut self.simulation_logs {
327 collector.sync_journal_logs(ecx.journal().logs());
328 collector.frame_start();
329 if matches!(inputs.scheme, CallScheme::Call)
330 && let Some(value) = inputs.transfer_value()
331 {
332 collector.push_transfer(inputs.transfer_from(), inputs.transfer_to(), value);
333 }
334 }
335 call_inspectors!(
336 #[ret]
337 [&mut self.tracer, &mut self.log_collector, &mut self.transfer],
338 |inspector| inspector.call(ecx, inputs).map(Some),
339 );
340 None
341 }
342
343 fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
344 if let Some(tracer) = &mut self.tracer {
345 tracer.call_end(ecx, inputs, outcome);
346 }
347 if let Some(collector) = &mut self.simulation_logs {
348 collector.sync_journal_logs(ecx.journal().logs());
349 collector.frame_end(outcome.instruction_result().is_ok(), ecx.journal().logs().len());
350 }
351 }
352
353 fn create(&mut self, ecx: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
354 if let Some(collector) = &mut self.simulation_logs {
355 collector.sync_journal_logs(ecx.journal().logs());
356 collector.frame_start();
357 if matches!(inputs.scheme(), CreateScheme::Create | CreateScheme::Create2 { .. })
358 && let Ok(account) = ecx.journal_mut().load_account(inputs.caller())
359 {
360 let address = inputs.created_address(account.data.info.nonce);
361 collector.push_transfer(inputs.caller(), address, inputs.value());
362 }
363 }
364 call_inspectors!(
365 #[ret]
366 [&mut self.tracer, &mut self.transfer],
367 |inspector| inspector.create(ecx, inputs).map(Some),
368 );
369 None
370 }
371
372 fn create_end(&mut self, ecx: &mut CTX, inputs: &CreateInputs, outcome: &mut CreateOutcome) {
373 if let Some(tracer) = &mut self.tracer {
374 tracer.create_end(ecx, inputs, outcome);
375 }
376 if let Some(collector) = &mut self.simulation_logs {
377 collector.sync_journal_logs(ecx.journal().logs());
378 collector.frame_end(
379 outcome.instruction_result().is_ok() && outcome.address.is_some(),
380 ecx.journal().logs().len(),
381 );
382 }
383 }
384
385 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
386 call_inspectors!([&mut self.tracer, &mut self.transfer], |inspector| {
387 Inspector::<CTX, EthInterpreter>::selfdestruct(inspector, contract, target, value)
388 });
389 if let Some(collector) = &mut self.simulation_logs {
390 collector.push_transfer(contract, target, value);
391 }
392 }
393}
394
395pub fn print_logs(logs: &[Log]) {
397 for log in decode_console_logs(logs) {
398 tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
399 }
400}