1use super::{
2 Cheatcodes, CheatsConfig, ChiselState, CmpOperands, CustomPrintTracer, EdgeCovConfig,
3 EdgeCovInspector, EdgeCoverage, Fuzzer, LineCoverageCollector, LogCollector, RevertDiagnostic,
4 ScriptExecutionInspector, TempoLabels, TracingInspector,
5};
6use alloy_primitives::{
7 Address, B256, Bytes, Log, TxKind, U256, keccak256,
8 map::{AddressHashMap, AddressHashSet, AddressMap},
9};
10
11use foundry_cheatcodes::{CheatcodeAnalysis, CheatcodesExecutor, NestedEvmClosure, Wallets};
12use foundry_common::{compile::Analysis, sh_warn};
13use foundry_config::FuzzCorpusConfig;
14use foundry_evm_core::{
15 FoundryBlock, FoundryTransaction, InspectorExt,
16 backend::{DatabaseError, DatabaseExt, JournaledState},
17 constants::DEFAULT_CREATE2_DEPLOYER_CODEHASH,
18 env::FoundryContextExt,
19 evm::{
20 BlockEnvFor, EthEvmNetwork, EvmEnvFor, FoundryContextFor, FoundryEvmFactory,
21 FoundryEvmNetwork, SpecFor, TxEnvFor, get_create2_factory_call_inputs, with_cloned_context,
22 },
23};
24use foundry_evm_coverage::HitMaps;
25use foundry_evm_networks::{NetworkConfigs, arbitrum};
26use foundry_evm_traces::{SparsedTraceArena, TraceRequirements};
27use revm::{
28 Inspector,
29 context::{
30 Block, Cfg, ContextTr, JournalTr, Transaction, TransactionType,
31 result::{EVMError, ExecutionResult, Output},
32 },
33 context_interface::CreateScheme,
34 handler::FrameResult,
35 interpreter::{
36 CallInputs, CallOutcome, CallScheme, CreateInputs, CreateOutcome, FrameInput, Gas,
37 InstructionResult, Interpreter, InterpreterResult,
38 bytecode::opcode as op,
39 interpreter_types::{InputsTr, Jumps},
40 return_ok,
41 },
42 primitives::KECCAK_EMPTY,
43 state::{Account, AccountStatus},
44};
45use std::{
46 ops::{Deref, DerefMut},
47 sync::Arc,
48};
49
50use crate::executors::{EarlyExit, EvmExecutionCancellation};
51
52#[derive(Clone, Debug)]
53#[must_use = "builders do nothing unless you call `build` on them"]
54pub struct InspectorStackBuilder<BLOCK: Clone> {
55 pub analysis: Option<Analysis>,
57 pub block: Option<BLOCK>,
62 pub gas_price: Option<u128>,
67 pub cheatcodes: Option<Arc<CheatsConfig>>,
69 pub fuzzer: Option<Fuzzer>,
71 pub trace_requirements: TraceRequirements,
73 pub logs: Option<bool>,
78 pub line_coverage: Option<bool>,
80 pub print: Option<bool>,
82 pub chisel_state: Option<usize>,
84 pub enable_isolation: bool,
88 pub networks: NetworkConfigs,
90 pub wallets: Option<Wallets>,
92 pub create2_deployer: Address,
94}
95
96impl<BLOCK: Clone> Default for InspectorStackBuilder<BLOCK> {
97 fn default() -> Self {
98 Self {
99 analysis: None,
100 block: None,
101 gas_price: None,
102 cheatcodes: None,
103 fuzzer: None,
104 trace_requirements: TraceRequirements::none(),
105 logs: None,
106 line_coverage: None,
107 print: None,
108 chisel_state: None,
109 enable_isolation: false,
110 networks: NetworkConfigs::default(),
111 wallets: None,
112 create2_deployer: Default::default(),
113 }
114 }
115}
116
117impl<BLOCK: Clone> InspectorStackBuilder<BLOCK> {
118 #[inline]
120 pub fn new() -> Self {
121 Self::default()
122 }
123
124 #[inline]
126 pub fn set_analysis(mut self, analysis: Analysis) -> Self {
127 self.analysis = Some(analysis);
128 self
129 }
130
131 #[inline]
133 pub fn block(mut self, block: BLOCK) -> Self {
134 self.block = Some(block);
135 self
136 }
137
138 #[inline]
140 pub const fn gas_price(mut self, gas_price: u128) -> Self {
141 self.gas_price = Some(gas_price);
142 self
143 }
144
145 #[inline]
147 pub fn cheatcodes(mut self, config: Arc<CheatsConfig>) -> Self {
148 self.cheatcodes = Some(config);
149 self
150 }
151
152 #[inline]
154 pub fn wallets(mut self, wallets: Wallets) -> Self {
155 self.wallets = Some(wallets);
156 self
157 }
158
159 #[inline]
161 pub fn fuzzer(mut self, fuzzer: Fuzzer) -> Self {
162 self.fuzzer = Some(fuzzer);
163 self
164 }
165
166 #[inline]
168 pub const fn chisel_state(mut self, final_pc: usize) -> Self {
169 self.chisel_state = Some(final_pc);
170 self
171 }
172
173 #[inline]
175 pub const fn logs(mut self, live_logs: bool) -> Self {
176 self.logs = Some(live_logs);
177 self
178 }
179
180 #[inline]
182 pub const fn line_coverage(mut self, yes: bool) -> Self {
183 self.line_coverage = Some(yes);
184 self
185 }
186
187 #[inline]
189 pub const fn print(mut self, yes: bool) -> Self {
190 self.print = Some(yes);
191 self
192 }
193
194 #[inline]
196 pub const fn trace_requirements(mut self, requirements: TraceRequirements) -> Self {
197 self.trace_requirements = self.trace_requirements.merge(requirements);
198 self
199 }
200
201 #[inline]
204 pub const fn enable_isolation(mut self, yes: bool) -> Self {
205 self.enable_isolation = yes;
206 self
207 }
208
209 #[inline]
211 pub const fn networks(mut self, networks: NetworkConfigs) -> Self {
212 self.networks = networks;
213 self
214 }
215
216 #[inline]
217 pub const fn create2_deployer(mut self, create2_deployer: Address) -> Self {
218 self.create2_deployer = create2_deployer;
219 self
220 }
221
222 pub fn build<FEN: FoundryEvmNetwork<EvmFactory: FoundryEvmFactory<BlockEnv = BLOCK>>>(
224 self,
225 ) -> InspectorStack<FEN> {
226 let Self {
227 analysis,
228 block,
229 gas_price,
230 cheatcodes,
231 fuzzer,
232 trace_requirements,
233 logs,
234 line_coverage,
235 print,
236 chisel_state,
237 enable_isolation,
238 networks,
239 wallets,
240 create2_deployer,
241 } = self;
242 let mut stack = InspectorStack::new();
243
244 if let Some(config) = cheatcodes {
246 let mut cheatcodes = Cheatcodes::new(config);
247 if let Some(analysis) = analysis {
249 stack.set_analysis(analysis.clone());
250 cheatcodes.set_analysis(CheatcodeAnalysis::new(analysis));
251 }
252 if let Some(wallets) = wallets {
254 cheatcodes.set_wallets(wallets);
255 }
256 stack.set_cheatcodes(cheatcodes);
257 }
258
259 if let Some(fuzzer) = fuzzer {
260 stack.set_fuzzer(fuzzer);
261 }
262 if let Some(chisel_state) = chisel_state {
263 stack.set_chisel(chisel_state);
264 }
265 stack.collect_line_coverage(line_coverage.unwrap_or(false));
266 stack.collect_logs(logs);
267 stack.print(print.unwrap_or(false));
268 stack.tracing_requirements(trace_requirements);
269
270 stack.enable_isolation(enable_isolation);
271 stack.networks(networks);
272 stack.set_create2_deployer(create2_deployer);
273
274 if networks.is_tempo() {
275 stack.inner.tempo_labels = Some(Box::default());
276 }
277
278 if let Some(block) = block {
280 stack.set_block(block);
281 }
282 if let Some(gas_price) = gas_price {
283 stack.set_gas_price(gas_price);
284 }
285
286 stack
287 }
288}
289
290#[macro_export]
293macro_rules! call_inspectors {
294 ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {
295 $(
296 if let Some($id) = $inspector {
297 $crate::utils::cold_path();
298 $body;
299 }
300 )+
301 };
302 (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{
303 $(
304 if let Some($id) = $inspector {
305 $crate::utils::cold_path();
306 if let Some(result) = $body {
307 return result;
308 }
309 }
310 )+
311 }};
312}
313
314pub struct InspectorData<FEN: FoundryEvmNetwork> {
316 pub logs: Vec<Log>,
317 pub labels: AddressHashMap<String>,
318 pub traces: Option<SparsedTraceArena>,
319 pub line_coverage: Option<HitMaps>,
320 pub edge_coverage: Option<EdgeCoverage>,
321 pub evm_cmp_values: Option<Vec<CmpOperands>>,
322 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
323 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
324 pub reverter: Option<Address>,
325}
326
327#[derive(Debug, Clone)]
333pub struct InnerContextData {
334 original_origin: Address,
336 locally_created_accounts: AddressHashSet,
338}
339
340#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
341enum OpcodeStepDispatch {
342 #[default]
343 None,
344 FuzzerOnly,
345 General,
346}
347
348#[derive(Clone, Debug)]
359pub struct InspectorStack<FEN: FoundryEvmNetwork = EthEvmNetwork> {
360 #[allow(clippy::type_complexity)]
361 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
362 pub inner: InspectorStackInner,
363}
364
365#[cfg(test)]
366#[derive(Clone, Debug)]
367struct EarlyExitTestGate {
368 entered: std::sync::mpsc::Sender<()>,
369 release: Arc<std::sync::Mutex<std::sync::mpsc::Receiver<()>>>,
370 target_pc: usize,
371 notified: Arc<std::sync::atomic::AtomicBool>,
372}
373
374#[derive(Clone, Copy, Debug)]
375struct PendingCreate2Redirect {
376 depth: usize,
377 charged_create_state_gas: bool,
378}
379
380#[derive(Default, Clone, Debug)]
384pub struct InspectorStackInner {
385 pub analysis: Option<Analysis>,
387
388 pub chisel_state: Option<Box<ChiselState>>,
392 pub edge_coverage: Option<Box<EdgeCovInspector>>,
393 pub fuzzer: Option<Box<Fuzzer>>,
394 pub line_coverage: Option<Box<LineCoverageCollector>>,
395 pub log_collector: Option<Box<LogCollector>>,
396 pub printer: Option<Box<CustomPrintTracer>>,
397 pub revert_diag: Option<Box<RevertDiagnostic>>,
398 pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
399 pub tempo_labels: Option<Box<TempoLabels>>,
400 pub tracer: Option<Box<TracingInspector>>,
401
402 pub sancov_edges: bool,
405 pub sancov_trace_cmp: bool,
407 pub enable_isolation: bool,
408 pub networks: NetworkConfigs,
409 pub create2_deployer: Address,
410 pub in_inner_context: bool,
412 pub inner_context_data: Option<InnerContextData>,
413 pub locally_created_accounts: AddressHashSet,
415 pub top_frame_journal: AddressMap<Account>,
416 pub reverter: Option<Address>,
418 pending_create2_redirects: Vec<PendingCreate2Redirect>,
420 pub pending_create2_error: Option<CreateOutcome>,
423 pub batch_create_counter: u64,
425 pub batch_rewrite_warned: bool,
427 pub batch_rewrite_process_salt: Option<u64>,
430 execution_cancellation: Option<EvmExecutionCancellation>,
432 cancellation_poll_counter: u8,
434 execution_cancelled: bool,
436 #[cfg(test)]
437 early_exit_test_gate: Option<EarlyExitTestGate>,
438 static_step_dispatch: OpcodeStepDispatch,
439 has_static_step_end_inspectors: bool,
440}
441
442pub struct InspectorStackRefMut<'a, FEN: FoundryEvmNetwork = EthEvmNetwork> {
445 pub cheatcodes: Option<&'a mut Cheatcodes<FEN>>,
446 pub inner: &'a mut InspectorStackInner,
447}
448
449impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for InspectorStackInner {
450 fn with_nested_evm(
451 &mut self,
452 cheats: &mut Cheatcodes<FEN>,
453 ecx: &mut FoundryContextFor<'_, FEN>,
454 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
455 ) -> Result<(), EVMError<DatabaseError>> {
456 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
457 with_cloned_context(ecx, |db, evm_env, journal_inner| {
458 let mut evm =
459 FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
460 *evm.journal_inner_mut() = journal_inner;
461 f(&mut *evm)?;
462 let sub_inner = evm.journal_inner_mut().clone();
463 let sub_evm_env = evm.to_evm_env();
464 Ok((sub_evm_env, sub_inner))
465 })
466 }
467
468 fn with_fresh_nested_evm(
469 &mut self,
470 cheats: &mut Cheatcodes<FEN>,
471 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
472 evm_env: EvmEnvFor<FEN>,
473 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
474 ) -> Result<EvmEnvFor<FEN>, EVMError<DatabaseError>> {
475 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
476 let mut evm =
477 FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
478 f(&mut *evm)?;
479 Ok(evm.to_evm_env())
480 }
481
482 fn transact_on_db(
483 &mut self,
484 cheats: &mut Cheatcodes<FEN>,
485 ecx: &mut FoundryContextFor<'_, FEN>,
486 fork_id: Option<U256>,
487 transaction: B256,
488 ) -> eyre::Result<()> {
489 let evm_env = ecx.evm_clone();
490 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
491 let (db, inner) = ecx.db_journal_inner_mut();
492 db.transact(fork_id, transaction, evm_env, inner, &mut inspector)
493 }
494
495 fn transact_from_tx_on_db(
496 &mut self,
497 cheats: &mut Cheatcodes<FEN>,
498 ecx: &mut FoundryContextFor<'_, FEN>,
499 tx_env: TxEnvFor<FEN>,
500 ) -> eyre::Result<()> {
501 let evm_env = ecx.evm_clone();
502 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
503 let (db, inner) = ecx.db_journal_inner_mut();
504 db.transact_from_tx(tx_env, evm_env, inner, &mut inspector)
505 }
506
507 fn console_log(&mut self, msg: &str) {
508 if let Some(ref mut collector) = self.log_collector {
509 InspectorExt::console_log(&mut **collector, msg);
510 }
511 }
512
513 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
514 self.tracer.as_deref_mut()
515 }
516
517 fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
518 self.in_inner_context = enabled;
519 self.inner_context_data = enabled.then(|| InnerContextData {
520 original_origin: original_origin.expect("origin required when enabling inner ctx"),
521 locally_created_accounts: AddressHashSet::default(),
522 });
523 }
524}
525
526impl<FEN: FoundryEvmNetwork> Default for InspectorStack<FEN> {
527 fn default() -> Self {
528 Self::new()
529 }
530}
531
532impl<FEN: FoundryEvmNetwork> InspectorStack<FEN> {
533 #[inline]
539 pub fn new() -> Self {
540 Self { cheatcodes: None, inner: InspectorStackInner::default() }
541 }
542
543 #[inline]
545 pub fn set_analysis(&mut self, analysis: Analysis) {
546 self.analysis = Some(analysis);
547 }
548
549 #[inline]
551 pub(crate) fn set_early_exit(&mut self, early_exit: EarlyExit) {
552 self.execution_cancellation = Some(EvmExecutionCancellation::early_exit(early_exit));
553 }
554
555 #[inline]
557 pub(crate) fn set_execution_cancellation(&mut self, cancellation: EvmExecutionCancellation) {
558 self.execution_cancellation = Some(cancellation);
559 }
560
561 #[inline]
563 pub(crate) const fn execution_cancelled(&self) -> bool {
564 self.inner.execution_cancelled
565 }
566
567 #[cfg(test)]
568 pub(crate) fn set_early_exit_test_gate(
569 &mut self,
570 entered: std::sync::mpsc::Sender<()>,
571 release: std::sync::mpsc::Receiver<()>,
572 target_pc: usize,
573 ) {
574 self.early_exit_test_gate = Some(EarlyExitTestGate {
575 entered,
576 release: Arc::new(std::sync::Mutex::new(release)),
577 target_pc,
578 notified: Arc::new(std::sync::atomic::AtomicBool::new(false)),
579 });
580 }
581
582 #[inline]
584 pub fn set_block(&mut self, block: BlockEnvFor<FEN>) {
585 if let Some(cheatcodes) = &mut self.cheatcodes {
586 cheatcodes.block = Some(block);
587 }
588 }
589
590 #[inline]
592 pub fn set_gas_price(&mut self, gas_price: u128) {
593 if let Some(cheatcodes) = &mut self.cheatcodes {
594 cheatcodes.gas_price = Some(gas_price);
595 }
596 }
597
598 #[inline]
600 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes<FEN>) {
601 self.cheatcodes = Some(cheatcodes.into());
602 }
603
604 #[inline]
606 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
607 self.fuzzer = Some(fuzzer.into());
608 self.refresh_static_step_dispatch();
609 }
610
611 #[inline]
613 pub fn set_chisel(&mut self, final_pc: usize) {
614 self.chisel_state = Some(ChiselState::new(final_pc).into());
615 self.refresh_static_step_end_dispatch();
616 }
617
618 #[inline]
620 pub fn collect_line_coverage(&mut self, yes: bool) {
621 self.line_coverage = yes.then(Default::default);
622 self.refresh_static_step_dispatch();
623 }
624
625 #[inline]
627 pub fn collect_edge_coverage(&mut self, yes: bool) {
628 self.edge_coverage =
629 yes.then(|| EdgeCovInspector::with_config(EdgeCovConfig::default()).into());
630 self.refresh_static_step_dispatch();
631 }
632
633 #[inline]
637 pub fn collect_edge_coverage_with_config(&mut self, corpus: &FuzzCorpusConfig) {
638 self.edge_coverage = corpus
639 .collect_evm_edge_coverage()
640 .then(|| EdgeCovInspector::with_config(corpus.into()).into());
641 self.refresh_static_step_dispatch();
642 }
643
644 #[inline]
646 pub fn collect_evm_cmp_log(&mut self, yes: bool) {
647 if yes {
648 self.edge_coverage
649 .get_or_insert_with(|| EdgeCovInspector::with_cmp_log_only().into())
650 .enable_cmp_log(true);
651 } else if let Some(edge_coverage) = &mut self.edge_coverage {
652 edge_coverage.enable_cmp_log(false);
653 }
654 self.refresh_static_step_dispatch();
655 }
656
657 #[inline]
659 pub const fn collect_sancov_edges(&mut self, yes: bool) {
660 self.inner.sancov_edges = yes;
661 }
662
663 #[inline]
665 pub const fn collect_sancov_trace_cmp(&mut self, yes: bool) {
666 self.inner.sancov_trace_cmp = yes;
667 }
668
669 #[inline]
671 pub const fn enable_isolation(&mut self, yes: bool) {
672 self.inner.enable_isolation = yes;
673 }
674
675 #[inline]
677 pub const fn networks(&mut self, networks: NetworkConfigs) {
678 self.inner.networks = networks;
679 }
680
681 #[inline]
683 pub fn set_create2_deployer(&mut self, deployer: Address) {
684 self.create2_deployer = deployer;
685 }
686
687 #[inline]
692 pub fn collect_logs(&mut self, live_logs: Option<bool>) {
693 self.log_collector = live_logs.map(|live_logs| {
694 Box::new(if live_logs {
695 LogCollector::LiveLogs
696 } else {
697 LogCollector::Capture { logs: Vec::new() }
698 })
699 });
700 }
701
702 #[inline]
704 pub fn print(&mut self, yes: bool) {
705 self.printer = yes.then(Default::default);
706 self.refresh_static_opcode_dispatch();
707 }
708
709 #[inline]
711 pub fn tracing_requirements(&mut self, requirements: TraceRequirements) {
712 let config = requirements.into_config();
713 self.revert_diag = config.is_some().then(RevertDiagnostic::default).map(Into::into);
714
715 if let Some(config) = config {
716 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
717 } else {
718 self.tracer = None;
719 }
720 self.refresh_static_opcode_dispatch();
721 }
722
723 #[inline]
725 pub fn script(&mut self, script_address: Address) {
726 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
727 script_address;
728 self.refresh_static_step_dispatch();
729 }
730
731 #[inline(always)]
732 fn as_mut(&mut self) -> InspectorStackRefMut<'_, FEN> {
733 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
734 }
735
736 pub fn collect(self) -> InspectorData<FEN> {
738 let Self {
739 mut cheatcodes,
740 inner:
741 InspectorStackInner {
742 chisel_state,
743 line_coverage,
744 edge_coverage,
745 log_collector,
746 tempo_labels,
747 tracer,
748 revert_diag,
749 reverter,
750 ..
751 },
752 } = self;
753
754 let trace_diagnostics =
755 revert_diag.map(|revert_diag| revert_diag.into_diagnostics()).unwrap_or_default();
756
757 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
758 let ignored = cheatcodes
759 .as_mut()
760 .map(|cheatcodes| {
761 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
762
763 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
765 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
766 }
767
768 ignored
769 })
770 .unwrap_or_default();
771
772 SparsedTraceArena { arena, ignored, diagnostics: trace_diagnostics }
773 });
774
775 let (edge_coverage, evm_cmp_values) = edge_coverage
776 .map(|edge_coverage| {
777 let (hitcount, cmp_values) = edge_coverage.into_parts();
778 (Some(hitcount), (!cmp_values.is_empty()).then_some(cmp_values))
779 })
780 .unwrap_or_default();
781
782 InspectorData {
783 logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
784 labels: {
785 let mut labels = cheatcodes.as_ref().map(|c| c.labels.clone()).unwrap_or_default();
786 if let Some(tempo_labels) = tempo_labels {
787 labels.extend(tempo_labels.labels);
788 }
789 labels
790 },
791 traces,
792 line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
793 edge_coverage,
794 evm_cmp_values,
795 cheatcodes,
796 chisel_state: chisel_state.and_then(|state| state.state),
797 reverter,
798 }
799 }
800}
801
802impl<FEN: FoundryEvmNetwork> InspectorStackRefMut<'_, FEN> {
803 fn adjust_evm_data_for_inner_context<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
808 let inner_context_data =
809 self.inner_context_data.as_ref().expect("should be called in inner context");
810 ecx.tx_mut().set_caller(inner_context_data.original_origin);
811 }
812
813 fn do_call_end(
814 &mut self,
815 ecx: &mut FoundryContextFor<'_, FEN>,
816 inputs: &CallInputs,
817 outcome: &mut CallOutcome,
818 ) {
819 let storage_hook_active =
820 self.cheatcodes.as_deref().is_some_and(Cheatcodes::is_storage_hook_active);
821 if !storage_hook_active && let Some(fuzzer) = &mut self.fuzzer {
822 fuzzer.call_end(ecx, inputs, outcome);
823 }
824
825 let result = outcome.result.result;
826 call_inspectors!(
827 #[ret]
828 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer, &mut self.revert_diag],
829 |inspector| {
830 let previous_output = outcome.output().clone();
831 inspector.call_end(ecx, inputs, outcome);
832
833 let different = outcome.result.result != result
836 || (outcome.result.result == InstructionResult::Revert
837 && outcome.output() != &previous_output);
838 different.then_some(())
839 },
840 );
841
842 if result.is_revert() && self.reverter.is_none() {
844 self.reverter = Some(inputs.target_address);
845 }
846 }
847
848 fn do_create_end(
849 &mut self,
850 ecx: &mut FoundryContextFor<'_, FEN>,
851 call: &CreateInputs,
852 outcome: &mut CreateOutcome,
853 ) {
854 let result = outcome.result.result;
855 call_inspectors!(
856 #[ret]
857 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
858 |inspector| {
859 let previous_output = outcome.output().clone();
860 inspector.create_end(ecx, call, outcome);
861
862 let different = outcome.result.result != result
865 || (outcome.result.result == InstructionResult::Revert
866 && outcome.output() != &previous_output);
867 different.then_some(())
868 },
869 );
870 }
871
872 fn transact_inner(
873 &mut self,
874 ecx: &mut FoundryContextFor<'_, FEN>,
875 kind: TxKind,
876 caller: Address,
877 input: Bytes,
878 gas_limit: u64,
879 value: U256,
880 ) -> (InterpreterResult, Option<Address>) {
881 let cached_evm_env = ecx.evm_clone();
882 let cached_tx_env = ecx.tx_clone();
883
884 ecx.block_mut().set_basefee(0);
885
886 let chain_id = ecx.cfg().chain_id();
887 ecx.tx_mut().set_chain_id(Some(chain_id));
888 ecx.tx_mut().set_caller(caller);
889 ecx.tx_mut().set_kind(kind);
890 ecx.tx_mut().set_data(input);
891 ecx.tx_mut().set_value(value);
892 ecx.tx_mut().set_gas_limit(gas_limit + 21000);
894
895 if !ecx.cfg().is_block_gas_limit_disabled() {
898 let gas_limit = std::cmp::min(ecx.tx().gas_limit(), ecx.block().gas_limit());
899 ecx.tx_mut().set_gas_limit(gas_limit);
900 }
901 ecx.tx_mut().set_gas_price(0);
902 if ecx.tx().tx_type() == TransactionType::Eip4844 as u8 {
910 ecx.tx_mut().set_tx_type(TransactionType::Eip1559 as u8);
911 ecx.tx_mut().set_blob_hashes(Vec::new());
912 }
913
914 let locally_created_accounts = ecx
915 .journal()
916 .evm_state()
917 .iter()
918 .filter_map(|(addr, acc)| acc.is_created_locally().then_some(*addr))
919 .collect();
920 self.inner_context_data = Some(InnerContextData {
921 original_origin: cached_tx_env.caller(),
922 locally_created_accounts,
923 });
924 self.in_inner_context = true;
925
926 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
930 cheats.in_isolation_context = true;
931 }
932
933 let evm_env = ecx.evm_clone();
934 let tx_env = ecx.tx_clone();
935
936 let res = self.with_inspector(|mut inspector| {
937 let (res, nested_env) = {
938 let (db, journal) = ecx.db_journal_inner_mut();
939 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(
940 db,
941 evm_env,
942 &mut inspector,
943 );
944
945 evm.journal_inner_mut().state = {
946 let mut state = journal.state.clone();
947
948 for (addr, acc_mut) in &mut state {
949 if journal.warm_addresses.is_cold(addr) {
954 acc_mut.mark_cold();
955 }
956
957 for slot_mut in acc_mut.storage.values_mut() {
959 slot_mut.is_cold = true;
960 slot_mut.original_value = slot_mut.present_value;
961 }
962 }
963
964 state
965 };
966
967 evm.journal_inner_mut().depth = 1;
969
970 let res = evm.transact_raw(tx_env);
971 let nested_evm_env = evm.to_evm_env();
972 (res, nested_evm_env)
973 };
974
975 let mut restored_evm_env = nested_env;
978 restored_evm_env.block_env.set_basefee(cached_evm_env.block_env.basefee());
979 ecx.set_evm(restored_evm_env);
980 ecx.set_tx(cached_tx_env);
981
982 res
983 });
984
985 self.in_inner_context = false;
986 self.inner_context_data = None;
987
988 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
991 cheats.in_isolation_context = false;
992 }
993
994 let mut gas = Gas::new(gas_limit);
995
996 let Ok(res) = res else {
997 let result =
999 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
1000 return (result, None);
1001 };
1002
1003 for (addr, mut acc) in res.state {
1004 let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else {
1005 ecx.journal_mut().evm_state_mut().insert(addr, acc);
1006 continue;
1007 };
1008
1009 if acc.status.contains(AccountStatus::Cold)
1011 && !acc_mut.status.contains(AccountStatus::Cold)
1012 {
1013 acc.status -= AccountStatus::Cold;
1014 }
1015 acc_mut.info = acc.info;
1016 acc_mut.status |= acc.status;
1017
1018 for (key, val) in acc.storage {
1019 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
1020 acc_mut.storage.insert(key, val);
1021 continue;
1022 };
1023 slot_mut.present_value = val.present_value;
1024 slot_mut.is_cold &= val.is_cold;
1025 }
1026 }
1027
1028 let (result, address, output) = match res.result {
1029 ExecutionResult::Success { reason, gas: result_gas, logs: _, output } => {
1030 gas.set_refund(result_gas.final_refunded() as i64);
1031 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
1032 let address = match output {
1033 Output::Create(_, address) => address,
1034 Output::Call(_) => None,
1035 };
1036 (reason.into(), address, output.into_data())
1037 }
1038 ExecutionResult::Halt { reason, gas: result_gas, .. } => {
1039 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
1040 (InstructionResult::from(reason), None, Bytes::new())
1041 }
1042 ExecutionResult::Revert { gas: result_gas, output, .. } => {
1043 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
1044 (InstructionResult::Revert, None, output)
1045 }
1046 };
1047 (InterpreterResult { result, output, gas }, address)
1048 }
1049
1050 fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_, FEN>) -> O) -> O {
1053 let mut cheatcodes = self
1054 .cheatcodes
1055 .as_deref_mut()
1056 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
1057 let mut inner = std::mem::take(self.inner);
1058
1059 let saved_create2_redirects = std::mem::take(&mut inner.pending_create2_redirects);
1062
1063 let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
1064
1065 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
1066 *cheats = cheatcodes.unwrap();
1067 }
1068
1069 inner.pending_create2_redirects = saved_create2_redirects;
1070 *self.inner = inner;
1071
1072 out
1073 }
1074
1075 fn top_level_frame_start(&mut self, ecx: &mut FoundryContextFor<'_, FEN>) {
1077 self.locally_created_accounts.clear();
1078 if let Some(cheatcodes) = &mut self.cheatcodes {
1079 cheatcodes.clear_storage_hook_mapping_slots();
1080 }
1081
1082 if self.enable_isolation {
1083 self.top_frame_journal.clone_from(ecx.journal().evm_state());
1086 }
1087 }
1088
1089 fn top_level_frame_end(
1091 &mut self,
1092 ecx: &mut FoundryContextFor<'_, FEN>,
1093 result: InstructionResult,
1094 ) {
1095 if let Some(cheatcodes) = &mut self.cheatcodes {
1096 cheatcodes.clear_storage_hook_mapping_slots();
1097 }
1098 if !result.is_revert() {
1099 return;
1100 }
1101 if let Some(cheats) = self.cheatcodes.as_mut() {
1105 cheats.on_revert(ecx);
1106 }
1107
1108 if self.enable_isolation {
1112 *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
1113 }
1114 }
1115
1116 #[inline(always)]
1122 fn step_inlined(
1123 &mut self,
1124 interpreter: &mut Interpreter,
1125 ecx: &mut FoundryContextFor<'_, FEN>,
1126 ) {
1127 let storage_hook_active = if let Some(cheats) = self.cheatcodes.as_mut() {
1128 if cheats.has_storage_hooks() && cheats.finish_storage_hook_callback(interpreter, ecx) {
1129 return;
1130 }
1131 cheats.pc = interpreter.bytecode.pc();
1132 cheats.is_storage_hook_active()
1133 } else {
1134 false
1135 };
1136
1137 #[cfg(test)]
1138 if interpreter.bytecode.opcode() == op::JUMPDEST
1139 && let Some(gate) = &self.inner.early_exit_test_gate
1140 && interpreter.bytecode.pc() == gate.target_pc
1141 && !gate.notified.swap(true, std::sync::atomic::Ordering::Relaxed)
1142 {
1143 let _ = gate.entered.send(());
1144 let _ = gate
1145 .release
1146 .lock()
1147 .expect("early-exit test gate lock poisoned")
1148 .recv_timeout(std::time::Duration::from_secs(1));
1149 }
1150
1151 if let Some(cancellation) = &self.inner.execution_cancellation {
1152 let poll_deadline = self.inner.cancellation_poll_counter == 0;
1153 self.inner.cancellation_poll_counter =
1154 self.inner.cancellation_poll_counter.wrapping_add(1);
1155 if cancellation.should_stop(poll_deadline) {
1156 self.inner.execution_cancelled = true;
1157 interpreter.halt(InstructionResult::Stop);
1158 return;
1159 }
1160 }
1161
1162 match self.static_step_dispatch {
1163 OpcodeStepDispatch::None => {}
1164 OpcodeStepDispatch::FuzzerOnly => {
1165 if !storage_hook_active && let Some(inspector) = &mut self.fuzzer {
1166 inspector.step(interpreter, ecx);
1167 }
1168 }
1169 OpcodeStepDispatch::General => {
1170 if storage_hook_active {
1171 call_inspectors!(
1172 [
1173 &mut self.printer,
1175 &mut self.revert_diag,
1176 &mut self.script_execution_inspector,
1177 &mut self.tracer,
1178 ],
1179 |inspector| (**inspector).step(interpreter, ecx),
1180 );
1181 } else {
1182 call_inspectors!(
1183 [
1184 &mut self.edge_coverage,
1186 &mut self.fuzzer,
1187 &mut self.line_coverage,
1188 &mut self.printer,
1189 &mut self.revert_diag,
1190 &mut self.script_execution_inspector,
1191 &mut self.tracer,
1192 ],
1193 |inspector| (**inspector).step(interpreter, ecx),
1194 );
1195 }
1196 }
1197 }
1198
1199 if let Some(cheats) = self.cheatcodes.as_mut()
1200 && cheats.has_step_hooks()
1201 {
1202 let opcode = interpreter.bytecode.opcode();
1203 if !cheats.has_recording_accesses_only_step_hook()
1204 || matches!(opcode, op::SLOAD | op::SSTORE)
1205 {
1206 crate::utils::cold_path();
1207 cheats.step(interpreter, ecx);
1208 }
1209 }
1210 }
1211
1212 #[inline(always)]
1213 fn step_end_inlined(
1214 &mut self,
1215 interpreter: &mut Interpreter,
1216 ecx: &mut FoundryContextFor<'_, FEN>,
1217 ) {
1218 if self.has_static_step_end_inspectors {
1219 call_inspectors!(
1220 [
1221 &mut self.chisel_state,
1223 &mut self.printer,
1224 &mut self.revert_diag,
1225 &mut self.tracer,
1226 ],
1227 |inspector| (**inspector).step_end(interpreter, ecx),
1228 );
1229 }
1230
1231 if let Some(fuzzer) = &mut self.fuzzer
1232 && fuzzer.mapping_slots.is_some()
1233 {
1234 fuzzer.step_end(interpreter, ecx);
1235 }
1236
1237 if let Some(cheats) = self.cheatcodes.as_mut()
1238 && cheats.has_step_end_hooks()
1239 {
1240 crate::utils::cold_path();
1241 cheats.step_end(interpreter, ecx);
1242 }
1243 }
1244}
1245
1246impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>>
1247 for InspectorStackRefMut<'_, FEN>
1248{
1249 fn initialize_interp(
1250 &mut self,
1251 interpreter: &mut Interpreter,
1252 ecx: &mut FoundryContextFor<'_, FEN>,
1253 ) {
1254 let address = interpreter.input.target_address();
1255 let should_mark_created_locally = self.locally_created_accounts.contains(&address)
1256 || self
1257 .inner_context_data
1258 .as_ref()
1259 .is_some_and(|ctx| ctx.locally_created_accounts.contains(&address));
1260 if should_mark_created_locally
1261 && let Some(account) = ecx.journal_mut().evm_state_mut().get_mut(&address)
1262 {
1263 account.mark_created_locally();
1264 }
1265
1266 call_inspectors!(
1267 [
1268 &mut self.line_coverage,
1269 &mut self.tracer,
1270 &mut self.cheatcodes,
1271 &mut self.script_execution_inspector,
1272 &mut self.printer
1273 ],
1274 |inspector| inspector.initialize_interp(interpreter, ecx),
1275 );
1276 }
1277
1278 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1279 self.step_inlined(interpreter, ecx);
1280 }
1281
1282 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1283 self.step_end_inlined(interpreter, ecx);
1284 }
1285
1286 #[allow(clippy::redundant_clone)]
1287 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1288 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
1289 inspector.log(ecx, log.clone())
1290 });
1291 if let Some(inspector) = &mut self.cheatcodes
1292 && inspector.has_log_hooks()
1293 {
1294 crate::utils::cold_path();
1295 inspector.log(ecx, log.clone());
1296 }
1297 call_inspectors!([&mut self.printer], |inspector| { inspector.log(ecx, log.clone()) });
1298 }
1299
1300 #[allow(clippy::redundant_clone)]
1301 fn log_full(
1302 &mut self,
1303 interpreter: &mut Interpreter,
1304 ecx: &mut FoundryContextFor<'_, FEN>,
1305 log: Log,
1306 ) {
1307 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
1308 inspector.log_full(interpreter, ecx, log.clone())
1309 });
1310 if let Some(inspector) = &mut self.cheatcodes
1311 && inspector.has_log_hooks()
1312 {
1313 crate::utils::cold_path();
1314 inspector.log_full(interpreter, ecx, log.clone());
1315 }
1316 call_inspectors!([&mut self.printer], |inspector| {
1317 inspector.log_full(interpreter, ecx, log.clone())
1318 });
1319 }
1320
1321 fn frame_start(
1322 &mut self,
1323 ecx: &mut FoundryContextFor<'_, FEN>,
1324 frame_input: &mut FrameInput,
1325 ) -> Option<FrameResult> {
1326 if let FrameInput::Create(inputs) = frame_input
1327 && self.should_use_create2_factory(ecx.journal().depth(), inputs)
1328 {
1329 let salt = match inputs.scheme() {
1334 CreateScheme::Create2 { salt } => salt,
1335 CreateScheme::Create => {
1338 if !self.inner.batch_rewrite_warned {
1339 let _ = sh_warn!(
1340 "--batch rewrites CREATE → CREATE2 via the Arachnid factory; \
1341 deployed addresses follow the CREATE2 formula and constructor \
1342 msg.sender is the factory, not the EOA."
1343 );
1344 self.inner.batch_rewrite_warned = true;
1345 }
1346 let chain_id = ecx.cfg().chain_id();
1347 let nonce = ecx.journal_mut().load_account(inputs.caller()).ok()?.info.nonce;
1348 self.inner.next_batch_create_salt(chain_id, nonce)
1349 }
1350 _ => return None,
1351 };
1352
1353 let gas_limit = inputs.gas_limit();
1354 let create2_deployer = self.create2_deployer();
1355
1356 let code_hash = ecx.journal_mut().load_account(create2_deployer).ok()?.info.code_hash;
1358 if code_hash == KECCAK_EMPTY {
1359 self.inner.pending_create2_error = Some(CreateOutcome {
1362 result: InterpreterResult {
1363 result: InstructionResult::Revert,
1364 output: Bytes::from(
1365 format!("missing CREATE2 deployer: {create2_deployer}").into_bytes(),
1366 ),
1367 gas: Gas::new(gas_limit),
1368 },
1369 address: None,
1370 charged_create_state_gas: inputs.charged_create_state_gas(),
1371 });
1372 return None;
1373 } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
1374 self.inner.pending_create2_error = Some(CreateOutcome {
1375 result: InterpreterResult {
1376 result: InstructionResult::Revert,
1377 output: "invalid CREATE2 deployer bytecode".into(),
1378 gas: Gas::new(gas_limit),
1379 },
1380 address: None,
1381 charged_create_state_gas: inputs.charged_create_state_gas(),
1382 });
1383 return None;
1384 }
1385
1386 let call_inputs =
1387 get_create2_factory_call_inputs(salt, inputs, create2_deployer, ecx.journal_mut())
1388 .ok()?;
1389
1390 self.inner.pending_create2_redirects.push(PendingCreate2Redirect {
1392 depth: ecx.journal().depth(),
1393 charged_create_state_gas: inputs.charged_create_state_gas(),
1394 });
1395
1396 *frame_input = FrameInput::Call(Box::new(call_inputs));
1398 }
1399
1400 None
1401 }
1402
1403 fn frame_end(
1404 &mut self,
1405 ecx: &mut FoundryContextFor<'_, FEN>,
1406 _frame_input: &FrameInput,
1407 frame_result: &mut FrameResult,
1408 ) {
1409 let depth = ecx.journal().depth();
1410 let Some(redirect) = self
1411 .inner
1412 .pending_create2_redirects
1413 .last()
1414 .copied()
1415 .filter(|redirect| redirect.depth == depth)
1416 else {
1417 return;
1418 };
1419 self.inner.pending_create2_redirects.pop();
1420
1421 let FrameResult::Call(call) = frame_result else {
1422 debug_assert!(false, "pending CREATE2 redirect ended with non-call result");
1423 return;
1424 };
1425
1426 let address = match call.instruction_result() {
1427 return_ok!() => Address::try_from(call.output().as_ref())
1428 .map_err(|_| {
1429 call.result = InterpreterResult {
1430 result: InstructionResult::Revert,
1431 output: "invalid CREATE2 factory output".into(),
1432 gas: Gas::new(call.result.gas.limit()),
1433 };
1434 })
1435 .ok(),
1436 _ => None,
1437 };
1438
1439 *frame_result = FrameResult::Create(CreateOutcome {
1440 result: call.result.clone(),
1441 address,
1442 charged_create_state_gas: redirect.charged_create_state_gas,
1443 });
1444 }
1445
1446 fn call(
1447 &mut self,
1448 ecx: &mut FoundryContextFor<'_, FEN>,
1449 call: &mut CallInputs,
1450 ) -> Option<CallOutcome> {
1451 if self.in_inner_context && ecx.journal().depth() == 1 {
1452 self.adjust_evm_data_for_inner_context(ecx);
1453 return None;
1454 }
1455
1456 if ecx.journal().depth() == 0 {
1457 self.top_level_frame_start(ecx);
1458 }
1459
1460 if let Some(revert_diag) = self.revert_diag.as_deref_mut() {
1461 revert_diag.frame_start();
1462 }
1463
1464 let storage_hook_callback = self
1465 .cheatcodes
1466 .as_deref()
1467 .is_some_and(|cheatcodes| cheatcodes.is_storage_hook_callback(ecx, call));
1468 let storage_hook_active =
1469 self.cheatcodes.as_deref().is_some_and(Cheatcodes::is_storage_hook_active);
1470
1471 if !storage_hook_active {
1472 call_inspectors!(
1473 #[ret]
1474 [&mut self.fuzzer],
1475 |inspector| {
1476 let mut out = None;
1477 if let Some(output) = inspector.call(ecx, call) {
1478 out = Some(Some(output));
1479 }
1480 out
1481 }
1482 );
1483 }
1484
1485 if self.tracer.is_some() {
1486 crate::utils::cold_path();
1487 let (output, trace_idx) = {
1488 let tracer = self.tracer.as_deref_mut().unwrap();
1489 let output = tracer.call(ecx, call);
1490 (output, tracer.traces().nodes().len() - 1)
1491 };
1492 if let Some(revert_diag) = self.revert_diag.as_deref_mut() {
1493 revert_diag.set_trace_node(trace_idx);
1494 }
1495 if output.is_some() {
1496 return output;
1497 }
1498 }
1499
1500 call_inspectors!(
1501 #[ret]
1502 [
1503 &mut self.log_collector,
1504 &mut self.printer,
1505 &mut self.revert_diag,
1506 &mut self.tempo_labels
1507 ],
1508 |inspector| inspector.call(ecx, call).map(Some),
1509 );
1510
1511 if storage_hook_callback {
1514 return None;
1515 }
1516
1517 let trace_idx = self.tracer.as_ref().map(|tracer| tracer.traces().nodes().len() - 1);
1521 let mut cheatcode_outcome = None;
1522 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1523 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1525 let input_bytes = call.input.bytes(ecx);
1526 if let Some(target) = mocks
1529 .get(&input_bytes)
1530 .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1531 {
1532 call.bytecode_address = *target;
1533
1534 let target = ecx
1535 .journal_mut()
1536 .load_account_with_code(*target)
1537 .expect("failed to load account");
1538 call.known_bytecode =
1539 (target.info.code_hash, target.info.code.clone().unwrap_or_default());
1540 }
1541 }
1542
1543 cheatcode_outcome = cheatcodes.call_with_executor(ecx, call, self.inner);
1544 }
1545
1546 if let Some(trace_idx) = trace_idx
1547 && let Some(tracer) = self.tracer.as_deref_mut()
1548 {
1549 let caller = match call.scheme {
1550 CallScheme::DelegateCall | CallScheme::CallCode => call.target_address,
1551 CallScheme::Call | CallScheme::StaticCall => call.caller,
1552 };
1553 let node = &mut tracer.traces_mut().nodes_mut()[trace_idx];
1554 debug_assert_eq!(node.trace.depth, ecx.journal().depth());
1555 node.trace.caller = caller;
1556 }
1557
1558 if let Some(output) = cheatcode_outcome {
1559 return Some(output);
1560 }
1561
1562 if let Some(outcome) = handle_arbitrum_system_call::<FEN>(ecx, call) {
1563 return Some(outcome);
1564 }
1565
1566 if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1567 match call.scheme {
1568 CallScheme::Call => {
1570 let input = call.input.bytes(ecx);
1571 let (result, _) = self.transact_inner(
1572 ecx,
1573 TxKind::Call(call.target_address),
1574 call.caller,
1575 input,
1576 call.gas_limit,
1577 call.value.get(),
1578 );
1579 return Some(CallOutcome {
1580 result,
1581 memory_offset: call.return_memory_offset.clone(),
1582 was_precompile_called: true,
1583 precompile_call_logs: vec![],
1584 charged_new_account_state_gas: call.charged_new_account_state_gas,
1585 });
1586 }
1587 CallScheme::StaticCall => {
1589 let (_, journal_inner) = ecx.db_journal_inner_mut();
1590 let JournaledState { state, warm_addresses, .. } = journal_inner;
1591 for (addr, acc_mut) in state {
1592 if let Some(cheatcodes) = &self.cheatcodes
1594 && cheatcodes.has_arbitrary_storage(addr)
1595 {
1596 continue;
1597 }
1598
1599 if warm_addresses.is_cold(addr) {
1600 acc_mut.mark_cold();
1601 }
1602
1603 for slot_mut in acc_mut.storage.values_mut() {
1604 slot_mut.is_cold = true;
1605 }
1606 }
1607 }
1608 CallScheme::CallCode | CallScheme::DelegateCall => {}
1610 }
1611 }
1612
1613 None
1614 }
1615
1616 fn call_end(
1617 &mut self,
1618 ecx: &mut FoundryContextFor<'_, FEN>,
1619 inputs: &CallInputs,
1620 outcome: &mut CallOutcome,
1621 ) {
1622 if self.in_inner_context && ecx.journal().depth() == 1 {
1625 return;
1626 }
1627
1628 self.do_call_end(ecx, inputs, outcome);
1629
1630 if let Some(revert_diag) = self.revert_diag.as_deref_mut() {
1631 revert_diag.frame_end();
1632 }
1633
1634 if ecx.journal().depth() == 0 {
1635 self.top_level_frame_end(ecx, outcome.result.result);
1636 }
1637 }
1638
1639 fn create(
1640 &mut self,
1641 ecx: &mut FoundryContextFor<'_, FEN>,
1642 create: &mut CreateInputs,
1643 ) -> Option<CreateOutcome> {
1644 if self.in_inner_context && ecx.journal().depth() == 1 {
1645 self.adjust_evm_data_for_inner_context(ecx);
1646 return None;
1647 }
1648
1649 if ecx.journal().depth() == 0 {
1650 self.top_level_frame_start(ecx);
1651 }
1652
1653 if let Some(revert_diag) = self.revert_diag.as_deref_mut() {
1654 revert_diag.frame_start();
1655 }
1656
1657 if self.tracer.is_some() {
1658 crate::utils::cold_path();
1659 let (output, trace_idx) = {
1660 let tracer = self.tracer.as_deref_mut().unwrap();
1661 let output = tracer.create(ecx, create);
1662 (output, tracer.traces().nodes().len() - 1)
1663 };
1664 if let Some(revert_diag) = self.revert_diag.as_deref_mut() {
1665 revert_diag.set_trace_node(trace_idx);
1666 }
1667 if output.is_some() {
1668 return output;
1669 }
1670 }
1671
1672 call_inspectors!(
1673 #[ret]
1674 [&mut self.line_coverage],
1675 |inspector| inspector.create(ecx, create).map(Some),
1676 );
1677
1678 let trace_idx = self.tracer.as_ref().map(|tracer| tracer.traces().nodes().len() - 1);
1679 let mut cheatcode_outcome = None;
1680 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1681 cheatcode_outcome = cheatcodes.create(ecx, create);
1682 }
1683
1684 if let Some(trace_idx) = trace_idx
1685 && let Some(tracer) = self.tracer.as_deref_mut()
1686 {
1687 let node = &mut tracer.traces_mut().nodes_mut()[trace_idx];
1688 debug_assert_eq!(node.trace.depth, ecx.journal().depth());
1689 node.trace.caller = create.caller();
1690 }
1691
1692 if let Some(output) = cheatcode_outcome {
1693 return Some(output);
1694 }
1695
1696 if let Some(error) = self.inner.pending_create2_error.take() {
1699 return Some(error);
1700 }
1701
1702 if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1703 && self.enable_isolation
1704 && !self.in_inner_context
1705 && ecx.journal().depth() == 1
1706 {
1707 let precomputed_address = ecx
1711 .journal()
1712 .evm_state()
1713 .get(&create.caller())
1714 .map(|acc| create.caller().create(acc.info.nonce));
1715
1716 let (result, address) = self.transact_inner(
1717 ecx,
1718 TxKind::Create,
1719 create.caller(),
1720 create.init_code().clone(),
1721 create.gas_limit(),
1722 create.value(),
1723 );
1724 let address =
1725 address.or_else(|| if result.is_revert() { precomputed_address } else { None });
1726 return Some(CreateOutcome {
1727 result,
1728 address,
1729 charged_create_state_gas: create.charged_create_state_gas(),
1730 });
1731 }
1732
1733 None
1734 }
1735
1736 fn create_end(
1737 &mut self,
1738 ecx: &mut FoundryContextFor<'_, FEN>,
1739 call: &CreateInputs,
1740 outcome: &mut CreateOutcome,
1741 ) {
1742 if outcome.result.result.is_ok()
1743 && let Some(address) = outcome.address
1744 {
1745 self.locally_created_accounts.insert(address);
1746
1747 if self.in_inner_context
1748 && let Some(inner_context) = &mut self.inner_context_data
1749 {
1750 inner_context.locally_created_accounts.insert(address);
1751 }
1752 }
1753
1754 if self.in_inner_context && ecx.journal().depth() == 1 {
1757 return;
1758 }
1759
1760 self.do_create_end(ecx, call, outcome);
1761
1762 if let Some(revert_diag) = self.revert_diag.as_deref_mut() {
1763 revert_diag.frame_end();
1764 }
1765
1766 if ecx.journal().depth() == 0 {
1767 self.top_level_frame_end(ecx, outcome.result.result);
1768 }
1769 }
1770
1771 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1772 call_inspectors!([&mut self.printer], |inspector| {
1773 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1774 inspector, contract, target, value,
1775 )
1776 });
1777 }
1778}
1779
1780fn handle_arbitrum_system_call<FEN: FoundryEvmNetwork>(
1781 ecx: &mut FoundryContextFor<'_, FEN>,
1782 call: &CallInputs,
1783) -> Option<CallOutcome> {
1784 if call.target_address != arbitrum::ARB_SYS_ADDRESS
1785 || call.bytecode_address != arbitrum::ARB_SYS_ADDRESS
1786 || !arbitrum::is_arbitrum_chain(ecx.cfg().chain_id)
1787 {
1788 return None;
1789 }
1790
1791 let input = call.input.bytes(ecx);
1792 if input.get(..4) != Some(&arbitrum::ARB_BLOCK_NUMBER_SELECTOR) {
1793 return None;
1794 }
1795
1796 let block_number = ecx.db().active_fork_block_number()?;
1797 let Some((gas_cost, output)) = arbitrum::arb_block_number_call(call.gas_limit, block_number)
1798 else {
1799 return Some(arbitrum_call_outcome(
1800 call,
1801 InstructionResult::PrecompileOOG,
1802 0,
1803 Bytes::new(),
1804 ));
1805 };
1806
1807 Some(arbitrum_call_outcome(call, InstructionResult::Return, gas_cost, output))
1808}
1809
1810fn arbitrum_call_outcome(
1811 call: &CallInputs,
1812 result: InstructionResult,
1813 gas_used: u64,
1814 output: Bytes,
1815) -> CallOutcome {
1816 let mut gas = Gas::new(call.gas_limit);
1817 if result.is_ok() {
1818 let _ = gas.record_regular_cost(gas_used);
1819 } else {
1820 gas.spend_all();
1821 }
1822
1823 CallOutcome {
1824 result: InterpreterResult { result, output, gas },
1825 memory_offset: call.return_memory_offset.clone(),
1826 was_precompile_called: true,
1827 precompile_call_logs: vec![],
1828 charged_new_account_state_gas: call.charged_new_account_state_gas,
1829 }
1830}
1831
1832impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStackRefMut<'_, FEN> {
1833 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1834 call_inspectors!(
1835 #[ret]
1836 [&mut self.cheatcodes],
1837 |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1838 );
1839
1840 false
1841 }
1842
1843 fn console_log(&mut self, msg: &str) {
1844 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1845 inspector, msg
1846 ));
1847 }
1848
1849 fn get_networks(&self) -> NetworkConfigs {
1850 self.inner.networks
1851 }
1852
1853 fn create2_deployer(&self) -> Address {
1854 self.inner.create2_deployer
1855 }
1856}
1857
1858impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for InspectorStack<FEN> {
1859 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1860 self.as_mut().step_inlined(interpreter, ecx)
1861 }
1862
1863 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1864 self.as_mut().step_end_inlined(interpreter, ecx)
1865 }
1866
1867 fn call(
1868 &mut self,
1869 context: &mut FoundryContextFor<'_, FEN>,
1870 inputs: &mut CallInputs,
1871 ) -> Option<CallOutcome> {
1872 self.as_mut().call(context, inputs)
1873 }
1874
1875 fn call_end(
1876 &mut self,
1877 context: &mut FoundryContextFor<'_, FEN>,
1878 inputs: &CallInputs,
1879 outcome: &mut CallOutcome,
1880 ) {
1881 self.as_mut().call_end(context, inputs, outcome)
1882 }
1883
1884 fn create(
1885 &mut self,
1886 context: &mut FoundryContextFor<'_, FEN>,
1887 create: &mut CreateInputs,
1888 ) -> Option<CreateOutcome> {
1889 self.as_mut().create(context, create)
1890 }
1891
1892 fn create_end(
1893 &mut self,
1894 context: &mut FoundryContextFor<'_, FEN>,
1895 call: &CreateInputs,
1896 outcome: &mut CreateOutcome,
1897 ) {
1898 self.as_mut().create_end(context, call, outcome)
1899 }
1900
1901 fn initialize_interp(
1902 &mut self,
1903 interpreter: &mut Interpreter,
1904 ecx: &mut FoundryContextFor<'_, FEN>,
1905 ) {
1906 self.as_mut().initialize_interp(interpreter, ecx)
1907 }
1908
1909 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1910 self.as_mut().log(ecx, log)
1911 }
1912
1913 fn log_full(
1914 &mut self,
1915 interpreter: &mut Interpreter,
1916 ecx: &mut FoundryContextFor<'_, FEN>,
1917 log: Log,
1918 ) {
1919 self.as_mut().log_full(interpreter, ecx, log)
1920 }
1921
1922 fn frame_start(
1923 &mut self,
1924 context: &mut FoundryContextFor<'_, FEN>,
1925 frame_input: &mut FrameInput,
1926 ) -> Option<FrameResult> {
1927 self.as_mut().frame_start(context, frame_input)
1928 }
1929
1930 fn frame_end(
1931 &mut self,
1932 context: &mut FoundryContextFor<'_, FEN>,
1933 frame_input: &FrameInput,
1934 frame_result: &mut FrameResult,
1935 ) {
1936 self.as_mut().frame_end(context, frame_input, frame_result)
1937 }
1938
1939 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1940 call_inspectors!([&mut self.inner.printer], |inspector| {
1941 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1942 inspector, contract, target, value,
1943 )
1944 });
1945 }
1946}
1947
1948impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStack<FEN> {
1949 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1950 self.as_mut().should_use_create2_factory(depth, inputs)
1951 }
1952
1953 fn get_networks(&self) -> NetworkConfigs {
1954 self.networks
1955 }
1956
1957 fn create2_deployer(&self) -> Address {
1958 self.create2_deployer
1959 }
1960}
1961
1962impl<'a, FEN: FoundryEvmNetwork> Deref for InspectorStackRefMut<'a, FEN> {
1963 type Target = &'a mut InspectorStackInner;
1964
1965 fn deref(&self) -> &Self::Target {
1966 &self.inner
1967 }
1968}
1969
1970impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStackRefMut<'_, FEN> {
1971 fn deref_mut(&mut self) -> &mut Self::Target {
1972 &mut self.inner
1973 }
1974}
1975
1976impl<FEN: FoundryEvmNetwork> Deref for InspectorStack<FEN> {
1977 type Target = InspectorStackInner;
1978
1979 fn deref(&self) -> &Self::Target {
1980 &self.inner
1981 }
1982}
1983
1984impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStack<FEN> {
1985 fn deref_mut(&mut self) -> &mut Self::Target {
1986 &mut self.inner
1987 }
1988}
1989
1990impl InspectorStackInner {
1991 #[inline]
1992 const fn refresh_static_opcode_dispatch(&mut self) {
1993 self.refresh_static_step_dispatch();
1994 self.refresh_static_step_end_dispatch();
1995 }
1996
1997 #[inline]
1998 const fn refresh_static_step_dispatch(&mut self) {
1999 self.static_step_dispatch = if self.edge_coverage.is_none()
2000 && self.line_coverage.is_none()
2001 && self.printer.is_none()
2002 && self.revert_diag.is_none()
2003 && self.script_execution_inspector.is_none()
2004 && self.tracer.is_none()
2005 {
2006 if self.fuzzer.is_some() {
2007 OpcodeStepDispatch::FuzzerOnly
2008 } else {
2009 OpcodeStepDispatch::None
2010 }
2011 } else {
2012 OpcodeStepDispatch::General
2013 };
2014 }
2015
2016 #[inline]
2017 const fn refresh_static_step_end_dispatch(&mut self) {
2018 self.has_static_step_end_inspectors = self.chisel_state.is_some()
2019 || self.printer.is_some()
2020 || self.revert_diag.is_some()
2021 || self.tracer.is_some();
2022 }
2023
2024 fn next_batch_create_salt(&mut self, chain_id: u64, nonce: u64) -> U256 {
2027 let process_salt = *self.batch_rewrite_process_salt.get_or_insert_with(rand::random);
2028 let counter = self.batch_create_counter;
2029 self.batch_create_counter = counter.wrapping_add(1);
2030 compute_batch_create_salt(process_salt, chain_id, nonce, counter)
2031 }
2032}
2033
2034fn compute_batch_create_salt(process_salt: u64, chain_id: u64, nonce: u64, counter: u64) -> U256 {
2040 let mut buf = [0u8; 32];
2041 buf[0..8].copy_from_slice(&process_salt.to_be_bytes());
2042 buf[8..16].copy_from_slice(&chain_id.to_be_bytes());
2043 buf[16..24].copy_from_slice(&nonce.to_be_bytes());
2044 buf[24..32].copy_from_slice(&counter.to_be_bytes());
2045 U256::from_be_bytes(keccak256(buf).0)
2046}
2047
2048#[cfg(test)]
2049mod tests {
2050 use super::{
2051 Address, Fuzzer, InspectorStack, InspectorStackInner, OpcodeStepDispatch, RevertDiagnostic,
2052 TraceRequirements, compute_batch_create_salt,
2053 };
2054 use foundry_evm_core::evm::EthEvmNetwork;
2055
2056 #[test]
2057 fn opcode_dispatch_defaults_to_no_static_inspectors() {
2058 let stack = InspectorStackInner::default();
2059
2060 assert_eq!(stack.static_step_dispatch, OpcodeStepDispatch::None);
2061 assert!(!stack.has_static_step_end_inspectors);
2062 }
2063
2064 #[test]
2065 fn opcode_dispatch_uses_fuzzer_fast_path_when_fuzzer_is_only_static_step_inspector() {
2066 let mut stack = InspectorStack::<EthEvmNetwork>::new();
2067 stack.set_fuzzer(Fuzzer::new(16, None));
2068
2069 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::FuzzerOnly);
2070 assert!(!stack.inner.has_static_step_end_inspectors);
2071
2072 stack.collect_line_coverage(true);
2073 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
2074
2075 stack.collect_line_coverage(false);
2076 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::FuzzerOnly);
2077 }
2078
2079 #[test]
2080 fn opcode_dispatch_tracks_general_step_and_step_end_inspectors() {
2081 let mut stack = InspectorStack::<EthEvmNetwork>::new();
2082
2083 stack.print(true);
2084 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
2085 assert!(stack.inner.has_static_step_end_inspectors);
2086
2087 stack.print(false);
2088 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
2089 assert!(!stack.inner.has_static_step_end_inspectors);
2090
2091 stack.tracing_requirements(TraceRequirements::none().with_calls(true));
2092 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
2093 assert!(stack.inner.has_static_step_end_inspectors);
2094
2095 stack.tracing_requirements(TraceRequirements::none());
2096 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
2097 assert!(!stack.inner.has_static_step_end_inspectors);
2098
2099 stack.set_chisel(0);
2100 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
2101 assert!(stack.inner.has_static_step_end_inspectors);
2102 }
2103
2104 #[test]
2105 fn opcode_dispatch_tracks_script_and_edge_coverage_inspectors() {
2106 let mut stack = InspectorStack::<EthEvmNetwork>::new();
2107
2108 stack.script(Address::with_last_byte(1));
2109 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
2110 assert!(!stack.inner.has_static_step_end_inspectors);
2111
2112 let mut stack = InspectorStack::<EthEvmNetwork>::new();
2113 stack.collect_edge_coverage(true);
2114 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
2115 stack.collect_edge_coverage(false);
2116 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
2117 }
2118
2119 #[test]
2120 fn revert_diagnostic_frames_remain_balanced() {
2121 let mut inspector = RevertDiagnostic::default();
2122 inspector.frame_start();
2123 inspector.set_trace_node(0);
2124 inspector.frame_start();
2125 inspector.set_trace_node(1);
2126 inspector.frame_end();
2127 inspector.frame_end();
2128
2129 assert!(inspector.into_diagnostics().is_empty());
2130 }
2131
2132 #[test]
2133 fn distinct_salts_across_simulations_at_same_nonce() {
2134 let a = compute_batch_create_salt(0xabcd_ef01_2345_6789, 1, 5, 0);
2136 let b = compute_batch_create_salt(0x1122_3344_5566_7788, 1, 5, 0);
2137 assert_ne!(a, b);
2138 }
2139
2140 #[test]
2141 fn counter_changes_salt() {
2142 let a = compute_batch_create_salt(1, 1, 5, 0);
2143 let b = compute_batch_create_salt(1, 1, 5, 1);
2144 assert_ne!(a, b);
2145 }
2146
2147 #[test]
2148 fn chain_id_changes_salt() {
2149 let a = compute_batch_create_salt(1, 1, 5, 0);
2150 let b = compute_batch_create_salt(1, 2, 5, 0);
2151 assert_ne!(a, b);
2152 }
2153
2154 #[test]
2155 fn deterministic_for_same_inputs() {
2156 let a = compute_batch_create_salt(42, 1, 5, 7);
2157 let b = compute_batch_create_salt(42, 1, 5, 7);
2158 assert_eq!(a, b);
2159 }
2160
2161 #[test]
2162 fn distinct_create2_addresses_across_inspector_instances_at_same_onchain_state() {
2163 let factory = Address::with_last_byte(0x42);
2166 let init_code = b"\x60\x80\x60\x40".as_slice();
2167
2168 let mut a = InspectorStackInner::default();
2169 let mut b = InspectorStackInner::default();
2170 let salt_a = a.next_batch_create_salt(1, 5).to_be_bytes::<32>();
2171 let salt_b = b.next_batch_create_salt(1, 5).to_be_bytes::<32>();
2172
2173 let addr_a = factory.create2_from_code(salt_a, init_code);
2174 let addr_b = factory.create2_from_code(salt_b, init_code);
2175 assert_ne!(addr_a, addr_b);
2176 }
2177}