foundry_evm/inspectors/
stack.rs

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