1use crate::inspectors::{
10 Cheatcodes, CmpOperands, EdgeCoverage, EdgeIndexMap, InspectorData, InspectorStack,
11 cheatcodes::BroadcastableTransactions,
12};
13use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
14use alloy_eips::eip4788::{BEACON_ROOTS_ADDRESS, SYSTEM_ADDRESS};
15use alloy_evm::Evm;
16use alloy_json_abi::Function;
17use alloy_primitives::{
18 Address, Bytes, Log, TxKind, U256, keccak256,
19 map::{AddressHashMap, HashMap},
20};
21use alloy_sol_types::{SolCall, sol};
22use foundry_evm_core::{
23 EvmEnv, FoundryBlock, FoundryChain, FoundryTransaction,
24 backend::{
25 Backend, BackendError, BackendResult, CowBackend, DatabaseError, DatabaseExt,
26 GLOBAL_FAIL_SLOT,
27 },
28 constants::{
29 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
30 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
31 },
32 decode::{RevertDecoder, SkipReason},
33 eip2935::{
34 HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE, history_storage_slot, history_storage_value,
35 history_window_start,
36 },
37 evm::{
38 BlockContext, ChainFor, EthEvmNetwork, EvmEnvFor, FoundryEvmFactory, FoundryEvmNetwork,
39 IntoInstructionResult, SpecFor, TxEnvFor,
40 },
41 utils::StateChangeset,
42};
43use foundry_evm_coverage::HitMaps;
44use foundry_evm_fuzz::ObservedCall;
45use foundry_evm_networks::NetworkConfigs;
46use foundry_evm_traces::{SparsedTraceArena, TraceRequirements};
47use revm::{
48 bytecode::Bytecode,
49 context::{Block, Cfg, Transaction},
50 context_interface::{
51 cfg::gas_params::Eip2780TxInfo,
52 result::{ExecutionResult, Output, ResultAndState},
53 transaction::SignedAuthorization,
54 },
55 database::{Database, DatabaseCommit, DatabaseRef},
56 interpreter::{InstructionResult, return_ok},
57 primitives::hardfork::SpecId,
58};
59use sancov::SancovGuard;
60use std::{
61 borrow::Cow,
62 sync::{
63 Arc,
64 atomic::{AtomicBool, Ordering},
65 },
66 time::{Duration, Instant},
67};
68
69mod builder;
70pub use builder::ExecutorBuilder;
71
72mod campaign;
73
74pub mod fuzz;
75pub use fuzz::FuzzedExecutor;
76
77pub mod invariant;
78pub use invariant::InvariantExecutor;
79
80mod corpus;
81mod corpus_io;
82mod sancov;
83mod showmap;
84mod trace;
85
86pub use corpus::{DynamicTargetCtx, StatelessReplayTarget, persist_corpus_seed};
87pub use corpus_io::{
88 CorpusDirEntry, canonical_replay_dirs, parse_corpus_filename, read_corpus_dir, read_corpus_tree,
89};
90pub use showmap::{
91 InvariantReplayOptions, MinimizationReplayInput, ReplayFailure, ReplayObservation,
92 ShowmapDomain, ShowmapOpts, ShowmapReplayTarget, ShowmapStats, replay_corpus_to_showmap,
93 replay_sequence_for_minimization,
94};
95pub use trace::TracingExecutor;
96
97const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
98
99sol! {
100 interface ITest {
101 function setUp() external;
102 function failed() external view returns (bool failed);
103
104 #[derive(Default)]
105 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
106 }
107}
108
109#[derive(Clone, Debug)]
121pub struct Executor<FEN: FoundryEvmNetwork> {
122 backend: Arc<Backend<FEN>>,
131 evm_env: EvmEnvFor<FEN>,
133 tx_env: TxEnvFor<FEN>,
135 inspector: InspectorStack<FEN>,
137 gas_limit: u64,
139 legacy_assertions: bool,
141 block_context: Option<BlockContext<FEN>>,
143}
144
145impl<FEN: FoundryEvmNetwork> Executor<FEN> {
146 #[inline]
148 pub fn new(
149 mut backend: Backend<FEN>,
150 evm_env: EvmEnvFor<FEN>,
151 tx_env: TxEnvFor<FEN>,
152 mut inspector: InspectorStack<FEN>,
153 networks: NetworkConfigs,
154 gas_limit: u64,
155 legacy_assertions: bool,
156 ) -> Self {
157 inspector.networks(networks);
158 backend.set_networks(networks);
159 let extra_cheatcode_addresses = networks.extra_cheatcode_addresses();
160 backend.extend_persistent_accounts(extra_cheatcode_addresses.iter().copied());
161
162 backend.insert_account_info(
165 CHEATCODE_ADDRESS,
166 revm::state::AccountInfo {
167 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
168 code_hash: CHEATCODE_CONTRACT_HASH,
171 ..Default::default()
172 },
173 );
174
175 for &address in extra_cheatcode_addresses {
176 backend.insert_account_info(
177 address,
178 revm::state::AccountInfo {
179 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
180 code_hash: keccak256(address),
181 ..Default::default()
182 },
183 );
184 }
185
186 if !backend.is_in_forking_mode() && evm_env.cfg_env.spec.into() >= SpecId::PRAGUE {
187 let mut account =
188 backend.basic_ref(HISTORY_STORAGE_ADDRESS).unwrap_or_default().unwrap_or_default();
189 account.code_hash = keccak256(&HISTORY_STORAGE_CODE);
190 account.code = Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone()));
191 backend.insert_account_info(HISTORY_STORAGE_ADDRESS, account);
192
193 let current_block = evm_env.block_env.number();
194 let mut block_number = history_window_start(current_block);
195 while block_number < current_block {
196 let block_hash =
197 backend.block_hash(block_number.saturating_to()).unwrap_or_default();
198 let slot = history_storage_slot(block_number);
199 let value = history_storage_value(block_hash);
200 let _ = backend.insert_account_storage(HISTORY_STORAGE_ADDRESS, slot, value);
201 block_number += U256::from(1);
202 }
203 }
204
205 Self {
206 backend: Arc::new(backend),
207 evm_env,
208 tx_env,
209 inspector,
210 gas_limit,
211 legacy_assertions,
212 block_context: None,
213 }
214 }
215
216 fn clone_with_backend(&self, backend: Backend<FEN>) -> Self {
217 let evm_env = self.evm_env.clone();
218 Self {
219 backend: Arc::new(backend),
220 evm_env,
221 tx_env: self.tx_env.clone(),
222 inspector: self.inspector().clone(),
223 gas_limit: self.gas_limit,
224 legacy_assertions: self.legacy_assertions,
225 block_context: self.block_context.clone(),
226 }
227 }
228
229 pub fn backend(&self) -> &Backend<FEN> {
231 &self.backend
232 }
233
234 pub fn backend_mut(&mut self) -> &mut Backend<FEN> {
239 Arc::make_mut(&mut self.backend)
240 }
241
242 pub fn enable_block_context_progression(&mut self) -> eyre::Result<()> {
247 self.block_context = self.backend().block_context_for_synthetic_transaction()?;
248 Ok(())
249 }
250
251 pub fn advance_block_context(&mut self) {
253 if let Some(context) = &mut self.block_context {
254 context.advance_block();
255 }
256 }
257
258 fn chain_context_for_synthetic_transaction(
259 &self,
260 tx: &TxEnvFor<FEN>,
261 ) -> eyre::Result<ChainFor<FEN>> {
262 self.block_context.as_ref().map_or_else(
263 || self.backend().chain_context_for_synthetic_transaction(tx),
264 |context| Ok(context.next_transaction(tx)),
265 )
266 }
267
268 fn record_block_transaction(&mut self, tx: TxEnvFor<FEN>) {
269 if let Some(context) = &mut self.block_context {
270 context.record_transaction(tx);
271 }
272 }
273
274 pub const fn evm_env(&self) -> &EvmEnvFor<FEN> {
276 &self.evm_env
277 }
278
279 pub const fn evm_env_mut(&mut self) -> &mut EvmEnvFor<FEN> {
281 &mut self.evm_env
282 }
283
284 pub const fn tx_env(&self) -> &TxEnvFor<FEN> {
286 &self.tx_env
287 }
288
289 pub const fn tx_env_mut(&mut self) -> &mut TxEnvFor<FEN> {
291 &mut self.tx_env
292 }
293
294 pub const fn inspector(&self) -> &InspectorStack<FEN> {
296 &self.inspector
297 }
298
299 pub const fn inspector_mut(&mut self) -> &mut InspectorStack<FEN> {
301 &mut self.inspector
302 }
303
304 pub const fn spec_id(&self) -> SpecFor<FEN> {
306 self.evm_env.cfg_env.spec
307 }
308
309 pub fn set_spec_id(&mut self, spec_id: SpecFor<FEN>) {
311 self.evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
312 }
313
314 pub const fn gas_limit(&self) -> u64 {
319 self.gas_limit
320 }
321
322 pub const fn set_gas_limit(&mut self, gas_limit: u64) {
324 self.gas_limit = gas_limit;
325 }
326
327 pub const fn legacy_assertions(&self) -> bool {
330 self.legacy_assertions
331 }
332
333 pub const fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
336 self.legacy_assertions = legacy_assertions;
337 }
338
339 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
341 trace!("deploying local create2 deployer");
342 let create2_deployer_account = self
343 .backend()
344 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
345 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
346
347 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
349 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
350
351 let initial_balance = self.get_balance(creator)?;
353 self.set_balance(creator, U256::MAX)?;
354
355 let res =
356 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
357 trace!(create2=?res.address, "deployed local create2 deployer");
358
359 self.set_balance(creator, initial_balance)?;
360 }
361 Ok(())
362 }
363
364 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
366 trace!(?address, ?amount, "setting account balance");
367 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
368 account.balance = amount;
369 self.backend_mut().insert_account_info(address, account);
370 Ok(())
371 }
372
373 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
375 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
376 }
377
378 pub fn set_account_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
380 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
381 account.nonce = nonce;
382 self.backend_mut().insert_account_info(address, account);
383 Ok(())
384 }
385
386 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
388 self.set_account_nonce(address, nonce)?;
389 self.tx_env_mut().set_nonce(nonce);
390 Ok(())
391 }
392
393 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
395 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
396 }
397
398 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
400 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
401 account.code_hash = keccak256(code.original_byte_slice());
402 account.code = Some(code);
403 self.backend_mut().insert_account_info(address, account);
404 Ok(())
405 }
406
407 pub fn set_storage(
409 &mut self,
410 address: Address,
411 storage: HashMap<U256, U256>,
412 ) -> BackendResult<()> {
413 self.backend_mut().replace_account_storage(address, storage)?;
414 Ok(())
415 }
416
417 pub fn set_storage_slot(
419 &mut self,
420 address: Address,
421 slot: U256,
422 value: U256,
423 ) -> BackendResult<()> {
424 self.backend_mut().insert_account_storage(address, slot, value)?;
425 Ok(())
426 }
427
428 pub fn apply_prestate_trace(
434 &mut self,
435 prestate: std::collections::BTreeMap<Address, alloy_rpc_types::trace::geth::AccountState>,
436 ) -> eyre::Result<()> {
437 let backend = self.backend_mut();
438 for (address, account_state) in prestate {
439 let code = account_state.code.map(Bytecode::new_raw).unwrap_or_default();
440 let info = revm::state::AccountInfo {
441 nonce: account_state.nonce.unwrap_or_default(),
442 balance: account_state.balance.unwrap_or_default(),
443 code_hash: keccak256(code.original_byte_slice()),
444 code: Some(code),
445 account_id: Default::default(),
446 };
447 backend.insert_account_info(address, info);
448
449 for (slot, value) in account_state.storage {
450 let slot = U256::from_be_bytes(slot.0);
451 let value = U256::from_be_bytes(value.0);
452 backend.insert_account_storage(address, slot, value)?;
453 }
454 }
455 Ok(())
456 }
457
458 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
460 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
461 }
462
463 #[inline]
464 pub fn set_trace_requirements(&mut self, requirements: TraceRequirements) -> &mut Self {
465 self.inspector_mut().tracing_requirements(requirements);
466 self
467 }
468
469 #[inline]
470 pub fn set_script_execution(&mut self, script_address: Address) {
471 self.inspector_mut().script(script_address);
472 }
473
474 #[inline]
475 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
476 self.inspector_mut().print(trace_printer);
477 self
478 }
479
480 #[inline]
481 pub fn create2_deployer(&self) -> Address {
482 self.inspector().create2_deployer
483 }
484
485 pub fn deploy(
490 &mut self,
491 from: Address,
492 code: Bytes,
493 value: U256,
494 rd: Option<&RevertDecoder>,
495 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
496 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Create, code, value);
497 self.deploy_with_env(evm_env, tx_env, rd)
498 }
499
500 pub fn deploy_with_context(
502 &mut self,
503 from: Address,
504 code: Bytes,
505 value: U256,
506 chain_context: ChainFor<FEN>,
507 rd: Option<&RevertDecoder>,
508 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
509 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Create, code, value);
510 self.deploy_with_env_and_context(evm_env, tx_env, chain_context, rd)
511 }
512
513 #[instrument(name = "deploy", level = "debug", skip_all)]
520 pub fn deploy_with_env(
521 &mut self,
522 evm_env: EvmEnvFor<FEN>,
523 tx_env: TxEnvFor<FEN>,
524 rd: Option<&RevertDecoder>,
525 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
526 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
527 self.deploy_with_env_and_context(evm_env, tx_env, chain_context, rd)
528 }
529
530 #[instrument(name = "deploy", level = "debug", skip_all)]
536 pub fn deploy_with_env_and_context(
537 &mut self,
538 evm_env: EvmEnvFor<FEN>,
539 tx_env: TxEnvFor<FEN>,
540 chain_context: ChainFor<FEN>,
541 rd: Option<&RevertDecoder>,
542 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
543 assert!(
544 matches!(tx_env.kind(), TxKind::Create),
545 "Expected create transaction, got {:?}",
546 tx_env.kind()
547 );
548 trace!(sender=%tx_env.caller(), "deploying contract");
549
550 let mut result = self.transact_with_env_and_context(evm_env, tx_env, chain_context)?;
551 result = result.into_result(rd)?;
552 let Some(Output::Create(_, Some(address))) = result.out else {
553 panic!("Deployment succeeded, but no address was returned: {result:#?}");
554 };
555
556 self.backend_mut().add_persistent_account(address);
559
560 trace!(%address, "deployed contract");
561
562 Ok(DeployResult { raw: result, address })
563 }
564
565 #[instrument(name = "setup", level = "debug", skip_all)]
572 pub fn setup(
573 &mut self,
574 from: Option<Address>,
575 to: Address,
576 rd: Option<&RevertDecoder>,
577 ) -> Result<RawCallResult<FEN>, EvmError<FEN>> {
578 trace!(?from, ?to, "setting up contract");
579
580 let from = from.unwrap_or(CALLER);
581 self.backend_mut().set_test_contract(to).set_caller(from);
582 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
583 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
584 res = res.into_result(rd)?;
585
586 self.evm_env_mut().block_env = res.evm_env.block_env.clone();
588 self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id;
590
591 let success =
592 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
593 if !success {
594 return Err(res.into_execution_error("execution error".to_string()).into());
595 }
596
597 Ok(res)
598 }
599
600 pub fn call(
602 &self,
603 from: Address,
604 to: Address,
605 func: &Function,
606 args: &[DynSolValue],
607 value: U256,
608 rd: Option<&RevertDecoder>,
609 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
610 let calldata = Bytes::from(func.abi_encode_input(args)?);
611 let result = self.call_raw(from, to, calldata, value)?;
612 result.into_decoded_result(func, rd)
613 }
614
615 pub fn call_sol<C: SolCall>(
617 &self,
618 from: Address,
619 to: Address,
620 args: &C,
621 value: U256,
622 rd: Option<&RevertDecoder>,
623 ) -> Result<CallResult<C::Return, FEN>, EvmError<FEN>> {
624 let calldata = Bytes::from(args.abi_encode());
625 let mut raw = self.call_raw(from, to, calldata, value)?;
626 raw = raw.into_result(rd)?;
627 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
628 }
629
630 pub fn transact(
632 &mut self,
633 from: Address,
634 to: Address,
635 func: &Function,
636 args: &[DynSolValue],
637 value: U256,
638 rd: Option<&RevertDecoder>,
639 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
640 let calldata = Bytes::from(func.abi_encode_input(args)?);
641 let result = self.transact_raw(from, to, calldata, value)?;
642 result.into_decoded_result(func, rd)
643 }
644
645 pub fn call_raw(
647 &self,
648 from: Address,
649 to: Address,
650 calldata: Bytes,
651 value: U256,
652 ) -> eyre::Result<RawCallResult<FEN>> {
653 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
654 self.call_with_env(evm_env, tx_env)
655 }
656
657 pub fn call_raw_with_authorization(
660 &mut self,
661 from: Address,
662 to: Address,
663 calldata: Bytes,
664 value: U256,
665 authorization_list: Vec<SignedAuthorization>,
666 ) -> eyre::Result<RawCallResult<FEN>> {
667 let (evm_env, mut tx_env) = self.build_test_env(from, to.into(), calldata, value);
668 tx_env.set_signed_authorization(authorization_list);
669 tx_env.set_tx_type(4);
670 self.call_with_env(evm_env, tx_env)
671 }
672
673 pub fn transact_raw(
675 &mut self,
676 from: Address,
677 to: Address,
678 calldata: Bytes,
679 value: U256,
680 ) -> eyre::Result<RawCallResult<FEN>> {
681 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
682 self.transact_with_env(evm_env, tx_env)
683 }
684
685 pub fn transact_raw_with_context(
687 &mut self,
688 from: Address,
689 to: Address,
690 calldata: Bytes,
691 value: U256,
692 chain_context: ChainFor<FEN>,
693 ) -> eyre::Result<RawCallResult<FEN>> {
694 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
695 self.transact_with_env_and_context(evm_env, tx_env, chain_context)
696 }
697
698 pub fn transact_raw_with_authorization(
701 &mut self,
702 from: Address,
703 to: Address,
704 calldata: Bytes,
705 value: U256,
706 authorization_list: Vec<SignedAuthorization>,
707 ) -> eyre::Result<RawCallResult<FEN>> {
708 let (evm_env, mut tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
709 tx_env.set_signed_authorization(authorization_list);
710 tx_env.set_tx_type(4);
711 self.transact_with_env(evm_env, tx_env)
712 }
713
714 pub fn apply_beacon_root(
717 &mut self,
718 parent_beacon_block_root: alloy_primitives::B256,
719 ) -> eyre::Result<()> {
720 let calldata = Bytes::copy_from_slice(parent_beacon_block_root.as_slice());
721 let mut evm_env = self.evm_env.clone();
722 let inspector = self.inspector().clone();
723 let mut state = {
724 let mut backend = CowBackend::new_borrowed(self.backend());
725 let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector(
726 &mut backend,
727 evm_env.clone(),
728 ChainFor::<FEN>::for_transaction(&TxEnvFor::<FEN>::default()),
729 inspector,
730 );
731 let result =
732 evm.transact_system_call(SYSTEM_ADDRESS, BEACON_ROOTS_ADDRESS, calldata)?;
733 evm_env = evm.finish().1;
734 result.state
735 };
736 state.retain(|address, _| *address == BEACON_ROOTS_ADDRESS);
737
738 self.backend_mut().commit(state);
739 self.inspector_mut().set_block(evm_env.block_env);
740
741 Ok(())
742 }
743
744 #[instrument(name = "call", level = "debug", skip_all)]
748 pub fn call_with_env(
749 &self,
750 evm_env: EvmEnvFor<FEN>,
751 tx_env: TxEnvFor<FEN>,
752 ) -> eyre::Result<RawCallResult<FEN>> {
753 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
754 self.call_with_env_and_context(evm_env, tx_env, chain_context)
755 }
756
757 #[instrument(name = "call", level = "debug", skip_all)]
759 pub fn call_with_env_and_context(
760 &self,
761 mut evm_env: EvmEnvFor<FEN>,
762 mut tx_env: TxEnvFor<FEN>,
763 chain_context: ChainFor<FEN>,
764 ) -> eyre::Result<RawCallResult<FEN>> {
765 let mut stack = self.inspector().clone();
766 let sancov_edges = stack.inner.sancov_edges;
767 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
768 let sancov_active = sancov_edges || sancov_trace_cmp;
769 let mut backend = CowBackend::new_borrowed(self.backend());
770 let result = {
771 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
772 backend.inspect_with_context(&mut evm_env, &mut tx_env, chain_context, &mut stack)?
773 };
774 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
775 let fork_block_number = backend.active_fork_block_number();
776 let mut result = convert_executed_result(
777 evm_env,
778 tx_env,
779 stack,
780 result,
781 &backend,
782 has_state_snapshot_failure,
783 fork_block_number,
784 )?;
785 if sancov_edges {
786 SancovGuard::append_edges_into(&mut result);
787 }
788 if sancov_trace_cmp {
789 SancovGuard::drain_cmp_into(&mut result);
790 }
791 Ok(result)
792 }
793
794 #[instrument(name = "transact", level = "debug", skip_all)]
796 pub fn transact_with_env(
797 &mut self,
798 evm_env: EvmEnvFor<FEN>,
799 tx_env: TxEnvFor<FEN>,
800 ) -> eyre::Result<RawCallResult<FEN>> {
801 let chain_context = self.chain_context_for_synthetic_transaction(&tx_env)?;
802 self.transact_with_env_and_context(evm_env, tx_env, chain_context)
803 }
804
805 #[instrument(name = "transact", level = "debug", skip_all)]
807 pub fn transact_with_env_and_context(
808 &mut self,
809 mut evm_env: EvmEnvFor<FEN>,
810 mut tx_env: TxEnvFor<FEN>,
811 chain_context: ChainFor<FEN>,
812 ) -> eyre::Result<RawCallResult<FEN>> {
813 let mut stack = self.inspector().clone();
814 let sancov_edges = stack.inner.sancov_edges;
815 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
816 let sancov_active = sancov_edges || sancov_trace_cmp;
817 let backend = self.backend_mut();
818 let result = {
819 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
820 backend.inspect_with_context(&mut evm_env, &mut tx_env, chain_context, &mut stack)?
821 };
822 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
823 let fork_block_number = backend.active_fork_block_number();
824 let mut result = convert_executed_result(
825 evm_env,
826 tx_env,
827 stack,
828 result,
829 &*backend,
830 has_state_snapshot_failure,
831 fork_block_number,
832 )?;
833 if sancov_edges {
834 SancovGuard::append_edges_into(&mut result);
835 }
836 if sancov_trace_cmp {
837 SancovGuard::drain_cmp_into(&mut result);
838 }
839 let committed_tx = result.tx_env.clone();
840 self.commit(&mut result);
841 self.record_block_transaction(committed_tx);
842 Ok(result)
843 }
844
845 #[cfg(feature = "monad")]
847 #[instrument(name = "transact_system_replay", level = "debug", skip_all)]
848 pub fn try_transact_system_replay_with_env_and_context(
849 &mut self,
850 mut evm_env: EvmEnvFor<FEN>,
851 mut tx_env: TxEnvFor<FEN>,
852 chain_context: ChainFor<FEN>,
853 ) -> eyre::Result<Option<RawCallResult<FEN>>> {
854 let mut stack = self.inspector().clone();
855 let mut backend = CowBackend::new_borrowed(self.backend());
856 let Some(result) = backend.try_inspect_system_replay_with_context(
857 &mut evm_env,
858 &mut tx_env,
859 chain_context,
860 &mut stack,
861 )?
862 else {
863 return Ok(None);
864 };
865 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
866 let fork_block_number = backend.active_fork_block_number();
867 let mut result = convert_executed_result(
868 evm_env,
869 tx_env,
870 stack,
871 result,
872 &backend,
873 has_state_snapshot_failure,
874 fork_block_number,
875 )?;
876 let committed_tx = result.tx_env.clone();
877 self.commit(&mut result);
878 self.record_block_transaction(committed_tx);
879 Ok(Some(result))
880 }
881
882 #[instrument(name = "commit", level = "debug", skip_all)]
887 fn commit(&mut self, result: &mut RawCallResult<FEN>) {
888 self.backend_mut().commit(result.state_changeset.clone());
890
891 self.inspector_mut().cheatcodes = result.cheatcodes.take();
893 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
894 cheats.broadcastable_transactions.clear();
896 cheats.ignored_traces.ignored.clear();
897 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
900 *last_pause_call = (0, 0);
901 }
902 }
903
904 self.inspector_mut().set_block(result.evm_env.block_env.clone());
906 self.inspector_mut().set_gas_price(result.tx_env.gas_price());
907 }
908
909 pub fn is_raw_call_mut_success(
914 &self,
915 address: Address,
916 call_result: &mut RawCallResult<FEN>,
917 should_fail: bool,
918 ) -> bool {
919 self.is_raw_call_success(
920 address,
921 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
922 call_result,
923 should_fail,
924 )
925 }
926
927 pub fn is_raw_call_success(
931 &self,
932 address: Address,
933 state_changeset: Cow<'_, StateChangeset>,
934 call_result: &RawCallResult<FEN>,
935 should_fail: bool,
936 ) -> bool {
937 if call_result.has_state_snapshot_failure {
938 return should_fail;
940 }
941 self.is_success(address, call_result.reverted, state_changeset, should_fail)
942 }
943
944 pub fn is_raw_call_mut_success_handler_gate(
948 &self,
949 address: Address,
950 call_result: &mut RawCallResult<FEN>,
951 ) -> bool {
952 if call_result.has_state_snapshot_failure {
953 return false;
954 }
955 let state_changeset = std::mem::take(&mut call_result.state_changeset);
956 self.is_success_handler_gate(address, call_result.reverted, Cow::Owned(state_changeset))
957 }
958
959 pub fn is_success(
981 &self,
982 address: Address,
983 reverted: bool,
984 state_changeset: Cow<'_, StateChangeset>,
985 should_fail: bool,
986 ) -> bool {
987 let success = self.is_success_raw(address, reverted, state_changeset, false);
988 should_fail ^ success
989 }
990
991 pub fn is_success_handler_gate(
997 &self,
998 address: Address,
999 reverted: bool,
1000 state_changeset: Cow<'_, StateChangeset>,
1001 ) -> bool {
1002 self.is_success_raw(address, reverted, state_changeset, true)
1003 }
1004
1005 #[instrument(name = "is_success", level = "debug", skip_all)]
1006 fn is_success_raw(
1007 &self,
1008 address: Address,
1009 reverted: bool,
1010 state_changeset: Cow<'_, StateChangeset>,
1011 pending_global_failure_only: bool,
1012 ) -> bool {
1013 if reverted {
1015 return false;
1016 }
1017
1018 if self.backend().has_state_snapshot_failure() {
1020 return false;
1021 }
1022
1023 let global_failed = if pending_global_failure_only {
1028 Self::has_pending_global_failure(&state_changeset)
1029 } else {
1030 self.has_global_failure(&state_changeset)
1031 };
1032 if global_failed {
1033 return false;
1034 }
1035
1036 if !self.legacy_assertions {
1037 return true;
1038 }
1039
1040 {
1042 let mut backend = self.backend().clone_empty();
1044
1045 for address in [address, CHEATCODE_ADDRESS] {
1048 let Ok(acc) = self.backend().basic_ref(address) else { return false };
1049 backend.insert_account_info(address, acc.unwrap_or_default());
1050 }
1051
1052 backend.commit(state_changeset.into_owned());
1057
1058 let executor = self.clone_with_backend(backend);
1060 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
1061 match call {
1062 Ok(CallResult { raw: _, decoded_result: failed }) => {
1063 trace!(failed, "DSTest::failed()");
1064 !failed
1065 }
1066 Err(err) => {
1067 trace!(%err, "failed to call DSTest::failed()");
1068 true
1069 }
1070 }
1071 }
1072 }
1073
1074 pub fn has_pending_global_failure(state_changeset: &StateChangeset) -> bool {
1077 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
1078 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
1079 && !failed_slot.present_value().is_zero()
1080 {
1081 return true;
1082 }
1083
1084 false
1085 }
1086
1087 pub fn has_global_failure(&self, state_changeset: &StateChangeset) -> bool {
1090 if Self::has_pending_global_failure(state_changeset) {
1091 return true;
1092 }
1093
1094 self.backend()
1095 .storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
1096 .is_ok_and(|failed_slot| !failed_slot.is_zero())
1097 }
1098
1099 fn build_test_env(
1104 &self,
1105 caller: Address,
1106 kind: TxKind,
1107 data: Bytes,
1108 value: U256,
1109 ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>) {
1110 let mut cfg_env = self.evm_env.cfg_env.clone();
1111 cfg_env.spec = self.spec_id();
1112
1113 let mut block_env = self.evm_env.block_env.clone();
1117 block_env.set_basefee(0);
1118 block_env.set_gas_limit(self.gas_limit);
1119
1120 let mut tx_env = self.tx_env.clone();
1121 tx_env.set_caller(caller);
1122 tx_env.set_kind(kind);
1123 tx_env.set_data(data);
1124 tx_env.set_value(value);
1125 tx_env.set_gas_price(0);
1127 tx_env.set_gas_priority_fee(None);
1128 tx_env.set_gas_limit(self.gas_limit);
1129 tx_env.set_chain_id(Some(self.evm_env.cfg_env.chain_id));
1130
1131 (EvmEnv { cfg_env, block_env }, tx_env)
1132 }
1133
1134 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
1135 where
1136 C::Return: Default,
1137 {
1138 self.call_sol(CALLER, to, args, U256::ZERO, None)
1139 .map(|c| c.decoded_result)
1140 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
1141 .unwrap_or_default()
1142 }
1143}
1144
1145#[derive(Debug, thiserror::Error)]
1147#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
1148pub struct ExecutionErr<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1149 pub raw: RawCallResult<FEN>,
1151 pub reason: String,
1153}
1154
1155impl<FEN: FoundryEvmNetwork> std::ops::Deref for ExecutionErr<FEN> {
1156 type Target = RawCallResult<FEN>;
1157
1158 #[inline]
1159 fn deref(&self) -> &Self::Target {
1160 &self.raw
1161 }
1162}
1163
1164impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for ExecutionErr<FEN> {
1165 #[inline]
1166 fn deref_mut(&mut self) -> &mut Self::Target {
1167 &mut self.raw
1168 }
1169}
1170
1171#[derive(Debug, thiserror::Error)]
1172pub enum EvmError<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1173 #[error(transparent)]
1175 Execution(Box<ExecutionErr<FEN>>),
1176 #[error(transparent)]
1178 Abi(#[from] alloy_dyn_abi::Error),
1179 #[error("{0}")]
1181 Skip(SkipReason),
1182 #[error("{0}")]
1184 Eyre(
1185 #[from]
1186 #[source]
1187 eyre::Report,
1188 ),
1189}
1190
1191impl<FEN: FoundryEvmNetwork> From<ExecutionErr<FEN>> for EvmError<FEN> {
1192 fn from(err: ExecutionErr<FEN>) -> Self {
1193 Self::Execution(Box::new(err))
1194 }
1195}
1196
1197impl<FEN: FoundryEvmNetwork> From<alloy_sol_types::Error> for EvmError<FEN> {
1198 fn from(err: alloy_sol_types::Error) -> Self {
1199 Self::Abi(err.into())
1200 }
1201}
1202
1203#[derive(Debug)]
1205pub struct DeployResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1206 pub raw: RawCallResult<FEN>,
1208 pub address: Address,
1210}
1211
1212impl<FEN: FoundryEvmNetwork> std::ops::Deref for DeployResult<FEN> {
1213 type Target = RawCallResult<FEN>;
1214
1215 #[inline]
1216 fn deref(&self) -> &Self::Target {
1217 &self.raw
1218 }
1219}
1220
1221impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for DeployResult<FEN> {
1222 #[inline]
1223 fn deref_mut(&mut self) -> &mut Self::Target {
1224 &mut self.raw
1225 }
1226}
1227
1228impl<FEN: FoundryEvmNetwork> From<DeployResult<FEN>> for RawCallResult<FEN> {
1229 fn from(d: DeployResult<FEN>) -> Self {
1230 d.raw
1231 }
1232}
1233
1234#[derive(Debug)]
1236pub struct RawCallResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1237 pub exit_reason: Option<InstructionResult>,
1239 pub execution_cancelled: bool,
1241 pub reverted: bool,
1243 pub has_state_snapshot_failure: bool,
1248 pub result: Bytes,
1250 pub gas_used: u64,
1252 pub gas_refunded: u64,
1254 pub stipend: u64,
1256 pub logs: Vec<Log>,
1258 pub labels: AddressHashMap<String>,
1260 pub traces: Option<SparsedTraceArena>,
1262 pub debug_bytecodes: AddressHashMap<Bytes>,
1264 pub line_coverage: Option<HitMaps>,
1266 pub edge_coverage: Option<EdgeCoverage>,
1268 pub evm_cmp_values: Option<Vec<CmpOperands>>,
1270 pub observed_calls: Vec<ObservedCall>,
1272 pub sancov_coverage: Option<Vec<u8>>,
1275 pub sancov_cmp_values: Option<Vec<foundry_evm_sancov::CmpSample>>,
1277 pub transactions: Option<BroadcastableTransactions<FEN::Network>>,
1279 pub state_changeset: StateChangeset,
1281 pub evm_env: EvmEnvFor<FEN>,
1283 pub tx_env: TxEnvFor<FEN>,
1285 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
1287 pub out: Option<Output>,
1289 pub fork_block_number: Option<u64>,
1291 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
1293 pub reverter: Option<Address>,
1294 pub skip_payloads: Vec<Bytes>,
1299}
1300
1301impl<FEN: FoundryEvmNetwork> Default for RawCallResult<FEN> {
1302 fn default() -> Self {
1303 Self {
1304 exit_reason: None,
1305 execution_cancelled: false,
1306 reverted: false,
1307 has_state_snapshot_failure: false,
1308 result: Bytes::new(),
1309 gas_used: 0,
1310 gas_refunded: 0,
1311 stipend: 0,
1312 logs: Vec::new(),
1313 labels: HashMap::default(),
1314 traces: None,
1315 debug_bytecodes: HashMap::default(),
1316 line_coverage: None,
1317 edge_coverage: None,
1318 evm_cmp_values: None,
1319 observed_calls: Vec::new(),
1320 sancov_coverage: None,
1321 sancov_cmp_values: None,
1322 transactions: None,
1323 state_changeset: HashMap::default(),
1324 evm_env: EvmEnv::default(),
1325 tx_env: TxEnvFor::<FEN>::default(),
1326 cheatcodes: Default::default(),
1327 out: None,
1328 fork_block_number: None,
1329 chisel_state: None,
1330 reverter: None,
1331 skip_payloads: Vec::new(),
1332 }
1333 }
1334}
1335
1336impl<FEN: FoundryEvmNetwork> RawCallResult<FEN> {
1337 pub fn from_evm_result(r: Result<Self, EvmError<FEN>>) -> eyre::Result<(Self, Option<String>)> {
1339 match r {
1340 Ok(r) => Ok((r, None)),
1341 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
1342 Err(e) => Err(e.into()),
1343 }
1344 }
1345
1346 pub fn skip_reason(&self) -> Option<SkipReason> {
1351 if !self.reverted || !self.skip_payloads.contains(&self.result) {
1352 return None;
1353 }
1354 SkipReason::decode(&self.result)
1355 }
1356
1357 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError<FEN> {
1359 if let Some(reason) = self.skip_reason() {
1360 return EvmError::Skip(reason);
1361 }
1362 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
1363 EvmError::Execution(Box::new(self.into_execution_error(reason)))
1364 }
1365
1366 pub const fn into_execution_error(self, reason: String) -> ExecutionErr<FEN> {
1368 ExecutionErr { raw: self, reason }
1369 }
1370
1371 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError<FEN>> {
1373 if let Some(reason) = self.exit_reason
1374 && reason.is_ok()
1375 {
1376 Ok(self)
1377 } else {
1378 Err(self.into_evm_error(rd))
1379 }
1380 }
1381
1382 pub fn into_decoded_result(
1384 mut self,
1385 func: &Function,
1386 rd: Option<&RevertDecoder>,
1387 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
1388 self = self.into_result(rd)?;
1389 let mut result = func.abi_decode_output(&self.result)?;
1390 let decoded_result =
1391 if result.len() == 1 { result.pop().unwrap() } else { DynSolValue::Tuple(result) };
1392 Ok(CallResult { raw: self, decoded_result })
1393 }
1394
1395 pub fn transactions(&self) -> Option<&BroadcastableTransactions<FEN::Network>> {
1397 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1398 }
1399
1400 pub fn merge_edge_coverage(
1402 &mut self,
1403 history_map: &mut Vec<u8>,
1404 edge_indices: &mut EdgeIndexMap,
1405 ) -> (bool, bool) {
1406 let mut new_coverage = false;
1407 let mut is_edge = false;
1408 if let Some(x) = &mut self.edge_coverage {
1409 match x {
1410 EdgeCoverage::Hash(x) => {
1411 if history_map.len() < x.len() {
1412 history_map.resize(x.len(), 0);
1413 }
1414 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1417 Self::merge_edge_count(*curr, hist, &mut new_coverage, &mut is_edge);
1418
1419 *curr = 0;
1421 }
1422 }
1423 EdgeCoverage::CollisionFree(hits) => {
1424 for hit in hits.drain(..) {
1425 let edge_index = edge_indices.edge_index(hit.edge);
1426 if history_map.len() <= edge_index {
1427 history_map.resize(edge_index + 1, 0);
1428 }
1429 Self::merge_edge_count(
1430 hit.count,
1431 &mut history_map[edge_index],
1432 &mut new_coverage,
1433 &mut is_edge,
1434 );
1435 }
1436 }
1437 }
1438 }
1439 (new_coverage, is_edge)
1440 }
1441
1442 const fn merge_edge_count(
1443 curr: u8,
1444 hist: &mut u8,
1445 new_coverage: &mut bool,
1446 is_edge: &mut bool,
1447 ) {
1448 let Some(bucket) = Self::bin_count(curr) else {
1449 return;
1450 };
1451
1452 if *hist < bucket {
1454 if *hist == 0 {
1455 *is_edge = true;
1457 }
1458 *hist = bucket;
1459 *new_coverage = true;
1460 }
1461 }
1462
1463 const fn bin_count(count: u8) -> Option<u8> {
1466 match count {
1467 0 => None,
1468 1 => Some(1),
1469 2 => Some(2),
1470 3 => Some(4),
1471 4..=7 => Some(8),
1472 8..=15 => Some(16),
1473 16..=31 => Some(32),
1474 32..=127 => Some(64),
1475 128..=255 => Some(128),
1476 }
1477 }
1478
1479 pub fn merge_sancov_coverage(&mut self, history_map: &mut Vec<u8>) -> (bool, bool) {
1482 let mut new_coverage = false;
1483 let mut is_edge = false;
1484 if let Some(x) = &mut self.sancov_coverage {
1485 if history_map.len() < x.len() {
1486 history_map.resize(x.len(), 0);
1487 }
1488 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1489 if *curr > 0 {
1490 if let Some(bucket) = Self::bin_count(*curr)
1491 && *hist < bucket
1492 {
1493 if *hist == 0 {
1494 is_edge = true;
1495 }
1496 *hist = bucket;
1497 new_coverage = true;
1498 }
1499 *curr = 0;
1500 }
1501 }
1502 }
1503 (new_coverage, is_edge)
1504 }
1505
1506 pub fn merge_all_coverage(
1509 &mut self,
1510 evm_history: &mut Vec<u8>,
1511 evm_edge_indices: &mut EdgeIndexMap,
1512 sancov_history: &mut Vec<u8>,
1513 ) -> (bool, bool) {
1514 let (new_evm, edge_evm) = self.merge_edge_coverage(evm_history, evm_edge_indices);
1515 let (new_san, edge_san) = self.merge_sancov_coverage(sancov_history);
1516 (new_evm || new_san, edge_evm || edge_san)
1517 }
1518}
1519
1520pub struct CallResult<T = DynSolValue, FEN: FoundryEvmNetwork = EthEvmNetwork> {
1522 pub raw: RawCallResult<FEN>,
1524 pub decoded_result: T,
1526}
1527
1528impl<T, FEN: FoundryEvmNetwork> std::ops::Deref for CallResult<T, FEN> {
1529 type Target = RawCallResult<FEN>;
1530
1531 #[inline]
1532 fn deref(&self) -> &Self::Target {
1533 &self.raw
1534 }
1535}
1536
1537impl<T, FEN: FoundryEvmNetwork> std::ops::DerefMut for CallResult<T, FEN> {
1538 #[inline]
1539 fn deref_mut(&mut self) -> &mut Self::Target {
1540 &mut self.raw
1541 }
1542}
1543
1544fn calculate_stipend(tx_env: &impl Transaction, spec: SpecId, eip2780_enabled: bool) -> u64 {
1545 let eip2780 = eip2780_enabled.then(|| Eip2780TxInfo {
1546 value: tx_env.value(),
1547 is_self_transfer: matches!(tx_env.kind(), TxKind::Call(to) if to == tx_env.caller()),
1548 });
1549 revm::interpreter::gas::calculate_initial_tx_gas_for_tx(tx_env, spec, eip2780)
1550 .initial_total_gas()
1551}
1552
1553fn convert_executed_result<FEN: FoundryEvmNetwork, H: IntoInstructionResult>(
1555 evm_env: EvmEnvFor<FEN>,
1556 tx_env: TxEnvFor<FEN>,
1557 mut inspector: InspectorStack<FEN>,
1558 ResultAndState { result, state: state_changeset }: ResultAndState<H>,
1559 db: &dyn DatabaseRef<Error = DatabaseError>,
1560 has_state_snapshot_failure: bool,
1561 fork_block_number: Option<u64>,
1562) -> eyre::Result<RawCallResult<FEN>> {
1563 let execution_cancelled = inspector.execution_cancelled();
1564 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1565 ExecutionResult::Success { reason, gas, output, logs } => {
1566 (reason.into(), gas.final_refunded(), gas.tx_gas_used(), Some(output), logs)
1567 }
1568 ExecutionResult::Revert { gas, output, logs } => {
1569 (InstructionResult::Revert, 0_u64, gas.tx_gas_used(), Some(Output::Call(output)), logs)
1570 }
1571 ExecutionResult::Halt { reason, gas, logs } => {
1572 (reason.into_instruction_result(), 0_u64, gas.tx_gas_used(), None, logs)
1573 }
1574 };
1575 let stipend = calculate_stipend(
1576 &tx_env,
1577 evm_env.cfg_env.spec.into(),
1578 evm_env.cfg_env.is_amsterdam_eip2780_enabled(),
1579 );
1580
1581 let result = match &out {
1582 Some(Output::Call(data)) => data.clone(),
1583 _ => Bytes::new(),
1584 };
1585 let observed_calls = inspector
1586 .inner
1587 .fuzzer
1588 .as_mut()
1589 .map(|fuzzer| fuzzer.take_observed_calls())
1590 .unwrap_or_default();
1591
1592 let InspectorData {
1593 mut logs,
1594 labels,
1595 traces,
1596 line_coverage,
1597 edge_coverage,
1598 evm_cmp_values,
1599 mut cheatcodes,
1600 chisel_state,
1601 reverter,
1602 } = inspector.collect();
1603 let fork_block_number = cheatcodes
1604 .as_ref()
1605 .and_then(|cheats| cheats.fork_block_number_override)
1606 .or(fork_block_number);
1607 let debug_bytecodes = collect_debug_bytecodes(traces.as_ref(), db);
1608
1609 if logs.is_empty() {
1610 logs = exec_logs;
1611 }
1612
1613 let transactions = cheatcodes
1614 .as_ref()
1615 .map(|c| c.broadcastable_transactions.clone())
1616 .filter(|txs| !txs.is_empty());
1617 let skip_payloads =
1618 cheatcodes.as_mut().map(|c| std::mem::take(&mut c.skip_payloads)).unwrap_or_default();
1619
1620 Ok(RawCallResult {
1621 exit_reason: Some(exit_reason),
1622 execution_cancelled,
1623 reverted: !matches!(exit_reason, return_ok!()),
1624 has_state_snapshot_failure,
1625 result,
1626 gas_used,
1627 gas_refunded,
1628 stipend,
1629 logs,
1630 labels,
1631 traces,
1632 debug_bytecodes,
1633 line_coverage,
1634 edge_coverage,
1635 evm_cmp_values,
1636 observed_calls,
1637 sancov_coverage: None,
1638 sancov_cmp_values: None,
1639 transactions,
1640 state_changeset,
1641 evm_env,
1642 tx_env,
1643 cheatcodes,
1644 out,
1645 fork_block_number,
1646 chisel_state,
1647 reverter,
1648 skip_payloads,
1649 })
1650}
1651
1652fn collect_debug_bytecodes(
1653 traces: Option<&SparsedTraceArena>,
1654 db: &dyn DatabaseRef<Error = DatabaseError>,
1655) -> AddressHashMap<Bytes> {
1656 let mut bytecodes = HashMap::default();
1657 let Some(traces) = traces else { return bytecodes };
1658
1659 for node in traces.arena.nodes() {
1660 let address = node.trace.address;
1661 if bytecodes.contains_key(&address) {
1662 continue;
1663 }
1664
1665 let Ok(Some(account)) = db.basic_ref(address) else { continue };
1666 let code: Option<Bytecode> =
1667 account.code.or_else(|| db.code_by_hash_ref(account.code_hash).ok());
1668 let code: Bytes = code.map(|code| code.original_bytes()).unwrap_or_default();
1669
1670 if !code.is_empty() {
1671 bytecodes.insert(address, code);
1672 }
1673 }
1674
1675 bytecodes
1676}
1677
1678pub struct FuzzTestTimer {
1680 inner: Option<(Instant, Duration)>,
1682}
1683
1684impl FuzzTestTimer {
1685 pub fn new(timeout: Option<u32>) -> Self {
1686 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1687 }
1688
1689 pub const fn is_enabled(&self) -> bool {
1691 self.inner.is_some()
1692 }
1693
1694 pub fn is_timed_out(&self) -> bool {
1696 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1697 }
1698}
1699
1700#[derive(Clone, Debug)]
1703pub struct EarlyExit {
1704 inner: Arc<AtomicBool>,
1706 fail_fast: bool,
1708}
1709
1710impl EarlyExit {
1711 pub fn new(fail_fast: bool) -> Self {
1712 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1713 }
1714
1715 pub fn record_failure(&self) {
1717 if self.fail_fast {
1718 self.inner.store(true, Ordering::Relaxed);
1719 }
1720 }
1721
1722 pub fn record_ctrl_c(&self) {
1724 self.inner.store(true, Ordering::Relaxed);
1725 }
1726
1727 pub fn should_stop(&self) -> bool {
1729 self.inner.load(Ordering::Relaxed)
1730 }
1731}
1732
1733#[derive(Clone, Debug)]
1735pub(crate) enum EvmExecutionCancellation {
1736 EarlyExit(EarlyExit),
1738 Campaign { early_exit: EarlyExit, stop: Arc<AtomicBool>, deadline: Option<Instant> },
1740}
1741
1742impl EvmExecutionCancellation {
1743 pub(crate) const fn early_exit(early_exit: EarlyExit) -> Self {
1744 Self::EarlyExit(early_exit)
1745 }
1746
1747 pub(crate) const fn campaign(
1748 early_exit: EarlyExit,
1749 stop: Arc<AtomicBool>,
1750 deadline: Option<Instant>,
1751 ) -> Self {
1752 Self::Campaign { early_exit, stop, deadline }
1753 }
1754
1755 pub(crate) fn should_stop(&self, poll_deadline: bool) -> bool {
1757 match self {
1758 Self::EarlyExit(early_exit) => early_exit.should_stop(),
1759 Self::Campaign { early_exit, stop, deadline } => {
1760 if early_exit.should_stop() || stop.load(Ordering::Relaxed) {
1761 return true;
1762 }
1763 if poll_deadline && deadline.is_some_and(|deadline| Instant::now() > deadline) {
1764 stop.store(true, Ordering::Relaxed);
1765 return true;
1766 }
1767 false
1768 }
1769 }
1770 }
1771
1772 pub(crate) fn request_stop(&self) {
1773 if let Self::Campaign { stop, .. } = self {
1774 stop.store(true, Ordering::Relaxed);
1775 }
1776 }
1777
1778 pub(crate) const fn early_exit_ref(&self) -> &EarlyExit {
1779 match self {
1780 Self::EarlyExit(early_exit) | Self::Campaign { early_exit, .. } => early_exit,
1781 }
1782 }
1783}
1784
1785#[inline]
1787pub fn should_ignore_revert(
1788 fail_on_revert: bool,
1789 target: Address,
1790 reverter: Option<Address>,
1791 extra_cheatcode_addresses: &[Address],
1792) -> bool {
1793 !fail_on_revert
1794 && reverter.is_some_and(|reverter| {
1795 reverter != target
1796 && reverter != CHEATCODE_ADDRESS
1797 && !extra_cheatcode_addresses.contains(&reverter)
1798 })
1799}
1800
1801#[cfg(test)]
1802mod tests {
1803 use super::*;
1804 use crate::inspectors::{EdgeCovHit, EdgeKey};
1805 use alloy_primitives::B256;
1806 use foundry_cheatcodes::{
1807 CheatsConfig,
1808 Vm::{blobhashesCall, mockCallRevert_1Call, revertToStateCall, snapshotStateCall},
1809 };
1810 use foundry_config::Config;
1811 use foundry_evm_core::{constants::MAGIC_SKIP, opts::EvmOpts};
1812 use foundry_evm_traces::InternalTraceMode;
1813 use revm::context::TxEnv;
1814 use std::{sync::mpsc, thread};
1815
1816 fn dense_call(edge: EdgeKey) -> RawCallResult {
1817 RawCallResult {
1818 edge_coverage: Some(EdgeCoverage::CollisionFree(vec![EdgeCovHit { edge, count: 1 }])),
1819 ..Default::default()
1820 }
1821 }
1822
1823 #[test]
1824 fn nested_revert_is_ignored_only_when_allowed() {
1825 let target = Address::from([0x11; 20]);
1826 let nested = Address::from([0x22; 20]);
1827
1828 assert!(should_ignore_revert(false, target, Some(nested), &[]));
1829 assert!(!should_ignore_revert(true, target, Some(nested), &[]));
1830 assert!(!should_ignore_revert(false, target, Some(target), &[]));
1831 assert!(!should_ignore_revert(false, target, Some(CHEATCODE_ADDRESS), &[]));
1832 assert!(!should_ignore_revert(false, target, None, &[]));
1833 }
1834
1835 #[cfg(feature = "monad")]
1836 #[test]
1837 fn network_cheatcode_revert_handling_is_monad_specific() {
1838 let target = Address::from([0x11; 20]);
1839
1840 assert!(should_ignore_revert(
1841 false,
1842 target,
1843 Some(foundry_evm_core::constants::MONAD_CHEATCODE_ADDRESS),
1844 &[]
1845 ));
1846 assert!(!should_ignore_revert(
1847 false,
1848 target,
1849 Some(foundry_evm_core::constants::MONAD_CHEATCODE_ADDRESS),
1850 NetworkConfigs::with_monad().extra_cheatcode_addresses(),
1851 ));
1852 }
1853
1854 #[cfg(feature = "monad")]
1855 #[test]
1856 fn executor_networks_follow_explicit_configuration() {
1857 let ethereum = ExecutorBuilder::<EthEvmNetwork>::default().build(
1858 EvmEnvFor::<EthEvmNetwork>::default(),
1859 TxEnvFor::<EthEvmNetwork>::default(),
1860 Backend::spawn(None).unwrap(),
1861 NetworkConfigs::default(),
1862 );
1863 assert!(!ethereum.backend().networks().is_monad());
1864 assert!(
1865 !ethereum
1866 .backend()
1867 .is_persistent(&foundry_evm_core::constants::MONAD_CHEATCODE_ADDRESS)
1868 );
1869
1870 let monad = ExecutorBuilder::<foundry_evm_core::evm::MonadEvmNetwork>::default()
1871 .inspectors(|stack| stack.networks(NetworkConfigs::default()))
1872 .build(
1873 EvmEnvFor::<foundry_evm_core::evm::MonadEvmNetwork>::default(),
1874 TxEnvFor::<foundry_evm_core::evm::MonadEvmNetwork>::default(),
1875 Backend::spawn(None).unwrap(),
1876 NetworkConfigs::with_monad(),
1877 );
1878 assert!(monad.inspector().networks.is_monad());
1879 assert!(monad.backend().networks().is_monad());
1880 assert!(
1881 monad.backend().is_persistent(&foundry_evm_core::constants::MONAD_CHEATCODE_ADDRESS)
1882 );
1883 }
1884
1885 #[test]
1886 fn collision_free_edge_merge_uses_stable_indices() {
1887 let first =
1888 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
1889 let second =
1890 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
1891 let mut history = Vec::new();
1892 let mut edge_indices = EdgeIndexMap::default();
1893
1894 assert_eq!(
1895 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
1896 (true, true)
1897 );
1898 assert_eq!(history, [1]);
1899
1900 assert_eq!(
1901 dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
1902 (true, true)
1903 );
1904 assert_eq!(history, [1, 1]);
1905
1906 assert_eq!(
1907 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
1908 (false, false)
1909 );
1910 assert_eq!(history, [1, 1]);
1911 }
1912
1913 #[test]
1914 fn collision_free_edge_merge_handles_sparse_observation_indices() {
1915 let first =
1916 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
1917 let second =
1918 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
1919 let mut edge_indices = EdgeIndexMap::default();
1920 edge_indices.edge_index(first);
1921 edge_indices.edge_index(second);
1922 let mut history = Vec::new();
1923
1924 assert_eq!(
1925 dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
1926 (true, true)
1927 );
1928 assert_eq!(history, [0, 1]);
1929 }
1930
1931 #[test]
1932 fn cheatcode_skip_payload_is_classified_as_skip() {
1933 let raw = RawCallResult::<EthEvmNetwork> {
1934 reverted: true,
1935 result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"),
1936 skip_payloads: vec![Bytes::from_static(b"FOUNDRY::SKIPwith reason")],
1937 ..Default::default()
1938 };
1939
1940 let err = raw.into_evm_error(None);
1941 assert!(matches!(err, EvmError::Skip(_)));
1942 }
1943
1944 #[test]
1945 fn forged_skip_payload_is_execution_error() {
1946 let raw = RawCallResult::<EthEvmNetwork> {
1947 reverted: true,
1948 result: Bytes::from_static(MAGIC_SKIP),
1949 reverter: Some(CHEATCODE_ADDRESS),
1950 ..Default::default()
1951 };
1952
1953 let err = raw.into_evm_error(None);
1954 assert!(matches!(err, EvmError::Execution(_)));
1955 }
1956
1957 #[test]
1958 fn mismatched_skip_payload_is_execution_error() {
1959 let raw = RawCallResult::<EthEvmNetwork> {
1960 reverted: true,
1961 result: Bytes::from_static(b"FOUNDRY::SKIPforged"),
1962 skip_payloads: vec![Bytes::from_static(b"FOUNDRY::SKIPgenuine")],
1963 ..Default::default()
1964 };
1965
1966 let err = raw.into_evm_error(None);
1967 assert!(matches!(err, EvmError::Execution(_)));
1968 }
1969
1970 #[test]
1971 fn set_spec_id_updates_spec_dependent_cfg_state() {
1972 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1973 let mut executor = ExecutorBuilder::default().build(
1974 EvmEnvFor::<EthEvmNetwork>::default(),
1975 TxEnvFor::<EthEvmNetwork>::default(),
1976 backend,
1977 NetworkConfigs::default(),
1978 );
1979
1980 executor.evm_env_mut().cfg_env.set_spec_and_mainnet_gas_params(SpecId::HOMESTEAD);
1981 assert_eq!(
1982 executor.evm_env().cfg_env.gas_params(),
1983 &revm::context_interface::cfg::GasParams::new_spec(SpecId::HOMESTEAD),
1984 );
1985 assert!(!executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1986
1987 executor.set_spec_id(SpecId::AMSTERDAM);
1988
1989 assert_eq!(executor.spec_id(), SpecId::AMSTERDAM);
1990 assert_eq!(
1991 executor.evm_env().cfg_env.gas_params(),
1992 &revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM),
1993 );
1994 assert!(executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1995 }
1996
1997 #[test]
1998 fn calculate_stipend_uses_eip2780_transaction_context() {
1999 let caller = Address::repeat_byte(0x11);
2000 let recipient = Address::repeat_byte(0x22);
2001 let mut tx = TxEnv { caller, kind: TxKind::Call(recipient), ..Default::default() };
2002
2003 assert_eq!(
2004 calculate_stipend(&tx, SpecId::AMSTERDAM, true),
2005 revm::primitives::eip2780::TX_BASE_COST
2006 + revm::primitives::eip8038::COLD_ACCOUNT_ACCESS
2007 );
2008 assert_eq!(
2009 calculate_stipend(&tx, SpecId::AMSTERDAM, false),
2010 revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM).tx_base_stipend()
2011 );
2012
2013 tx.kind = TxKind::Call(caller);
2014 assert_eq!(
2015 calculate_stipend(&tx, SpecId::AMSTERDAM, true),
2016 revm::primitives::eip2780::TX_BASE_COST
2017 );
2018 }
2019
2020 #[test]
2021 fn amsterdam_intercepted_create_refunds_state_gas() {
2022 let cheats_config =
2023 Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2024 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2025 let mut executor = ExecutorBuilder::default()
2026 .inspectors(|stack| stack.cheatcodes(cheats_config))
2027 .spec_id(SpecId::AMSTERDAM)
2028 .gas_limit(1_000_000)
2029 .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2030
2031 let target = Address::repeat_byte(0x11);
2032 executor
2034 .set_code(
2035 target,
2036 Bytecode::new_raw(Bytes::from_static(&[0x5f, 0x5f, 0x5f, 0xf0, 0x50, 0x00])),
2037 )
2038 .unwrap();
2039 executor.inspector_mut().cheatcodes.as_mut().unwrap().intercept_next_create_call = true;
2040
2041 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2042
2043 assert!(!result.reverted);
2044 assert!(
2045 result.gas_used
2046 < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
2047 .create_state_gas(),
2048 "failed CREATE retained its conditional state-gas charge"
2049 );
2050 }
2051
2052 #[test]
2053 fn amsterdam_mocked_call_revert_refunds_state_gas() {
2054 let cheats_config =
2055 Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2056 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2057 let mut executor = ExecutorBuilder::default()
2058 .inspectors(|stack| stack.cheatcodes(cheats_config))
2059 .spec_id(SpecId::AMSTERDAM)
2060 .gas_limit(1_000_000)
2061 .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2062
2063 let target = Address::repeat_byte(0x11);
2064 let mocked = Address::repeat_byte(0x22);
2065 executor
2066 .transact_raw(
2067 CALLER,
2068 CHEATCODE_ADDRESS,
2069 mockCallRevert_1Call {
2070 callee: mocked,
2071 msgValue: U256::from(1),
2072 data: Bytes::new(),
2073 revertData: Bytes::new(),
2074 }
2075 .abi_encode()
2076 .into(),
2077 U256::ZERO,
2078 )
2079 .unwrap();
2080 executor.set_code(mocked, Bytecode::default()).unwrap();
2081 executor.set_balance(target, U256::from(1)).unwrap();
2082
2083 let mut code = vec![0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x01, 0x73];
2085 code.extend_from_slice(mocked.as_slice());
2086 code.extend_from_slice(&[0x5a, 0xf1, 0x50, 0x00]);
2087 executor.set_code(target, Bytecode::new_raw(code.into())).unwrap();
2088
2089 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2090
2091 assert!(!result.reverted);
2092 assert!(
2093 result.gas_used
2094 < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
2095 .new_account_state_gas(),
2096 "reverted mocked CALL retained its conditional state-gas charge"
2097 );
2098 }
2099
2100 #[test]
2101 fn set_trace_requirements_replaces_trace_mode_between_transactions() {
2102 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2103 let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
2104 EvmEnvFor::<EthEvmNetwork>::default(),
2105 TxEnvFor::<EthEvmNetwork>::default(),
2106 backend,
2107 NetworkConfigs::default(),
2108 );
2109 executor.evm_env_mut().cfg_env.disable_nonce_check = true;
2110 let target = Address::repeat_byte(0x11);
2111 executor
2113 .set_code(
2114 target,
2115 Bytecode::new_raw(Bytes::from_static(&[
2116 0x60, 0x04, 0x56, 0x00, 0x5b, 0x60, 0x01, 0x60, 0x00, 0x55, 0x00,
2117 ])),
2118 )
2119 .unwrap();
2120
2121 let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2122 assert!(untraced.traces.is_none());
2123
2124 executor.set_trace_requirements(TraceRequirements::none().with_debug(true));
2125 let debug = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2126 let debug_steps = &debug.traces.as_ref().unwrap().nodes()[0].trace.steps;
2127 assert_eq!(debug_steps.len(), 7);
2128 assert!(debug_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
2129
2130 executor.set_trace_requirements(
2131 TraceRequirements::none().with_decode_internal(InternalTraceMode::Full),
2132 );
2133 let internal = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2134 let internal_steps = &internal.traces.as_ref().unwrap().nodes()[0].trace.steps;
2135 assert_eq!(internal_steps.len(), 2);
2136 assert!(internal_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
2137
2138 executor.set_trace_requirements(TraceRequirements::none());
2139 let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2140 assert!(untraced.traces.is_none());
2141 }
2142
2143 #[test]
2144 fn early_exit_interrupts_active_evm_execution() {
2145 const GAS_LIMIT: u64 = 1 << 24;
2146 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2147 let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
2148 EvmEnvFor::<EthEvmNetwork>::default(),
2149 TxEnvFor::<EthEvmNetwork>::default(),
2150 backend,
2151 NetworkConfigs::default(),
2152 );
2153 let early_exit = EarlyExit::new(false);
2154 executor.inspector_mut().set_early_exit(early_exit.clone());
2155
2156 let target = Address::repeat_byte(0x11);
2157 executor
2159 .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
2160 .unwrap();
2161
2162 let (started_tx, started_rx) = mpsc::channel();
2163 let (result_tx, result_rx) = mpsc::channel();
2164 let handle = thread::spawn(move || {
2165 started_tx.send(()).unwrap();
2166 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO);
2167 let _ = result_tx.send(result);
2168 });
2169
2170 started_rx.recv().unwrap();
2171 thread::sleep(Duration::from_millis(1));
2172 early_exit.record_ctrl_c();
2173
2174 let result = result_rx.recv_timeout(Duration::from_secs(1));
2175 handle.join().unwrap();
2176 let result = result.expect("active EVM execution did not observe early exit").unwrap();
2177 assert!(result.execution_cancelled);
2178 assert!(!result.reverted);
2179 assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
2180 assert!(result.gas_used > 21_000, "interrupt fired before EVM execution started");
2181 assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of exiting");
2182 }
2183
2184 #[test]
2185 fn completed_execution_is_not_retroactively_cancelled() {
2186 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2187 let mut executor = ExecutorBuilder::default().gas_limit(1 << 24).build(
2188 EvmEnvFor::<EthEvmNetwork>::default(),
2189 TxEnvFor::<EthEvmNetwork>::default(),
2190 backend,
2191 NetworkConfigs::default(),
2192 );
2193 let early_exit = EarlyExit::new(false);
2194 executor.inspector_mut().set_early_exit(early_exit.clone());
2195
2196 let target = Address::repeat_byte(0x11);
2197 executor.set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x00]))).unwrap();
2198 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2199 early_exit.record_ctrl_c();
2200
2201 assert!(!result.execution_cancelled);
2202 assert!(!result.reverted);
2203 }
2204
2205 #[test]
2206 fn campaign_deadline_interrupts_active_evm_execution() {
2207 const GAS_LIMIT: u64 = 1 << 24;
2208 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2209 let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
2210 EvmEnvFor::<EthEvmNetwork>::default(),
2211 TxEnvFor::<EthEvmNetwork>::default(),
2212 backend,
2213 NetworkConfigs::default(),
2214 );
2215 let cancellation = EvmExecutionCancellation::campaign(
2216 EarlyExit::new(false),
2217 Arc::new(AtomicBool::new(false)),
2218 Some(Instant::now()),
2219 );
2220 executor.inspector_mut().set_execution_cancellation(cancellation);
2221
2222 let target = Address::repeat_byte(0x11);
2223 executor
2224 .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
2225 .unwrap();
2226
2227 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
2228 assert!(result.execution_cancelled);
2229 assert!(!result.reverted);
2230 assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
2231 assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of timing out");
2232 }
2233
2234 #[test]
2235 fn beacon_root_system_call_does_not_persist_system_address() {
2236 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2237 let mut executor = ExecutorBuilder::default().spec_id(SpecId::CANCUN).build(
2238 EvmEnvFor::<EthEvmNetwork>::default(),
2239 TxEnvFor::<EthEvmNetwork>::default(),
2240 backend,
2241 NetworkConfigs::default(),
2242 );
2243 let before = executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap();
2244
2245 executor.apply_beacon_root(B256::repeat_byte(0x11)).unwrap();
2246
2247 assert_eq!(
2248 executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap(),
2249 before,
2250 "EIP-4788 system calls must not persist the system caller account",
2251 );
2252 }
2253
2254 #[test]
2269 fn pre_override_blob_hashes_restored_on_revert_to_state() {
2270 let cheats_config =
2271 Arc::new(CheatsConfig::new(&Config::default(), EvmOpts::default(), None, None, false));
2272
2273 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2274 let mut executor = ExecutorBuilder::default()
2275 .inspectors(|stack| stack.cheatcodes(cheats_config))
2276 .spec_id(SpecId::CANCUN)
2277 .build(EvmEnv::default(), TxEnv::default(), backend, NetworkConfigs::default());
2278
2279 let original: Vec<B256> = vec![B256::repeat_byte(0x11), B256::repeat_byte(0x22)];
2280 executor.tx_env_mut().set_blob_hashes(original.clone());
2281
2282 let snap_result = executor
2283 .transact_raw(
2284 CALLER,
2285 CHEATCODE_ADDRESS,
2286 snapshotStateCall {}.abi_encode().into(),
2287 U256::ZERO,
2288 )
2289 .expect("snapshotState failed");
2290 assert!(!snap_result.reverted, "snapshotState reverted unexpectedly");
2291 let snapshot_id = U256::from_be_slice(&snap_result.result[..32]);
2292
2293 let new_hashes = vec![B256::repeat_byte(0x33)];
2294 let blob_result = executor
2295 .transact_raw(
2296 CALLER,
2297 CHEATCODE_ADDRESS,
2298 blobhashesCall { hashes: new_hashes }.abi_encode().into(),
2299 U256::ZERO,
2300 )
2301 .expect("blobhashes failed");
2302 assert!(!blob_result.reverted, "blobhashes reverted unexpectedly");
2303
2304 let revert_result = executor
2305 .transact_raw(
2306 CALLER,
2307 CHEATCODE_ADDRESS,
2308 revertToStateCall { snapshotId: snapshot_id }.abi_encode().into(),
2309 U256::ZERO,
2310 )
2311 .expect("revertToState failed");
2312 assert!(!revert_result.reverted, "revertToState reverted unexpectedly");
2313
2314 assert_eq!(
2315 revert_result.tx_env.blob_hashes, original,
2316 "pre_override_blob_hashes must be restored to original non-empty hashes, not []",
2317 );
2318 assert!(
2319 executor.inspector().cheatcodes.as_ref().unwrap().env_overrides.is_empty(),
2320 "inactive env overrides must be removed after restoring their metadata",
2321 );
2322 }
2323}