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 let trace_idx = self.tracer.as_ref().map(|tracer| tracer.traces().nodes().len() - 1);
1424 let mut cheatcode_outcome = None;
1425 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1426 if let Some(mocks) = cheatcodes.mocked_functions.get(&call.bytecode_address) {
1428 let input_bytes = call.input.bytes(ecx);
1429 if let Some(target) = mocks
1432 .get(&input_bytes)
1433 .or_else(|| input_bytes.get(..4).and_then(|selector| mocks.get(selector)))
1434 {
1435 call.bytecode_address = *target;
1436
1437 let target = ecx
1438 .journal_mut()
1439 .load_account_with_code(*target)
1440 .expect("failed to load account");
1441 call.known_bytecode =
1442 (target.info.code_hash, target.info.code.clone().unwrap_or_default());
1443 }
1444 }
1445
1446 cheatcode_outcome = cheatcodes.call_with_executor(ecx, call, self.inner);
1447 }
1448
1449 if let Some(trace_idx) = trace_idx
1450 && let Some(tracer) = self.tracer.as_deref_mut()
1451 {
1452 let caller = match call.scheme {
1453 CallScheme::DelegateCall | CallScheme::CallCode => call.target_address,
1454 CallScheme::Call | CallScheme::StaticCall => call.caller,
1455 };
1456 let node = &mut tracer.traces_mut().nodes_mut()[trace_idx];
1457 debug_assert_eq!(node.trace.depth, ecx.journal().depth());
1458 node.trace.caller = caller;
1459 }
1460
1461 if let Some(output) = cheatcode_outcome {
1462 return Some(output);
1463 }
1464
1465 if let Some(outcome) = handle_arbitrum_system_call::<FEN>(ecx, call) {
1466 return Some(outcome);
1467 }
1468
1469 if self.enable_isolation && !self.in_inner_context && ecx.journal().depth() == 1 {
1470 match call.scheme {
1471 CallScheme::Call => {
1473 let input = call.input.bytes(ecx);
1474 let (result, _) = self.transact_inner(
1475 ecx,
1476 TxKind::Call(call.target_address),
1477 call.caller,
1478 input,
1479 call.gas_limit,
1480 call.value.get(),
1481 );
1482 return Some(CallOutcome {
1483 result,
1484 memory_offset: call.return_memory_offset.clone(),
1485 was_precompile_called: true,
1486 precompile_call_logs: vec![],
1487 charged_new_account_state_gas: false,
1488 });
1489 }
1490 CallScheme::StaticCall => {
1492 let (_, journal_inner) = ecx.db_journal_inner_mut();
1493 let JournaledState { state, warm_addresses, .. } = journal_inner;
1494 for (addr, acc_mut) in state {
1495 if let Some(cheatcodes) = &self.cheatcodes
1497 && cheatcodes.has_arbitrary_storage(addr)
1498 {
1499 continue;
1500 }
1501
1502 if warm_addresses.is_cold(addr) {
1503 acc_mut.mark_cold();
1504 }
1505
1506 for slot_mut in acc_mut.storage.values_mut() {
1507 slot_mut.is_cold = true;
1508 }
1509 }
1510 }
1511 CallScheme::CallCode | CallScheme::DelegateCall => {}
1513 }
1514 }
1515
1516 None
1517 }
1518
1519 fn call_end(
1520 &mut self,
1521 ecx: &mut FoundryContextFor<'_, FEN>,
1522 inputs: &CallInputs,
1523 outcome: &mut CallOutcome,
1524 ) {
1525 if self.in_inner_context && ecx.journal().depth() == 1 {
1528 return;
1529 }
1530
1531 self.do_call_end(ecx, inputs, outcome);
1532
1533 if ecx.journal().depth() == 0 {
1534 self.top_level_frame_end(ecx, outcome.result.result);
1535 }
1536 }
1537
1538 fn create(
1539 &mut self,
1540 ecx: &mut FoundryContextFor<'_, FEN>,
1541 create: &mut CreateInputs,
1542 ) -> Option<CreateOutcome> {
1543 if self.in_inner_context && ecx.journal().depth() == 1 {
1544 self.adjust_evm_data_for_inner_context(ecx);
1545 return None;
1546 }
1547
1548 if ecx.journal().depth() == 0 {
1549 self.top_level_frame_start(ecx);
1550 }
1551
1552 call_inspectors!(
1553 #[ret]
1554 [&mut self.tracer, &mut self.line_coverage],
1555 |inspector| inspector.create(ecx, create).map(Some),
1556 );
1557
1558 let trace_idx = self.tracer.as_ref().map(|tracer| tracer.traces().nodes().len() - 1);
1559 let mut cheatcode_outcome = None;
1560 if let Some(cheatcodes) = self.cheatcodes.as_deref_mut() {
1561 cheatcode_outcome = cheatcodes.create(ecx, create);
1562 }
1563
1564 if let Some(trace_idx) = trace_idx
1565 && let Some(tracer) = self.tracer.as_deref_mut()
1566 {
1567 let node = &mut tracer.traces_mut().nodes_mut()[trace_idx];
1568 debug_assert_eq!(node.trace.depth, ecx.journal().depth());
1569 node.trace.caller = create.caller();
1570 }
1571
1572 if let Some(output) = cheatcode_outcome {
1573 return Some(output);
1574 }
1575
1576 if let Some(error) = self.inner.pending_create2_error.take() {
1579 return Some(error);
1580 }
1581
1582 if !matches!(create.scheme(), CreateScheme::Create2 { .. })
1583 && self.enable_isolation
1584 && !self.in_inner_context
1585 && ecx.journal().depth() == 1
1586 {
1587 let precomputed_address = ecx
1591 .journal()
1592 .evm_state()
1593 .get(&create.caller())
1594 .map(|acc| create.caller().create(acc.info.nonce));
1595
1596 let (result, address) = self.transact_inner(
1597 ecx,
1598 TxKind::Create,
1599 create.caller(),
1600 create.init_code().clone(),
1601 create.gas_limit(),
1602 create.value(),
1603 );
1604 let address =
1605 address.or_else(|| if result.is_revert() { precomputed_address } else { None });
1606 return Some(CreateOutcome { result, address });
1607 }
1608
1609 None
1610 }
1611
1612 fn create_end(
1613 &mut self,
1614 ecx: &mut FoundryContextFor<'_, FEN>,
1615 call: &CreateInputs,
1616 outcome: &mut CreateOutcome,
1617 ) {
1618 if outcome.result.result.is_ok()
1619 && let Some(address) = outcome.address
1620 {
1621 self.locally_created_accounts.insert(address);
1622
1623 if self.in_inner_context
1624 && let Some(inner_context) = &mut self.inner_context_data
1625 {
1626 inner_context.locally_created_accounts.insert(address);
1627 }
1628 }
1629
1630 if self.in_inner_context && ecx.journal().depth() == 1 {
1633 return;
1634 }
1635
1636 self.do_create_end(ecx, call, outcome);
1637
1638 if ecx.journal().depth() == 0 {
1639 self.top_level_frame_end(ecx, outcome.result.result);
1640 }
1641 }
1642
1643 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1644 call_inspectors!([&mut self.printer], |inspector| {
1645 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1646 inspector, contract, target, value,
1647 )
1648 });
1649 }
1650}
1651
1652fn handle_arbitrum_system_call<FEN: FoundryEvmNetwork>(
1653 ecx: &mut FoundryContextFor<'_, FEN>,
1654 call: &CallInputs,
1655) -> Option<CallOutcome> {
1656 if call.target_address != arbitrum::ARB_SYS_ADDRESS
1657 || call.bytecode_address != arbitrum::ARB_SYS_ADDRESS
1658 || !arbitrum::is_arbitrum_chain(ecx.cfg().chain_id)
1659 {
1660 return None;
1661 }
1662
1663 let input = call.input.bytes(ecx);
1664 if input.get(..4) != Some(&arbitrum::ARB_BLOCK_NUMBER_SELECTOR) {
1665 return None;
1666 }
1667
1668 let block_number = ecx.db().active_fork_block_number()?;
1669 let Some((gas_cost, output)) = arbitrum::arb_block_number_call(call.gas_limit, block_number)
1670 else {
1671 return Some(arbitrum_call_outcome(
1672 call,
1673 InstructionResult::PrecompileOOG,
1674 0,
1675 Bytes::new(),
1676 ));
1677 };
1678
1679 Some(arbitrum_call_outcome(call, InstructionResult::Return, gas_cost, output))
1680}
1681
1682fn arbitrum_call_outcome(
1683 call: &CallInputs,
1684 result: InstructionResult,
1685 gas_used: u64,
1686 output: Bytes,
1687) -> CallOutcome {
1688 let mut gas = Gas::new(call.gas_limit);
1689 if result.is_ok() {
1690 let _ = gas.record_regular_cost(gas_used);
1691 } else {
1692 gas.spend_all();
1693 }
1694
1695 CallOutcome {
1696 result: InterpreterResult { result, output, gas },
1697 memory_offset: call.return_memory_offset.clone(),
1698 was_precompile_called: true,
1699 precompile_call_logs: vec![],
1700 charged_new_account_state_gas: call.charged_new_account_state_gas,
1701 }
1702}
1703
1704impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStackRefMut<'_, FEN> {
1705 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1706 call_inspectors!(
1707 #[ret]
1708 [&mut self.cheatcodes],
1709 |inspector| { inspector.should_use_create2_factory(depth, inputs).then_some(true) },
1710 );
1711
1712 false
1713 }
1714
1715 fn console_log(&mut self, msg: &str) {
1716 call_inspectors!([&mut self.log_collector], |inspector| InspectorExt::console_log(
1717 inspector, msg
1718 ));
1719 }
1720
1721 fn get_networks(&self) -> NetworkConfigs {
1722 self.inner.networks
1723 }
1724
1725 fn create2_deployer(&self) -> Address {
1726 self.inner.create2_deployer
1727 }
1728}
1729
1730impl<FEN: FoundryEvmNetwork> Inspector<FoundryContextFor<'_, FEN>> for InspectorStack<FEN> {
1731 fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1732 self.as_mut().step_inlined(interpreter, ecx)
1733 }
1734
1735 fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut FoundryContextFor<'_, FEN>) {
1736 self.as_mut().step_end_inlined(interpreter, ecx)
1737 }
1738
1739 fn call(
1740 &mut self,
1741 context: &mut FoundryContextFor<'_, FEN>,
1742 inputs: &mut CallInputs,
1743 ) -> Option<CallOutcome> {
1744 self.as_mut().call(context, inputs)
1745 }
1746
1747 fn call_end(
1748 &mut self,
1749 context: &mut FoundryContextFor<'_, FEN>,
1750 inputs: &CallInputs,
1751 outcome: &mut CallOutcome,
1752 ) {
1753 self.as_mut().call_end(context, inputs, outcome)
1754 }
1755
1756 fn create(
1757 &mut self,
1758 context: &mut FoundryContextFor<'_, FEN>,
1759 create: &mut CreateInputs,
1760 ) -> Option<CreateOutcome> {
1761 self.as_mut().create(context, create)
1762 }
1763
1764 fn create_end(
1765 &mut self,
1766 context: &mut FoundryContextFor<'_, FEN>,
1767 call: &CreateInputs,
1768 outcome: &mut CreateOutcome,
1769 ) {
1770 self.as_mut().create_end(context, call, outcome)
1771 }
1772
1773 fn initialize_interp(
1774 &mut self,
1775 interpreter: &mut Interpreter,
1776 ecx: &mut FoundryContextFor<'_, FEN>,
1777 ) {
1778 self.as_mut().initialize_interp(interpreter, ecx)
1779 }
1780
1781 fn log(&mut self, ecx: &mut FoundryContextFor<'_, FEN>, log: Log) {
1782 self.as_mut().log(ecx, log)
1783 }
1784
1785 fn log_full(
1786 &mut self,
1787 interpreter: &mut Interpreter,
1788 ecx: &mut FoundryContextFor<'_, FEN>,
1789 log: Log,
1790 ) {
1791 self.as_mut().log_full(interpreter, ecx, log)
1792 }
1793
1794 fn frame_start(
1795 &mut self,
1796 context: &mut FoundryContextFor<'_, FEN>,
1797 frame_input: &mut FrameInput,
1798 ) -> Option<FrameResult> {
1799 self.as_mut().frame_start(context, frame_input)
1800 }
1801
1802 fn frame_end(
1803 &mut self,
1804 context: &mut FoundryContextFor<'_, FEN>,
1805 frame_input: &FrameInput,
1806 frame_result: &mut FrameResult,
1807 ) {
1808 self.as_mut().frame_end(context, frame_input, frame_result)
1809 }
1810
1811 fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
1812 call_inspectors!([&mut self.inner.printer], |inspector| {
1813 Inspector::<FoundryContextFor<'_, FEN>>::selfdestruct(
1814 inspector, contract, target, value,
1815 )
1816 });
1817 }
1818}
1819
1820impl<FEN: FoundryEvmNetwork> InspectorExt for InspectorStack<FEN> {
1821 fn should_use_create2_factory(&mut self, depth: usize, inputs: &CreateInputs) -> bool {
1822 self.as_mut().should_use_create2_factory(depth, inputs)
1823 }
1824
1825 fn get_networks(&self) -> NetworkConfigs {
1826 self.networks
1827 }
1828
1829 fn create2_deployer(&self) -> Address {
1830 self.create2_deployer
1831 }
1832}
1833
1834impl<'a, FEN: FoundryEvmNetwork> Deref for InspectorStackRefMut<'a, FEN> {
1835 type Target = &'a mut InspectorStackInner;
1836
1837 fn deref(&self) -> &Self::Target {
1838 &self.inner
1839 }
1840}
1841
1842impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStackRefMut<'_, FEN> {
1843 fn deref_mut(&mut self) -> &mut Self::Target {
1844 &mut self.inner
1845 }
1846}
1847
1848impl<FEN: FoundryEvmNetwork> Deref for InspectorStack<FEN> {
1849 type Target = InspectorStackInner;
1850
1851 fn deref(&self) -> &Self::Target {
1852 &self.inner
1853 }
1854}
1855
1856impl<FEN: FoundryEvmNetwork> DerefMut for InspectorStack<FEN> {
1857 fn deref_mut(&mut self) -> &mut Self::Target {
1858 &mut self.inner
1859 }
1860}
1861
1862impl InspectorStackInner {
1863 #[inline]
1864 const fn refresh_static_opcode_dispatch(&mut self) {
1865 self.refresh_static_step_dispatch();
1866 self.refresh_static_step_end_dispatch();
1867 }
1868
1869 #[inline]
1870 const fn refresh_static_step_dispatch(&mut self) {
1871 self.static_step_dispatch = if self.edge_coverage.is_none()
1872 && self.line_coverage.is_none()
1873 && self.printer.is_none()
1874 && self.revert_diag.is_none()
1875 && self.script_execution_inspector.is_none()
1876 && self.tracer.is_none()
1877 {
1878 if self.fuzzer.is_some() {
1879 OpcodeStepDispatch::FuzzerOnly
1880 } else {
1881 OpcodeStepDispatch::None
1882 }
1883 } else {
1884 OpcodeStepDispatch::General
1885 };
1886 }
1887
1888 #[inline]
1889 const fn refresh_static_step_end_dispatch(&mut self) {
1890 self.has_static_step_end_inspectors = self.chisel_state.is_some()
1891 || self.printer.is_some()
1892 || self.revert_diag.is_some()
1893 || self.tracer.is_some();
1894 }
1895
1896 fn next_batch_create_salt(&mut self, chain_id: u64, nonce: u64) -> U256 {
1899 let process_salt = *self.batch_rewrite_process_salt.get_or_insert_with(rand::random);
1900 let counter = self.batch_create_counter;
1901 self.batch_create_counter = counter.wrapping_add(1);
1902 compute_batch_create_salt(process_salt, chain_id, nonce, counter)
1903 }
1904}
1905
1906fn compute_batch_create_salt(process_salt: u64, chain_id: u64, nonce: u64, counter: u64) -> U256 {
1912 let mut buf = [0u8; 32];
1913 buf[0..8].copy_from_slice(&process_salt.to_be_bytes());
1914 buf[8..16].copy_from_slice(&chain_id.to_be_bytes());
1915 buf[16..24].copy_from_slice(&nonce.to_be_bytes());
1916 buf[24..32].copy_from_slice(&counter.to_be_bytes());
1917 U256::from_be_bytes(keccak256(buf).0)
1918}
1919
1920#[cfg(test)]
1921mod tests {
1922 use super::{
1923 Address, Fuzzer, InspectorStack, InspectorStackInner, OpcodeStepDispatch,
1924 TraceRequirements, compute_batch_create_salt,
1925 };
1926 use foundry_evm_core::evm::EthEvmNetwork;
1927
1928 #[test]
1929 fn opcode_dispatch_defaults_to_no_static_inspectors() {
1930 let stack = InspectorStackInner::default();
1931
1932 assert_eq!(stack.static_step_dispatch, OpcodeStepDispatch::None);
1933 assert!(!stack.has_static_step_end_inspectors);
1934 }
1935
1936 #[test]
1937 fn opcode_dispatch_uses_fuzzer_fast_path_when_fuzzer_is_only_static_step_inspector() {
1938 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1939 stack.set_fuzzer(Fuzzer::new(16, None));
1940
1941 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::FuzzerOnly);
1942 assert!(!stack.inner.has_static_step_end_inspectors);
1943
1944 stack.collect_line_coverage(true);
1945 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1946
1947 stack.collect_line_coverage(false);
1948 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::FuzzerOnly);
1949 }
1950
1951 #[test]
1952 fn opcode_dispatch_tracks_general_step_and_step_end_inspectors() {
1953 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1954
1955 stack.print(true);
1956 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1957 assert!(stack.inner.has_static_step_end_inspectors);
1958
1959 stack.print(false);
1960 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1961 assert!(!stack.inner.has_static_step_end_inspectors);
1962
1963 stack.tracing_requirements(TraceRequirements::none().with_calls(true));
1964 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1965 assert!(stack.inner.has_static_step_end_inspectors);
1966
1967 stack.tracing_requirements(TraceRequirements::none());
1968 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1969 assert!(!stack.inner.has_static_step_end_inspectors);
1970
1971 stack.set_chisel(0);
1972 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1973 assert!(stack.inner.has_static_step_end_inspectors);
1974 }
1975
1976 #[test]
1977 fn opcode_dispatch_tracks_script_and_edge_coverage_inspectors() {
1978 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1979
1980 stack.script(Address::with_last_byte(1));
1981 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1982 assert!(!stack.inner.has_static_step_end_inspectors);
1983
1984 let mut stack = InspectorStack::<EthEvmNetwork>::new();
1985 stack.collect_edge_coverage(true);
1986 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::General);
1987 stack.collect_edge_coverage(false);
1988 assert_eq!(stack.inner.static_step_dispatch, OpcodeStepDispatch::None);
1989 }
1990
1991 #[test]
1992 fn distinct_salts_across_simulations_at_same_nonce() {
1993 let a = compute_batch_create_salt(0xabcd_ef01_2345_6789, 1, 5, 0);
1995 let b = compute_batch_create_salt(0x1122_3344_5566_7788, 1, 5, 0);
1996 assert_ne!(a, b);
1997 }
1998
1999 #[test]
2000 fn counter_changes_salt() {
2001 let a = compute_batch_create_salt(1, 1, 5, 0);
2002 let b = compute_batch_create_salt(1, 1, 5, 1);
2003 assert_ne!(a, b);
2004 }
2005
2006 #[test]
2007 fn chain_id_changes_salt() {
2008 let a = compute_batch_create_salt(1, 1, 5, 0);
2009 let b = compute_batch_create_salt(1, 2, 5, 0);
2010 assert_ne!(a, b);
2011 }
2012
2013 #[test]
2014 fn deterministic_for_same_inputs() {
2015 let a = compute_batch_create_salt(42, 1, 5, 7);
2016 let b = compute_batch_create_salt(42, 1, 5, 7);
2017 assert_eq!(a, b);
2018 }
2019
2020 #[test]
2021 fn distinct_create2_addresses_across_inspector_instances_at_same_onchain_state() {
2022 let factory = Address::with_last_byte(0x42);
2025 let init_code = b"\x60\x80\x60\x40".as_slice();
2026
2027 let mut a = InspectorStackInner::default();
2028 let mut b = InspectorStackInner::default();
2029 let salt_a = a.next_batch_create_salt(1, 5).to_be_bytes::<32>();
2030 let salt_b = b.next_batch_create_salt(1, 5).to_be_bytes::<32>();
2031
2032 let addr_a = factory.create2_from_code(salt_a, init_code);
2033 let addr_b = factory.create2_from_code(salt_b, init_code);
2034 assert_ne!(addr_a, addr_b);
2035 }
2036}