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(Default, Clone, Debug)]
378pub struct InspectorStackInner {
379 pub analysis: Option<Analysis>,
381
382 pub chisel_state: Option<Box<ChiselState>>,
386 pub edge_coverage: Option<Box<EdgeCovInspector>>,
387 pub fuzzer: Option<Box<Fuzzer>>,
388 pub line_coverage: Option<Box<LineCoverageCollector>>,
389 pub log_collector: Option<Box<LogCollector>>,
390 pub printer: Option<Box<CustomPrintTracer>>,
391 pub revert_diag: Option<Box<RevertDiagnostic>>,
392 pub script_execution_inspector: Option<Box<ScriptExecutionInspector>>,
393 pub tempo_labels: Option<Box<TempoLabels>>,
394 pub tracer: Option<Box<TracingInspector>>,
395
396 pub sancov_edges: bool,
399 pub sancov_trace_cmp: bool,
401 pub enable_isolation: bool,
402 pub networks: NetworkConfigs,
403 pub create2_deployer: Address,
404 pub in_inner_context: bool,
406 pub inner_context_data: Option<InnerContextData>,
407 pub locally_created_accounts: AddressHashSet,
409 pub top_frame_journal: AddressMap<Account>,
410 pub reverter: Option<Address>,
412 pub pending_create2_redirects: Vec<usize>,
415 pub pending_create2_error: Option<CreateOutcome>,
418 pub batch_create_counter: u64,
420 pub batch_rewrite_warned: bool,
422 pub batch_rewrite_process_salt: Option<u64>,
425 execution_cancellation: Option<EvmExecutionCancellation>,
427 cancellation_poll_counter: u8,
429 execution_cancelled: bool,
431 #[cfg(test)]
432 early_exit_test_gate: Option<EarlyExitTestGate>,
433 static_step_dispatch: OpcodeStepDispatch,
434 has_static_step_end_inspectors: bool,
435}
436
437pub struct InspectorStackRefMut<'a, FEN: FoundryEvmNetwork = EthEvmNetwork> {
440 pub cheatcodes: Option<&'a mut Cheatcodes<FEN>>,
441 pub inner: &'a mut InspectorStackInner,
442}
443
444impl<FEN: FoundryEvmNetwork> CheatcodesExecutor<FEN> for InspectorStackInner {
445 fn with_nested_evm(
446 &mut self,
447 cheats: &mut Cheatcodes<FEN>,
448 ecx: &mut FoundryContextFor<'_, FEN>,
449 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
450 ) -> Result<(), EVMError<DatabaseError>> {
451 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
452 with_cloned_context(ecx, |db, evm_env, journal_inner| {
453 let mut evm =
454 FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
455 *evm.journal_inner_mut() = journal_inner;
456 f(&mut *evm)?;
457 let sub_inner = evm.journal_inner_mut().clone();
458 let sub_evm_env = evm.to_evm_env();
459 Ok((sub_evm_env, sub_inner))
460 })
461 }
462
463 fn with_fresh_nested_evm(
464 &mut self,
465 cheats: &mut Cheatcodes<FEN>,
466 db: &mut <FoundryContextFor<'_, FEN> as ContextTr>::Db,
467 evm_env: EvmEnvFor<FEN>,
468 f: NestedEvmClosure<'_, SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>,
469 ) -> Result<EvmEnvFor<FEN>, EVMError<DatabaseError>> {
470 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
471 let mut evm =
472 FEN::EvmFactory::default().create_foundry_nested_evm(db, evm_env, &mut inspector);
473 f(&mut *evm)?;
474 Ok(evm.to_evm_env())
475 }
476
477 fn transact_on_db(
478 &mut self,
479 cheats: &mut Cheatcodes<FEN>,
480 ecx: &mut FoundryContextFor<'_, FEN>,
481 fork_id: Option<U256>,
482 transaction: B256,
483 ) -> eyre::Result<()> {
484 let evm_env = ecx.evm_clone();
485 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
486 let (db, inner) = ecx.db_journal_inner_mut();
487 db.transact(fork_id, transaction, evm_env, inner, &mut inspector)
488 }
489
490 fn transact_from_tx_on_db(
491 &mut self,
492 cheats: &mut Cheatcodes<FEN>,
493 ecx: &mut FoundryContextFor<'_, FEN>,
494 tx_env: TxEnvFor<FEN>,
495 ) -> eyre::Result<()> {
496 let evm_env = ecx.evm_clone();
497 let mut inspector = InspectorStackRefMut { cheatcodes: Some(cheats), inner: self };
498 let (db, inner) = ecx.db_journal_inner_mut();
499 db.transact_from_tx(tx_env, evm_env, inner, &mut inspector)
500 }
501
502 fn console_log(&mut self, msg: &str) {
503 if let Some(ref mut collector) = self.log_collector {
504 InspectorExt::console_log(&mut **collector, msg);
505 }
506 }
507
508 fn tracing_inspector(&mut self) -> Option<&mut TracingInspector> {
509 self.tracer.as_deref_mut()
510 }
511
512 fn set_in_inner_context(&mut self, enabled: bool, original_origin: Option<Address>) {
513 self.in_inner_context = enabled;
514 self.inner_context_data = enabled.then(|| InnerContextData {
515 original_origin: original_origin.expect("origin required when enabling inner ctx"),
516 locally_created_accounts: AddressHashSet::default(),
517 });
518 }
519}
520
521impl<FEN: FoundryEvmNetwork> Default for InspectorStack<FEN> {
522 fn default() -> Self {
523 Self::new()
524 }
525}
526
527impl<FEN: FoundryEvmNetwork> InspectorStack<FEN> {
528 #[inline]
534 pub fn new() -> Self {
535 Self { cheatcodes: None, inner: InspectorStackInner::default() }
536 }
537
538 #[inline]
540 pub fn set_analysis(&mut self, analysis: Analysis) {
541 self.analysis = Some(analysis);
542 }
543
544 #[inline]
546 pub(crate) fn set_early_exit(&mut self, early_exit: EarlyExit) {
547 self.execution_cancellation = Some(EvmExecutionCancellation::early_exit(early_exit));
548 }
549
550 #[inline]
552 pub(crate) fn set_execution_cancellation(&mut self, cancellation: EvmExecutionCancellation) {
553 self.execution_cancellation = Some(cancellation);
554 }
555
556 #[inline]
558 pub(crate) const fn execution_cancelled(&self) -> bool {
559 self.inner.execution_cancelled
560 }
561
562 #[cfg(test)]
563 pub(crate) fn set_early_exit_test_gate(
564 &mut self,
565 entered: std::sync::mpsc::Sender<()>,
566 release: std::sync::mpsc::Receiver<()>,
567 target_pc: usize,
568 ) {
569 self.early_exit_test_gate = Some(EarlyExitTestGate {
570 entered,
571 release: Arc::new(std::sync::Mutex::new(release)),
572 target_pc,
573 notified: Arc::new(std::sync::atomic::AtomicBool::new(false)),
574 });
575 }
576
577 #[inline]
579 pub fn set_block(&mut self, block: BlockEnvFor<FEN>) {
580 if let Some(cheatcodes) = &mut self.cheatcodes {
581 cheatcodes.block = Some(block);
582 }
583 }
584
585 #[inline]
587 pub fn set_gas_price(&mut self, gas_price: u128) {
588 if let Some(cheatcodes) = &mut self.cheatcodes {
589 cheatcodes.gas_price = Some(gas_price);
590 }
591 }
592
593 #[inline]
595 pub fn set_cheatcodes(&mut self, cheatcodes: Cheatcodes<FEN>) {
596 self.cheatcodes = Some(cheatcodes.into());
597 }
598
599 #[inline]
601 pub fn set_fuzzer(&mut self, fuzzer: Fuzzer) {
602 self.fuzzer = Some(fuzzer.into());
603 self.refresh_static_step_dispatch();
604 }
605
606 #[inline]
608 pub fn set_chisel(&mut self, final_pc: usize) {
609 self.chisel_state = Some(ChiselState::new(final_pc).into());
610 self.refresh_static_step_end_dispatch();
611 }
612
613 #[inline]
615 pub fn collect_line_coverage(&mut self, yes: bool) {
616 self.line_coverage = yes.then(Default::default);
617 self.refresh_static_step_dispatch();
618 }
619
620 #[inline]
622 pub fn collect_edge_coverage(&mut self, yes: bool) {
623 self.edge_coverage =
624 yes.then(|| EdgeCovInspector::with_config(EdgeCovConfig::default()).into());
625 self.refresh_static_step_dispatch();
626 }
627
628 #[inline]
632 pub fn collect_edge_coverage_with_config(&mut self, corpus: &FuzzCorpusConfig) {
633 self.edge_coverage = corpus
634 .collect_evm_edge_coverage()
635 .then(|| EdgeCovInspector::with_config(corpus.into()).into());
636 self.refresh_static_step_dispatch();
637 }
638
639 #[inline]
641 pub fn collect_evm_cmp_log(&mut self, yes: bool) {
642 if yes {
643 self.edge_coverage
644 .get_or_insert_with(|| EdgeCovInspector::with_cmp_log_only().into())
645 .enable_cmp_log(true);
646 } else if let Some(edge_coverage) = &mut self.edge_coverage {
647 edge_coverage.enable_cmp_log(false);
648 }
649 self.refresh_static_step_dispatch();
650 }
651
652 #[inline]
654 pub const fn collect_sancov_edges(&mut self, yes: bool) {
655 self.inner.sancov_edges = yes;
656 }
657
658 #[inline]
660 pub const fn collect_sancov_trace_cmp(&mut self, yes: bool) {
661 self.inner.sancov_trace_cmp = yes;
662 }
663
664 #[inline]
666 pub const fn enable_isolation(&mut self, yes: bool) {
667 self.inner.enable_isolation = yes;
668 }
669
670 #[inline]
672 pub const fn networks(&mut self, networks: NetworkConfigs) {
673 self.inner.networks = networks;
674 }
675
676 #[inline]
678 pub fn set_create2_deployer(&mut self, deployer: Address) {
679 self.create2_deployer = deployer;
680 }
681
682 #[inline]
687 pub fn collect_logs(&mut self, live_logs: Option<bool>) {
688 self.log_collector = live_logs.map(|live_logs| {
689 Box::new(if live_logs {
690 LogCollector::LiveLogs
691 } else {
692 LogCollector::Capture { logs: Vec::new() }
693 })
694 });
695 }
696
697 #[inline]
699 pub fn print(&mut self, yes: bool) {
700 self.printer = yes.then(Default::default);
701 self.refresh_static_opcode_dispatch();
702 }
703
704 #[inline]
706 pub fn tracing_requirements(&mut self, requirements: TraceRequirements) {
707 let config = requirements.into_config();
708 self.revert_diag = config.is_some().then(RevertDiagnostic::default).map(Into::into);
709
710 if let Some(config) = config {
711 *self.tracer.get_or_insert_with(Default::default).config_mut() = config;
712 } else {
713 self.tracer = None;
714 }
715 self.refresh_static_opcode_dispatch();
716 }
717
718 #[inline]
720 pub fn script(&mut self, script_address: Address) {
721 self.script_execution_inspector.get_or_insert_with(Default::default).script_address =
722 script_address;
723 self.refresh_static_step_dispatch();
724 }
725
726 #[inline(always)]
727 fn as_mut(&mut self) -> InspectorStackRefMut<'_, FEN> {
728 InspectorStackRefMut { cheatcodes: self.cheatcodes.as_deref_mut(), inner: &mut self.inner }
729 }
730
731 pub fn collect(self) -> InspectorData<FEN> {
733 let Self {
734 mut cheatcodes,
735 inner:
736 InspectorStackInner {
737 chisel_state,
738 line_coverage,
739 edge_coverage,
740 log_collector,
741 tempo_labels,
742 tracer,
743 reverter,
744 ..
745 },
746 } = self;
747
748 let traces = tracer.map(|tracer| tracer.into_traces()).map(|arena| {
749 let ignored = cheatcodes
750 .as_mut()
751 .map(|cheatcodes| {
752 let mut ignored = std::mem::take(&mut cheatcodes.ignored_traces.ignored);
753
754 if let Some(last_pause_call) = cheatcodes.ignored_traces.last_pause_call {
756 ignored.insert(last_pause_call, (arena.nodes().len(), 0));
757 }
758
759 ignored
760 })
761 .unwrap_or_default();
762
763 SparsedTraceArena { arena, ignored }
764 });
765
766 let (edge_coverage, evm_cmp_values) = edge_coverage
767 .map(|edge_coverage| {
768 let (hitcount, cmp_values) = edge_coverage.into_parts();
769 (Some(hitcount), (!cmp_values.is_empty()).then_some(cmp_values))
770 })
771 .unwrap_or_default();
772
773 InspectorData {
774 logs: log_collector.and_then(|logs| logs.into_captured_logs()).unwrap_or_default(),
775 labels: {
776 let mut labels = cheatcodes.as_ref().map(|c| c.labels.clone()).unwrap_or_default();
777 if let Some(tempo_labels) = tempo_labels {
778 labels.extend(tempo_labels.labels);
779 }
780 labels
781 },
782 traces,
783 line_coverage: line_coverage.map(|line_coverage| line_coverage.finish()),
784 edge_coverage,
785 evm_cmp_values,
786 cheatcodes,
787 chisel_state: chisel_state.and_then(|state| state.state),
788 reverter,
789 }
790 }
791}
792
793impl<FEN: FoundryEvmNetwork> InspectorStackRefMut<'_, FEN> {
794 fn adjust_evm_data_for_inner_context<CTX: FoundryContextExt>(&mut self, ecx: &mut CTX) {
799 let inner_context_data =
800 self.inner_context_data.as_ref().expect("should be called in inner context");
801 ecx.tx_mut().set_caller(inner_context_data.original_origin);
802 }
803
804 fn do_call_end(
805 &mut self,
806 ecx: &mut FoundryContextFor<'_, FEN>,
807 inputs: &CallInputs,
808 outcome: &mut CallOutcome,
809 ) {
810 if let Some(fuzzer) = &mut self.fuzzer {
811 fuzzer.call_end(ecx, inputs, outcome);
812 }
813
814 let result = outcome.result.result;
815 call_inspectors!(
816 #[ret]
817 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer, &mut self.revert_diag],
818 |inspector| {
819 let previous_output = outcome.output().clone();
820 inspector.call_end(ecx, inputs, outcome);
821
822 let different = outcome.result.result != result
825 || (outcome.result.result == InstructionResult::Revert
826 && outcome.output() != &previous_output);
827 different.then_some(())
828 },
829 );
830
831 if result.is_revert() && self.reverter.is_none() {
833 self.reverter = Some(inputs.target_address);
834 }
835 }
836
837 fn do_create_end(
838 &mut self,
839 ecx: &mut FoundryContextFor<'_, FEN>,
840 call: &CreateInputs,
841 outcome: &mut CreateOutcome,
842 ) {
843 let result = outcome.result.result;
844 call_inspectors!(
845 #[ret]
846 [&mut self.tracer, &mut self.cheatcodes, &mut self.printer],
847 |inspector| {
848 let previous_output = outcome.output().clone();
849 inspector.create_end(ecx, call, outcome);
850
851 let different = outcome.result.result != result
854 || (outcome.result.result == InstructionResult::Revert
855 && outcome.output() != &previous_output);
856 different.then_some(())
857 },
858 );
859 }
860
861 fn transact_inner(
862 &mut self,
863 ecx: &mut FoundryContextFor<'_, FEN>,
864 kind: TxKind,
865 caller: Address,
866 input: Bytes,
867 gas_limit: u64,
868 value: U256,
869 ) -> (InterpreterResult, Option<Address>) {
870 let cached_evm_env = ecx.evm_clone();
871 let cached_tx_env = ecx.tx_clone();
872
873 ecx.block_mut().set_basefee(0);
874
875 let chain_id = ecx.cfg().chain_id();
876 ecx.tx_mut().set_chain_id(Some(chain_id));
877 ecx.tx_mut().set_caller(caller);
878 ecx.tx_mut().set_kind(kind);
879 ecx.tx_mut().set_data(input);
880 ecx.tx_mut().set_value(value);
881 ecx.tx_mut().set_gas_limit(gas_limit + 21000);
883
884 if !ecx.cfg().is_block_gas_limit_disabled() {
887 let gas_limit = std::cmp::min(ecx.tx().gas_limit(), ecx.block().gas_limit());
888 ecx.tx_mut().set_gas_limit(gas_limit);
889 }
890 ecx.tx_mut().set_gas_price(0);
891 if ecx.tx().tx_type() == TransactionType::Eip4844 as u8 {
899 ecx.tx_mut().set_tx_type(TransactionType::Eip1559 as u8);
900 ecx.tx_mut().set_blob_hashes(Vec::new());
901 }
902
903 let locally_created_accounts = ecx
904 .journal()
905 .evm_state()
906 .iter()
907 .filter_map(|(addr, acc)| acc.is_created_locally().then_some(*addr))
908 .collect();
909 self.inner_context_data = Some(InnerContextData {
910 original_origin: cached_tx_env.caller(),
911 locally_created_accounts,
912 });
913 self.in_inner_context = true;
914
915 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
919 cheats.in_isolation_context = true;
920 }
921
922 let evm_env = ecx.evm_clone();
923 let tx_env = ecx.tx_clone();
924
925 let res = self.with_inspector(|mut inspector| {
926 let (res, nested_env) = {
927 let (db, journal) = ecx.db_journal_inner_mut();
928 let mut evm = FEN::EvmFactory::default().create_foundry_nested_evm(
929 db,
930 evm_env,
931 &mut inspector,
932 );
933
934 evm.journal_inner_mut().state = {
935 let mut state = journal.state.clone();
936
937 for (addr, acc_mut) in &mut state {
938 if journal.warm_addresses.is_cold(addr) {
943 acc_mut.mark_cold();
944 }
945
946 for slot_mut in acc_mut.storage.values_mut() {
948 slot_mut.is_cold = true;
949 slot_mut.original_value = slot_mut.present_value;
950 }
951 }
952
953 state
954 };
955
956 evm.journal_inner_mut().depth = 1;
958
959 let res = evm.transact_raw(tx_env);
960 let nested_evm_env = evm.to_evm_env();
961 (res, nested_evm_env)
962 };
963
964 let mut restored_evm_env = nested_env;
967 restored_evm_env.block_env.set_basefee(cached_evm_env.block_env.basefee());
968 ecx.set_evm(restored_evm_env);
969 ecx.set_tx(cached_tx_env);
970
971 res
972 });
973
974 self.in_inner_context = false;
975 self.inner_context_data = None;
976
977 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
980 cheats.in_isolation_context = false;
981 }
982
983 let mut gas = Gas::new(gas_limit);
984
985 let Ok(res) = res else {
986 let result =
988 InterpreterResult { result: InstructionResult::Revert, output: Bytes::new(), gas };
989 return (result, None);
990 };
991
992 for (addr, mut acc) in res.state {
993 let Some(acc_mut) = ecx.journal_mut().evm_state_mut().get_mut(&addr) else {
994 ecx.journal_mut().evm_state_mut().insert(addr, acc);
995 continue;
996 };
997
998 if acc.status.contains(AccountStatus::Cold)
1000 && !acc_mut.status.contains(AccountStatus::Cold)
1001 {
1002 acc.status -= AccountStatus::Cold;
1003 }
1004 acc_mut.info = acc.info;
1005 acc_mut.status |= acc.status;
1006
1007 for (key, val) in acc.storage {
1008 let Some(slot_mut) = acc_mut.storage.get_mut(&key) else {
1009 acc_mut.storage.insert(key, val);
1010 continue;
1011 };
1012 slot_mut.present_value = val.present_value;
1013 slot_mut.is_cold &= val.is_cold;
1014 }
1015 }
1016
1017 let (result, address, output) = match res.result {
1018 ExecutionResult::Success { reason, gas: result_gas, logs: _, output } => {
1019 gas.set_refund(result_gas.final_refunded() as i64);
1020 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
1021 let address = match output {
1022 Output::Create(_, address) => address,
1023 Output::Call(_) => None,
1024 };
1025 (reason.into(), address, output.into_data())
1026 }
1027 ExecutionResult::Halt { reason, gas: result_gas, .. } => {
1028 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
1029 (InstructionResult::from(reason), None, Bytes::new())
1030 }
1031 ExecutionResult::Revert { gas: result_gas, output, .. } => {
1032 let _ = gas.record_regular_cost(result_gas.tx_gas_used());
1033 (InstructionResult::Revert, None, output)
1034 }
1035 };
1036 (InterpreterResult { result, output, gas }, address)
1037 }
1038
1039 fn with_inspector<O>(&mut self, f: impl FnOnce(InspectorStackRefMut<'_, FEN>) -> O) -> O {
1042 let mut cheatcodes = self
1043 .cheatcodes
1044 .as_deref_mut()
1045 .map(|cheats| core::mem::replace(cheats, Cheatcodes::new(cheats.config.clone())));
1046 let mut inner = std::mem::take(self.inner);
1047
1048 let saved_create2_redirects = std::mem::take(&mut inner.pending_create2_redirects);
1051
1052 let out = f(InspectorStackRefMut { cheatcodes: cheatcodes.as_mut(), inner: &mut inner });
1053
1054 if let Some(cheats) = self.cheatcodes.as_deref_mut() {
1055 *cheats = cheatcodes.unwrap();
1056 }
1057
1058 inner.pending_create2_redirects = saved_create2_redirects;
1059 *self.inner = inner;
1060
1061 out
1062 }
1063
1064 fn top_level_frame_start(&mut self, ecx: &mut FoundryContextFor<'_, FEN>) {
1066 self.locally_created_accounts.clear();
1067
1068 if self.enable_isolation {
1069 self.top_frame_journal.clone_from(ecx.journal().evm_state());
1072 }
1073 }
1074
1075 fn top_level_frame_end(
1077 &mut self,
1078 ecx: &mut FoundryContextFor<'_, FEN>,
1079 result: InstructionResult,
1080 ) {
1081 if !result.is_revert() {
1082 return;
1083 }
1084 if let Some(cheats) = self.cheatcodes.as_mut() {
1088 cheats.on_revert(ecx);
1089 }
1090
1091 if self.enable_isolation {
1095 *ecx.journal_mut().evm_state_mut() = std::mem::take(&mut self.top_frame_journal);
1096 }
1097 }
1098
1099 #[inline(always)]
1105 fn step_inlined(
1106 &mut self,
1107 interpreter: &mut Interpreter,
1108 ecx: &mut FoundryContextFor<'_, FEN>,
1109 ) {
1110 #[cfg(test)]
1111 if interpreter.bytecode.opcode() == op::JUMPDEST
1112 && let Some(gate) = &self.inner.early_exit_test_gate
1113 && interpreter.bytecode.pc() == gate.target_pc
1114 && !gate.notified.swap(true, std::sync::atomic::Ordering::Relaxed)
1115 {
1116 let _ = gate.entered.send(());
1117 let _ = gate
1118 .release
1119 .lock()
1120 .expect("early-exit test gate lock poisoned")
1121 .recv_timeout(std::time::Duration::from_secs(1));
1122 }
1123
1124 if let Some(cancellation) = &self.inner.execution_cancellation {
1125 let poll_deadline = self.inner.cancellation_poll_counter == 0;
1126 self.inner.cancellation_poll_counter =
1127 self.inner.cancellation_poll_counter.wrapping_add(1);
1128 if cancellation.should_stop(poll_deadline) {
1129 self.inner.execution_cancelled = true;
1130 interpreter.halt(InstructionResult::Stop);
1131 return;
1132 }
1133 }
1134
1135 match self.static_step_dispatch {
1136 OpcodeStepDispatch::None => {}
1137 OpcodeStepDispatch::FuzzerOnly => {
1138 if let Some(inspector) = &mut self.fuzzer {
1139 inspector.step(interpreter, ecx);
1140 }
1141 }
1142 OpcodeStepDispatch::General => {
1143 call_inspectors!(
1144 [
1145 &mut self.edge_coverage,
1147 &mut self.fuzzer,
1148 &mut self.line_coverage,
1149 &mut self.printer,
1150 &mut self.revert_diag,
1151 &mut self.script_execution_inspector,
1152 &mut self.tracer,
1153 ],
1154 |inspector| (**inspector).step(interpreter, ecx),
1155 );
1156 }
1157 }
1158
1159 if let Some(cheats) = self.cheatcodes.as_mut() {
1160 cheats.pc = interpreter.bytecode.pc();
1161 if cheats.has_step_hooks() {
1162 let opcode = interpreter.bytecode.opcode();
1163 if !cheats.has_recording_accesses_only_step_hook()
1164 || matches!(opcode, op::SLOAD | op::SSTORE)
1165 {
1166 crate::utils::cold_path();
1167 cheats.step(interpreter, ecx);
1168 }
1169 }
1170 }
1171 }
1172
1173 #[inline(always)]
1174 fn step_end_inlined(
1175 &mut self,
1176 interpreter: &mut Interpreter,
1177 ecx: &mut FoundryContextFor<'_, FEN>,
1178 ) {
1179 if self.has_static_step_end_inspectors {
1180 call_inspectors!(
1181 [
1182 &mut self.chisel_state,
1184 &mut self.printer,
1185 &mut self.revert_diag,
1186 &mut self.tracer,
1187 ],
1188 |inspector| (**inspector).step_end(interpreter, ecx),
1189 );
1190 }
1191
1192 if let Some(cheats) = self.cheatcodes.as_mut()
1193 && cheats.has_step_end_hooks()
1194 {
1195 crate::utils::cold_path();
1196 cheats.step_end(interpreter, ecx);
1197 }
1198 }
1199}
1200
1201impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>>
1202 for InspectorStackRefMut<'_, FEN>
1203{
1204 fn initialize_interp(
1205 &mut self,
1206 interpreter: &mut Interpreter,
1207 ecx: &mut FoundryContextFor<'_, FEN>,
1208 ) {
1209 let address = interpreter.input.target_address();
1210 let should_mark_created_locally = self.locally_created_accounts.contains(&address)
1211 || self
1212 .inner_context_data
1213 .as_ref()
1214 .is_some_and(|ctx| ctx.locally_created_accounts.contains(&address));
1215 if should_mark_created_locally
1216 && let Some(account) = ecx.journal_mut().evm_state_mut().get_mut(&address)
1217 {
1218 account.mark_created_locally();
1219 }
1220
1221 call_inspectors!(
1222 [
1223 &mut self.line_coverage,
1224 &mut self.tracer,
1225 &mut self.cheatcodes,
1226 &mut self.script_execution_inspector,
1227 &mut self.printer
1228 ],
1229 |inspector| inspector.initialize_interp(interpreter, ecx),
1230 );
1231 }
1232
1233 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1234 self.step_inlined(interpreter, ecx);
1235 }
1236
1237 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1238 self.step_end_inlined(interpreter, ecx);
1239 }
1240
1241 #[allow(clippy::redundant_clone)]
1242 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1243 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
1244 inspector.log(ecx, log.clone())
1245 });
1246 if let Some(inspector) = &mut self.cheatcodes
1247 && inspector.has_log_hooks()
1248 {
1249 crate::utils::cold_path();
1250 inspector.log(ecx, log.clone());
1251 }
1252 call_inspectors!([&mut self.printer], |inspector| { inspector.log(ecx, log.clone()) });
1253 }
1254
1255 #[allow(clippy::redundant_clone)]
1256 fn log_full(
1257 &mut self,
1258 interpreter: &mut Interpreter,
1259 ecx: &mut FoundryContextFor<'_, FEN>,
1260 log: Log,
1261 ) {
1262 call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
1263 inspector.log_full(interpreter, ecx, log.clone())
1264 });
1265 if let Some(inspector) = &mut self.cheatcodes
1266 && inspector.has_log_hooks()
1267 {
1268 crate::utils::cold_path();
1269 inspector.log_full(interpreter, ecx, log.clone());
1270 }
1271 call_inspectors!([&mut self.printer], |inspector| {
1272 inspector.log_full(interpreter, ecx, log.clone())
1273 });
1274 }
1275
1276 fn frame_start(
1277 &mut self,
1278 ecx: &mut FoundryContextFor<'_, FEN>,
1279 frame_input: &mut FrameInput,
1280 ) -> Option<FrameResult> {
1281 if let FrameInput::Create(inputs) = frame_input
1282 && self.should_use_create2_factory(ecx.journal().depth(), inputs)
1283 {
1284 let salt = match inputs.scheme() {
1289 CreateScheme::Create2 { salt } => salt,
1290 CreateScheme::Create => {
1293 if !self.inner.batch_rewrite_warned {
1294 let _ = sh_warn!(
1295 "--batch rewrites CREATE → CREATE2 via the Arachnid factory; \
1296 deployed addresses follow the CREATE2 formula and constructor \
1297 msg.sender is the factory, not the EOA."
1298 );
1299 self.inner.batch_rewrite_warned = true;
1300 }
1301 let chain_id = ecx.cfg().chain_id();
1302 let nonce = ecx.journal_mut().load_account(inputs.caller()).ok()?.info.nonce;
1303 self.inner.next_batch_create_salt(chain_id, nonce)
1304 }
1305 _ => return None,
1306 };
1307
1308 let gas_limit = inputs.gas_limit();
1309 let create2_deployer = self.create2_deployer();
1310
1311 let code_hash = ecx.journal_mut().load_account(create2_deployer).ok()?.info.code_hash;
1313 if code_hash == KECCAK_EMPTY {
1314 self.inner.pending_create2_error = Some(CreateOutcome {
1317 result: InterpreterResult {
1318 result: InstructionResult::Revert,
1319 output: Bytes::from(
1320 format!("missing CREATE2 deployer: {create2_deployer}").into_bytes(),
1321 ),
1322 gas: Gas::new(gas_limit),
1323 },
1324 address: None,
1325 });
1326 return None;
1327 } else if code_hash != DEFAULT_CREATE2_DEPLOYER_CODEHASH {
1328 self.inner.pending_create2_error = Some(CreateOutcome {
1329 result: InterpreterResult {
1330 result: InstructionResult::Revert,
1331 output: "invalid CREATE2 deployer bytecode".into(),
1332 gas: Gas::new(gas_limit),
1333 },
1334 address: None,
1335 });
1336 return None;
1337 }
1338
1339 let call_inputs =
1340 get_create2_factory_call_inputs(salt, inputs, create2_deployer, ecx.journal_mut())
1341 .ok()?;
1342
1343 self.inner.pending_create2_redirects.push(ecx.journal().depth());
1345
1346 *frame_input = FrameInput::Call(Box::new(call_inputs));
1348 }
1349
1350 None
1351 }
1352
1353 fn frame_end(
1354 &mut self,
1355 ecx: &mut FoundryContextFor<'_, FEN>,
1356 _frame_input: &FrameInput,
1357 frame_result: &mut FrameResult,
1358 ) {
1359 let depth = ecx.journal().depth();
1360 if self.inner.pending_create2_redirects.last().copied() != Some(depth) {
1361 return;
1362 }
1363
1364 self.inner.pending_create2_redirects.pop();
1365
1366 let FrameResult::Call(call) = frame_result else {
1367 debug_assert!(false, "pending CREATE2 redirect ended with non-call result");
1368 return;
1369 };
1370
1371 let address = match call.instruction_result() {
1372 return_ok!() => Address::try_from(call.output().as_ref())
1373 .map_err(|_| {
1374 call.result = InterpreterResult {
1375 result: InstructionResult::Revert,
1376 output: "invalid CREATE2 factory output".into(),
1377 gas: Gas::new(call.result.gas.limit()),
1378 };
1379 })
1380 .ok(),
1381 _ => None,
1382 };
1383
1384 *frame_result = FrameResult::Create(CreateOutcome { result: call.result.clone(), address });
1385 }
1386
1387 fn call(
1388 &mut self,
1389 ecx: &mut FoundryContextFor<'_, FEN>,
1390 call: &mut CallInputs,
1391 ) -> Option<CallOutcome> {
1392 if self.in_inner_context && ecx.journal().depth() == 1 {
1393 self.adjust_evm_data_for_inner_context(ecx);
1394 return None;
1395 }
1396
1397 if ecx.journal().depth() == 0 {
1398 self.top_level_frame_start(ecx);
1399 }
1400
1401 call_inspectors!(
1402 #[ret]
1403 [
1404 &mut self.fuzzer,
1405 &mut self.tracer,
1406 &mut self.log_collector,
1407 &mut self.printer,
1408 &mut self.revert_diag,
1409 &mut self.tempo_labels
1410 ],
1411 |inspector| {
1412 let mut out = None;
1413 if let Some(output) = inspector.call(ecx, call) {
1414 out = Some(Some(output));
1415 }
1416 out
1417 },
1418 );
1419
1420 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1421 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1423 let input_bytes = call.input.bytes(ecx);
1424 if let Some(target) = mocks
1427 .get(&input_bytes)
1428 .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1429 {
1430 call.bytecode_address = *target;
1431
1432 let target = ecx
1433 .journal_mut()
1434 .load_account_with_code(*target)
1435 .expect("failed to load account");
1436 call.known_bytecode =
1437 (target.info.code_hash, target.info.code.clone().unwrap_or_default());
1438 }
1439 }
1440
1441 if let Some(output) = cheatcodes.call_with_executor(ecx, call, self.inner) {
1442 return Some(output);
1443 }
1444 }
1445
1446 if let Some(outcome) = handle_arbitrum_system_call::<FEN>(ecx, call) {
1447 return Some(outcome);
1448 }
1449
1450 if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1451 match call.scheme {
1452 CallScheme::Call => {
1454 let input = call.input.bytes(ecx);
1455 let (result, _) = self.transact_inner(
1456 ecx,
1457 TxKind::Call(call.target_address),
1458 call.caller,
1459 input,
1460 call.gas_limit,
1461 call.value.get(),
1462 );
1463 return Some(CallOutcome {
1464 result,
1465 memory_offset: call.return_memory_offset.clone(),
1466 was_precompile_called: true,
1467 precompile_call_logs: vec![],
1468 charged_new_account_state_gas: false,
1469 });
1470 }
1471 CallScheme::StaticCall => {
1473 let (_, journal_inner) = ecx.db_journal_inner_mut();
1474 let JournaledState { state, warm_addresses, .. } = journal_inner;
1475 for (addr, acc_mut) in state {
1476 if let Some(cheatcodes) = &self.cheatcodes
1478 && cheatcodes.has_arbitrary_storage(addr)
1479 {
1480 continue;
1481 }
1482
1483 if warm_addresses.is_cold(addr) {
1484 acc_mut.mark_cold();
1485 }
1486
1487 for slot_mut in acc_mut.storage.values_mut() {
1488 slot_mut.is_cold = true;
1489 }
1490 }
1491 }
1492 CallScheme::CallCode | CallScheme::DelegateCall => {}
1494 }
1495 }
1496
1497 None
1498 }
1499
1500 fn call_end(
1501 &mut self,
1502 ecx: &mut FoundryContextFor<'_, FEN>,
1503 inputs: &CallInputs,
1504 outcome: &mut CallOutcome,
1505 ) {
1506 if self.in_inner_context && ecx.journal().depth() == 1 {
1509 return;
1510 }
1511
1512 self.do_call_end(ecx, inputs, outcome);
1513
1514 if ecx.journal().depth() == 0 {
1515 self.top_level_frame_end(ecx, outcome.result.result);
1516 }
1517 }
1518
1519 fn create(
1520 &mut self,
1521 ecx: &mut FoundryContextFor<'_, FEN>,
1522 create: &mut CreateInputs,
1523 ) -> Option<CreateOutcome> {
1524 if self.in_inner_context && ecx.journal().depth() == 1 {
1525 self.adjust_evm_data_for_inner_context(ecx);
1526 return None;
1527 }
1528
1529 if ecx.journal().depth() == 0 {
1530 self.top_level_frame_start(ecx);
1531 }
1532
1533 call_inspectors!(
1534 #[ret]
1535 [&mut self.tracer, &mut self.line_coverage, &mut self.cheatcodes],
1536 |inspector| inspector.create(ecx, create).map(Some),
1537 );
1538
1539 if let Some(error) = self.inner.pending_create2_error.take() {
1542 return Some(error);
1543 }
1544
1545 if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1546 && self.enable_isolation
1547 && !self.in_inner_context
1548 && ecx.journal().depth() == 1
1549 {
1550 let precomputed_address = ecx
1554 .journal()
1555 .evm_state()
1556 .get(&create.caller())
1557 .map(|acc| create.caller().create(acc.info.nonce));
1558
1559 let (result, address) = self.transact_inner(
1560 ecx,
1561 TxKind::Create,
1562 create.caller(),
1563 create.init_code().clone(),
1564 create.gas_limit(),
1565 create.value(),
1566 );
1567 let address =
1568 address.or_else(|| if result.is_revert() { precomputed_address } else { None });
1569 return Some(CreateOutcome { result, address });
1570 }
1571
1572 None
1573 }
1574
1575 fn create_end(
1576 &mut self,
1577 ecx: &mut FoundryContextFor<'_, FEN>,
1578 call: &CreateInputs,
1579 outcome: &mut CreateOutcome,
1580 ) {
1581 if outcome.result.result.is_ok()
1582 && let Some(address) = outcome.address
1583 {
1584 self.locally_created_accounts.insert(address);
1585
1586 if self.in_inner_context
1587 && let Some(inner_context) = &mut self.inner_context_data
1588 {
1589 inner_context.locally_created_accounts.insert(address);
1590 }
1591 }
1592
1593 if self.in_inner_context && ecx.journal().depth() == 1 {
1596 return;
1597 }
1598
1599 self.do_create_end(ecx, call, outcome);
1600
1601 if ecx.journal().depth() == 0 {
1602 self.top_level_frame_end(ecx, outcome.result.result);
1603 }
1604 }
1605
1606 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1607 call_inspectors!([&mut self.printer], |inspector| {
1608 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1609 inspector, contract, target, value,
1610 )
1611 });
1612 }
1613}
1614
1615fn handle_arbitrum_system_call<FEN: FoundryEvmNetwork>(
1616 ecx: &mut FoundryContextFor<'_, FEN>,
1617 call: &CallInputs,
1618) -> Option<CallOutcome> {
1619 if call.target_address != arbitrum::ARB_SYS_ADDRESS
1620 || call.bytecode_address != arbitrum::ARB_SYS_ADDRESS
1621 || !arbitrum::is_arbitrum_chain(ecx.cfg().chain_id)
1622 {
1623 return None;
1624 }
1625
1626 let input = call.input.bytes(ecx);
1627 if input.get(..4) != Some(&arbitrum::ARB_BLOCK_NUMBER_SELECTOR) {
1628 return None;
1629 }
1630
1631 let block_number = ecx.db().active_fork_block_number()?;
1632 let Some((gas_cost, output)) = arbitrum::arb_block_number_call(call.gas_limit, block_number)
1633 else {
1634 return Some(arbitrum_call_outcome(
1635 call,
1636 InstructionResult::PrecompileOOG,
1637 0,
1638 Bytes::new(),
1639 ));
1640 };
1641
1642 Some(arbitrum_call_outcome(call, InstructionResult::Return, gas_cost, output))
1643}
1644
1645fn arbitrum_call_outcome(
1646 call: &CallInputs,
1647 result: InstructionResult,
1648 gas_used: u64,
1649 output: Bytes,
1650) -> CallOutcome {
1651 let mut gas = Gas::new(call.gas_limit);
1652 if result.is_ok() {
1653 let _ = gas.record_regular_cost(gas_used);
1654 } else {
1655 gas.spend_all();
1656 }
1657
1658 CallOutcome {
1659 result: InterpreterResult { result, output, gas },
1660 memory_offset: call.return_memory_offset.clone(),
1661 was_precompile_called: true,
1662 precompile_call_logs: vec![],
1663 charged_new_account_state_gas: call.charged_new_account_state_gas,
1664 }
1665}
1666
1667impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStackRefMut<'_, FEN> {
1668 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1669 call_inspectors!(
1670 #[ret]
1671 [&mut self.cheatcodes],
1672 |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1673 );
1674
1675 false
1676 }
1677
1678 fn console_log(&mut self, msg: &str) {
1679 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1680 inspector, msg
1681 ));
1682 }
1683
1684 fn get_networks(&self) -> NetworkConfigs {
1685 self.inner.networks
1686 }
1687
1688 fn create2_deployer(&self) -> Address {
1689 self.inner.create2_deployer
1690 }
1691}
1692
1693impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for InspectorStack<FEN> {
1694 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1695 self.as_mut().step_inlined(interpreter, ecx)
1696 }
1697
1698 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1699 self.as_mut().step_end_inlined(interpreter, ecx)
1700 }
1701
1702 fn call(
1703 &mut self,
1704 context: &mut FoundryContextFor<'_, FEN>,
1705 inputs: &mut CallInputs,
1706 ) -> Option<CallOutcome> {
1707 self.as_mut().call(context, inputs)
1708 }
1709
1710 fn call_end(
1711 &mut self,
1712 context: &mut FoundryContextFor<'_, FEN>,
1713 inputs: &CallInputs,
1714 outcome: &mut CallOutcome,
1715 ) {
1716 self.as_mut().call_end(context, inputs, outcome)
1717 }
1718
1719 fn create(
1720 &mut self,
1721 context: &mut FoundryContextFor<'_, FEN>,
1722 create: &mut CreateInputs,
1723 ) -> Option<CreateOutcome> {
1724 self.as_mut().create(context, create)
1725 }
1726
1727 fn create_end(
1728 &mut self,
1729 context: &mut FoundryContextFor<'_, FEN>,
1730 call: &CreateInputs,
1731 outcome: &mut CreateOutcome,
1732 ) {
1733 self.as_mut().create_end(context, call, outcome)
1734 }
1735
1736 fn initialize_interp(
1737 &mut self,
1738 interpreter: &mut Interpreter,
1739 ecx: &mut FoundryContextFor<'_, FEN>,
1740 ) {
1741 self.as_mut().initialize_interp(interpreter, ecx)
1742 }
1743
1744 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1745 self.as_mut().log(ecx, log)
1746 }
1747
1748 fn log_full(
1749 &mut self,
1750 interpreter: &mut Interpreter,
1751 ecx: &mut FoundryContextFor<'_, FEN>,
1752 log: Log,
1753 ) {
1754 self.as_mut().log_full(interpreter, ecx, log)
1755 }
1756
1757 fn frame_start(
1758 &mut self,
1759 context: &mut FoundryContextFor<'_, FEN>,
1760 frame_input: &mut FrameInput,
1761 ) -> Option<FrameResult> {
1762 self.as_mut().frame_start(context, frame_input)
1763 }
1764
1765 fn frame_end(
1766 &mut self,
1767 context: &mut FoundryContextFor<'_, FEN>,
1768 frame_input: &FrameInput,
1769 frame_result: &mut FrameResult,
1770 ) {
1771 self.as_mut().frame_end(context, frame_input, frame_result)
1772 }
1773
1774 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1775 call_inspectors!([&mut self.inner.printer], |inspector| {
1776 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1777 inspector, contract, target, value,
1778 )
1779 });
1780 }
1781}
1782
1783impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStack<FEN> {
1784 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1785 self.as_mut().should_use_create2_factory(depth, inputs)
1786 }
1787
1788 fn get_networks(&self) -> NetworkConfigs {
1789 self.networks
1790 }
1791
1792 fn create2_deployer(&self) -> Address {
1793 self.create2_deployer
1794 }
1795}
1796
1797impl<'a, FEN: FoundryEvmNetwork> Deref for InspectorStackRefMut<'a, FEN> {
1798 type Target = &'a mut InspectorStackInner;
1799
1800 fn deref(&self) -> &Self::Target {
1801 &self.inner
1802 }
1803}
1804
1805impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStackRefMut<'_, FEN> {
1806 fn deref_mut(&mut self) -> &mut Self::Target {
1807 &mut self.inner
1808 }
1809}
1810
1811impl<FEN: FoundryEvmNetwork> Deref for InspectorStack<FEN> {
1812 type Target = InspectorStackInner;
1813
1814 fn deref(&self) -> &Self::Target {
1815 &self.inner
1816 }
1817}
1818
1819impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStack<FEN> {
1820 fn deref_mut(&mut self) -> &mut Self::Target {
1821 &mut self.inner
1822 }
1823}
1824
1825impl InspectorStackInner {
1826 #[inline]
1827 const fn refresh_static_opcode_dispatch(&mut self) {
1828 self.refresh_static_step_dispatch();
1829 self.refresh_static_step_end_dispatch();
1830 }
1831
1832 #[inline]
1833 const fn refresh_static_step_dispatch(&mut self) {
1834 self.static_step_dispatch = if self.edge_coverage.is_none()
1835 && self.line_coverage.is_none()
1836 && self.printer.is_none()
1837 && self.revert_diag.is_none()
1838 && self.script_execution_inspector.is_none()
1839 && self.tracer.is_none()
1840 {
1841 if self.fuzzer.is_some() {
1842 OpcodeStepDispatch::FuzzerOnly
1843 } else {
1844 OpcodeStepDispatch::None
1845 }
1846 } else {
1847 OpcodeStepDispatch::General
1848 };
1849 }
1850
1851 #[inline]
1852 const fn refresh_static_step_end_dispatch(&mut self) {
1853 self.has_static_step_end_inspectors = self.chisel_state.is_some()
1854 || self.printer.is_some()
1855 || self.revert_diag.is_some()
1856 || self.tracer.is_some();
1857 }
1858
1859 fn next_batch_create_salt(&mut self, chain_id: u64, nonce: u64) -> U256 {
1862 let process_salt = *self.batch_rewrite_process_salt.get_or_insert_with(rand::random);
1863 let counter = self.batch_create_counter;
1864 self.batch_create_counter = counter.wrapping_add(1);
1865 compute_batch_create_salt(process_salt, chain_id, nonce, counter)
1866 }
1867}
1868
1869fn compute_batch_create_salt(process_salt: u64, chain_id: u64, nonce: u64, counter: u64) -> U256 {
1875 let mut buf = [0u8; 32];
1876 buf[0..8].copy_from_slice(&process_salt.to_be_bytes());
1877 buf[8..16].copy_from_slice(&chain_id.to_be_bytes());
1878 buf[16..24].copy_from_slice(&nonce.to_be_bytes());
1879 buf[24..32].copy_from_slice(&counter.to_be_bytes());
1880 U256::from_be_bytes(keccak256(buf).0)
1881}
1882
1883#[cfg(test)]
1884mod tests {
1885 use super::{
1886 Address, Fuzzer, InspectorStack, InspectorStackInner, OpcodeStepDispatch,
1887 TraceRequirements, compute_batch_create_salt,
1888 };
1889 use foundry_evm_core::evm::EthEvmNetwork;
1890
1891 #[test]
1892 fn opcode_dispatch_defaults_to_no_static_inspectors() {
1893 let stack = InspectorStackInner::default();
1894
1895 assert_eq!(stack.static_step_dispatch, OpcodeStepDispatch::None);
1896 assert!(!stack.has_static_step_end_inspectors);
1897 }
1898
1899 #[test]
1900 fn opcode_dispatch_uses_fuzzer_fast_path_when_fuzzer_is_only_static_step_inspector() {
1901 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1902 stack.set_fuzzer(Fuzzer::new(16, None));
1903
1904 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::FuzzerOnly);
1905 assert!(!stack.inner.has_static_step_end_inspectors);
1906
1907 stack.collect_line_coverage(true);
1908 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1909
1910 stack.collect_line_coverage(false);
1911 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::FuzzerOnly);
1912 }
1913
1914 #[test]
1915 fn opcode_dispatch_tracks_general_step_and_step_end_inspectors() {
1916 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1917
1918 stack.print(true);
1919 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1920 assert!(stack.inner.has_static_step_end_inspectors);
1921
1922 stack.print(false);
1923 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1924 assert!(!stack.inner.has_static_step_end_inspectors);
1925
1926 stack.tracing_requirements(TraceRequirements::none().with_calls(true));
1927 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1928 assert!(stack.inner.has_static_step_end_inspectors);
1929
1930 stack.tracing_requirements(TraceRequirements::none());
1931 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1932 assert!(!stack.inner.has_static_step_end_inspectors);
1933
1934 stack.set_chisel(0);
1935 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1936 assert!(stack.inner.has_static_step_end_inspectors);
1937 }
1938
1939 #[test]
1940 fn opcode_dispatch_tracks_script_and_edge_coverage_inspectors() {
1941 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1942
1943 stack.script(Address::with_last_byte(1));
1944 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1945 assert!(!stack.inner.has_static_step_end_inspectors);
1946
1947 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1948 stack.collect_edge_coverage(true);
1949 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1950 stack.collect_edge_coverage(false);
1951 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1952 }
1953
1954 #[test]
1955 fn distinct_salts_across_simulations_at_same_nonce() {
1956 let a = compute_batch_create_salt(0xabcd_ef01_2345_6789, 1, 5, 0);
1958 let b = compute_batch_create_salt(0x1122_3344_5566_7788, 1, 5, 0);
1959 assert_ne!(a, b);
1960 }
1961
1962 #[test]
1963 fn counter_changes_salt() {
1964 let a = compute_batch_create_salt(1, 1, 5, 0);
1965 let b = compute_batch_create_salt(1, 1, 5, 1);
1966 assert_ne!(a, b);
1967 }
1968
1969 #[test]
1970 fn chain_id_changes_salt() {
1971 let a = compute_batch_create_salt(1, 1, 5, 0);
1972 let b = compute_batch_create_salt(1, 2, 5, 0);
1973 assert_ne!(a, b);
1974 }
1975
1976 #[test]
1977 fn deterministic_for_same_inputs() {
1978 let a = compute_batch_create_salt(42, 1, 5, 7);
1979 let b = compute_batch_create_salt(42, 1, 5, 7);
1980 assert_eq!(a, b);
1981 }
1982
1983 #[test]
1984 fn distinct_create2_addresses_across_inspector_instances_at_same_onchain_state() {
1985 let factory = Address::with_last_byte(0x42);
1988 let init_code = b"\x60\x80\x60\x40".as_slice();
1989
1990 let mut a = InspectorStackInner::default();
1991 let mut b = InspectorStackInner::default();
1992 let salt_a = a.next_batch_create_salt(1, 5).to_be_bytes::<32>();
1993 let salt_b = b.next_batch_create_salt(1, 5).to_be_bytes::<32>();
1994
1995 let addr_a = factory.create2_from_code(salt_a, init_code);
1996 let addr_b = factory.create2_from_code(salt_b, init_code);
1997 assert_ne!(addr_a, addr_b);
1998 }
1999}