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