1use crate::inspectors::{
10 Cheatcodes, CmpOperands, EdgeCoverage, EdgeIndexMap, InspectorData, InspectorStack,
11 cheatcodes::BroadcastableTransactions,
12};
13use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
14use alloy_json_abi::Function;
15use alloy_primitives::{
16 Address, Bytes, Log, TxKind, U256, keccak256,
17 map::{AddressHashMap, HashMap},
18};
19use alloy_sol_types::{SolCall, sol};
20use foundry_evm_core::{
21 EvmEnv, FoundryBlock, FoundryTransaction,
22 backend::{
23 Backend, BackendError, BackendResult, CowBackend, DatabaseError, DatabaseExt,
24 GLOBAL_FAIL_SLOT,
25 },
26 constants::{
27 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
28 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
29 },
30 decode::{RevertDecoder, SkipReason},
31 evm::{
32 EthEvmNetwork, EvmEnvFor, FoundryEvmNetwork, HaltReasonFor, IntoInstructionResult, SpecFor,
33 TxEnvFor,
34 },
35 utils::StateChangeset,
36};
37use foundry_evm_coverage::HitMaps;
38use foundry_evm_traces::{SparsedTraceArena, TraceMode};
39use revm::{
40 bytecode::Bytecode,
41 context::Transaction,
42 context_interface::{
43 result::{ExecutionResult, Output, ResultAndState},
44 transaction::SignedAuthorization,
45 },
46 database::{DatabaseCommit, DatabaseRef},
47 interpreter::{InstructionResult, return_ok},
48};
49use sancov::SancovGuard;
50use std::{
51 borrow::Cow,
52 sync::{
53 Arc,
54 atomic::{AtomicBool, Ordering},
55 },
56 time::{Duration, Instant},
57};
58
59mod builder;
60pub use builder::ExecutorBuilder;
61
62pub mod fuzz;
63pub use fuzz::FuzzedExecutor;
64
65pub mod invariant;
66pub use invariant::InvariantExecutor;
67
68mod corpus;
69mod corpus_io;
70mod sancov;
71mod showmap;
72mod trace;
73
74pub use corpus::DynamicTargetCtx;
75pub use showmap::{ShowmapDomain, ShowmapOpts, ShowmapStats, replay_corpus_to_showmap};
76pub use trace::TracingExecutor;
77
78const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
79
80sol! {
81 interface ITest {
82 function setUp() external;
83 function failed() external view returns (bool failed);
84
85 #[derive(Default)]
86 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
87 }
88}
89
90#[derive(Clone, Debug)]
102pub struct Executor<FEN: FoundryEvmNetwork> {
103 backend: Arc<Backend<FEN>>,
112 evm_env: EvmEnvFor<FEN>,
114 tx_env: TxEnvFor<FEN>,
116 inspector: InspectorStack<FEN>,
118 gas_limit: u64,
120 legacy_assertions: bool,
122}
123
124impl<FEN: FoundryEvmNetwork> Executor<FEN> {
125 #[inline]
127 pub fn new(
128 mut backend: Backend<FEN>,
129 evm_env: EvmEnvFor<FEN>,
130 tx_env: TxEnvFor<FEN>,
131 inspector: InspectorStack<FEN>,
132 gas_limit: u64,
133 legacy_assertions: bool,
134 ) -> Self {
135 backend.insert_account_info(
138 CHEATCODE_ADDRESS,
139 revm::state::AccountInfo {
140 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
141 code_hash: CHEATCODE_CONTRACT_HASH,
144 ..Default::default()
145 },
146 );
147
148 Self {
149 backend: Arc::new(backend),
150 evm_env,
151 tx_env,
152 inspector,
153 gas_limit,
154 legacy_assertions,
155 }
156 }
157
158 fn clone_with_backend(&self, backend: Backend<FEN>) -> Self {
159 let evm_env = self.evm_env.clone();
160 Self {
161 backend: Arc::new(backend),
162 evm_env,
163 tx_env: self.tx_env.clone(),
164 inspector: self.inspector().clone(),
165 gas_limit: self.gas_limit,
166 legacy_assertions: self.legacy_assertions,
167 }
168 }
169
170 pub fn backend(&self) -> &Backend<FEN> {
172 &self.backend
173 }
174
175 pub fn backend_mut(&mut self) -> &mut Backend<FEN> {
180 Arc::make_mut(&mut self.backend)
181 }
182
183 pub const fn evm_env(&self) -> &EvmEnvFor<FEN> {
185 &self.evm_env
186 }
187
188 pub const fn evm_env_mut(&mut self) -> &mut EvmEnvFor<FEN> {
190 &mut self.evm_env
191 }
192
193 pub const fn tx_env(&self) -> &TxEnvFor<FEN> {
195 &self.tx_env
196 }
197
198 pub const fn tx_env_mut(&mut self) -> &mut TxEnvFor<FEN> {
200 &mut self.tx_env
201 }
202
203 pub const fn inspector(&self) -> &InspectorStack<FEN> {
205 &self.inspector
206 }
207
208 pub const fn inspector_mut(&mut self) -> &mut InspectorStack<FEN> {
210 &mut self.inspector
211 }
212
213 pub const fn spec_id(&self) -> SpecFor<FEN> {
215 self.evm_env.cfg_env.spec
216 }
217
218 pub fn set_spec_id(&mut self, spec_id: SpecFor<FEN>) {
220 self.evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
221 }
222
223 pub const fn gas_limit(&self) -> u64 {
228 self.gas_limit
229 }
230
231 pub const fn set_gas_limit(&mut self, gas_limit: u64) {
233 self.gas_limit = gas_limit;
234 }
235
236 pub const fn legacy_assertions(&self) -> bool {
239 self.legacy_assertions
240 }
241
242 pub const fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
245 self.legacy_assertions = legacy_assertions;
246 }
247
248 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
250 trace!("deploying local create2 deployer");
251 let create2_deployer_account = self
252 .backend()
253 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
254 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
255
256 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
258 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
259
260 let initial_balance = self.get_balance(creator)?;
262 self.set_balance(creator, U256::MAX)?;
263
264 let res =
265 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
266 trace!(create2=?res.address, "deployed local create2 deployer");
267
268 self.set_balance(creator, initial_balance)?;
269 }
270 Ok(())
271 }
272
273 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
275 trace!(?address, ?amount, "setting account balance");
276 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
277 account.balance = amount;
278 self.backend_mut().insert_account_info(address, account);
279 Ok(())
280 }
281
282 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
284 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
285 }
286
287 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
289 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
290 account.nonce = nonce;
291 self.backend_mut().insert_account_info(address, account);
292 self.tx_env_mut().set_nonce(nonce);
293 Ok(())
294 }
295
296 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
298 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
299 }
300
301 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
303 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
304 account.code_hash = keccak256(code.original_byte_slice());
305 account.code = Some(code);
306 self.backend_mut().insert_account_info(address, account);
307 Ok(())
308 }
309
310 pub fn set_storage(
312 &mut self,
313 address: Address,
314 storage: HashMap<U256, U256>,
315 ) -> BackendResult<()> {
316 self.backend_mut().replace_account_storage(address, storage)?;
317 Ok(())
318 }
319
320 pub fn set_storage_slot(
322 &mut self,
323 address: Address,
324 slot: U256,
325 value: U256,
326 ) -> BackendResult<()> {
327 self.backend_mut().insert_account_storage(address, slot, value)?;
328 Ok(())
329 }
330
331 pub fn apply_prestate_trace(
337 &mut self,
338 prestate: std::collections::BTreeMap<Address, alloy_rpc_types::trace::geth::AccountState>,
339 ) -> eyre::Result<()> {
340 let backend = self.backend_mut();
341 for (address, account_state) in prestate {
342 let code = account_state.code.map(Bytecode::new_raw).unwrap_or_default();
343 let info = revm::state::AccountInfo {
344 nonce: account_state.nonce.unwrap_or_default(),
345 balance: account_state.balance.unwrap_or_default(),
346 code_hash: keccak256(code.original_byte_slice()),
347 code: Some(code),
348 account_id: Default::default(),
349 };
350 backend.insert_account_info(address, info);
351
352 for (slot, value) in account_state.storage {
353 let slot = U256::from_be_bytes(slot.0);
354 let value = U256::from_be_bytes(value.0);
355 backend.insert_account_storage(address, slot, value)?;
356 }
357 }
358 Ok(())
359 }
360
361 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
363 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
364 }
365
366 #[inline]
367 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
368 self.inspector_mut().tracing(mode);
369 self
370 }
371
372 #[inline]
373 pub fn set_script_execution(&mut self, script_address: Address) {
374 self.inspector_mut().script(script_address);
375 }
376
377 #[inline]
378 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
379 self.inspector_mut().print(trace_printer);
380 self
381 }
382
383 #[inline]
384 pub fn create2_deployer(&self) -> Address {
385 self.inspector().create2_deployer
386 }
387
388 pub fn deploy(
393 &mut self,
394 from: Address,
395 code: Bytes,
396 value: U256,
397 rd: Option<&RevertDecoder>,
398 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
399 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Create, code, value);
400 self.deploy_with_env(evm_env, tx_env, rd)
401 }
402
403 #[instrument(name = "deploy", level = "debug", skip_all)]
410 pub fn deploy_with_env(
411 &mut self,
412 evm_env: EvmEnvFor<FEN>,
413 tx_env: TxEnvFor<FEN>,
414 rd: Option<&RevertDecoder>,
415 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
416 assert!(
417 matches!(tx_env.kind(), TxKind::Create),
418 "Expected create transaction, got {:?}",
419 tx_env.kind()
420 );
421 trace!(sender=%tx_env.caller(), "deploying contract");
422
423 let mut result = self.transact_with_env(evm_env, tx_env)?;
424 result = result.into_result(rd)?;
425 let Some(Output::Create(_, Some(address))) = result.out else {
426 panic!("Deployment succeeded, but no address was returned: {result:#?}");
427 };
428
429 self.backend_mut().add_persistent_account(address);
432
433 trace!(%address, "deployed contract");
434
435 Ok(DeployResult { raw: result, address })
436 }
437
438 #[instrument(name = "setup", level = "debug", skip_all)]
445 pub fn setup(
446 &mut self,
447 from: Option<Address>,
448 to: Address,
449 rd: Option<&RevertDecoder>,
450 ) -> Result<RawCallResult<FEN>, EvmError<FEN>> {
451 trace!(?from, ?to, "setting up contract");
452
453 let from = from.unwrap_or(CALLER);
454 self.backend_mut().set_test_contract(to).set_caller(from);
455 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
456 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
457 res = res.into_result(rd)?;
458
459 self.evm_env_mut().block_env = res.evm_env.block_env.clone();
461 self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id;
463
464 let success =
465 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
466 if !success {
467 return Err(res.into_execution_error("execution error".to_string()).into());
468 }
469
470 Ok(res)
471 }
472
473 pub fn call(
475 &self,
476 from: Address,
477 to: Address,
478 func: &Function,
479 args: &[DynSolValue],
480 value: U256,
481 rd: Option<&RevertDecoder>,
482 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
483 let calldata = Bytes::from(func.abi_encode_input(args)?);
484 let result = self.call_raw(from, to, calldata, value)?;
485 result.into_decoded_result(func, rd)
486 }
487
488 pub fn call_sol<C: SolCall>(
490 &self,
491 from: Address,
492 to: Address,
493 args: &C,
494 value: U256,
495 rd: Option<&RevertDecoder>,
496 ) -> Result<CallResult<C::Return, FEN>, EvmError<FEN>> {
497 let calldata = Bytes::from(args.abi_encode());
498 let mut raw = self.call_raw(from, to, calldata, value)?;
499 raw = raw.into_result(rd)?;
500 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
501 }
502
503 pub fn transact(
505 &mut self,
506 from: Address,
507 to: Address,
508 func: &Function,
509 args: &[DynSolValue],
510 value: U256,
511 rd: Option<&RevertDecoder>,
512 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
513 let calldata = Bytes::from(func.abi_encode_input(args)?);
514 let result = self.transact_raw(from, to, calldata, value)?;
515 result.into_decoded_result(func, rd)
516 }
517
518 pub fn call_raw(
520 &self,
521 from: Address,
522 to: Address,
523 calldata: Bytes,
524 value: U256,
525 ) -> eyre::Result<RawCallResult<FEN>> {
526 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
527 self.call_with_env(evm_env, tx_env)
528 }
529
530 pub fn call_raw_with_authorization(
533 &mut self,
534 from: Address,
535 to: Address,
536 calldata: Bytes,
537 value: U256,
538 authorization_list: Vec<SignedAuthorization>,
539 ) -> eyre::Result<RawCallResult<FEN>> {
540 let (evm_env, mut tx_env) = self.build_test_env(from, to.into(), calldata, value);
541 tx_env.set_signed_authorization(authorization_list);
542 tx_env.set_tx_type(4);
543 self.call_with_env(evm_env, tx_env)
544 }
545
546 pub fn transact_raw(
548 &mut self,
549 from: Address,
550 to: Address,
551 calldata: Bytes,
552 value: U256,
553 ) -> eyre::Result<RawCallResult<FEN>> {
554 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
555 self.transact_with_env(evm_env, tx_env)
556 }
557
558 pub fn transact_raw_with_authorization(
561 &mut self,
562 from: Address,
563 to: Address,
564 calldata: Bytes,
565 value: U256,
566 authorization_list: Vec<SignedAuthorization>,
567 ) -> eyre::Result<RawCallResult<FEN>> {
568 let (evm_env, mut tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
569 tx_env.set_signed_authorization(authorization_list);
570 tx_env.set_tx_type(4);
571 self.transact_with_env(evm_env, tx_env)
572 }
573
574 #[instrument(name = "call", level = "debug", skip_all)]
578 pub fn call_with_env(
579 &self,
580 mut evm_env: EvmEnvFor<FEN>,
581 mut tx_env: TxEnvFor<FEN>,
582 ) -> eyre::Result<RawCallResult<FEN>> {
583 let mut stack = self.inspector().clone();
584 let sancov_edges = stack.inner.sancov_edges;
585 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
586 let sancov_active = sancov_edges || sancov_trace_cmp;
587 let mut backend = CowBackend::new_borrowed(self.backend());
588 let result = {
589 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
590 backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?
591 };
592 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
593 let mut result = convert_executed_result(
594 evm_env,
595 tx_env,
596 stack,
597 result,
598 &backend,
599 has_state_snapshot_failure,
600 )?;
601 if sancov_edges {
602 SancovGuard::append_edges_into(&mut result);
603 }
604 if sancov_trace_cmp {
605 SancovGuard::drain_cmp_into(&mut result);
606 }
607 Ok(result)
608 }
609
610 #[instrument(name = "transact", level = "debug", skip_all)]
612 pub fn transact_with_env(
613 &mut self,
614 mut evm_env: EvmEnvFor<FEN>,
615 mut tx_env: TxEnvFor<FEN>,
616 ) -> eyre::Result<RawCallResult<FEN>> {
617 let mut stack = self.inspector().clone();
618 let sancov_edges = stack.inner.sancov_edges;
619 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
620 let sancov_active = sancov_edges || sancov_trace_cmp;
621 let backend = self.backend_mut();
622 let result = {
623 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
624 backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?
625 };
626 let has_state_snapshot_failure = backend.has_state_snapshot_failure();
627 let mut result = convert_executed_result(
628 evm_env,
629 tx_env,
630 stack,
631 result,
632 &*backend,
633 has_state_snapshot_failure,
634 )?;
635 if sancov_edges {
636 SancovGuard::append_edges_into(&mut result);
637 }
638 if sancov_trace_cmp {
639 SancovGuard::drain_cmp_into(&mut result);
640 }
641 self.commit(&mut result);
642 Ok(result)
643 }
644
645 #[instrument(name = "commit", level = "debug", skip_all)]
650 fn commit(&mut self, result: &mut RawCallResult<FEN>) {
651 self.backend_mut().commit(result.state_changeset.clone());
653
654 self.inspector_mut().cheatcodes = result.cheatcodes.take();
656 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
657 cheats.broadcastable_transactions.clear();
659 cheats.ignored_traces.ignored.clear();
660
661 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
664 *last_pause_call = (0, 0);
665 }
666 }
667
668 self.inspector_mut().set_block(result.evm_env.block_env.clone());
670 self.inspector_mut().set_gas_price(result.tx_env.gas_price());
671 }
672
673 pub fn is_raw_call_mut_success(
678 &self,
679 address: Address,
680 call_result: &mut RawCallResult<FEN>,
681 should_fail: bool,
682 ) -> bool {
683 self.is_raw_call_success(
684 address,
685 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
686 call_result,
687 should_fail,
688 )
689 }
690
691 pub fn is_raw_call_success(
695 &self,
696 address: Address,
697 state_changeset: Cow<'_, StateChangeset>,
698 call_result: &RawCallResult<FEN>,
699 should_fail: bool,
700 ) -> bool {
701 if call_result.has_state_snapshot_failure {
702 return should_fail;
704 }
705 self.is_success(address, call_result.reverted, state_changeset, should_fail)
706 }
707
708 pub fn is_raw_call_mut_success_handler_gate(
712 &self,
713 address: Address,
714 call_result: &mut RawCallResult<FEN>,
715 ) -> bool {
716 if call_result.has_state_snapshot_failure {
717 return false;
718 }
719 let state_changeset = std::mem::take(&mut call_result.state_changeset);
720 self.is_success_handler_gate(address, call_result.reverted, Cow::Owned(state_changeset))
721 }
722
723 pub fn is_success(
745 &self,
746 address: Address,
747 reverted: bool,
748 state_changeset: Cow<'_, StateChangeset>,
749 should_fail: bool,
750 ) -> bool {
751 let success = self.is_success_raw(address, reverted, state_changeset, false);
752 should_fail ^ success
753 }
754
755 pub fn is_success_handler_gate(
761 &self,
762 address: Address,
763 reverted: bool,
764 state_changeset: Cow<'_, StateChangeset>,
765 ) -> bool {
766 self.is_success_raw(address, reverted, state_changeset, true)
767 }
768
769 #[instrument(name = "is_success", level = "debug", skip_all)]
770 fn is_success_raw(
771 &self,
772 address: Address,
773 reverted: bool,
774 state_changeset: Cow<'_, StateChangeset>,
775 pending_global_failure_only: bool,
776 ) -> bool {
777 if reverted {
779 return false;
780 }
781
782 if self.backend().has_state_snapshot_failure() {
784 return false;
785 }
786
787 let global_failed = if pending_global_failure_only {
792 Self::has_pending_global_failure(&state_changeset)
793 } else {
794 self.has_global_failure(&state_changeset)
795 };
796 if global_failed {
797 return false;
798 }
799
800 if !self.legacy_assertions {
801 return true;
802 }
803
804 {
806 let mut backend = self.backend().clone_empty();
808
809 for address in [address, CHEATCODE_ADDRESS] {
812 let Ok(acc) = self.backend().basic_ref(address) else { return false };
813 backend.insert_account_info(address, acc.unwrap_or_default());
814 }
815
816 backend.commit(state_changeset.into_owned());
821
822 let executor = self.clone_with_backend(backend);
824 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
825 match call {
826 Ok(CallResult { raw: _, decoded_result: failed }) => {
827 trace!(failed, "DSTest::failed()");
828 !failed
829 }
830 Err(err) => {
831 trace!(%err, "failed to call DSTest::failed()");
832 true
833 }
834 }
835 }
836 }
837
838 pub fn has_pending_global_failure(state_changeset: &StateChangeset) -> bool {
841 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
842 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
843 && !failed_slot.present_value().is_zero()
844 {
845 return true;
846 }
847
848 false
849 }
850
851 pub fn has_global_failure(&self, state_changeset: &StateChangeset) -> bool {
854 if Self::has_pending_global_failure(state_changeset) {
855 return true;
856 }
857
858 self.backend()
859 .storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
860 .is_ok_and(|failed_slot| !failed_slot.is_zero())
861 }
862
863 fn build_test_env(
868 &self,
869 caller: Address,
870 kind: TxKind,
871 data: Bytes,
872 value: U256,
873 ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>) {
874 let mut cfg_env = self.evm_env.cfg_env.clone();
875 cfg_env.spec = self.spec_id();
876
877 let mut block_env = self.evm_env.block_env.clone();
881 block_env.set_basefee(0);
882 block_env.set_gas_limit(self.gas_limit);
883
884 let mut tx_env = self.tx_env.clone();
885 tx_env.set_caller(caller);
886 tx_env.set_kind(kind);
887 tx_env.set_data(data);
888 tx_env.set_value(value);
889 tx_env.set_gas_price(0);
891 tx_env.set_gas_priority_fee(None);
892 tx_env.set_gas_limit(self.gas_limit);
893 tx_env.set_chain_id(Some(self.evm_env.cfg_env.chain_id));
894
895 (EvmEnv { cfg_env, block_env }, tx_env)
896 }
897
898 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
899 where
900 C::Return: Default,
901 {
902 self.call_sol(CALLER, to, args, U256::ZERO, None)
903 .map(|c| c.decoded_result)
904 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
905 .unwrap_or_default()
906 }
907}
908
909#[derive(Debug, thiserror::Error)]
911#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
912pub struct ExecutionErr<FEN: FoundryEvmNetwork = EthEvmNetwork> {
913 pub raw: RawCallResult<FEN>,
915 pub reason: String,
917}
918
919impl<FEN: FoundryEvmNetwork> std::ops::Deref for ExecutionErr<FEN> {
920 type Target = RawCallResult<FEN>;
921
922 #[inline]
923 fn deref(&self) -> &Self::Target {
924 &self.raw
925 }
926}
927
928impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for ExecutionErr<FEN> {
929 #[inline]
930 fn deref_mut(&mut self) -> &mut Self::Target {
931 &mut self.raw
932 }
933}
934
935#[derive(Debug, thiserror::Error)]
936pub enum EvmError<FEN: FoundryEvmNetwork = EthEvmNetwork> {
937 #[error(transparent)]
939 Execution(Box<ExecutionErr<FEN>>),
940 #[error(transparent)]
942 Abi(#[from] alloy_dyn_abi::Error),
943 #[error("{0}")]
945 Skip(SkipReason),
946 #[error("{0}")]
948 Eyre(
949 #[from]
950 #[source]
951 eyre::Report,
952 ),
953}
954
955impl<FEN: FoundryEvmNetwork> From<ExecutionErr<FEN>> for EvmError<FEN> {
956 fn from(err: ExecutionErr<FEN>) -> Self {
957 Self::Execution(Box::new(err))
958 }
959}
960
961impl<FEN: FoundryEvmNetwork> From<alloy_sol_types::Error> for EvmError<FEN> {
962 fn from(err: alloy_sol_types::Error) -> Self {
963 Self::Abi(err.into())
964 }
965}
966
967#[derive(Debug)]
969pub struct DeployResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
970 pub raw: RawCallResult<FEN>,
972 pub address: Address,
974}
975
976impl<FEN: FoundryEvmNetwork> std::ops::Deref for DeployResult<FEN> {
977 type Target = RawCallResult<FEN>;
978
979 #[inline]
980 fn deref(&self) -> &Self::Target {
981 &self.raw
982 }
983}
984
985impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for DeployResult<FEN> {
986 #[inline]
987 fn deref_mut(&mut self) -> &mut Self::Target {
988 &mut self.raw
989 }
990}
991
992impl<FEN: FoundryEvmNetwork> From<DeployResult<FEN>> for RawCallResult<FEN> {
993 fn from(d: DeployResult<FEN>) -> Self {
994 d.raw
995 }
996}
997
998#[derive(Debug)]
1000pub struct RawCallResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
1001 pub exit_reason: Option<InstructionResult>,
1003 pub reverted: bool,
1005 pub has_state_snapshot_failure: bool,
1010 pub result: Bytes,
1012 pub gas_used: u64,
1014 pub gas_refunded: u64,
1016 pub stipend: u64,
1018 pub logs: Vec<Log>,
1020 pub labels: AddressHashMap<String>,
1022 pub traces: Option<SparsedTraceArena>,
1024 pub debug_bytecodes: AddressHashMap<Bytes>,
1026 pub line_coverage: Option<HitMaps>,
1028 pub edge_coverage: Option<EdgeCoverage>,
1030 pub evm_cmp_values: Option<Vec<CmpOperands>>,
1032 pub sancov_coverage: Option<Vec<u8>>,
1035 pub sancov_cmp_values: Option<Vec<foundry_evm_sancov::CmpSample>>,
1037 pub transactions: Option<BroadcastableTransactions<FEN::Network>>,
1039 pub state_changeset: StateChangeset,
1041 pub evm_env: EvmEnvFor<FEN>,
1043 pub tx_env: TxEnvFor<FEN>,
1045 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
1047 pub out: Option<Output>,
1049 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
1051 pub reverter: Option<Address>,
1052}
1053
1054impl<FEN: FoundryEvmNetwork> Default for RawCallResult<FEN> {
1055 fn default() -> Self {
1056 Self {
1057 exit_reason: None,
1058 reverted: false,
1059 has_state_snapshot_failure: false,
1060 result: Bytes::new(),
1061 gas_used: 0,
1062 gas_refunded: 0,
1063 stipend: 0,
1064 logs: Vec::new(),
1065 labels: HashMap::default(),
1066 traces: None,
1067 debug_bytecodes: HashMap::default(),
1068 line_coverage: None,
1069 edge_coverage: None,
1070 evm_cmp_values: None,
1071 sancov_coverage: None,
1072 sancov_cmp_values: None,
1073 transactions: None,
1074 state_changeset: HashMap::default(),
1075 evm_env: EvmEnv::default(),
1076 tx_env: TxEnvFor::<FEN>::default(),
1077 cheatcodes: Default::default(),
1078 out: None,
1079 chisel_state: None,
1080 reverter: None,
1081 }
1082 }
1083}
1084
1085impl<FEN: FoundryEvmNetwork> RawCallResult<FEN> {
1086 pub fn from_evm_result(r: Result<Self, EvmError<FEN>>) -> eyre::Result<(Self, Option<String>)> {
1088 match r {
1089 Ok(r) => Ok((r, None)),
1090 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
1091 Err(e) => Err(e.into()),
1092 }
1093 }
1094
1095 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError<FEN> {
1097 if self.reverter == Some(CHEATCODE_ADDRESS)
1098 && let Some(reason) = SkipReason::decode(&self.result)
1099 {
1100 return EvmError::Skip(reason);
1101 }
1102 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
1103 EvmError::Execution(Box::new(self.into_execution_error(reason)))
1104 }
1105
1106 pub const fn into_execution_error(self, reason: String) -> ExecutionErr<FEN> {
1108 ExecutionErr { raw: self, reason }
1109 }
1110
1111 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError<FEN>> {
1113 if let Some(reason) = self.exit_reason
1114 && reason.is_ok()
1115 {
1116 Ok(self)
1117 } else {
1118 Err(self.into_evm_error(rd))
1119 }
1120 }
1121
1122 pub fn into_decoded_result(
1124 mut self,
1125 func: &Function,
1126 rd: Option<&RevertDecoder>,
1127 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
1128 self = self.into_result(rd)?;
1129 let mut result = func.abi_decode_output(&self.result)?;
1130 let decoded_result =
1131 if result.len() == 1 { result.pop().unwrap() } else { DynSolValue::Tuple(result) };
1132 Ok(CallResult { raw: self, decoded_result })
1133 }
1134
1135 pub fn transactions(&self) -> Option<&BroadcastableTransactions<FEN::Network>> {
1137 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1138 }
1139
1140 pub fn merge_edge_coverage(
1142 &mut self,
1143 history_map: &mut Vec<u8>,
1144 edge_indices: &mut EdgeIndexMap,
1145 ) -> (bool, bool) {
1146 let mut new_coverage = false;
1147 let mut is_edge = false;
1148 if let Some(x) = &mut self.edge_coverage {
1149 match x {
1150 EdgeCoverage::Hash(x) => {
1151 if history_map.len() < x.len() {
1152 history_map.resize(x.len(), 0);
1153 }
1154 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1157 Self::merge_edge_count(*curr, hist, &mut new_coverage, &mut is_edge);
1158
1159 *curr = 0;
1161 }
1162 }
1163 EdgeCoverage::CollisionFree(hits) => {
1164 for hit in hits.drain(..) {
1165 let edge_index = edge_indices.edge_index(hit.edge);
1166 if history_map.len() <= edge_index {
1167 debug_assert_eq!(history_map.len(), edge_index);
1168 history_map.push(0);
1171 }
1172 Self::merge_edge_count(
1173 hit.count,
1174 &mut history_map[edge_index],
1175 &mut new_coverage,
1176 &mut is_edge,
1177 );
1178 }
1179 }
1180 }
1181 }
1182 (new_coverage, is_edge)
1183 }
1184
1185 const fn merge_edge_count(
1186 curr: u8,
1187 hist: &mut u8,
1188 new_coverage: &mut bool,
1189 is_edge: &mut bool,
1190 ) {
1191 let Some(bucket) = Self::bin_count(curr) else {
1192 return;
1193 };
1194
1195 if *hist < bucket {
1197 if *hist == 0 {
1198 *is_edge = true;
1200 }
1201 *hist = bucket;
1202 *new_coverage = true;
1203 }
1204 }
1205
1206 const fn bin_count(count: u8) -> Option<u8> {
1209 match count {
1210 0 => None,
1211 1 => Some(1),
1212 2 => Some(2),
1213 3 => Some(4),
1214 4..=7 => Some(8),
1215 8..=15 => Some(16),
1216 16..=31 => Some(32),
1217 32..=127 => Some(64),
1218 128..=255 => Some(128),
1219 }
1220 }
1221
1222 pub fn merge_sancov_coverage(&mut self, history_map: &mut Vec<u8>) -> (bool, bool) {
1225 let mut new_coverage = false;
1226 let mut is_edge = false;
1227 if let Some(x) = &mut self.sancov_coverage {
1228 if history_map.len() < x.len() {
1229 history_map.resize(x.len(), 0);
1230 }
1231 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1232 if *curr > 0 {
1233 if let Some(bucket) = Self::bin_count(*curr)
1234 && *hist < bucket
1235 {
1236 if *hist == 0 {
1237 is_edge = true;
1238 }
1239 *hist = bucket;
1240 new_coverage = true;
1241 }
1242 *curr = 0;
1243 }
1244 }
1245 }
1246 (new_coverage, is_edge)
1247 }
1248
1249 pub fn merge_all_coverage(
1252 &mut self,
1253 evm_history: &mut Vec<u8>,
1254 evm_edge_indices: &mut EdgeIndexMap,
1255 sancov_history: &mut Vec<u8>,
1256 ) -> (bool, bool) {
1257 let (new_evm, edge_evm) = self.merge_edge_coverage(evm_history, evm_edge_indices);
1258 let (new_san, edge_san) = self.merge_sancov_coverage(sancov_history);
1259 (new_evm || new_san, edge_evm || edge_san)
1260 }
1261}
1262
1263pub struct CallResult<T = DynSolValue, FEN: FoundryEvmNetwork = EthEvmNetwork> {
1265 pub raw: RawCallResult<FEN>,
1267 pub decoded_result: T,
1269}
1270
1271impl<T, FEN: FoundryEvmNetwork> std::ops::Deref for CallResult<T, FEN> {
1272 type Target = RawCallResult<FEN>;
1273
1274 #[inline]
1275 fn deref(&self) -> &Self::Target {
1276 &self.raw
1277 }
1278}
1279
1280impl<T, FEN: FoundryEvmNetwork> std::ops::DerefMut for CallResult<T, FEN> {
1281 #[inline]
1282 fn deref_mut(&mut self) -> &mut Self::Target {
1283 &mut self.raw
1284 }
1285}
1286
1287fn convert_executed_result<FEN: FoundryEvmNetwork>(
1289 evm_env: EvmEnvFor<FEN>,
1290 tx_env: TxEnvFor<FEN>,
1291 inspector: InspectorStack<FEN>,
1292 ResultAndState { result, state: state_changeset }: ResultAndState<HaltReasonFor<FEN>>,
1293 db: &dyn DatabaseRef<Error = DatabaseError>,
1294 has_state_snapshot_failure: bool,
1295) -> eyre::Result<RawCallResult<FEN>> {
1296 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1297 ExecutionResult::Success { reason, gas, output, logs } => {
1298 (reason.into(), gas.final_refunded(), gas.tx_gas_used(), Some(output), logs)
1299 }
1300 ExecutionResult::Revert { gas, output, logs } => {
1301 (InstructionResult::Revert, 0_u64, gas.tx_gas_used(), Some(Output::Call(output)), logs)
1302 }
1303 ExecutionResult::Halt { reason, gas, logs } => {
1304 (reason.into_instruction_result(), 0_u64, gas.tx_gas_used(), None, logs)
1305 }
1306 };
1307 let gas = revm::interpreter::gas::calculate_initial_tx_gas_for_tx(
1308 &tx_env,
1309 evm_env.cfg_env.spec.into(),
1310 );
1311
1312 let result = match &out {
1313 Some(Output::Call(data)) => data.clone(),
1314 _ => Bytes::new(),
1315 };
1316
1317 let InspectorData {
1318 mut logs,
1319 labels,
1320 traces,
1321 line_coverage,
1322 edge_coverage,
1323 evm_cmp_values,
1324 cheatcodes,
1325 chisel_state,
1326 reverter,
1327 } = inspector.collect();
1328 let debug_bytecodes = collect_debug_bytecodes(traces.as_ref(), db);
1329
1330 if logs.is_empty() {
1331 logs = exec_logs;
1332 }
1333
1334 let transactions = cheatcodes
1335 .as_ref()
1336 .map(|c| c.broadcastable_transactions.clone())
1337 .filter(|txs| !txs.is_empty());
1338
1339 Ok(RawCallResult {
1340 exit_reason: Some(exit_reason),
1341 reverted: !matches!(exit_reason, return_ok!()),
1342 has_state_snapshot_failure,
1343 result,
1344 gas_used,
1345 gas_refunded,
1346 stipend: gas.initial_total_gas(),
1347 logs,
1348 labels,
1349 traces,
1350 debug_bytecodes,
1351 line_coverage,
1352 edge_coverage,
1353 evm_cmp_values,
1354 sancov_coverage: None,
1355 sancov_cmp_values: None,
1356 transactions,
1357 state_changeset,
1358 evm_env,
1359 tx_env,
1360 cheatcodes,
1361 out,
1362 chisel_state,
1363 reverter,
1364 })
1365}
1366
1367fn collect_debug_bytecodes(
1368 traces: Option<&SparsedTraceArena>,
1369 db: &dyn DatabaseRef<Error = DatabaseError>,
1370) -> AddressHashMap<Bytes> {
1371 let mut bytecodes = HashMap::default();
1372 let Some(traces) = traces else { return bytecodes };
1373
1374 for node in traces.arena.nodes() {
1375 let address = node.trace.address;
1376 if bytecodes.contains_key(&address) {
1377 continue;
1378 }
1379
1380 let Ok(Some(account)) = db.basic_ref(address) else { continue };
1381 let code: Option<Bytecode> =
1382 account.code.or_else(|| db.code_by_hash_ref(account.code_hash).ok());
1383 let code: Bytes = code.map(|code| code.original_bytes()).unwrap_or_default();
1384
1385 if !code.is_empty() {
1386 bytecodes.insert(address, code);
1387 }
1388 }
1389
1390 bytecodes
1391}
1392
1393pub struct FuzzTestTimer {
1395 inner: Option<(Instant, Duration)>,
1397}
1398
1399impl FuzzTestTimer {
1400 pub fn new(timeout: Option<u32>) -> Self {
1401 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1402 }
1403
1404 pub const fn is_enabled(&self) -> bool {
1406 self.inner.is_some()
1407 }
1408
1409 pub fn is_timed_out(&self) -> bool {
1411 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1412 }
1413}
1414
1415#[derive(Clone, Debug)]
1418pub struct EarlyExit {
1419 inner: Arc<AtomicBool>,
1421 fail_fast: bool,
1423}
1424
1425impl EarlyExit {
1426 pub fn new(fail_fast: bool) -> Self {
1427 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1428 }
1429
1430 pub fn record_failure(&self) {
1432 if self.fail_fast {
1433 self.inner.store(true, Ordering::Relaxed);
1434 }
1435 }
1436
1437 pub fn record_ctrl_c(&self) {
1439 self.inner.store(true, Ordering::Relaxed);
1440 }
1441
1442 pub fn should_stop(&self) -> bool {
1444 self.inner.load(Ordering::Relaxed)
1445 }
1446}
1447
1448#[cfg(test)]
1449mod tests {
1450 use super::*;
1451 use crate::inspectors::{EdgeCovHit, EdgeKey};
1452 use alloy_primitives::B256;
1453 use foundry_cheatcodes::{
1454 CheatsConfig,
1455 Vm::{blobhashesCall, revertToStateCall, snapshotStateCall},
1456 };
1457 use foundry_config::Config;
1458 use foundry_evm_core::{constants::MAGIC_SKIP, opts::EvmOpts};
1459 use revm::{
1460 context::{Cfg, TxEnv},
1461 primitives::hardfork::SpecId,
1462 };
1463
1464 fn dense_call(edge: EdgeKey) -> RawCallResult {
1465 RawCallResult {
1466 edge_coverage: Some(EdgeCoverage::CollisionFree(vec![EdgeCovHit { edge, count: 1 }])),
1467 ..Default::default()
1468 }
1469 }
1470
1471 #[test]
1472 fn collision_free_edge_merge_uses_stable_indices() {
1473 let first =
1474 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(10) };
1475 let second =
1476 EdgeKey { address: Address::ZERO, depth: None, pc: 0, jump_dest: U256::from(20) };
1477 let mut history = Vec::new();
1478 let mut edge_indices = EdgeIndexMap::default();
1479
1480 assert_eq!(
1481 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
1482 (true, true)
1483 );
1484 assert_eq!(history, [1]);
1485
1486 assert_eq!(
1487 dense_call(second).merge_edge_coverage(&mut history, &mut edge_indices),
1488 (true, true)
1489 );
1490 assert_eq!(history, [1, 1]);
1491
1492 assert_eq!(
1493 dense_call(first).merge_edge_coverage(&mut history, &mut edge_indices),
1494 (false, false)
1495 );
1496 assert_eq!(history, [1, 1]);
1497 }
1498
1499 #[test]
1500 fn cheatcode_skip_payload_is_classified_as_skip() {
1501 let raw = RawCallResult::<EthEvmNetwork> {
1502 result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"),
1503 reverter: Some(CHEATCODE_ADDRESS),
1504 ..Default::default()
1505 };
1506
1507 let err = raw.into_evm_error(None);
1508 assert!(matches!(err, EvmError::Skip(_)));
1509 }
1510
1511 #[test]
1512 fn forged_skip_payload_from_non_cheatcode_is_execution_error() {
1513 let raw = RawCallResult::<EthEvmNetwork> {
1514 result: Bytes::from_static(MAGIC_SKIP),
1515 reverter: Some(CALLER),
1516 ..Default::default()
1517 };
1518
1519 let err = raw.into_evm_error(None);
1520 assert!(matches!(err, EvmError::Execution(_)));
1521 }
1522
1523 #[test]
1524 fn skip_payload_without_reverter_is_execution_error() {
1525 let raw = RawCallResult::<EthEvmNetwork> {
1526 result: Bytes::from_static(MAGIC_SKIP),
1527 reverter: None,
1528 ..Default::default()
1529 };
1530
1531 let err = raw.into_evm_error(None);
1532 assert!(matches!(err, EvmError::Execution(_)));
1533 }
1534
1535 #[test]
1536 fn set_spec_id_updates_spec_dependent_cfg_state() {
1537 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1538 let mut executor = ExecutorBuilder::default().build(
1539 EvmEnvFor::<EthEvmNetwork>::default(),
1540 TxEnvFor::<EthEvmNetwork>::default(),
1541 backend,
1542 );
1543
1544 executor.evm_env_mut().cfg_env.set_spec_and_mainnet_gas_params(SpecId::HOMESTEAD);
1545 assert_eq!(
1546 executor.evm_env().cfg_env.gas_params(),
1547 &revm::context_interface::cfg::GasParams::new_spec(SpecId::HOMESTEAD),
1548 );
1549 assert!(!executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1550
1551 executor.set_spec_id(SpecId::AMSTERDAM);
1552
1553 assert_eq!(executor.spec_id(), SpecId::AMSTERDAM);
1554 assert_eq!(
1555 executor.evm_env().cfg_env.gas_params(),
1556 &revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM),
1557 );
1558 assert!(executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1559 }
1560
1561 #[test]
1576 fn pre_override_blob_hashes_restored_on_revert_to_state() {
1577 let cheats_config = Arc::new(CheatsConfig::new(
1578 &Config::default(),
1579 EvmOpts::default(),
1580 None,
1581 None,
1582 None,
1583 false,
1584 ));
1585
1586 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1587 let mut executor = ExecutorBuilder::default()
1588 .inspectors(|stack| stack.cheatcodes(cheats_config))
1589 .spec_id(SpecId::CANCUN)
1590 .build(EvmEnv::default(), TxEnv::default(), backend);
1591
1592 let original: Vec<B256> = vec![B256::repeat_byte(0x11), B256::repeat_byte(0x22)];
1593 executor.tx_env_mut().set_blob_hashes(original.clone());
1594
1595 let snap_result = executor
1596 .transact_raw(
1597 CALLER,
1598 CHEATCODE_ADDRESS,
1599 snapshotStateCall {}.abi_encode().into(),
1600 U256::ZERO,
1601 )
1602 .expect("snapshotState failed");
1603 assert!(!snap_result.reverted, "snapshotState reverted unexpectedly");
1604 let snapshot_id = U256::from_be_slice(&snap_result.result[..32]);
1605
1606 let new_hashes = vec![B256::repeat_byte(0x33)];
1607 let blob_result = executor
1608 .transact_raw(
1609 CALLER,
1610 CHEATCODE_ADDRESS,
1611 blobhashesCall { hashes: new_hashes }.abi_encode().into(),
1612 U256::ZERO,
1613 )
1614 .expect("blobhashes failed");
1615 assert!(!blob_result.reverted, "blobhashes reverted unexpectedly");
1616
1617 let revert_result = executor
1618 .transact_raw(
1619 CALLER,
1620 CHEATCODE_ADDRESS,
1621 revertToStateCall { snapshotId: snapshot_id }.abi_encode().into(),
1622 U256::ZERO,
1623 )
1624 .expect("revertToState failed");
1625 assert!(!revert_result.reverted, "revertToState reverted unexpectedly");
1626
1627 assert_eq!(
1628 revert_result.tx_env.blob_hashes, original,
1629 "pre_override_blob_hashes must be restored to original non-empty hashes, not []",
1630 );
1631 }
1632}