Skip to main content

foundry_evm/inspectors/
stack.rs

1use super::{
2    Cheatcodes, CheatsConfig, ChiselState, CustomPrintTracer, Fuzzer, LineCoverageCollector,
3    LogCollector, RevertDiagnostic, ScriptExecutionInspector, TracingInspector,
4};
5use alloy_evm::EvmEnv;
6use alloy_primitives::{
7    Address, B256, Bytes, Log, TxKind, U256,
8    map::{AddressHashMap, HashMap},
9};
10use foundry_cheatcodes::{
11    CheatcodeAnalysis, CheatcodesExecutor, EthCheatCtx, NestedEvmClosure, Wallets,
12};
13use foundry_common::compile::Analysis;
14use foundry_compilers::ProjectPathsConfig;
15use foundry_evm_core::{
16    FoundryBlock, FoundryInspectorExt, FoundryTransaction,
17    backend::{DatabaseError, DatabaseExt, JournaledState},
18    env::FoundryContextExt,
19    evm::{NestedEvm, new_eth_evm_with_inspector, with_cloned_context},
20};
21use foundry_evm_coverage::HitMaps;
22use foundry_evm_networks::NetworkConfigs;
23use foundry_evm_traces::{SparsedTraceArena, TraceMode};
24use revm::{
25    Inspector,
26    context::{
27        Block, BlockEnv, Cfg, ContextTr, JournalTr, Transaction, TxEnv,
28        result::{EVMError, ExecutionResult, Output},
29    },
30    context_interface::CreateScheme,
31    inspector::JournalExt,
32    interpreter::{
33        CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, Gas, InstructionResult,
34        Interpreter, InterpreterResult,
35    },
36    state::{Account, AccountStatus},
37};
38use revm_inspectors::edge_cov::EdgeCovInspector;
39use std::{
40    ops::{Deref, DerefMut},
41    sync::Arc,
42};
43
44#[derive(Clone, Debug, Default)]
45#[must_use = "builders do nothing unless you call `build` on them"]
46pub struct InspectorStackBuilder {
47    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities.
48    pub analysis: Option<Analysis>,
49    /// The block environment.
50    ///
51    /// Used in the cheatcode handler to overwrite the block environment separately from the
52    /// execution block environment.
53    pub block: Option<BlockEnv>,
54    /// The gas price.
55    ///
56    /// Used in the cheatcode handler to overwrite the gas price separately from the gas price
57    /// in the execution environment.
58    pub gas_price: Option<u128>,
59    /// The cheatcodes config.
60    pub cheatcodes: Option<Arc<CheatsConfig>>,
61    /// The fuzzer inspector and its state, if it exists.
62    pub fuzzer: Option<Fuzzer>,
63    /// Whether to enable tracing and revert diagnostics.
64    pub trace_mode: TraceMode,
65    /// Whether logs should be collected.
66    /// - None for no log collection.
67    /// - Some(true) for realtime console.log-ing.
68    /// - Some(false) for log collection.
69    pub logs: Option<bool>,
70    /// Whether line coverage info should be collected.
71    pub line_coverage: Option<bool>,
72    /// Whether to print all opcode traces into the console. Useful for debugging the EVM.
73    pub print: Option<bool>,
74    /// The chisel state inspector.
75    pub chisel_state: Option<usize>,
76    /// Whether to enable call isolation.
77    /// In isolation mode all top-level calls are executed as a separate transaction in a separate
78    /// EVM context, enabling more precise gas accounting and transaction state changes.
79    pub enable_isolation: bool,
80    /// Networks with enabled features.
81    pub networks: NetworkConfigs,
82    /// The wallets to set in the cheatcodes context.
83    pub wallets: Option<Wallets>,
84    /// The CREATE2 deployer address.
85    pub create2_deployer: Address,
86}
87
88impl InspectorStackBuilder {
89    /// Create a new inspector stack builder.
90    #[inline]
91    pub fn new() -> Self {
92        Self::default()
93    }
94
95    /// Set the solar compiler instance that grants syntactic and semantic analysis capabilities
96    #[inline]
97    pub fn set_analysis(mut self, analysis: Analysis) -> Self {
98        self.analysis = Some(analysis);
99        self
100    }
101
102    /// Set the block environment.
103    #[inline]
104    pub fn block(mut self, block: BlockEnv) -> Self {
105        self.block = Some(block);
106        self
107    }
108
109    /// Set the gas price.
110    #[inline]
111    pub fn gas_price(mut self, gas_price: u128) -> Self {
112        self.gas_price = Some(gas_price);
113        self
114    }
115
116    /// Enable cheatcodes with the given config.
117    #[inline]
118    pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
119        self.cheatcodes = Some(config);
120        self
121    }
122
123    /// Set the wallets.
124    #[inline]
125    pub fn wallets(mut self, wallets: Wallets) -> Self {
126        self.wallets = Some(wallets);
127        self
128    }
129
130    /// Set the fuzzer inspector.
131    #[inline]
132    pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
133        self.fuzzer = Some(fuzzer);
134        self
135    }
136
137    /// Set the Chisel inspector.
138    #[inline]
139    pub fn chisel_state(mut self, final_pc: usize) -> Self {
140        self.chisel_state = Some(final_pc);
141        self
142    }
143
144    /// Set the log collector, and whether to print the logs directly to stdout.
145    #[inline]
146    pub fn logs(mut self, live_logs: bool) -> Self {
147        self.logs = Some(live_logs);
148        self
149    }
150
151    /// Set whether to collect line coverage information.
152    #[inline]
153    pub fn line_coverage(mut self, yes: bool) -> Self {
154        self.line_coverage = Some(yes);
155        self
156    }
157
158    /// Set whether to enable the trace printer.
159    #[inline]
160    pub fn print(mut self, yes: bool) -> Self {
161        self.print = Some(yes);
162        self
163    }
164
165    /// Set whether to enable the tracer.
166    /// Revert diagnostic inspector is activated when `mode != TraceMode::None`
167    #[inline]
168    pub fn trace_mode(mut self, mode: TraceMode) -> Self {
169        if self.trace_mode < mode {
170            self.trace_mode = mode
171        }
172        self
173    }
174
175    /// Set whether to enable the call isolation.
176    /// For description of call isolation, see [`InspectorStack::enable_isolation`].
177    #[inline]
178    pub fn enable_isolation(mut self, yes: bool) -> Self {
179        self.enable_isolation = yes;
180        self
181    }
182
183    /// Set networks with enabled features.
184    #[inline]
185    pub fn networks(mut self, networks: NetworkConfigs) -> Self {
186        self.networks = networks;
187        self
188    }
189
190    #[inline]
191    pub fn create2_deployer(mut self, create2_deployer: Address) -> Self {
192        self.create2_deployer = create2_deployer;
193        self
194    }
195
196    /// Builds the stack of inspectors to use when transacting/committing on the EVM.
197    pub fn build(self) -> InspectorStack {
198        let Self {
199            analysis,
200            block,
201            gas_price,
202            cheatcodes,
203            fuzzer,
204            trace_mode,
205            logs,
206            line_coverage,
207            print,
208            chisel_state,
209            enable_isolation,
210            networks,
211            wallets,
212            create2_deployer,
213        } = self;
214        let mut stack = InspectorStack::new();
215
216        // inspectors
217        if let Some(config) = cheatcodes {
218            let mut cheatcodes = Cheatcodes::new(config);
219            // Set analysis capabilities if they are provided
220            if let Some(analysis) = analysis {
221                stack.set_analysis(analysis.clone());
222                cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
223            }
224            // Set wallets if they are provided
225            if let Some(wallets) = wallets {
226                cheatcodes.set_wallets(wallets);
227            }
228            stack.set_cheatcodes(cheatcodes);
229        }
230
231        if let Some(fuzzer) = fuzzer {
232            stack.set_fuzzer(fuzzer);
233        }
234        if let Some(chisel_state) = chisel_state {
235            stack.set_chisel(chisel_state);
236        }
237        stack.collect_line_coverage(line_coverage.unwrap_or(false));
238        stack.collect_logs(logs);
239        stack.print(print.unwrap_or(false));
240        stack.tracing(trace_mode);
241
242        stack.enable_isolation(enable_isolation);
243        stack.networks(networks);
244        stack.set_create2_deployer(create2_deployer);
245
246        // environment, must come after all of the inspectors
247        if let Some(block) = block {
248            stack.set_block(&block);
249        }
250        if let Some(gas_price) = gas_price {
251            stack.set_gas_price(gas_price);
252        }
253
254        stack
255    }
256}
257
258/// Helper macro to call the same method on multiple inspectors without resorting to dynamic
259/// dispatch.
260#[macro_export]
261macro_rules! call_inspectors {
262    ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
263        $(
264            if let Some($id) = $inspector {
265                $crate::utils::cold_path();
266                $body;
267            }
268        )+
269    };
270    (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
271        $(
272            if let Some($id) = $inspector {
273                $crate::utils::cold_path();
274                if let Some(result) = $body {
275                    return result;
276                }
277            }
278        )+
279    }};
280}
281
282/// The collected results of [`InspectorStack`].
283pub struct InspectorData {
284    pub logs: Vec<Log>,
285    pub labels: AddressHashMap<String>,
286    pub traces: Option<SparsedTraceArena>,
287    pub line_coverage: Option<HitMaps>,
288    pub edge_coverage: Option<Vec<u8>>,
289    pub cheatcodes: Option<Box<Cheatcodes>>,
290    pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
291    pub reverter: Option<Address>,
292}
293
294/// Contains data about the state of outer/main EVM which created and invoked the inner EVM context.
295/// Used to adjust EVM state while in inner context.
296///
297/// We need this to avoid breaking changes due to EVM behavior differences in isolated vs
298/// non-isolated mode. For descriptions and workarounds for those changes see: <https://github.com/foundry-rs/foundry/pull/7186#issuecomment-1959102195>
299#[derive(Debug, Clone)]
300pub struct InnerContextData {
301    /// Origin of the transaction in the outer EVM context.
302    original_origin: Address,
303}
304
305/// An inspector that calls multiple inspectors in sequence.
306///
307/// If a call to an inspector returns a value (indicating a stop or revert) the remaining inspectors
308/// are not called.
309///
310/// Stack is divided into [Cheatcodes] and `InspectorStackInner`. This is done to allow assembling
311/// `InspectorStackRefMut` inside [Cheatcodes] to allow usage of it as [revm::Inspector]. This gives
312/// us ability to create and execute separate EVM frames from inside cheatcodes while still having
313/// access to entire stack of inspectors and correctly handling traces, logs, debugging info
314/// collection, etc.
315#[derive(Clone, Debug, Default)]
316pub struct InspectorStack {
317    pub cheatcodes: Option<Box<Cheatcodes>>,
318    pub inner: InspectorStackInner,
319}
320
321impl InspectorStack {
322    pub fn paths_config(&self) -> Option<&ProjectPathsConfig> {
323        self.cheatcodes.as_ref().map(|c| &c.config.paths)
324    }
325}
326
327/// All used inpectors besides [Cheatcodes].
328///
329/// See [`InspectorStack`].
330#[derive(Default, Clone, Debug)]
331pub struct InspectorStackInner {
332    /// Solar compiler instance, to grant syntactic and semantic analysis capabilities.
333    pub analysis: Option<Analysis>,
334
335    // Inspectors.
336    // These are boxed to reduce the size of the struct and slightly improve performance of the
337    // `if let Some` checks.
338    pub chisel_state: Option<Box<ChiselState>>,
339    pub edge_coverage: Option<Box<EdgeCovInspector>>,
340    pub fuzzer: Option<Box<Fuzzer>>,
341    pub line_coverage: Option<Box<LineCoverageCollector>>,
342    pub log_collector: Option<Box<LogCollector>>,
343    pub printer: Option<Box<CustomPrintTracer>>,
344    pub revert_diag: Option<Box<RevertDiagnostic>>,
345    pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
346    pub tracer: Option<Box<TracingInspector>>,
347
348    // EthInspectorExt and other internal data.
349    pub enable_isolation: bool,
350    pub networks: NetworkConfigs,
351    pub create2_deployer: Address,
352    /// Flag marking if we are in the inner EVM context.
353    pub in_inner_context: bool,
354    pub inner_context_data: Option<InnerContextData>,
355    pub top_frame_journal: HashMap<Address, Account>,
356    /// Address that reverted the call, if any.
357    pub reverter: Option<Address>,
358}
359
360/// Struct keeping mutable references to both parts of [InspectorStack] and implementing
361/// [revm::Inspector]. This struct can be obtained via [InspectorStack::as_mut].
362pub struct InspectorStackRefMut<'a> {
363    pub cheatcodes: Option<&'a mut Cheatcodes>,
364    pub inner: &'a mut InspectorStackInner,
365}
366
367impl<CTX: EthCheatCtx> CheatcodesExecutor<CTX> for InspectorStackInner {
368    fn with_nested_evm(
369        &mut self,
370        cheats: &mut Cheatcodes,
371        ecx: &mut CTX,
372        f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
373    ) -> Result<(), EVMError<DatabaseError>> {
374        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
375        with_cloned_context(ecx, |db, evm_env, tx_env, journal_inner| {
376            let mut evm = new_eth_evm_with_inspector(db, evm_env, tx_env, &mut inspector);
377            *evm.journal_inner_mut() = journal_inner;
378            f(&mut evm)?;
379            let sub_evm_env = evm.to_evm_env();
380            let sub_inner = evm.journaled_state.inner.clone();
381            Ok((sub_evm_env, sub_inner))
382        })
383    }
384
385    fn with_fresh_nested_evm(
386        &mut self,
387        cheats: &mut Cheatcodes,
388        db: &mut dyn DatabaseExt<CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
389        evm_env: EvmEnv<<CTX::Cfg as Cfg>::Spec, CTX::Block>,
390        tx_env: CTX::Tx,
391        f: NestedEvmClosure<'_, CTX::Block, CTX::Tx, <CTX::Cfg as Cfg>::Spec>,
392    ) -> Result<(), EVMError<DatabaseError>> {
393        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
394        let mut evm = new_eth_evm_with_inspector(db, evm_env, tx_env, &mut inspector);
395        f(&mut evm)
396    }
397
398    fn transact_on_db(
399        &mut self,
400        cheats: &mut Cheatcodes,
401        ecx: &mut CTX,
402        fork_id: Option<U256>,
403        transaction: B256,
404    ) -> eyre::Result<()> {
405        let evm_env = ecx.evm_clone();
406        let tx_env = ecx.tx_clone();
407        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
408        let (db, inner) = ecx.db_journal_inner_mut();
409        db.transact(fork_id, transaction, evm_env, tx_env, inner, &mut inspector)
410    }
411
412    fn transact_from_tx_on_db(
413        &mut self,
414        cheats: &mut Cheatcodes,
415        ecx: &mut CTX,
416        tx_env: &CTX::Tx,
417    ) -> eyre::Result<()> {
418        let evm_env = ecx.evm_clone();
419        let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
420        let (db, inner) = ecx.db_journal_inner_mut();
421        db.transact_from_tx(tx_env, evm_env, inner, &mut inspector)
422    }
423
424    fn console_log(&mut self, _cheats: &mut Cheatcodes, msg: &str) {
425        if let Some(ref mut collector) = self.log_collector {
426            FoundryInspectorExt::console_log(&mut **collector, msg);
427        }
428    }
429
430    fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
431        self.tracer.as_deref_mut()
432    }
433
434    fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
435        self.in_inner_context = enabled;
436        self.inner_context_data = if enabled {
437            Some(InnerContextData {
438                original_origin: original_origin.expect("origin required when enabling inner ctx"),
439            })
440        } else {
441            None
442        };
443    }
444}
445
446impl InspectorStack {
447    /// Creates a new inspector stack.
448    ///
449    /// Note that the stack is empty by default, and you must add inspectors to it.
450    /// This is done by calling the `set_*` methods on the stack directly, or by building the stack
451    /// with [`InspectorStack`].
452    #[inline]
453    pub fn new() -> Self {
454        Self::default()
455    }
456
457    /// Logs the status of the inspectors.
458    pub fn log_status(&self) {
459        trace!(enabled=%{
460            let mut enabled = Vec::with_capacity(16);
461            macro_rules! push {
462                ($($id:ident),* $(,)?) => {
463                    $(
464                        if self.$id.is_some() {
465                            enabled.push(stringify!($id));
466                        }
467                    )*
468                };
469            }
470            push!(cheatcodes, chisel_state, line_coverage, fuzzer, log_collector, printer, tracer);
471            if self.enable_isolation {
472                enabled.push("isolation");
473            }
474            format!("[{}]", enabled.join(", "))
475        });
476    }
477
478    /// Set the solar compiler instance.
479    #[inline]
480    pub fn set_analysis(&mut self, analysis: Analysis) {
481        self.analysis = Some(analysis);
482    }
483
484    /// Set variables from an environment for the relevant inspectors.
485    #[inline]
486    pub fn set_env(&mut self, evm_env: &EvmEnv, tx_env: &TxEnv) {
487        self.set_block(&evm_env.block_env);
488        self.set_gas_price(tx_env.gas_price);
489    }
490
491    /// Sets the block for the relevant inspectors.
492    #[inline]
493    pub fn set_block(&mut self, block: &BlockEnv) {
494        if let Some(cheatcodes) = &mut self.cheatcodes {
495            cheatcodes.block = Some(block.clone());
496        }
497    }
498
499    /// Sets the gas price for the relevant inspectors.
500    #[inline]
501    pub fn set_gas_price(&mut self, gas_price: u128) {
502        if let Some(cheatcodes) = &mut self.cheatcodes {
503            cheatcodes.gas_price = Some(gas_price);
504        }
505    }
506
507    /// Set the cheatcodes inspector.
508    #[inline]
509    pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes) {
510        self.cheatcodes = Some(cheatcodes.into());
511    }
512
513    /// Set the fuzzer inspector.
514    #[inline]
515    pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
516        self.fuzzer = Some(fuzzer.into());
517    }
518
519    /// Set the Chisel inspector.
520    #[inline]
521    pub fn set_chisel(&mut self, final_pc: usize) {
522        self.chisel_state = Some(ChiselState::new(final_pc).into());
523    }
524
525    /// Set whether to enable the line coverage collector.
526    #[inline]
527    pub fn collect_line_coverage(&mut self, yes: bool) {
528        self.line_coverage = yes.then(Default::default);
529    }
530
531    /// Set whether to enable the edge coverage collector.
532    #[inline]
533    pub fn collect_edge_coverage(&mut self, yes: bool) {
534        // TODO: configurable edge size?
535        self.edge_coverage = yes.then(EdgeCovInspector::new).map(Into::into);
536    }
537
538    /// Set whether to enable call isolation.
539    #[inline]
540    pub fn enable_isolation(&mut self, yes: bool) {
541        self.enable_isolation = yes;
542    }
543
544    /// Set networks with enabled features.
545    #[inline]
546    pub fn networks(&mut self, networks: NetworkConfigs) {
547        self.networks = networks;
548    }
549
550    /// Set the CREATE2 deployer address.
551    #[inline]
552    pub fn set_create2_deployer(&mut self, deployer: Address) {
553        self.create2_deployer = deployer;
554    }
555
556    /// Set whether to enable the log collector.
557    /// - None for no log collection.
558    /// - Some(true) for realtime console.log-ing.
559    /// - Some(false) for log collection.
560    #[inline]
561    pub fn collect_logs(&mut self, live_logs: Option<bool>) {
562        self.log_collector = live_logs.map(|live_logs| {
563            Box::new(if live_logs {
564                LogCollector::LiveLogs
565            } else {
566                LogCollector::Capture { logs: Vec::new() }
567            })
568        });
569    }
570
571    /// Set whether to enable the trace printer.
572    #[inline]
573    pub fn print(&mut self, yes: bool) {
574        self.printer = yes.then(Default::default);
575    }
576
577    /// Set whether to enable the tracer.
578    /// Revert diagnostic inspector is activated when `mode != TraceMode::None`
579    #[inline]
580    pub fn tracing(&mut self, mode: TraceMode) {
581        self.revert_diag = (!mode.is_none()).then(RevertDiagnostic::default).map(Into::into);
582
583        if let Some(config) = mode.into_config() {
584            *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
585        } else {
586            self.tracer = None;
587        }
588    }
589
590    /// Set whether to enable script execution inspector.
591    #[inline]
592    pub fn script(&mut self, script_address: Address) {
593        self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
594            script_address;
595    }
596
597    #[inline(always)]
598    fn as_mut(&mut self) -> InspectorStackRefMut<'_> {
599        InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
600    }
601
602    /// Collects all the data gathered during inspection into a single struct.
603    pub fn collect(self) -> InspectorData {
604        let Self {
605            mut cheatcodes,
606            inner:
607                InspectorStackInner {
608                    chisel_state,
609                    line_coverage,
610                    edge_coverage,
611                    log_collector,
612                    tracer,
613                    reverter,
614                    ..
615                },
616        } = self;
617
618        let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
619            let ignored = cheatcodes
620                .as_mut()
621                .map(|cheatcodes| {
622                    let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
623
624                    // If the last pause call was not resumed, ignore the rest of the trace
625                    if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
626                        ignored.insert(last_pause_call, (arena.nodes().len(), 0));
627                    }
628
629                    ignored
630                })
631                .unwrap_or_default();
632
633            SparsedTraceArena { arena, ignored }
634        });
635
636        InspectorData {
637            logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
638            labels: cheatcodes
639                .as_ref()
640                .map(|cheatcodes| cheatcodes.labels.clone())
641                .unwrap_or_default(),
642            traces,
643            line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
644            edge_coverage: edge_coverage.map(|edge_coverage| edge_coverage.into_hitcount()),
645            cheatcodes,
646            chisel_state: chisel_state.and_then(|state| state.state),
647            reverter,
648        }
649    }
650}
651
652impl InspectorStackRefMut<'_> {
653    /// Adjusts the EVM data for the inner EVM context.
654    /// Should be called on the top-level call of inner context (depth == 0 &&
655    /// self.in_inner_context) Decreases sender nonce for CALLs to keep backwards compatibility
656    /// Updates tx.origin to the value before entering inner context
657    fn adjust_evm_data_for_inner_context<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
658        let inner_context_data =
659            self.inner_context_data.as_ref().expect("should be called in inner context");
660        ecx.tx_mut().set_caller(inner_context_data.original_origin);
661    }
662
663    fn do_call_end<CTX: EthCheatCtx>(
664        &mut self,
665        ecx: &mut CTX,
666        inputs: &CallInputs,
667        outcome: &mut CallOutcome,
668    ) -> CallOutcome {
669        let result = outcome.result.result;
670        call_inspectors!(
671            #[ret]
672            [
673                &mut self.fuzzer,
674                &mut self.tracer,
675                &mut self.cheatcodes,
676                &mut self.printer,
677                &mut self.revert_diag
678            ],
679            |inspector| {
680                let previous_outcome = outcome.clone();
681                inspector.call_end(ecx, inputs, outcome);
682
683                // If the inspector returns a different status or a revert with a non-empty message,
684                // we assume it wants to tell us something
685                let different = outcome.result.result != result
686                    || (outcome.result.result == InstructionResult::Revert
687                        && outcome.output() != previous_outcome.output());
688                different.then_some(outcome.clone())
689            },
690        );
691
692        // Record first address that reverted the call.
693        if result.is_revert() && self.reverter.is_none() {
694            self.reverter = Some(inputs.target_address);
695        }
696
697        outcome.clone()
698    }
699
700    fn do_create_end<CTX: EthCheatCtx>(
701        &mut self,
702        ecx: &mut CTX,
703        call: &CreateInputs,
704        outcome: &mut CreateOutcome,
705    ) -> CreateOutcome {
706        let result = outcome.result.result;
707        call_inspectors!(
708            #[ret]
709            [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
710            |inspector| {
711                let previous_outcome = outcome.clone();
712                inspector.create_end(ecx, call, outcome);
713
714                // If the inspector returns a different status or a revert with a non-empty message,
715                // we assume it wants to tell us something
716                let different = outcome.result.result != result
717                    || (outcome.result.result == InstructionResult::Revert
718                        && outcome.output() != previous_outcome.output());
719                different.then_some(outcome.clone())
720            },
721        );
722
723        outcome.clone()
724    }
725
726    fn transact_inner<CTX: EthCheatCtx>(
727        &mut self,
728        ecx: &mut CTX,
729        kind: TxKind,
730        caller: Address,
731        input: Bytes,
732        gas_limit: u64,
733        value: U256,
734    ) -> (InterpreterResult, Option<Address>) {
735        let cached_evm_env = ecx.evm_clone();
736        let cached_tx_env = ecx.tx_clone();
737
738        ecx.block_mut().set_basefee(0);
739
740        let chain_id = ecx.cfg().chain_id();
741        ecx.tx_mut().set_chain_id(Some(chain_id));
742        ecx.tx_mut().set_caller(caller);
743        ecx.tx_mut().set_kind(kind);
744        ecx.tx_mut().set_data(input);
745        ecx.tx_mut().set_value(value);
746        // Add 21000 to the gas limit to account for the base cost of transaction.
747        ecx.tx_mut().set_gas_limit(gas_limit + 21000);
748
749        // If we haven't disabled gas limit checks, ensure that transaction gas limit will not
750        // exceed block gas limit.
751        if !ecx.cfg().is_block_gas_limit_disabled() {
752            let gas_limit = std::cmp::min(ecx.tx().gas_limit(), ecx.block().gas_limit());
753            ecx.tx_mut().set_gas_limit(gas_limit);
754        }
755        ecx.tx_mut().set_gas_price(0);
756
757        self.inner_context_data = Some(InnerContextData { original_origin: cached_tx_env.caller });
758        self.in_inner_context = true;
759
760        let evm_env = ecx.evm_clone();
761        let tx_env = ecx.tx_clone();
762
763        let res = self.with_inspector(|mut inspector| {
764            let (res, nested_env) = {
765                let (db, journal) = ecx.db_journal_inner_mut();
766                let mut evm =
767                    new_eth_evm_with_inspector(db, evm_env, tx_env.clone(), &mut inspector);
768
769                evm.journal_inner_mut().state = {
770                    let mut state = journal.state.clone();
771
772                    for (addr, acc_mut) in &mut state {
773                        // mark all accounts cold, besides preloaded addresses
774                        if journal.warm_addresses.is_cold(addr) {
775                            acc_mut.mark_cold();
776                        }
777
778                        // mark all slots cold
779                        for slot_mut in acc_mut.storage.values_mut() {
780                            slot_mut.is_cold = true;
781                            slot_mut.original_value = slot_mut.present_value;
782                        }
783                    }
784
785                    state
786                };
787
788                // set depth to 1 to make sure traces are collected correctly
789                evm.journal_inner_mut().depth = 1;
790
791                let res = evm.transact(tx_env);
792                let nested_evm_env = evm.to_evm_env();
793                (res, nested_evm_env)
794            };
795
796            // Restore env, preserving cheatcode cfg/block changes from the nested EVM
797            // but restoring the original tx and basefee (which we zeroed for the nested call).
798            let mut restored_evm_env = nested_env;
799            restored_evm_env.block_env.basefee = cached_evm_env.block_env.basefee;
800            ecx.set_evm(restored_evm_env);
801            ecx.set_tx(cached_tx_env);
802
803            res
804        });
805
806        self.in_inner_context = false;
807        self.inner_context_data = None;
808
809        let mut gas = Gas::new(gas_limit);
810
811        let Ok(res) = res else {
812            // Should we match, encode and propagate error as a revert reason?
813            let result =
814                InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
815            return (result, None);
816        };
817
818        for (addr, mut acc) in res.state {
819            let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else {
820                ecx.journal_mut().evm_state_mut().insert(addr, acc);
821                continue;
822            };
823
824            // make sure accounts that were warmed earlier do not become cold
825            if acc.status.contains(AccountStatus::Cold)
826                && !acc_mut.status.contains(AccountStatus::Cold)
827            {
828                acc.status -= AccountStatus::Cold;
829            }
830            acc_mut.info = acc.info;
831            acc_mut.status |= acc.status;
832
833            for (key, val) in acc.storage {
834                let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
835                    acc_mut.storage.insert(key, val);
836                    continue;
837                };
838                slot_mut.present_value = val.present_value;
839                slot_mut.is_cold &= val.is_cold;
840            }
841        }
842
843        let (result, address, output) = match res.result {
844            ExecutionResult::Success { reason, gas_used, gas_refunded, logs: _, output } => {
845                gas.set_refund(gas_refunded as i64);
846                let _ = gas.record_cost(gas_used);
847                let address = match output {
848                    Output::Create(_, address) => address,
849                    Output::Call(_) => None,
850                };
851                (reason.into(), address, output.into_data())
852            }
853            ExecutionResult::Halt { reason, gas_used } => {
854                let _ = gas.record_cost(gas_used);
855                (reason.into(), None, Bytes::new())
856            }
857            ExecutionResult::Revert { gas_used, output } => {
858                let _ = gas.record_cost(gas_used);
859                (InstructionResult::Revert, None, output)
860            }
861        };
862        (InterpreterResult { result, output, gas }, address)
863    }
864
865    /// Moves out of references, constructs a new [`InspectorStackRefMut`] and runs the given
866    /// closure with it.
867    fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_>) -> O) -> O {
868        let mut cheatcodes = self
869            .cheatcodes
870            .as_deref_mut()
871            .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
872        let mut inner = std::mem::take(self.inner);
873
874        let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
875
876        if let Some(cheats) = self.cheatcodes.as_deref_mut() {
877            *cheats = cheatcodes.unwrap();
878        }
879
880        *self.inner = inner;
881
882        out
883    }
884
885    /// Invoked at the beginning of a new top-level (0 depth) frame.
886    fn top_level_frame_start<CTX: ContextTr<Journal: JournalExt>>(&mut self, ecx: &mut CTX) {
887        if self.enable_isolation {
888            // If we're in isolation mode, we need to keep track of the state at the beginning of
889            // the frame to be able to roll back on revert
890            self.top_frame_journal.clone_from(ecx.journal().evm_state());
891        }
892    }
893
894    /// Invoked at the end of root frame.
895    fn top_level_frame_end<CTX: ContextTr<Journal: JournalExt>>(
896        &mut self,
897        ecx: &mut CTX,
898        result: InstructionResult,
899    ) {
900        if !result.is_revert() {
901            return;
902        }
903        // Encountered a revert, since cheatcodes may have altered the evm state in such a way
904        // that violates some constraints, e.g. `deal`, we need to manually roll back on revert
905        // before revm reverts the state itself
906        if let Some(cheats) = self.cheatcodes.as_mut() {
907            cheats.on_revert(ecx);
908        }
909
910        // If we're in isolation mode, we need to rollback to state before the root frame was
911        // created We can't rely on revm's journal because it doesn't account for changes
912        // made by isolated calls
913        if self.enable_isolation {
914            *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
915        }
916    }
917
918    // We take extra care in optimizing `step` and `step_end`, as they're are likely the most
919    // hot functions in all of Foundry.
920    // We want to `#[inline(always)]` these functions so that `InspectorStack` does not
921    // delegate to `InspectorStackRefMut` in this case.
922
923    #[inline(always)]
924    fn step_inlined<CTX: EthCheatCtx>(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
925        call_inspectors!(
926            [
927                // These are sorted in definition order.
928                &mut self.edge_coverage,
929                &mut self.fuzzer,
930                &mut self.line_coverage,
931                &mut self.printer,
932                &mut self.revert_diag,
933                &mut self.script_execution_inspector,
934                &mut self.tracer,
935                // Keep `cheatcodes` last to make use of the tail call.
936                &mut self.cheatcodes,
937            ],
938            |inspector| (**inspector).step(interpreter, ecx),
939        );
940    }
941
942    #[inline(always)]
943    fn step_end_inlined<CTX: EthCheatCtx>(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
944        call_inspectors!(
945            [
946                // These are sorted in definition order.
947                &mut self.chisel_state,
948                &mut self.printer,
949                &mut self.revert_diag,
950                &mut self.tracer,
951                // Keep `cheatcodes` last to make use of the tail call.
952                &mut self.cheatcodes,
953            ],
954            |inspector| (**inspector).step_end(interpreter, ecx),
955        );
956    }
957}
958
959impl<CTX: EthCheatCtx> Inspector<CTX> for InspectorStackRefMut<'_> {
960    fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
961        call_inspectors!(
962            [
963                &mut self.line_coverage,
964                &mut self.tracer,
965                &mut self.cheatcodes,
966                &mut self.script_execution_inspector,
967                &mut self.printer
968            ],
969            |inspector| inspector.initialize_interp(interpreter, ecx),
970        );
971    }
972
973    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
974        self.step_inlined(interpreter, ecx);
975    }
976
977    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
978        self.step_end_inlined(interpreter, ecx);
979    }
980
981    #[allow(clippy::redundant_clone)]
982    fn log(&mut self, ecx: &mut CTX, log: Log) {
983        call_inspectors!(
984            [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
985            |inspector| inspector.log(ecx, log.clone()),
986        );
987    }
988
989    #[allow(clippy::redundant_clone)]
990    fn log_full(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX, log: Log) {
991        call_inspectors!(
992            [&mut self.tracer, &mut self.log_collector, &mut self.cheatcodes, &mut self.printer],
993            |inspector| inspector.log_full(interpreter, ecx, log.clone()),
994        );
995    }
996
997    fn call(&mut self, ecx: &mut CTX, call: &mut CallInputs) -> Option<CallOutcome> {
998        if self.in_inner_context && ecx.journal().depth() == 1 {
999            self.adjust_evm_data_for_inner_context(ecx);
1000            return None;
1001        }
1002
1003        if ecx.journal().depth() == 0 {
1004            self.top_level_frame_start(ecx);
1005        }
1006
1007        call_inspectors!(
1008            #[ret]
1009            [
1010                &mut self.fuzzer,
1011                &mut self.tracer,
1012                &mut self.log_collector,
1013                &mut self.printer,
1014                &mut self.revert_diag
1015            ],
1016            |inspector| {
1017                let mut out = None;
1018                if let Some(output) = inspector.call(ecx, call) {
1019                    out = Some(Some(output));
1020                }
1021                out
1022            },
1023        );
1024
1025        if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1026            // Handle mocked functions, replace bytecode address with mock if matched.
1027            if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1028                let input_bytes = call.input.bytes(ecx);
1029                // Check if any mock function set for call data or if catch-all mock function set
1030                // for selector.
1031                if let Some(target) = mocks
1032                    .get(&input_bytes)
1033                    .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1034                {
1035                    call.bytecode_address = *target;
1036                    call.known_bytecode = None;
1037                }
1038            }
1039
1040            if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
1041                return Some(output);
1042            }
1043        }
1044
1045        if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1046            match call.scheme {
1047                // Isolate CALLs
1048                CallScheme::Call => {
1049                    let input = call.input.bytes(ecx);
1050                    let (result, _) = self.transact_inner(
1051                        ecx,
1052                        TxKind::Call(call.target_address),
1053                        call.caller,
1054                        input,
1055                        call.gas_limit,
1056                        call.value.get(),
1057                    );
1058                    return Some(CallOutcome {
1059                        result,
1060                        memory_offset: call.return_memory_offset.clone(),
1061                        was_precompile_called: true,
1062                        precompile_call_logs: vec![],
1063                    });
1064                }
1065                // Mark accounts and storage cold before STATICCALLs
1066                CallScheme::StaticCall => {
1067                    let (_, journal_inner) = ecx.db_journal_inner_mut();
1068                    let JournaledState { state, warm_addresses, .. } = journal_inner;
1069                    for (addr, acc_mut) in state {
1070                        // Do not mark accounts and storage cold accounts with arbitrary storage.
1071                        if let Some(cheatcodes) = &self.cheatcodes
1072                            && cheatcodes.has_arbitrary_storage(addr)
1073                        {
1074                            continue;
1075                        }
1076
1077                        if warm_addresses.is_cold(addr) {
1078                            acc_mut.mark_cold();
1079                        }
1080
1081                        for slot_mut in acc_mut.storage.values_mut() {
1082                            slot_mut.is_cold = true;
1083                        }
1084                    }
1085                }
1086                // Process other variants as usual
1087                CallScheme::CallCode | CallScheme::DelegateCall => {}
1088            }
1089        }
1090
1091        None
1092    }
1093
1094    fn call_end(&mut self, ecx: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
1095        // We are processing inner context outputs in the outer context, so need to avoid processing
1096        // twice.
1097        if self.in_inner_context && ecx.journal().depth() == 1 {
1098            return;
1099        }
1100
1101        self.do_call_end(ecx, inputs, outcome);
1102
1103        if ecx.journal().depth() == 0 {
1104            self.top_level_frame_end(ecx, outcome.result.result);
1105        }
1106    }
1107
1108    fn create(&mut self, ecx: &mut CTX, create: &mut CreateInputs) -> Option<CreateOutcome> {
1109        if self.in_inner_context && ecx.journal().depth() == 1 {
1110            self.adjust_evm_data_for_inner_context(ecx);
1111            return None;
1112        }
1113
1114        if ecx.journal().depth() == 0 {
1115            self.top_level_frame_start(ecx);
1116        }
1117
1118        call_inspectors!(
1119            #[ret]
1120            [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1121            |inspector| inspector.create(ecx, create).map(Some),
1122        );
1123
1124        if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1125            && self.enable_isolation
1126            && !self.in_inner_context
1127            && ecx.journal().depth() == 1
1128        {
1129            let (result, address) = self.transact_inner(
1130                ecx,
1131                TxKind::Create,
1132                create.caller(),
1133                create.init_code().clone(),
1134                create.gas_limit(),
1135                create.value(),
1136            );
1137            return Some(CreateOutcome { result, address });
1138        }
1139
1140        None
1141    }
1142
1143    fn create_end(&mut self, ecx: &mut CTX, call: &CreateInputs, outcome: &mut CreateOutcome) {
1144        // We are processing inner context outputs in the outer context, so need to avoid processing
1145        // twice.
1146        if self.in_inner_context && ecx.journal().depth() == 1 {
1147            return;
1148        }
1149
1150        self.do_create_end(ecx, call, outcome);
1151
1152        if ecx.journal().depth() == 0 {
1153            self.top_level_frame_end(ecx, outcome.result.result);
1154        }
1155    }
1156
1157    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1158        call_inspectors!([&mut self.printer], |inspector| Inspector::<CTX>::selfdestruct(
1159            inspector, contract, target, value,
1160        ));
1161    }
1162}
1163
1164impl FoundryInspectorExt for InspectorStackRefMut<'_> {
1165    fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1166        call_inspectors!(
1167            #[ret]
1168            [&mut self.cheatcodes],
1169            |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1170        );
1171
1172        false
1173    }
1174
1175    fn console_log(&mut self, msg: &str) {
1176        call_inspectors!([&mut self.log_collector], |inspector| FoundryInspectorExt::console_log(
1177            inspector, msg
1178        ));
1179    }
1180
1181    fn get_networks(&self) -> NetworkConfigs {
1182        self.inner.networks
1183    }
1184
1185    fn create2_deployer(&self) -> Address {
1186        self.inner.create2_deployer
1187    }
1188}
1189
1190impl<CTX: EthCheatCtx> Inspector<CTX> for InspectorStack {
1191    fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1192        self.as_mut().step_inlined(interpreter, ecx)
1193    }
1194
1195    fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1196        self.as_mut().step_end_inlined(interpreter, ecx)
1197    }
1198
1199    fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
1200        self.as_mut().call(context, inputs)
1201    }
1202
1203    fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
1204        self.as_mut().call_end(context, inputs, outcome)
1205    }
1206
1207    fn create(&mut self, context: &mut CTX, create: &mut CreateInputs) -> Option<CreateOutcome> {
1208        self.as_mut().create(context, create)
1209    }
1210
1211    fn create_end(&mut self, context: &mut CTX, call: &CreateInputs, outcome: &mut CreateOutcome) {
1212        self.as_mut().create_end(context, call, outcome)
1213    }
1214
1215    fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX) {
1216        self.as_mut().initialize_interp(interpreter, ecx)
1217    }
1218
1219    fn log(&mut self, ecx: &mut CTX, log: Log) {
1220        self.as_mut().log(ecx, log)
1221    }
1222
1223    fn log_full(&mut self, interpreter: &mut Interpreter, ecx: &mut CTX, log: Log) {
1224        self.as_mut().log_full(interpreter, ecx, log)
1225    }
1226
1227    fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1228        call_inspectors!([&mut self.inner.printer], |inspector| Inspector::<CTX>::selfdestruct(
1229            inspector, contract, target, value,
1230        ));
1231    }
1232}
1233
1234impl FoundryInspectorExt for InspectorStack {
1235    fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1236        self.as_mut().should_use_create2_factory(depth, inputs)
1237    }
1238
1239    fn get_networks(&self) -> NetworkConfigs {
1240        self.networks
1241    }
1242
1243    fn create2_deployer(&self) -> Address {
1244        self.create2_deployer
1245    }
1246}
1247
1248impl<'a> Deref for InspectorStackRefMut<'a> {
1249    type Target = &'a mut InspectorStackInner;
1250
1251    fn deref(&self) -> &Self::Target {
1252        &self.inner
1253    }
1254}
1255
1256impl DerefMut for InspectorStackRefMut<'_> {
1257    fn deref_mut(&mut self) -> &mut Self::Target {
1258        &mut self.inner
1259    }
1260}
1261
1262impl Deref for InspectorStack {
1263    type Target = InspectorStackInner;
1264
1265    fn deref(&self) -> &Self::Target {
1266        &self.inner
1267    }
1268}
1269
1270impl DerefMut for InspectorStack {
1271    fn deref_mut(&mut self) -> &mut Self::Target {
1272        &mut self.inner
1273    }
1274}