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, 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 EthEvmNetwork, EvmEnvFor, FoundryEvmFactory, FoundryEvmNetwork, HaltReasonFor,
39 IntoInstructionResult, SpecFor, TxEnvFor,
40 },
41 utils::StateChangeset,
42};
43use foundry_evm_coverage::HitMaps;
44use foundry_evm_fuzz::ObservedCall;
45use foundry_evm_traces::{SparsedTraceArena, TraceRequirements};
46use revm::{
47 bytecode::Bytecode,
48 context::{Block, Cfg, Transaction},
49 context_interface::{
50 cfg::gas_params::Eip2780TxInfo,
51 result::{ExecutionResult, Output, ResultAndState},
52 transaction::SignedAuthorization,
53 },
54 database::{Database, DatabaseCommit, DatabaseRef},
55 interpreter::{InstructionResult, return_ok},
56 primitives::hardfork::SpecId,
57};
58use sancov::SancovGuard;
59use std::{
60 borrow::Cow,
61 sync::{
62 Arc,
63 atomic::{AtomicBool, Ordering},
64 },
65 time::{Duration, Instant},
66};
67
68mod builder;
69pub use builder::ExecutorBuilder;
70
71pub mod fuzz;
72pub use fuzz::FuzzedExecutor;
73
74pub mod invariant;
75pub use invariant::InvariantExecutor;
76
77mod corpus;
78mod corpus_io;
79mod sancov;
80mod showmap;
81mod trace;
82
83pub use corpus::{DynamicTargetCtx, StatelessReplayTarget, persist_corpus_seed};
84pub use corpus_io::{
85 CorpusDirEntry, canonical_replay_dirs, parse_corpus_filename, read_corpus_dir, read_corpus_tree,
86};
87pub use showmap::{
88 InvariantReplayOptions, MinimizationReplayInput, ReplayFailure, ReplayObservation,
89 ShowmapDomain, ShowmapOpts, ShowmapReplayTarget, ShowmapStats, replay_corpus_to_showmap,
90 replay_sequence_for_minimization,
91};
92pub use trace::TracingExecutor;
93
94const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
95
96sol! {
97 interface ITest {
98 function setUp() external;
99 function failed() external view returns (bool failed);
100
101 #[derive(Default)]
102 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
103 }
104}
105
106#[derive(Clone, Debug)]
118pub struct Executor<FEN: FoundryEvmNetwork> {
119 backend: Arc<Backend<FEN>>,
128 evm_env: EvmEnvFor<FEN>,
130 tx_env: TxEnvFor<FEN>,
132 inspector: InspectorStack<FEN>,
134 gas_limit: u64,
136 legacy_assertions: bool,
138}
139
140impl<FEN: FoundryEvmNetwork> Executor<FEN> {
141 #[inline]
143 pub fn new(
144 mut backend: Backend<FEN>,
145 evm_env: EvmEnvFor<FEN>,
146 tx_env: TxEnvFor<FEN>,
147 inspector: InspectorStack<FEN>,
148 gas_limit: u64,
149 legacy_assertions: bool,
150 ) -> Self {
151 backend.insert_account_info(
154 CHEATCODE_ADDRESS,
155 revm::state::AccountInfo {
156 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
157 code_hash: CHEATCODE_CONTRACT_HASH,
160 ..Default::default()
161 },
162 );
163
164 if !backend.is_in_forking_mode() && evm_env.cfg_env.spec.into() >= SpecId::PRAGUE {
165 let mut account =
166 backend.basic_ref(HISTORY_STORAGE_ADDRESS).unwrap_or_default().unwrap_or_default();
167 account.code_hash = keccak256(&HISTORY_STORAGE_CODE);
168 account.code = Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone()));
169 backend.insert_account_info(HISTORY_STORAGE_ADDRESS, account);
170
171 let current_block = evm_env.block_env.number();
172 let mut block_number = history_window_start(current_block);
173 while block_number < current_block {
174 let block_hash =
175 backend.block_hash(block_number.saturating_to()).unwrap_or_default();
176 let slot = history_storage_slot(block_number);
177 let value = history_storage_value(block_hash);
178 let _ = backend.insert_account_storage(HISTORY_STORAGE_ADDRESS, slot, value);
179 block_number += U256::from(1);
180 }
181 }
182
183 Self {
184 backend: Arc::new(backend),
185 evm_env,
186 tx_env,
187 inspector,
188 gas_limit,
189 legacy_assertions,
190 }
191 }
192
193 fn clone_with_backend(&self, backend: Backend<FEN>) -> Self {
194 let evm_env = self.evm_env.clone();
195 Self {
196 backend: Arc::new(backend),
197 evm_env,
198 tx_env: self.tx_env.clone(),
199 inspector: self.inspector().clone(),
200 gas_limit: self.gas_limit,
201 legacy_assertions: self.legacy_assertions,
202 }
203 }
204
205 pub fn backend(&self) -> &Backend<FEN> {
207 &self.backend
208 }
209
210 pub fn backend_mut(&mut self) -> &mut Backend<FEN> {
215 Arc::make_mut(&mut self.backend)
216 }
217
218 pub const fn evm_env(&self) -> &EvmEnvFor<FEN> {
220 &self.evm_env
221 }
222
223 pub const fn evm_env_mut(&mut self) -> &mut EvmEnvFor<FEN> {
225 &mut self.evm_env
226 }
227
228 pub const fn tx_env(&self) -> &TxEnvFor<FEN> {
230 &self.tx_env
231 }
232
233 pub const fn tx_env_mut(&mut self) -> &mut TxEnvFor<FEN> {
235 &mut self.tx_env
236 }
237
238 pub const fn inspector(&self) -> &InspectorStack<FEN> {
240 &self.inspector
241 }
242
243 pub const fn inspector_mut(&mut self) -> &mut InspectorStack<FEN> {
245 &mut self.inspector
246 }
247
248 pub const fn spec_id(&self) -> SpecFor<FEN> {
250 self.evm_env.cfg_env.spec
251 }
252
253 pub fn set_spec_id(&mut self, spec_id: SpecFor<FEN>) {
255 self.evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
256 }
257
258 pub const fn gas_limit(&self) -> u64 {
263 self.gas_limit
264 }
265
266 pub const fn set_gas_limit(&mut self, gas_limit: u64) {
268 self.gas_limit = gas_limit;
269 }
270
271 pub const fn legacy_assertions(&self) -> bool {
274 self.legacy_assertions
275 }
276
277 pub const fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
280 self.legacy_assertions = legacy_assertions;
281 }
282
283 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
285 trace!("deploying local create2 deployer");
286 let create2_deployer_account = self
287 .backend()
288 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
289 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
290
291 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
293 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
294
295 let initial_balance = self.get_balance(creator)?;
297 self.set_balance(creator, U256::MAX)?;
298
299 let res =
300 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
301 trace!(create2=?res.address, "deployed local create2 deployer");
302
303 self.set_balance(creator, initial_balance)?;
304 }
305 Ok(())
306 }
307
308 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
310 trace!(?address, ?amount, "setting account balance");
311 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
312 account.balance = amount;
313 self.backend_mut().insert_account_info(address, account);
314 Ok(())
315 }
316
317 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
319 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
320 }
321
322 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
324 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
325 account.nonce = nonce;
326 self.backend_mut().insert_account_info(address, account);
327 self.tx_env_mut().set_nonce(nonce);
328 Ok(())
329 }
330
331 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
333 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
334 }
335
336 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
338 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
339 account.code_hash = keccak256(code.original_byte_slice());
340 account.code = Some(code);
341 self.backend_mut().insert_account_info(address, account);
342 Ok(())
343 }
344
345 pub fn set_storage(
347 &mut self,
348 address: Address,
349 storage: HashMap<U256, U256>,
350 ) -> BackendResult<()> {
351 self.backend_mut().replace_account_storage(address, storage)?;
352 Ok(())
353 }
354
355 pub fn set_storage_slot(
357 &mut self,
358 address: Address,
359 slot: U256,
360 value: U256,
361 ) -> BackendResult<()> {
362 self.backend_mut().insert_account_storage(address, slot, value)?;
363 Ok(())
364 }
365
366 pub fn apply_prestate_trace(
372 &mut self,
373 prestate: std::collections::BTreeMap<Address, alloy_rpc_types::trace::geth::AccountState>,
374 ) -> eyre::Result<()> {
375 let backend = self.backend_mut();
376 for (address, account_state) in prestate {
377 let code = account_state.code.map(Bytecode::new_raw).unwrap_or_default();
378 let info = revm::state::AccountInfo {
379 nonce: account_state.nonce.unwrap_or_default(),
380 balance: account_state.balance.unwrap_or_default(),
381 code_hash: keccak256(code.original_byte_slice()),
382 code: Some(code),
383 account_id: Default::default(),
384 };
385 backend.insert_account_info(address, info);
386
387 for (slot, value) in account_state.storage {
388 let slot = U256::from_be_bytes(slot.0);
389 let value = U256::from_be_bytes(value.0);
390 backend.insert_account_storage(address, slot, value)?;
391 }
392 }
393 Ok(())
394 }
395
396 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
398 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
399 }
400
401 #[inline]
402 pub fn set_trace_requirements(&mut self, requirements: TraceRequirements) -> &mut Self {
403 self.inspector_mut().tracing_requirements(requirements);
404 self
405 }
406
407 #[inline]
408 pub fn set_script_execution(&mut self, script_address: Address) {
409 self.inspector_mut().script(script_address);
410 }
411
412 #[inline]
413 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
414 self.inspector_mut().print(trace_printer);
415 self
416 }
417
418 #[inline]
419 pub fn create2_deployer(&self) -> Address {
420 self.inspector().create2_deployer
421 }
422
423 pub fn deploy(
428 &mut self,
429 from: Address,
430 code: Bytes,
431 value: U256,
432 rd: Option<&RevertDecoder>,
433 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
434 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Create, code, value);
435 self.deploy_with_env(evm_env, tx_env, rd)
436 }
437
438 #[instrument(name = "deploy", level = "debug", skip_all)]
445 pub fn deploy_with_env(
446 &mut self,
447 evm_env: EvmEnvFor<FEN>,
448 tx_env: TxEnvFor<FEN>,
449 rd: Option<&RevertDecoder>,
450 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
451 assert!(
452 matches!(tx_env.kind(), TxKind::Create),
453 "Expected create transaction, got {:?}",
454 tx_env.kind()
455 );
456 trace!(sender=%tx_env.caller(), "deploying contract");
457
458 let mut result = self.transact_with_env(evm_env, tx_env)?;
459 result = result.into_result(rd)?;
460 let Some(Output::Create(_, Some(address))) = result.out else {
461 panic!("Deployment succeeded, but no address was returned: {result:#?}");
462 };
463
464 self.backend_mut().add_persistent_account(address);
467
468 trace!(%address, "deployed contract");
469
470 Ok(DeployResult { raw: result, address })
471 }
472
473 #[instrument(name = "setup", level = "debug", skip_all)]
480 pub fn setup(
481 &mut self,
482 from: Option<Address>,
483 to: Address,
484 rd: Option<&RevertDecoder>,
485 ) -> Result<RawCallResult<FEN>, EvmError<FEN>> {
486 trace!(?from, ?to, "setting up contract");
487
488 let from = from.unwrap_or(CALLER);
489 self.backend_mut().set_test_contract(to).set_caller(from);
490 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
491 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
492 res = res.into_result(rd)?;
493
494 self.evm_env_mut().block_env = res.evm_env.block_env.clone();
496 self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id;
498
499 let success =
500 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
501 if !success {
502 return Err(res.into_execution_error("execution error".to_string()).into());
503 }
504
505 Ok(res)
506 }
507
508 pub fn call(
510 &self,
511 from: Address,
512 to: Address,
513 func: &Function,
514 args: &[DynSolValue],
515 value: U256,
516 rd: Option<&RevertDecoder>,
517 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
518 let calldata = Bytes::from(func.abi_encode_input(args)?);
519 let result = self.call_raw(from, to, calldata, value)?;
520 result.into_decoded_result(func, rd)
521 }
522
523 pub fn call_sol<C: SolCall>(
525 &self,
526 from: Address,
527 to: Address,
528 args: &C,
529 value: U256,
530 rd: Option<&RevertDecoder>,
531 ) -> Result<CallResult<C::Return, FEN>, EvmError<FEN>> {
532 let calldata = Bytes::from(args.abi_encode());
533 let mut raw = self.call_raw(from, to, calldata, value)?;
534 raw = raw.into_result(rd)?;
535 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
536 }
537
538 pub fn transact(
540 &mut self,
541 from: Address,
542 to: Address,
543 func: &Function,
544 args: &[DynSolValue],
545 value: U256,
546 rd: Option<&RevertDecoder>,
547 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
548 let calldata = Bytes::from(func.abi_encode_input(args)?);
549 let result = self.transact_raw(from, to, calldata, value)?;
550 result.into_decoded_result(func, rd)
551 }
552
553 pub fn call_raw(
555 &self,
556 from: Address,
557 to: Address,
558 calldata: Bytes,
559 value: U256,
560 ) -> eyre::Result<RawCallResult<FEN>> {
561 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
562 self.call_with_env(evm_env, tx_env)
563 }
564
565 pub fn call_raw_with_authorization(
568 &mut self,
569 from: Address,
570 to: Address,
571 calldata: Bytes,
572 value: U256,
573 authorization_list: Vec<SignedAuthorization>,
574 ) -> eyre::Result<RawCallResult<FEN>> {
575 let (evm_env, mut tx_env) = self.build_test_env(from, to.into(), calldata, value);
576 tx_env.set_signed_authorization(authorization_list);
577 tx_env.set_tx_type(4);
578 self.call_with_env(evm_env, tx_env)
579 }
580
581 pub fn transact_raw(
583 &mut self,
584 from: Address,
585 to: Address,
586 calldata: Bytes,
587 value: U256,
588 ) -> eyre::Result<RawCallResult<FEN>> {
589 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
590 self.transact_with_env(evm_env, tx_env)
591 }
592
593 pub fn transact_raw_with_authorization(
596 &mut self,
597 from: Address,
598 to: Address,
599 calldata: Bytes,
600 value: U256,
601 authorization_list: Vec<SignedAuthorization>,
602 ) -> eyre::Result<RawCallResult<FEN>> {
603 let (evm_env, mut tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
604 tx_env.set_signed_authorization(authorization_list);
605 tx_env.set_tx_type(4);
606 self.transact_with_env(evm_env, tx_env)
607 }
608
609 pub fn apply_beacon_root(
612 &mut self,
613 parent_beacon_block_root: alloy_primitives::B256,
614 ) -> eyre::Result<()> {
615 let calldata = Bytes::copy_from_slice(parent_beacon_block_root.as_slice());
616 let mut evm_env = self.evm_env.clone();
617 let inspector = self.inspector().clone();
618 let mut state = {
619 let mut backend = CowBackend::new_borrowed(self.backend());
620 let mut evm = FEN::EvmFactory::default().create_foundry_evm_with_inspector(
621 &mut backend,
622 evm_env.clone(),
623 inspector,
624 );
625 let result =
626 evm.transact_system_call(SYSTEM_ADDRESS, BEACON_ROOTS_ADDRESS, calldata)?;
627 evm_env = evm.finish().1;
628 result.state
629 };
630 state.retain(|address, _| *address == BEACON_ROOTS_ADDRESS);
631
632 self.backend_mut().commit(state);
633 self.inspector_mut().set_block(evm_env.block_env);
634
635 Ok(())
636 }
637
638 #[instrument(name = "call", level = "debug", skip_all)]
642 pub fn call_with_env(
643 &self,
644 mut evm_env: EvmEnvFor<FEN>,
645 mut tx_env: TxEnvFor<FEN>,
646 ) -> eyre::Result<RawCallResult<FEN>> {
647 let mut stack = self.inspector().clone();
648 let sancov_edges = stack.inner.sancov_edges;
649 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
650 let sancov_active = sancov_edges || sancov_trace_cmp;
651 let mut backend = CowBackend::new_borrowed(self.backend());
652 let result = {
653 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
654 backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?
655 };
656 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
657 let mut result = convert_executed_result(
658 evm_env,
659 tx_env,
660 stack,
661 result,
662 &backend,
663 has_state_snapshot_failure,
664 )?;
665 if sancov_edges {
666 SancovGuard::append_edges_into(&mut result);
667 }
668 if sancov_trace_cmp {
669 SancovGuard::drain_cmp_into(&mut result);
670 }
671 Ok(result)
672 }
673
674 #[instrument(name = "transact", level = "debug", skip_all)]
676 pub fn transact_with_env(
677 &mut self,
678 mut evm_env: EvmEnvFor<FEN>,
679 mut tx_env: TxEnvFor<FEN>,
680 ) -> eyre::Result<RawCallResult<FEN>> {
681 let mut stack = self.inspector().clone();
682 let sancov_edges = stack.inner.sancov_edges;
683 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
684 let sancov_active = sancov_edges || sancov_trace_cmp;
685 let backend = self.backend_mut();
686 let result = {
687 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
688 backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?
689 };
690 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
691 let mut result = convert_executed_result(
692 evm_env,
693 tx_env,
694 stack,
695 result,
696 &*backend,
697 has_state_snapshot_failure,
698 )?;
699 if sancov_edges {
700 SancovGuard::append_edges_into(&mut result);
701 }
702 if sancov_trace_cmp {
703 SancovGuard::drain_cmp_into(&mut result);
704 }
705 self.commit(&mut result);
706 Ok(result)
707 }
708
709 #[instrument(name = "commit", level = "debug", skip_all)]
714 fn commit(&mut self, result: &mut RawCallResult<FEN>) {
715 self.backend_mut().commit(result.state_changeset.clone());
717
718 self.inspector_mut().cheatcodes = result.cheatcodes.take();
720 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
721 cheats.broadcastable_transactions.clear();
723 cheats.ignored_traces.ignored.clear();
724
725 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
728 *last_pause_call = (0, 0);
729 }
730 }
731
732 self.inspector_mut().set_block(result.evm_env.block_env.clone());
734 self.inspector_mut().set_gas_price(result.tx_env.gas_price());
735 }
736
737 pub fn is_raw_call_mut_success(
742 &self,
743 address: Address,
744 call_result: &mut RawCallResult<FEN>,
745 should_fail: bool,
746 ) -> bool {
747 self.is_raw_call_success(
748 address,
749 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
750 call_result,
751 should_fail,
752 )
753 }
754
755 pub fn is_raw_call_success(
759 &self,
760 address: Address,
761 state_changeset: Cow<'_, StateChangeset>,
762 call_result: &RawCallResult<FEN>,
763 should_fail: bool,
764 ) -> bool {
765 if call_result.has_state_snapshot_failure {
766 return should_fail;
768 }
769 self.is_success(address, call_result.reverted, state_changeset, should_fail)
770 }
771
772 pub fn is_raw_call_mut_success_handler_gate(
776 &self,
777 address: Address,
778 call_result: &mut RawCallResult<FEN>,
779 ) -> bool {
780 if call_result.has_state_snapshot_failure {
781 return false;
782 }
783 let state_changeset = std::mem::take(&mut call_result.state_changeset);
784 self.is_success_handler_gate(address, call_result.reverted, Cow::Owned(state_changeset))
785 }
786
787 pub fn is_success(
809 &self,
810 address: Address,
811 reverted: bool,
812 state_changeset: Cow<'_, StateChangeset>,
813 should_fail: bool,
814 ) -> bool {
815 let success = self.is_success_raw(address, reverted, state_changeset, false);
816 should_fail ^ success
817 }
818
819 pub fn is_success_handler_gate(
825 &self,
826 address: Address,
827 reverted: bool,
828 state_changeset: Cow<'_, StateChangeset>,
829 ) -> bool {
830 self.is_success_raw(address, reverted, state_changeset, true)
831 }
832
833 #[instrument(name = "is_success", level = "debug", skip_all)]
834 fn is_success_raw(
835 &self,
836 address: Address,
837 reverted: bool,
838 state_changeset: Cow<'_, StateChangeset>,
839 pending_global_failure_only: bool,
840 ) -> bool {
841 if reverted {
843 return false;
844 }
845
846 if self.backend().has_state_snapshot_failure() {
848 return false;
849 }
850
851 let global_failed = if pending_global_failure_only {
856 Self::has_pending_global_failure(&state_changeset)
857 } else {
858 self.has_global_failure(&state_changeset)
859 };
860 if global_failed {
861 return false;
862 }
863
864 if !self.legacy_assertions {
865 return true;
866 }
867
868 {
870 let mut backend = self.backend().clone_empty();
872
873 for address in [address, CHEATCODE_ADDRESS] {
876 let Ok(acc) = self.backend().basic_ref(address) else { return false };
877 backend.insert_account_info(address, acc.unwrap_or_default());
878 }
879
880 backend.commit(state_changeset.into_owned());
885
886 let executor = self.clone_with_backend(backend);
888 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
889 match call {
890 Ok(CallResult { raw: _, decoded_result: failed }) => {
891 trace!(failed, "DSTest::failed()");
892 !failed
893 }
894 Err(err) => {
895 trace!(%err, "failed to call DSTest::failed()");
896 true
897 }
898 }
899 }
900 }
901
902 pub fn has_pending_global_failure(state_changeset: &StateChangeset) -> bool {
905 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
906 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
907 && !failed_slot.present_value().is_zero()
908 {
909 return true;
910 }
911
912 false
913 }
914
915 pub fn has_global_failure(&self, state_changeset: &StateChangeset) -> bool {
918 if Self::has_pending_global_failure(state_changeset) {
919 return true;
920 }
921
922 self.backend()
923 .storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
924 .is_ok_and(|failed_slot| !failed_slot.is_zero())
925 }
926
927 fn build_test_env(
932 &self,
933 caller: Address,
934 kind: TxKind,
935 data: Bytes,
936 value: U256,
937 ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>) {
938 let mut cfg_env = self.evm_env.cfg_env.clone();
939 cfg_env.spec = self.spec_id();
940
941 let mut block_env = self.evm_env.block_env.clone();
945 block_env.set_basefee(0);
946 block_env.set_gas_limit(self.gas_limit);
947
948 let mut tx_env = self.tx_env.clone();
949 tx_env.set_caller(caller);
950 tx_env.set_kind(kind);
951 tx_env.set_data(data);
952 tx_env.set_value(value);
953 tx_env.set_gas_price(0);
955 tx_env.set_gas_priority_fee(None);
956 tx_env.set_gas_limit(self.gas_limit);
957 tx_env.set_chain_id(Some(self.evm_env.cfg_env.chain_id));
958
959 (EvmEnv { cfg_env, block_env }, tx_env)
960 }
961
962 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
963 where
964 C::Return: Default,
965 {
966 self.call_sol(CALLER, to, args, U256::ZERO, None)
967 .map(|c| c.decoded_result)
968 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
969 .unwrap_or_default()
970 }
971}
972
973#[derive(Debug, thiserror::Error)]
975#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
976pub struct ExecutionErr<FEN: FoundryEvmNetwork = EthEvmNetwork> {
977 pub raw: RawCallResult<FEN>,
979 pub reason: String,
981}
982
983impl<FEN: FoundryEvmNetwork> std::ops::Deref for ExecutionErr<FEN> {
984 type Target = RawCallResult<FEN>;
985
986 #[inline]
987 fn deref(&self) -> &Self::Target {
988 &self.raw
989 }
990}
991
992impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for ExecutionErr<FEN> {
993 #[inline]
994 fn deref_mut(&mut self) -> &mut Self::Target {
995 &mut self.raw
996 }
997}
998
999#[derive(Debug, thiserror::Error)]
1000pub enum EvmError<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1001 #[error(transparent)]
1003 Execution(Box<ExecutionErr<FEN>>),
1004 #[error(transparent)]
1006 Abi(#[from] alloy_dyn_abi::Error),
1007 #[error("{0}")]
1009 Skip(SkipReason),
1010 #[error("{0}")]
1012 Eyre(
1013 #[from]
1014 #[source]
1015 eyre::Report,
1016 ),
1017}
1018
1019impl<FEN: FoundryEvmNetwork> From<ExecutionErr<FEN>> for EvmError<FEN> {
1020 fn from(err: ExecutionErr<FEN>) -> Self {
1021 Self::Execution(Box::new(err))
1022 }
1023}
1024
1025impl<FEN: FoundryEvmNetwork> From<alloy_sol_types::Error> for EvmError<FEN> {
1026 fn from(err: alloy_sol_types::Error) -> Self {
1027 Self::Abi(err.into())
1028 }
1029}
1030
1031#[derive(Debug)]
1033pub struct DeployResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1034 pub raw: RawCallResult<FEN>,
1036 pub address: Address,
1038}
1039
1040impl<FEN: FoundryEvmNetwork> std::ops::Deref for DeployResult<FEN> {
1041 type Target = RawCallResult<FEN>;
1042
1043 #[inline]
1044 fn deref(&self) -> &Self::Target {
1045 &self.raw
1046 }
1047}
1048
1049impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for DeployResult<FEN> {
1050 #[inline]
1051 fn deref_mut(&mut self) -> &mut Self::Target {
1052 &mut self.raw
1053 }
1054}
1055
1056impl<FEN: FoundryEvmNetwork> From<DeployResult<FEN>> for RawCallResult<FEN> {
1057 fn from(d: DeployResult<FEN>) -> Self {
1058 d.raw
1059 }
1060}
1061
1062#[derive(Debug)]
1064pub struct RawCallResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1065 pub exit_reason: Option<InstructionResult>,
1067 pub execution_cancelled: bool,
1069 pub reverted: bool,
1071 pub has_state_snapshot_failure: bool,
1076 pub result: Bytes,
1078 pub gas_used: u64,
1080 pub gas_refunded: u64,
1082 pub stipend: u64,
1084 pub logs: Vec<Log>,
1086 pub labels: AddressHashMap<String>,
1088 pub traces: Option<SparsedTraceArena>,
1090 pub debug_bytecodes: AddressHashMap<Bytes>,
1092 pub line_coverage: Option<HitMaps>,
1094 pub edge_coverage: Option<EdgeCoverage>,
1096 pub evm_cmp_values: Option<Vec<CmpOperands>>,
1098 pub observed_calls: Vec<ObservedCall>,
1100 pub sancov_coverage: Option<Vec<u8>>,
1103 pub sancov_cmp_values: Option<Vec<foundry_evm_sancov::CmpSample>>,
1105 pub transactions: Option<BroadcastableTransactions<FEN::Network>>,
1107 pub state_changeset: StateChangeset,
1109 pub evm_env: EvmEnvFor<FEN>,
1111 pub tx_env: TxEnvFor<FEN>,
1113 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
1115 pub out: Option<Output>,
1117 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
1119 pub reverter: Option<Address>,
1120}
1121
1122impl<FEN: FoundryEvmNetwork> Default for RawCallResult<FEN> {
1123 fn default() -> Self {
1124 Self {
1125 exit_reason: None,
1126 execution_cancelled: false,
1127 reverted: false,
1128 has_state_snapshot_failure: false,
1129 result: Bytes::new(),
1130 gas_used: 0,
1131 gas_refunded: 0,
1132 stipend: 0,
1133 logs: Vec::new(),
1134 labels: HashMap::default(),
1135 traces: None,
1136 debug_bytecodes: HashMap::default(),
1137 line_coverage: None,
1138 edge_coverage: None,
1139 evm_cmp_values: None,
1140 observed_calls: Vec::new(),
1141 sancov_coverage: None,
1142 sancov_cmp_values: None,
1143 transactions: None,
1144 state_changeset: HashMap::default(),
1145 evm_env: EvmEnv::default(),
1146 tx_env: TxEnvFor::<FEN>::default(),
1147 cheatcodes: Default::default(),
1148 out: None,
1149 chisel_state: None,
1150 reverter: None,
1151 }
1152 }
1153}
1154
1155impl<FEN: FoundryEvmNetwork> RawCallResult<FEN> {
1156 pub fn from_evm_result(r: Result<Self, EvmError<FEN>>) -> eyre::Result<(Self, Option<String>)> {
1158 match r {
1159 Ok(r) => Ok((r, None)),
1160 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
1161 Err(e) => Err(e.into()),
1162 }
1163 }
1164
1165 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError<FEN> {
1167 if self.reverter == Some(CHEATCODE_ADDRESS)
1168 && let Some(reason) = SkipReason::decode(&self.result)
1169 {
1170 return EvmError::Skip(reason);
1171 }
1172 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
1173 EvmError::Execution(Box::new(self.into_execution_error(reason)))
1174 }
1175
1176 pub const fn into_execution_error(self, reason: String) -> ExecutionErr<FEN> {
1178 ExecutionErr { raw: self, reason }
1179 }
1180
1181 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError<FEN>> {
1183 if let Some(reason) = self.exit_reason
1184 && reason.is_ok()
1185 {
1186 Ok(self)
1187 } else {
1188 Err(self.into_evm_error(rd))
1189 }
1190 }
1191
1192 pub fn into_decoded_result(
1194 mut self,
1195 func: &Function,
1196 rd: Option<&RevertDecoder>,
1197 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
1198 self = self.into_result(rd)?;
1199 let mut result = func.abi_decode_output(&self.result)?;
1200 let decoded_result =
1201 if result.len() == 1 { result.pop().unwrap() } else { DynSolValue::Tuple(result) };
1202 Ok(CallResult { raw: self, decoded_result })
1203 }
1204
1205 pub fn transactions(&self) -> Option<&BroadcastableTransactions<FEN::Network>> {
1207 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1208 }
1209
1210 pub fn merge_edge_coverage(
1212 &mut self,
1213 history_map: &mut Vec<u8>,
1214 edge_indices: &mut EdgeIndexMap,
1215 ) -> (bool, bool) {
1216 let mut new_coverage = false;
1217 let mut is_edge = false;
1218 if let Some(x) = &mut self.edge_coverage {
1219 match x {
1220 EdgeCoverage::Hash(x) => {
1221 if history_map.len() < x.len() {
1222 history_map.resize(x.len(), 0);
1223 }
1224 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1227 Self::merge_edge_count(*curr, hist, &mut new_coverage, &mut is_edge);
1228
1229 *curr = 0;
1231 }
1232 }
1233 EdgeCoverage::CollisionFree(hits) => {
1234 for hit in hits.drain(..) {
1235 let edge_index = edge_indices.edge_index(hit.edge);
1236 if history_map.len() <= edge_index {
1237 history_map.resize(edge_index + 1, 0);
1238 }
1239 Self::merge_edge_count(
1240 hit.count,
1241 &mut history_map[edge_index],
1242 &mut new_coverage,
1243 &mut is_edge,
1244 );
1245 }
1246 }
1247 }
1248 }
1249 (new_coverage, is_edge)
1250 }
1251
1252 const fn merge_edge_count(
1253 curr: u8,
1254 hist: &mut u8,
1255 new_coverage: &mut bool,
1256 is_edge: &mut bool,
1257 ) {
1258 let Some(bucket) = Self::bin_count(curr) else {
1259 return;
1260 };
1261
1262 if *hist < bucket {
1264 if *hist == 0 {
1265 *is_edge = true;
1267 }
1268 *hist = bucket;
1269 *new_coverage = true;
1270 }
1271 }
1272
1273 const fn bin_count(count: u8) -> Option<u8> {
1276 match count {
1277 0 => None,
1278 1 => Some(1),
1279 2 => Some(2),
1280 3 => Some(4),
1281 4..=7 => Some(8),
1282 8..=15 => Some(16),
1283 16..=31 => Some(32),
1284 32..=127 => Some(64),
1285 128..=255 => Some(128),
1286 }
1287 }
1288
1289 pub fn merge_sancov_coverage(&mut self, history_map: &mut Vec<u8>) -> (bool, bool) {
1292 let mut new_coverage = false;
1293 let mut is_edge = false;
1294 if let Some(x) = &mut self.sancov_coverage {
1295 if history_map.len() < x.len() {
1296 history_map.resize(x.len(), 0);
1297 }
1298 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1299 if *curr > 0 {
1300 if let Some(bucket) = Self::bin_count(*curr)
1301 && *hist < bucket
1302 {
1303 if *hist == 0 {
1304 is_edge = true;
1305 }
1306 *hist = bucket;
1307 new_coverage = true;
1308 }
1309 *curr = 0;
1310 }
1311 }
1312 }
1313 (new_coverage, is_edge)
1314 }
1315
1316 pub fn merge_all_coverage(
1319 &mut self,
1320 evm_history: &mut Vec<u8>,
1321 evm_edge_indices: &mut EdgeIndexMap,
1322 sancov_history: &mut Vec<u8>,
1323 ) -> (bool, bool) {
1324 let (new_evm, edge_evm) = self.merge_edge_coverage(evm_history, evm_edge_indices);
1325 let (new_san, edge_san) = self.merge_sancov_coverage(sancov_history);
1326 (new_evm || new_san, edge_evm || edge_san)
1327 }
1328}
1329
1330pub struct CallResult<T = DynSolValue, FEN: FoundryEvmNetwork = EthEvmNetwork> {
1332 pub raw: RawCallResult<FEN>,
1334 pub decoded_result: T,
1336}
1337
1338impl<T, FEN: FoundryEvmNetwork> std::ops::Deref for CallResult<T, FEN> {
1339 type Target = RawCallResult<FEN>;
1340
1341 #[inline]
1342 fn deref(&self) -> &Self::Target {
1343 &self.raw
1344 }
1345}
1346
1347impl<T, FEN: FoundryEvmNetwork> std::ops::DerefMut for CallResult<T, FEN> {
1348 #[inline]
1349 fn deref_mut(&mut self) -> &mut Self::Target {
1350 &mut self.raw
1351 }
1352}
1353
1354fn calculate_stipend(tx_env: &impl Transaction, spec: SpecId, eip2780_enabled: bool) -> u64 {
1355 let eip2780 = eip2780_enabled.then(|| Eip2780TxInfo {
1356 value: tx_env.value(),
1357 is_self_transfer: matches!(tx_env.kind(), TxKind::Call(to) if to == tx_env.caller()),
1358 });
1359 revm::interpreter::gas::calculate_initial_tx_gas_for_tx(tx_env, spec, eip2780)
1360 .initial_total_gas()
1361}
1362
1363fn convert_executed_result<FEN: FoundryEvmNetwork>(
1365 evm_env: EvmEnvFor<FEN>,
1366 tx_env: TxEnvFor<FEN>,
1367 mut inspector: InspectorStack<FEN>,
1368 ResultAndState { result, state: state_changeset }: ResultAndState<HaltReasonFor<FEN>>,
1369 db: &dyn DatabaseRef<Error = DatabaseError>,
1370 has_state_snapshot_failure: bool,
1371) -> eyre::Result<RawCallResult<FEN>> {
1372 let execution_cancelled = inspector.execution_cancelled();
1373 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1374 ExecutionResult::Success { reason, gas, output, logs } => {
1375 (reason.into(), gas.final_refunded(), gas.tx_gas_used(), Some(output), logs)
1376 }
1377 ExecutionResult::Revert { gas, output, logs } => {
1378 (InstructionResult::Revert, 0_u64, gas.tx_gas_used(), Some(Output::Call(output)), logs)
1379 }
1380 ExecutionResult::Halt { reason, gas, logs } => {
1381 (reason.into_instruction_result(), 0_u64, gas.tx_gas_used(), None, logs)
1382 }
1383 };
1384 let stipend = calculate_stipend(
1385 &tx_env,
1386 evm_env.cfg_env.spec.into(),
1387 evm_env.cfg_env.is_amsterdam_eip2780_enabled(),
1388 );
1389
1390 let result = match &out {
1391 Some(Output::Call(data)) => data.clone(),
1392 _ => Bytes::new(),
1393 };
1394 let observed_calls = inspector
1395 .inner
1396 .fuzzer
1397 .as_mut()
1398 .map(|fuzzer| fuzzer.take_observed_calls())
1399 .unwrap_or_default();
1400
1401 let InspectorData {
1402 mut logs,
1403 labels,
1404 traces,
1405 line_coverage,
1406 edge_coverage,
1407 evm_cmp_values,
1408 cheatcodes,
1409 chisel_state,
1410 reverter,
1411 } = inspector.collect();
1412 let debug_bytecodes = collect_debug_bytecodes(traces.as_ref(), db);
1413
1414 if logs.is_empty() {
1415 logs = exec_logs;
1416 }
1417
1418 let transactions = cheatcodes
1419 .as_ref()
1420 .map(|c| c.broadcastable_transactions.clone())
1421 .filter(|txs| !txs.is_empty());
1422
1423 Ok(RawCallResult {
1424 exit_reason: Some(exit_reason),
1425 execution_cancelled,
1426 reverted: !matches!(exit_reason, return_ok!()),
1427 has_state_snapshot_failure,
1428 result,
1429 gas_used,
1430 gas_refunded,
1431 stipend,
1432 logs,
1433 labels,
1434 traces,
1435 debug_bytecodes,
1436 line_coverage,
1437 edge_coverage,
1438 evm_cmp_values,
1439 observed_calls,
1440 sancov_coverage: None,
1441 sancov_cmp_values: None,
1442 transactions,
1443 state_changeset,
1444 evm_env,
1445 tx_env,
1446 cheatcodes,
1447 out,
1448 chisel_state,
1449 reverter,
1450 })
1451}
1452
1453fn collect_debug_bytecodes(
1454 traces: Option<&SparsedTraceArena>,
1455 db: &dyn DatabaseRef<Error = DatabaseError>,
1456) -> AddressHashMap<Bytes> {
1457 let mut bytecodes = HashMap::default();
1458 let Some(traces) = traces else { return bytecodes };
1459
1460 for node in traces.arena.nodes() {
1461 let address = node.trace.address;
1462 if bytecodes.contains_key(&address) {
1463 continue;
1464 }
1465
1466 let Ok(Some(account)) = db.basic_ref(address) else { continue };
1467 let code: Option<Bytecode> =
1468 account.code.or_else(|| db.code_by_hash_ref(account.code_hash).ok());
1469 let code: Bytes = code.map(|code| code.original_bytes()).unwrap_or_default();
1470
1471 if !code.is_empty() {
1472 bytecodes.insert(address, code);
1473 }
1474 }
1475
1476 bytecodes
1477}
1478
1479pub struct FuzzTestTimer {
1481 inner: Option<(Instant, Duration)>,
1483}
1484
1485impl FuzzTestTimer {
1486 pub fn new(timeout: Option<u32>) -> Self {
1487 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1488 }
1489
1490 pub const fn is_enabled(&self) -> bool {
1492 self.inner.is_some()
1493 }
1494
1495 pub fn is_timed_out(&self) -> bool {
1497 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1498 }
1499}
1500
1501#[derive(Clone, Debug)]
1504pub struct EarlyExit {
1505 inner: Arc<AtomicBool>,
1507 fail_fast: bool,
1509}
1510
1511impl EarlyExit {
1512 pub fn new(fail_fast: bool) -> Self {
1513 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1514 }
1515
1516 pub fn record_failure(&self) {
1518 if self.fail_fast {
1519 self.inner.store(true, Ordering::Relaxed);
1520 }
1521 }
1522
1523 pub fn record_ctrl_c(&self) {
1525 self.inner.store(true, Ordering::Relaxed);
1526 }
1527
1528 pub fn should_stop(&self) -> bool {
1530 self.inner.load(Ordering::Relaxed)
1531 }
1532}
1533
1534#[derive(Clone, Debug)]
1536pub(crate) enum EvmExecutionCancellation {
1537 EarlyExit(EarlyExit),
1539 Campaign { early_exit: EarlyExit, stop: Arc<AtomicBool>, deadline: Option<Instant> },
1541}
1542
1543impl EvmExecutionCancellation {
1544 pub(crate) const fn early_exit(early_exit: EarlyExit) -> Self {
1545 Self::EarlyExit(early_exit)
1546 }
1547
1548 pub(crate) const fn campaign(
1549 early_exit: EarlyExit,
1550 stop: Arc<AtomicBool>,
1551 deadline: Option<Instant>,
1552 ) -> Self {
1553 Self::Campaign { early_exit, stop, deadline }
1554 }
1555
1556 pub(crate) fn should_stop(&self, poll_deadline: bool) -> bool {
1558 match self {
1559 Self::EarlyExit(early_exit) => early_exit.should_stop(),
1560 Self::Campaign { early_exit, stop, deadline } => {
1561 if early_exit.should_stop() || stop.load(Ordering::Relaxed) {
1562 return true;
1563 }
1564 if poll_deadline && deadline.is_some_and(|deadline| Instant::now() > deadline) {
1565 stop.store(true, Ordering::Relaxed);
1566 return true;
1567 }
1568 false
1569 }
1570 }
1571 }
1572
1573 pub(crate) fn request_stop(&self) {
1574 if let Self::Campaign { stop, .. } = self {
1575 stop.store(true, Ordering::Relaxed);
1576 }
1577 }
1578
1579 pub(crate) const fn early_exit_ref(&self) -> &EarlyExit {
1580 match self {
1581 Self::EarlyExit(early_exit) | Self::Campaign { early_exit, .. } => early_exit,
1582 }
1583 }
1584}
1585
1586#[cfg(test)]
1587mod tests {
1588 use super::*;
1589 use crate::inspectors::{EdgeCovHit, EdgeKey};
1590 use alloy_primitives::B256;
1591 use foundry_cheatcodes::{
1592 CheatsConfig,
1593 Vm::{blobhashesCall, mockCallRevert_1Call, revertToStateCall, snapshotStateCall},
1594 };
1595 use foundry_config::Config;
1596 use foundry_evm_core::{constants::MAGIC_SKIP, opts::EvmOpts};
1597 use foundry_evm_traces::InternalTraceMode;
1598 use revm::context::TxEnv;
1599 use std::{sync::mpsc, thread};
1600
1601 fn dense_call(edge: EdgeKey) -> RawCallResult {
1602 RawCallResult {
1603 edge_coverage: Some(EdgeCoverage::CollisionFree(vec![EdgeCovHit { edge, count: 1 }])),
1604 ..Default::default()
1605 }
1606 }
1607
1608 #[test]
1609 fn collision_free_edge_merge_uses_stable_indices() {
1610 let first =
1611 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
1612 let second =
1613 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
1614 let mut history = Vec::new();
1615 let mut edge_indices = EdgeIndexMap::default();
1616
1617 assert_eq!(
1618 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
1619 (true, true)
1620 );
1621 assert_eq!(history, [1]);
1622
1623 assert_eq!(
1624 dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
1625 (true, true)
1626 );
1627 assert_eq!(history, [1, 1]);
1628
1629 assert_eq!(
1630 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
1631 (false, false)
1632 );
1633 assert_eq!(history, [1, 1]);
1634 }
1635
1636 #[test]
1637 fn collision_free_edge_merge_handles_sparse_observation_indices() {
1638 let first =
1639 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
1640 let second =
1641 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
1642 let mut edge_indices = EdgeIndexMap::default();
1643 edge_indices.edge_index(first);
1644 edge_indices.edge_index(second);
1645 let mut history = Vec::new();
1646
1647 assert_eq!(
1648 dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
1649 (true, true)
1650 );
1651 assert_eq!(history, [0, 1]);
1652 }
1653
1654 #[test]
1655 fn cheatcode_skip_payload_is_classified_as_skip() {
1656 let raw = RawCallResult::<EthEvmNetwork> {
1657 result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"),
1658 reverter: Some(CHEATCODE_ADDRESS),
1659 ..Default::default()
1660 };
1661
1662 let err = raw.into_evm_error(None);
1663 assert!(matches!(err, EvmError::Skip(_)));
1664 }
1665
1666 #[test]
1667 fn forged_skip_payload_from_non_cheatcode_is_execution_error() {
1668 let raw = RawCallResult::<EthEvmNetwork> {
1669 result: Bytes::from_static(MAGIC_SKIP),
1670 reverter: Some(CALLER),
1671 ..Default::default()
1672 };
1673
1674 let err = raw.into_evm_error(None);
1675 assert!(matches!(err, EvmError::Execution(_)));
1676 }
1677
1678 #[test]
1679 fn skip_payload_without_reverter_is_execution_error() {
1680 let raw = RawCallResult::<EthEvmNetwork> {
1681 result: Bytes::from_static(MAGIC_SKIP),
1682 reverter: None,
1683 ..Default::default()
1684 };
1685
1686 let err = raw.into_evm_error(None);
1687 assert!(matches!(err, EvmError::Execution(_)));
1688 }
1689
1690 #[test]
1691 fn set_spec_id_updates_spec_dependent_cfg_state() {
1692 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1693 let mut executor = ExecutorBuilder::default().build(
1694 EvmEnvFor::<EthEvmNetwork>::default(),
1695 TxEnvFor::<EthEvmNetwork>::default(),
1696 backend,
1697 );
1698
1699 executor.evm_env_mut().cfg_env.set_spec_and_mainnet_gas_params(SpecId::HOMESTEAD);
1700 assert_eq!(
1701 executor.evm_env().cfg_env.gas_params(),
1702 &revm::context_interface::cfg::GasParams::new_spec(SpecId::HOMESTEAD),
1703 );
1704 assert!(!executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1705
1706 executor.set_spec_id(SpecId::AMSTERDAM);
1707
1708 assert_eq!(executor.spec_id(), SpecId::AMSTERDAM);
1709 assert_eq!(
1710 executor.evm_env().cfg_env.gas_params(),
1711 &revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM),
1712 );
1713 assert!(executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1714 }
1715
1716 #[test]
1717 fn calculate_stipend_uses_eip2780_transaction_context() {
1718 let caller = Address::repeat_byte(0x11);
1719 let recipient = Address::repeat_byte(0x22);
1720 let mut tx = TxEnv { caller, kind: TxKind::Call(recipient), ..Default::default() };
1721
1722 assert_eq!(
1723 calculate_stipend(&tx, SpecId::AMSTERDAM, true),
1724 revm::primitives::eip2780::TX_BASE_COST
1725 + revm::primitives::eip8038::COLD_ACCOUNT_ACCESS
1726 );
1727 assert_eq!(
1728 calculate_stipend(&tx, SpecId::AMSTERDAM, false),
1729 revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM).tx_base_stipend()
1730 );
1731
1732 tx.kind = TxKind::Call(caller);
1733 assert_eq!(
1734 calculate_stipend(&tx, SpecId::AMSTERDAM, true),
1735 revm::primitives::eip2780::TX_BASE_COST
1736 );
1737 }
1738
1739 #[test]
1740 fn amsterdam_intercepted_create_refunds_state_gas() {
1741 let cheats_config = Arc::new(CheatsConfig::new(
1742 &Config::default(),
1743 EvmOpts::default(),
1744 None,
1745 None,
1746 None,
1747 false,
1748 ));
1749 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1750 let mut executor = ExecutorBuilder::default()
1751 .inspectors(|stack| stack.cheatcodes(cheats_config))
1752 .spec_id(SpecId::AMSTERDAM)
1753 .gas_limit(1_000_000)
1754 .build(EvmEnv::default(), TxEnv::default(), backend);
1755
1756 let target = Address::repeat_byte(0x11);
1757 executor
1759 .set_code(
1760 target,
1761 Bytecode::new_raw(Bytes::from_static(&[0x5f, 0x5f, 0x5f, 0xf0, 0x50, 0x00])),
1762 )
1763 .unwrap();
1764 executor.inspector_mut().cheatcodes.as_mut().unwrap().intercept_next_create_call = true;
1765
1766 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1767
1768 assert!(!result.reverted);
1769 assert!(
1770 result.gas_used
1771 < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
1772 .create_state_gas(),
1773 "failed CREATE retained its conditional state-gas charge"
1774 );
1775 }
1776
1777 #[test]
1778 fn amsterdam_mocked_call_revert_refunds_state_gas() {
1779 let cheats_config = Arc::new(CheatsConfig::new(
1780 &Config::default(),
1781 EvmOpts::default(),
1782 None,
1783 None,
1784 None,
1785 false,
1786 ));
1787 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1788 let mut executor = ExecutorBuilder::default()
1789 .inspectors(|stack| stack.cheatcodes(cheats_config))
1790 .spec_id(SpecId::AMSTERDAM)
1791 .gas_limit(1_000_000)
1792 .build(EvmEnv::default(), TxEnv::default(), backend);
1793
1794 let target = Address::repeat_byte(0x11);
1795 let mocked = Address::repeat_byte(0x22);
1796 executor
1797 .transact_raw(
1798 CALLER,
1799 CHEATCODE_ADDRESS,
1800 mockCallRevert_1Call {
1801 callee: mocked,
1802 msgValue: U256::from(1),
1803 data: Bytes::new(),
1804 revertData: Bytes::new(),
1805 }
1806 .abi_encode()
1807 .into(),
1808 U256::ZERO,
1809 )
1810 .unwrap();
1811 executor.set_code(mocked, Bytecode::default()).unwrap();
1812 executor.set_balance(target, U256::from(1)).unwrap();
1813
1814 let mut code = vec![0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x01, 0x73];
1816 code.extend_from_slice(mocked.as_slice());
1817 code.extend_from_slice(&[0x5a, 0xf1, 0x50, 0x00]);
1818 executor.set_code(target, Bytecode::new_raw(code.into())).unwrap();
1819
1820 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1821
1822 assert!(!result.reverted);
1823 assert!(
1824 result.gas_used
1825 < revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM)
1826 .new_account_state_gas(),
1827 "reverted mocked CALL retained its conditional state-gas charge"
1828 );
1829 }
1830
1831 #[test]
1832 fn set_trace_requirements_replaces_trace_mode_between_transactions() {
1833 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1834 let mut executor = ExecutorBuilder::default().gas_limit(1 << 20).build(
1835 EvmEnvFor::<EthEvmNetwork>::default(),
1836 TxEnvFor::<EthEvmNetwork>::default(),
1837 backend,
1838 );
1839 executor.evm_env_mut().cfg_env.disable_nonce_check = true;
1840 let target = Address::repeat_byte(0x11);
1841 executor
1843 .set_code(
1844 target,
1845 Bytecode::new_raw(Bytes::from_static(&[
1846 0x60, 0x04, 0x56, 0x00, 0x5b, 0x60, 0x01, 0x60, 0x00, 0x55, 0x00,
1847 ])),
1848 )
1849 .unwrap();
1850
1851 let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1852 assert!(untraced.traces.is_none());
1853
1854 executor.set_trace_requirements(TraceRequirements::none().with_debug(true));
1855 let debug = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1856 let debug_steps = &debug.traces.as_ref().unwrap().nodes()[0].trace.steps;
1857 assert_eq!(debug_steps.len(), 7);
1858 assert!(debug_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
1859
1860 executor.set_trace_requirements(
1861 TraceRequirements::none().with_decode_internal(InternalTraceMode::Full),
1862 );
1863 let internal = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1864 let internal_steps = &internal.traces.as_ref().unwrap().nodes()[0].trace.steps;
1865 assert_eq!(internal_steps.len(), 2);
1866 assert!(internal_steps.iter().all(|step| step.stack.is_some() && step.memory.is_some()));
1867
1868 executor.set_trace_requirements(TraceRequirements::none());
1869 let untraced = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1870 assert!(untraced.traces.is_none());
1871 }
1872
1873 #[test]
1874 fn early_exit_interrupts_active_evm_execution() {
1875 const GAS_LIMIT: u64 = 1 << 24;
1876 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1877 let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
1878 EvmEnvFor::<EthEvmNetwork>::default(),
1879 TxEnvFor::<EthEvmNetwork>::default(),
1880 backend,
1881 );
1882 let early_exit = EarlyExit::new(false);
1883 executor.inspector_mut().set_early_exit(early_exit.clone());
1884
1885 let target = Address::repeat_byte(0x11);
1886 executor
1888 .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
1889 .unwrap();
1890
1891 let (started_tx, started_rx) = mpsc::channel();
1892 let (result_tx, result_rx) = mpsc::channel();
1893 let handle = thread::spawn(move || {
1894 started_tx.send(()).unwrap();
1895 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO);
1896 let _ = result_tx.send(result);
1897 });
1898
1899 started_rx.recv().unwrap();
1900 thread::sleep(Duration::from_millis(1));
1901 early_exit.record_ctrl_c();
1902
1903 let result = result_rx.recv_timeout(Duration::from_secs(1));
1904 handle.join().unwrap();
1905 let result = result.expect("active EVM execution did not observe early exit").unwrap();
1906 assert!(result.execution_cancelled);
1907 assert!(!result.reverted);
1908 assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
1909 assert!(result.gas_used > 21_000, "interrupt fired before EVM execution started");
1910 assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of exiting");
1911 }
1912
1913 #[test]
1914 fn completed_execution_is_not_retroactively_cancelled() {
1915 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1916 let mut executor = ExecutorBuilder::default().gas_limit(1 << 24).build(
1917 EvmEnvFor::<EthEvmNetwork>::default(),
1918 TxEnvFor::<EthEvmNetwork>::default(),
1919 backend,
1920 );
1921 let early_exit = EarlyExit::new(false);
1922 executor.inspector_mut().set_early_exit(early_exit.clone());
1923
1924 let target = Address::repeat_byte(0x11);
1925 executor.set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x00]))).unwrap();
1926 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1927 early_exit.record_ctrl_c();
1928
1929 assert!(!result.execution_cancelled);
1930 assert!(!result.reverted);
1931 }
1932
1933 #[test]
1934 fn campaign_deadline_interrupts_active_evm_execution() {
1935 const GAS_LIMIT: u64 = 1 << 24;
1936 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1937 let mut executor = ExecutorBuilder::default().gas_limit(GAS_LIMIT).build(
1938 EvmEnvFor::<EthEvmNetwork>::default(),
1939 TxEnvFor::<EthEvmNetwork>::default(),
1940 backend,
1941 );
1942 let cancellation = EvmExecutionCancellation::campaign(
1943 EarlyExit::new(false),
1944 Arc::new(AtomicBool::new(false)),
1945 Some(Instant::now()),
1946 );
1947 executor.inspector_mut().set_execution_cancellation(cancellation);
1948
1949 let target = Address::repeat_byte(0x11);
1950 executor
1951 .set_code(target, Bytecode::new_raw(Bytes::from_static(&[0x5b, 0x60, 0x00, 0x56])))
1952 .unwrap();
1953
1954 let result = executor.transact_raw(CALLER, target, Bytes::new(), U256::ZERO).unwrap();
1955 assert!(result.execution_cancelled);
1956 assert!(!result.reverted);
1957 assert_eq!(result.exit_reason, Some(InstructionResult::Stop));
1958 assert!(result.gas_used < GAS_LIMIT, "execution ran out of gas instead of timing out");
1959 }
1960
1961 #[test]
1962 fn beacon_root_system_call_does_not_persist_system_address() {
1963 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1964 let mut executor = ExecutorBuilder::default().spec_id(SpecId::CANCUN).build(
1965 EvmEnvFor::<EthEvmNetwork>::default(),
1966 TxEnvFor::<EthEvmNetwork>::default(),
1967 backend,
1968 );
1969 let before = executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap();
1970
1971 executor.apply_beacon_root(B256::repeat_byte(0x11)).unwrap();
1972
1973 assert_eq!(
1974 executor.backend().basic_ref(SYSTEM_ADDRESS).unwrap(),
1975 before,
1976 "EIP-4788 system calls must not persist the system caller account",
1977 );
1978 }
1979
1980 #[test]
1995 fn pre_override_blob_hashes_restored_on_revert_to_state() {
1996 let cheats_config = Arc::new(CheatsConfig::new(
1997 &Config::default(),
1998 EvmOpts::default(),
1999 None,
2000 None,
2001 None,
2002 false,
2003 ));
2004
2005 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
2006 let mut executor = ExecutorBuilder::default()
2007 .inspectors(|stack| stack.cheatcodes(cheats_config))
2008 .spec_id(SpecId::CANCUN)
2009 .build(EvmEnv::default(), TxEnv::default(), backend);
2010
2011 let original: Vec<B256> = vec![B256::repeat_byte(0x11), B256::repeat_byte(0x22)];
2012 executor.tx_env_mut().set_blob_hashes(original.clone());
2013
2014 let snap_result = executor
2015 .transact_raw(
2016 CALLER,
2017 CHEATCODE_ADDRESS,
2018 snapshotStateCall {}.abi_encode().into(),
2019 U256::ZERO,
2020 )
2021 .expect("snapshotState failed");
2022 assert!(!snap_result.reverted, "snapshotState reverted unexpectedly");
2023 let snapshot_id = U256::from_be_slice(&snap_result.result[..32]);
2024
2025 let new_hashes = vec![B256::repeat_byte(0x33)];
2026 let blob_result = executor
2027 .transact_raw(
2028 CALLER,
2029 CHEATCODE_ADDRESS,
2030 blobhashesCall { hashes: new_hashes }.abi_encode().into(),
2031 U256::ZERO,
2032 )
2033 .expect("blobhashes failed");
2034 assert!(!blob_result.reverted, "blobhashes reverted unexpectedly");
2035
2036 let revert_result = executor
2037 .transact_raw(
2038 CALLER,
2039 CHEATCODE_ADDRESS,
2040 revertToStateCall { snapshotId: snapshot_id }.abi_encode().into(),
2041 U256::ZERO,
2042 )
2043 .expect("revertToState failed");
2044 assert!(!revert_result.reverted, "revertToState reverted unexpectedly");
2045
2046 assert_eq!(
2047 revert_result.tx_env.blob_hashes, original,
2048 "pre_override_blob_hashes must be restored to original non-empty hashes, not []",
2049 );
2050 }
2051}