Skip to main content

foundry_evm/inspectors/
stack.rs

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