1use crate::inspectors::{
10 Cheatcodes, CmpOperands, InspectorData, InspectorStack, cheatcodes::BroadcastableTransactions,
11};
12use alloy_dyn_abi::{DynSolValue, FunctionExt, JsonAbiExt};
13use alloy_json_abi::Function;
14use alloy_primitives::{
15 Address, Bytes, Log, TxKind, U256, keccak256,
16 map::{AddressHashMap, HashMap},
17};
18use alloy_sol_types::{SolCall, sol};
19use foundry_evm_core::{
20 EvmEnv, FoundryBlock, FoundryTransaction,
21 backend::{Backend, BackendError, BackendResult, CowBackend, DatabaseExt, GLOBAL_FAIL_SLOT},
22 constants::{
23 CALLER, CHEATCODE_ADDRESS, CHEATCODE_CONTRACT_HASH, DEFAULT_CREATE2_DEPLOYER,
24 DEFAULT_CREATE2_DEPLOYER_CODE, DEFAULT_CREATE2_DEPLOYER_DEPLOYER,
25 },
26 decode::{RevertDecoder, SkipReason},
27 evm::{
28 EthEvmNetwork, EvmEnvFor, FoundryEvmNetwork, HaltReasonFor, IntoInstructionResult, SpecFor,
29 TxEnvFor,
30 },
31 utils::StateChangeset,
32};
33use foundry_evm_coverage::HitMaps;
34use foundry_evm_traces::{SparsedTraceArena, TraceMode};
35use revm::{
36 bytecode::Bytecode,
37 context::Transaction,
38 context_interface::{
39 result::{ExecutionResult, Output, ResultAndState},
40 transaction::SignedAuthorization,
41 },
42 database::{DatabaseCommit, DatabaseRef},
43 interpreter::{InstructionResult, return_ok},
44};
45use sancov::SancovGuard;
46use std::{
47 borrow::Cow,
48 sync::{
49 Arc,
50 atomic::{AtomicBool, Ordering},
51 },
52 time::{Duration, Instant},
53};
54
55mod builder;
56pub use builder::ExecutorBuilder;
57
58pub mod fuzz;
59pub use fuzz::FuzzedExecutor;
60
61pub mod invariant;
62pub use invariant::InvariantExecutor;
63
64mod corpus;
65mod sancov;
66mod trace;
67
68pub use trace::TracingExecutor;
69
70const DURATION_BETWEEN_METRICS_REPORT: Duration = Duration::from_secs(5);
71
72sol! {
73 interface ITest {
74 function setUp() external;
75 function failed() external view returns (bool failed);
76
77 #[derive(Default)]
78 function beforeTestSetup(bytes4 testSelector) public view returns (bytes[] memory beforeTestCalldata);
79 }
80}
81
82#[derive(Clone, Debug)]
94pub struct Executor<FEN: FoundryEvmNetwork> {
95 backend: Arc<Backend<FEN>>,
104 evm_env: EvmEnvFor<FEN>,
106 tx_env: TxEnvFor<FEN>,
108 inspector: InspectorStack<FEN>,
110 gas_limit: u64,
112 legacy_assertions: bool,
114}
115
116impl<FEN: FoundryEvmNetwork> Executor<FEN> {
117 #[inline]
119 pub fn new(
120 mut backend: Backend<FEN>,
121 evm_env: EvmEnvFor<FEN>,
122 tx_env: TxEnvFor<FEN>,
123 inspector: InspectorStack<FEN>,
124 gas_limit: u64,
125 legacy_assertions: bool,
126 ) -> Self {
127 backend.insert_account_info(
130 CHEATCODE_ADDRESS,
131 revm::state::AccountInfo {
132 code: Some(Bytecode::new_raw(Bytes::from_static(&[0]))),
133 code_hash: CHEATCODE_CONTRACT_HASH,
136 ..Default::default()
137 },
138 );
139
140 Self {
141 backend: Arc::new(backend),
142 evm_env,
143 tx_env,
144 inspector,
145 gas_limit,
146 legacy_assertions,
147 }
148 }
149
150 fn clone_with_backend(&self, backend: Backend<FEN>) -> Self {
151 let evm_env = self.evm_env.clone();
152 Self {
153 backend: Arc::new(backend),
154 evm_env,
155 tx_env: self.tx_env.clone(),
156 inspector: self.inspector().clone(),
157 gas_limit: self.gas_limit,
158 legacy_assertions: self.legacy_assertions,
159 }
160 }
161
162 pub fn backend(&self) -> &Backend<FEN> {
164 &self.backend
165 }
166
167 pub fn backend_mut(&mut self) -> &mut Backend<FEN> {
172 Arc::make_mut(&mut self.backend)
173 }
174
175 pub const fn evm_env(&self) -> &EvmEnvFor<FEN> {
177 &self.evm_env
178 }
179
180 pub const fn evm_env_mut(&mut self) -> &mut EvmEnvFor<FEN> {
182 &mut self.evm_env
183 }
184
185 pub const fn tx_env(&self) -> &TxEnvFor<FEN> {
187 &self.tx_env
188 }
189
190 pub const fn tx_env_mut(&mut self) -> &mut TxEnvFor<FEN> {
192 &mut self.tx_env
193 }
194
195 pub const fn inspector(&self) -> &InspectorStack<FEN> {
197 &self.inspector
198 }
199
200 pub const fn inspector_mut(&mut self) -> &mut InspectorStack<FEN> {
202 &mut self.inspector
203 }
204
205 pub const fn spec_id(&self) -> SpecFor<FEN> {
207 self.evm_env.cfg_env.spec
208 }
209
210 pub fn set_spec_id(&mut self, spec_id: SpecFor<FEN>) {
212 self.evm_env.cfg_env.set_spec_and_mainnet_gas_params(spec_id);
213 }
214
215 pub const fn gas_limit(&self) -> u64 {
220 self.gas_limit
221 }
222
223 pub const fn set_gas_limit(&mut self, gas_limit: u64) {
225 self.gas_limit = gas_limit;
226 }
227
228 pub const fn legacy_assertions(&self) -> bool {
231 self.legacy_assertions
232 }
233
234 pub const fn set_legacy_assertions(&mut self, legacy_assertions: bool) {
237 self.legacy_assertions = legacy_assertions;
238 }
239
240 pub fn deploy_create2_deployer(&mut self) -> eyre::Result<()> {
242 trace!("deploying local create2 deployer");
243 let create2_deployer_account = self
244 .backend()
245 .basic_ref(DEFAULT_CREATE2_DEPLOYER)?
246 .ok_or_else(|| BackendError::MissingAccount(DEFAULT_CREATE2_DEPLOYER))?;
247
248 if create2_deployer_account.code.is_none_or(|code| code.is_empty()) {
250 let creator = DEFAULT_CREATE2_DEPLOYER_DEPLOYER;
251
252 let initial_balance = self.get_balance(creator)?;
254 self.set_balance(creator, U256::MAX)?;
255
256 let res =
257 self.deploy(creator, DEFAULT_CREATE2_DEPLOYER_CODE.into(), U256::ZERO, None)?;
258 trace!(create2=?res.address, "deployed local create2 deployer");
259
260 self.set_balance(creator, initial_balance)?;
261 }
262 Ok(())
263 }
264
265 pub fn set_balance(&mut self, address: Address, amount: U256) -> BackendResult<()> {
267 trace!(?address, ?amount, "setting account balance");
268 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
269 account.balance = amount;
270 self.backend_mut().insert_account_info(address, account);
271 Ok(())
272 }
273
274 pub fn get_balance(&self, address: Address) -> BackendResult<U256> {
276 Ok(self.backend().basic_ref(address)?.map(|acc| acc.balance).unwrap_or_default())
277 }
278
279 pub fn set_nonce(&mut self, address: Address, nonce: u64) -> BackendResult<()> {
281 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
282 account.nonce = nonce;
283 self.backend_mut().insert_account_info(address, account);
284 self.tx_env_mut().set_nonce(nonce);
285 Ok(())
286 }
287
288 pub fn get_nonce(&self, address: Address) -> BackendResult<u64> {
290 Ok(self.backend().basic_ref(address)?.map(|acc| acc.nonce).unwrap_or_default())
291 }
292
293 pub fn set_code(&mut self, address: Address, code: Bytecode) -> BackendResult<()> {
295 let mut account = self.backend().basic_ref(address)?.unwrap_or_default();
296 account.code_hash = keccak256(code.original_byte_slice());
297 account.code = Some(code);
298 self.backend_mut().insert_account_info(address, account);
299 Ok(())
300 }
301
302 pub fn set_storage(
304 &mut self,
305 address: Address,
306 storage: HashMap<U256, U256>,
307 ) -> BackendResult<()> {
308 self.backend_mut().replace_account_storage(address, storage)?;
309 Ok(())
310 }
311
312 pub fn set_storage_slot(
314 &mut self,
315 address: Address,
316 slot: U256,
317 value: U256,
318 ) -> BackendResult<()> {
319 self.backend_mut().insert_account_storage(address, slot, value)?;
320 Ok(())
321 }
322
323 pub fn is_empty_code(&self, address: Address) -> BackendResult<bool> {
325 Ok(self.backend().basic_ref(address)?.map(|acc| acc.is_empty_code_hash()).unwrap_or(true))
326 }
327
328 #[inline]
329 pub fn set_tracing(&mut self, mode: TraceMode) -> &mut Self {
330 self.inspector_mut().tracing(mode);
331 self
332 }
333
334 #[inline]
335 pub fn set_script_execution(&mut self, script_address: Address) {
336 self.inspector_mut().script(script_address);
337 }
338
339 #[inline]
340 pub fn set_trace_printer(&mut self, trace_printer: bool) -> &mut Self {
341 self.inspector_mut().print(trace_printer);
342 self
343 }
344
345 #[inline]
346 pub fn create2_deployer(&self) -> Address {
347 self.inspector().create2_deployer
348 }
349
350 pub fn deploy(
355 &mut self,
356 from: Address,
357 code: Bytes,
358 value: U256,
359 rd: Option<&RevertDecoder>,
360 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
361 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Create, code, value);
362 self.deploy_with_env(evm_env, tx_env, rd)
363 }
364
365 #[instrument(name = "deploy", level = "debug", skip_all)]
372 pub fn deploy_with_env(
373 &mut self,
374 evm_env: EvmEnvFor<FEN>,
375 tx_env: TxEnvFor<FEN>,
376 rd: Option<&RevertDecoder>,
377 ) -> Result<DeployResult<FEN>, EvmError<FEN>> {
378 assert!(
379 matches!(tx_env.kind(), TxKind::Create),
380 "Expected create transaction, got {:?}",
381 tx_env.kind()
382 );
383 trace!(sender=%tx_env.caller(), "deploying contract");
384
385 let mut result = self.transact_with_env(evm_env, tx_env)?;
386 result = result.into_result(rd)?;
387 let Some(Output::Create(_, Some(address))) = result.out else {
388 panic!("Deployment succeeded, but no address was returned: {result:#?}");
389 };
390
391 self.backend_mut().add_persistent_account(address);
394
395 trace!(%address, "deployed contract");
396
397 Ok(DeployResult { raw: result, address })
398 }
399
400 #[instrument(name = "setup", level = "debug", skip_all)]
407 pub fn setup(
408 &mut self,
409 from: Option<Address>,
410 to: Address,
411 rd: Option<&RevertDecoder>,
412 ) -> Result<RawCallResult<FEN>, EvmError<FEN>> {
413 trace!(?from, ?to, "setting up contract");
414
415 let from = from.unwrap_or(CALLER);
416 self.backend_mut().set_test_contract(to).set_caller(from);
417 let calldata = Bytes::from_static(&ITest::setUpCall::SELECTOR);
418 let mut res = self.transact_raw(from, to, calldata, U256::ZERO)?;
419 res = res.into_result(rd)?;
420
421 self.evm_env_mut().block_env = res.evm_env.block_env.clone();
423 self.evm_env_mut().cfg_env.chain_id = res.evm_env.cfg_env.chain_id;
425
426 let success =
427 self.is_raw_call_success(to, Cow::Borrowed(&res.state_changeset), &res, false);
428 if !success {
429 return Err(res.into_execution_error("execution error".to_string()).into());
430 }
431
432 Ok(res)
433 }
434
435 pub fn call(
437 &self,
438 from: Address,
439 to: Address,
440 func: &Function,
441 args: &[DynSolValue],
442 value: U256,
443 rd: Option<&RevertDecoder>,
444 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
445 let calldata = Bytes::from(func.abi_encode_input(args)?);
446 let result = self.call_raw(from, to, calldata, value)?;
447 result.into_decoded_result(func, rd)
448 }
449
450 pub fn call_sol<C: SolCall>(
452 &self,
453 from: Address,
454 to: Address,
455 args: &C,
456 value: U256,
457 rd: Option<&RevertDecoder>,
458 ) -> Result<CallResult<C::Return, FEN>, EvmError<FEN>> {
459 let calldata = Bytes::from(args.abi_encode());
460 let mut raw = self.call_raw(from, to, calldata, value)?;
461 raw = raw.into_result(rd)?;
462 Ok(CallResult { decoded_result: C::abi_decode_returns(&raw.result)?, raw })
463 }
464
465 pub fn transact(
467 &mut self,
468 from: Address,
469 to: Address,
470 func: &Function,
471 args: &[DynSolValue],
472 value: U256,
473 rd: Option<&RevertDecoder>,
474 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
475 let calldata = Bytes::from(func.abi_encode_input(args)?);
476 let result = self.transact_raw(from, to, calldata, value)?;
477 result.into_decoded_result(func, rd)
478 }
479
480 pub fn call_raw(
482 &self,
483 from: Address,
484 to: Address,
485 calldata: Bytes,
486 value: U256,
487 ) -> eyre::Result<RawCallResult<FEN>> {
488 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
489 self.call_with_env(evm_env, tx_env)
490 }
491
492 pub fn call_raw_with_authorization(
495 &mut self,
496 from: Address,
497 to: Address,
498 calldata: Bytes,
499 value: U256,
500 authorization_list: Vec<SignedAuthorization>,
501 ) -> eyre::Result<RawCallResult<FEN>> {
502 let (evm_env, mut tx_env) = self.build_test_env(from, to.into(), calldata, value);
503 tx_env.set_signed_authorization(authorization_list);
504 tx_env.set_tx_type(4);
505 self.call_with_env(evm_env, tx_env)
506 }
507
508 pub fn transact_raw(
510 &mut self,
511 from: Address,
512 to: Address,
513 calldata: Bytes,
514 value: U256,
515 ) -> eyre::Result<RawCallResult<FEN>> {
516 let (evm_env, tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
517 self.transact_with_env(evm_env, tx_env)
518 }
519
520 pub fn transact_raw_with_authorization(
523 &mut self,
524 from: Address,
525 to: Address,
526 calldata: Bytes,
527 value: U256,
528 authorization_list: Vec<SignedAuthorization>,
529 ) -> eyre::Result<RawCallResult<FEN>> {
530 let (evm_env, mut tx_env) = self.build_test_env(from, TxKind::Call(to), calldata, value);
531 tx_env.set_signed_authorization(authorization_list);
532 tx_env.set_tx_type(4);
533 self.transact_with_env(evm_env, tx_env)
534 }
535
536 #[instrument(name = "call", level = "debug", skip_all)]
540 pub fn call_with_env(
541 &self,
542 mut evm_env: EvmEnvFor<FEN>,
543 mut tx_env: TxEnvFor<FEN>,
544 ) -> eyre::Result<RawCallResult<FEN>> {
545 let mut stack = self.inspector().clone();
546 let sancov_edges = stack.inner.sancov_edges;
547 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
548 let sancov_active = sancov_edges || sancov_trace_cmp;
549 let mut backend = CowBackend::new_borrowed(self.backend());
550 let result = {
551 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
552 backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?
553 };
554 let mut result = convert_executed_result(
555 evm_env,
556 tx_env,
557 stack,
558 result,
559 backend.has_state_snapshot_failure(),
560 )?;
561 if sancov_edges {
562 SancovGuard::append_edges_into(&mut result);
563 }
564 if sancov_trace_cmp {
565 SancovGuard::drain_cmp_into(&mut result);
566 }
567 Ok(result)
568 }
569
570 #[instrument(name = "transact", level = "debug", skip_all)]
572 pub fn transact_with_env(
573 &mut self,
574 mut evm_env: EvmEnvFor<FEN>,
575 mut tx_env: TxEnvFor<FEN>,
576 ) -> eyre::Result<RawCallResult<FEN>> {
577 let mut stack = self.inspector().clone();
578 let sancov_edges = stack.inner.sancov_edges;
579 let sancov_trace_cmp = stack.inner.sancov_trace_cmp;
580 let sancov_active = sancov_edges || sancov_trace_cmp;
581 let backend = self.backend_mut();
582 let result = {
583 let _guard = sancov_active.then(|| SancovGuard::new(sancov_edges, sancov_trace_cmp));
584 backend.inspect(&mut evm_env, &mut tx_env, &mut stack)?
585 };
586 let mut result = convert_executed_result(
587 evm_env,
588 tx_env,
589 stack,
590 result,
591 backend.has_state_snapshot_failure(),
592 )?;
593 if sancov_edges {
594 SancovGuard::append_edges_into(&mut result);
595 }
596 if sancov_trace_cmp {
597 SancovGuard::drain_cmp_into(&mut result);
598 }
599 self.commit(&mut result);
600 Ok(result)
601 }
602
603 #[instrument(name = "commit", level = "debug", skip_all)]
608 fn commit(&mut self, result: &mut RawCallResult<FEN>) {
609 self.backend_mut().commit(result.state_changeset.clone());
611
612 self.inspector_mut().cheatcodes = result.cheatcodes.take();
614 if let Some(cheats) = self.inspector_mut().cheatcodes.as_mut() {
615 cheats.broadcastable_transactions.clear();
617 cheats.ignored_traces.ignored.clear();
618
619 if let Some(last_pause_call) = cheats.ignored_traces.last_pause_call.as_mut() {
622 *last_pause_call = (0, 0);
623 }
624 }
625
626 self.inspector_mut().set_block(result.evm_env.block_env.clone());
628 self.inspector_mut().set_gas_price(result.tx_env.gas_price());
629 }
630
631 pub fn is_raw_call_mut_success(
636 &self,
637 address: Address,
638 call_result: &mut RawCallResult<FEN>,
639 should_fail: bool,
640 ) -> bool {
641 self.is_raw_call_success(
642 address,
643 Cow::Owned(std::mem::take(&mut call_result.state_changeset)),
644 call_result,
645 should_fail,
646 )
647 }
648
649 pub fn is_raw_call_success(
653 &self,
654 address: Address,
655 state_changeset: Cow<'_, StateChangeset>,
656 call_result: &RawCallResult<FEN>,
657 should_fail: bool,
658 ) -> bool {
659 if call_result.has_state_snapshot_failure {
660 return should_fail;
662 }
663 self.is_success(address, call_result.reverted, state_changeset, should_fail)
664 }
665
666 pub fn is_raw_call_mut_success_handler_gate(
670 &self,
671 address: Address,
672 call_result: &mut RawCallResult<FEN>,
673 ) -> bool {
674 if call_result.has_state_snapshot_failure {
675 return false;
676 }
677 let state_changeset = std::mem::take(&mut call_result.state_changeset);
678 self.is_success_handler_gate(address, call_result.reverted, Cow::Owned(state_changeset))
679 }
680
681 pub fn is_success(
703 &self,
704 address: Address,
705 reverted: bool,
706 state_changeset: Cow<'_, StateChangeset>,
707 should_fail: bool,
708 ) -> bool {
709 let success = self.is_success_raw(address, reverted, state_changeset, false);
710 should_fail ^ success
711 }
712
713 pub fn is_success_handler_gate(
719 &self,
720 address: Address,
721 reverted: bool,
722 state_changeset: Cow<'_, StateChangeset>,
723 ) -> bool {
724 self.is_success_raw(address, reverted, state_changeset, true)
725 }
726
727 #[instrument(name = "is_success", level = "debug", skip_all)]
728 fn is_success_raw(
729 &self,
730 address: Address,
731 reverted: bool,
732 state_changeset: Cow<'_, StateChangeset>,
733 pending_global_failure_only: bool,
734 ) -> bool {
735 if reverted {
737 return false;
738 }
739
740 if self.backend().has_state_snapshot_failure() {
742 return false;
743 }
744
745 let global_failed = if pending_global_failure_only {
750 Self::has_pending_global_failure(&state_changeset)
751 } else {
752 self.has_global_failure(&state_changeset)
753 };
754 if global_failed {
755 return false;
756 }
757
758 if !self.legacy_assertions {
759 return true;
760 }
761
762 {
764 let mut backend = self.backend().clone_empty();
766
767 for address in [address, CHEATCODE_ADDRESS] {
770 let Ok(acc) = self.backend().basic_ref(address) else { return false };
771 backend.insert_account_info(address, acc.unwrap_or_default());
772 }
773
774 backend.commit(state_changeset.into_owned());
779
780 let executor = self.clone_with_backend(backend);
782 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
783 match call {
784 Ok(CallResult { raw: _, decoded_result: failed }) => {
785 trace!(failed, "DSTest::failed()");
786 !failed
787 }
788 Err(err) => {
789 trace!(%err, "failed to call DSTest::failed()");
790 true
791 }
792 }
793 }
794 }
795
796 pub fn has_pending_global_failure(state_changeset: &StateChangeset) -> bool {
799 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
800 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
801 && !failed_slot.present_value().is_zero()
802 {
803 return true;
804 }
805
806 false
807 }
808
809 pub fn has_global_failure(&self, state_changeset: &StateChangeset) -> bool {
812 if Self::has_pending_global_failure(state_changeset) {
813 return true;
814 }
815
816 self.backend()
817 .storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
818 .is_ok_and(|failed_slot| !failed_slot.is_zero())
819 }
820
821 fn build_test_env(
826 &self,
827 caller: Address,
828 kind: TxKind,
829 data: Bytes,
830 value: U256,
831 ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>) {
832 let mut cfg_env = self.evm_env.cfg_env.clone();
833 cfg_env.spec = self.spec_id();
834
835 let mut block_env = self.evm_env.block_env.clone();
839 block_env.set_basefee(0);
840 block_env.set_gas_limit(self.gas_limit);
841
842 let mut tx_env = self.tx_env.clone();
843 tx_env.set_caller(caller);
844 tx_env.set_kind(kind);
845 tx_env.set_data(data);
846 tx_env.set_value(value);
847 tx_env.set_gas_price(0);
849 tx_env.set_gas_priority_fee(None);
850 tx_env.set_gas_limit(self.gas_limit);
851 tx_env.set_chain_id(Some(self.evm_env.cfg_env.chain_id));
852
853 (EvmEnv { cfg_env, block_env }, tx_env)
854 }
855
856 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
857 where
858 C::Return: Default,
859 {
860 self.call_sol(CALLER, to, args, U256::ZERO, None)
861 .map(|c| c.decoded_result)
862 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
863 .unwrap_or_default()
864 }
865}
866
867#[derive(Debug, thiserror::Error)]
869#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
870pub struct ExecutionErr<FEN: FoundryEvmNetwork = EthEvmNetwork> {
871 pub raw: RawCallResult<FEN>,
873 pub reason: String,
875}
876
877impl<FEN: FoundryEvmNetwork> std::ops::Deref for ExecutionErr<FEN> {
878 type Target = RawCallResult<FEN>;
879
880 #[inline]
881 fn deref(&self) -> &Self::Target {
882 &self.raw
883 }
884}
885
886impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for ExecutionErr<FEN> {
887 #[inline]
888 fn deref_mut(&mut self) -> &mut Self::Target {
889 &mut self.raw
890 }
891}
892
893#[derive(Debug, thiserror::Error)]
894pub enum EvmError<FEN: FoundryEvmNetwork = EthEvmNetwork> {
895 #[error(transparent)]
897 Execution(Box<ExecutionErr<FEN>>),
898 #[error(transparent)]
900 Abi(#[from] alloy_dyn_abi::Error),
901 #[error("{0}")]
903 Skip(SkipReason),
904 #[error("{0}")]
906 Eyre(
907 #[from]
908 #[source]
909 eyre::Report,
910 ),
911}
912
913impl<FEN: FoundryEvmNetwork> From<ExecutionErr<FEN>> for EvmError<FEN> {
914 fn from(err: ExecutionErr<FEN>) -> Self {
915 Self::Execution(Box::new(err))
916 }
917}
918
919impl<FEN: FoundryEvmNetwork> From<alloy_sol_types::Error> for EvmError<FEN> {
920 fn from(err: alloy_sol_types::Error) -> Self {
921 Self::Abi(err.into())
922 }
923}
924
925#[derive(Debug)]
927pub struct DeployResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
928 pub raw: RawCallResult<FEN>,
930 pub address: Address,
932}
933
934impl<FEN: FoundryEvmNetwork> std::ops::Deref for DeployResult<FEN> {
935 type Target = RawCallResult<FEN>;
936
937 #[inline]
938 fn deref(&self) -> &Self::Target {
939 &self.raw
940 }
941}
942
943impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for DeployResult<FEN> {
944 #[inline]
945 fn deref_mut(&mut self) -> &mut Self::Target {
946 &mut self.raw
947 }
948}
949
950impl<FEN: FoundryEvmNetwork> From<DeployResult<FEN>> for RawCallResult<FEN> {
951 fn from(d: DeployResult<FEN>) -> Self {
952 d.raw
953 }
954}
955
956#[derive(Debug)]
958pub struct RawCallResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
959 pub exit_reason: Option<InstructionResult>,
961 pub reverted: bool,
963 pub has_state_snapshot_failure: bool,
968 pub result: Bytes,
970 pub gas_used: u64,
972 pub gas_refunded: u64,
974 pub stipend: u64,
976 pub logs: Vec<Log>,
978 pub labels: AddressHashMap<String>,
980 pub traces: Option<SparsedTraceArena>,
982 pub line_coverage: Option<HitMaps>,
984 pub edge_coverage: Option<Vec<u8>>,
986 pub evm_cmp_values: Option<Vec<CmpOperands>>,
988 pub sancov_coverage: Option<Vec<u8>>,
991 pub sancov_cmp_values: Option<Vec<foundry_evm_sancov::CmpSample>>,
993 pub transactions: Option<BroadcastableTransactions<FEN::Network>>,
995 pub state_changeset: StateChangeset,
997 pub evm_env: EvmEnvFor<FEN>,
999 pub tx_env: TxEnvFor<FEN>,
1001 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
1003 pub out: Option<Output>,
1005 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
1007 pub reverter: Option<Address>,
1008}
1009
1010impl<FEN: FoundryEvmNetwork> Default for RawCallResult<FEN> {
1011 fn default() -> Self {
1012 Self {
1013 exit_reason: None,
1014 reverted: false,
1015 has_state_snapshot_failure: false,
1016 result: Bytes::new(),
1017 gas_used: 0,
1018 gas_refunded: 0,
1019 stipend: 0,
1020 logs: Vec::new(),
1021 labels: HashMap::default(),
1022 traces: None,
1023 line_coverage: None,
1024 edge_coverage: None,
1025 evm_cmp_values: None,
1026 sancov_coverage: None,
1027 sancov_cmp_values: None,
1028 transactions: None,
1029 state_changeset: HashMap::default(),
1030 evm_env: EvmEnv::default(),
1031 tx_env: TxEnvFor::<FEN>::default(),
1032 cheatcodes: Default::default(),
1033 out: None,
1034 chisel_state: None,
1035 reverter: None,
1036 }
1037 }
1038}
1039
1040impl<FEN: FoundryEvmNetwork> RawCallResult<FEN> {
1041 pub fn from_evm_result(r: Result<Self, EvmError<FEN>>) -> eyre::Result<(Self, Option<String>)> {
1043 match r {
1044 Ok(r) => Ok((r, None)),
1045 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
1046 Err(e) => Err(e.into()),
1047 }
1048 }
1049
1050 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError<FEN> {
1052 if self.reverter == Some(CHEATCODE_ADDRESS)
1053 && let Some(reason) = SkipReason::decode(&self.result)
1054 {
1055 return EvmError::Skip(reason);
1056 }
1057 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
1058 EvmError::Execution(Box::new(self.into_execution_error(reason)))
1059 }
1060
1061 pub const fn into_execution_error(self, reason: String) -> ExecutionErr<FEN> {
1063 ExecutionErr { raw: self, reason }
1064 }
1065
1066 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError<FEN>> {
1068 if let Some(reason) = self.exit_reason
1069 && reason.is_ok()
1070 {
1071 Ok(self)
1072 } else {
1073 Err(self.into_evm_error(rd))
1074 }
1075 }
1076
1077 pub fn into_decoded_result(
1079 mut self,
1080 func: &Function,
1081 rd: Option<&RevertDecoder>,
1082 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
1083 self = self.into_result(rd)?;
1084 let mut result = func.abi_decode_output(&self.result)?;
1085 let decoded_result =
1086 if result.len() == 1 { result.pop().unwrap() } else { DynSolValue::Tuple(result) };
1087 Ok(CallResult { raw: self, decoded_result })
1088 }
1089
1090 pub fn transactions(&self) -> Option<&BroadcastableTransactions<FEN::Network>> {
1092 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1093 }
1094
1095 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
1098 let mut new_coverage = false;
1099 let mut is_edge = false;
1100 if let Some(x) = &mut self.edge_coverage {
1101 for (curr, hist) in std::iter::zip(x, history_map) {
1104 if *curr > 0 {
1106 let bucket = match *curr {
1108 0 => 0,
1109 1 => 1,
1110 2 => 2,
1111 3 => 4,
1112 4..=7 => 8,
1113 8..=15 => 16,
1114 16..=31 => 32,
1115 32..=127 => 64,
1116 128..=255 => 128,
1117 };
1118
1119 if *hist < bucket {
1121 if *hist == 0 {
1122 is_edge = true;
1124 }
1125 *hist = bucket;
1126 new_coverage = true;
1127 }
1128
1129 *curr = 0;
1131 }
1132 }
1133 }
1134 (new_coverage, is_edge)
1135 }
1136
1137 pub fn merge_sancov_coverage(&mut self, history_map: &mut Vec<u8>) -> (bool, bool) {
1140 let mut new_coverage = false;
1141 let mut is_edge = false;
1142 if let Some(x) = &mut self.sancov_coverage {
1143 if history_map.len() < x.len() {
1144 history_map.resize(x.len(), 0);
1145 }
1146 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1147 if *curr > 0 {
1148 let bucket = match *curr {
1149 0 => 0,
1150 1 => 1,
1151 2 => 2,
1152 3 => 4,
1153 4..=7 => 8,
1154 8..=15 => 16,
1155 16..=31 => 32,
1156 32..=127 => 64,
1157 128..=255 => 128,
1158 };
1159 if *hist < bucket {
1160 if *hist == 0 {
1161 is_edge = true;
1162 }
1163 *hist = bucket;
1164 new_coverage = true;
1165 }
1166 *curr = 0;
1167 }
1168 }
1169 }
1170 (new_coverage, is_edge)
1171 }
1172
1173 pub fn merge_all_coverage(
1176 &mut self,
1177 evm_history: &mut [u8],
1178 sancov_history: &mut Vec<u8>,
1179 ) -> (bool, bool) {
1180 let (new_evm, edge_evm) = self.merge_edge_coverage(evm_history);
1181 let (new_san, edge_san) = self.merge_sancov_coverage(sancov_history);
1182 (new_evm || new_san, edge_evm || edge_san)
1183 }
1184}
1185
1186pub struct CallResult<T = DynSolValue, FEN: FoundryEvmNetwork = EthEvmNetwork> {
1188 pub raw: RawCallResult<FEN>,
1190 pub decoded_result: T,
1192}
1193
1194impl<T, FEN: FoundryEvmNetwork> std::ops::Deref for CallResult<T, FEN> {
1195 type Target = RawCallResult<FEN>;
1196
1197 #[inline]
1198 fn deref(&self) -> &Self::Target {
1199 &self.raw
1200 }
1201}
1202
1203impl<T, FEN: FoundryEvmNetwork> std::ops::DerefMut for CallResult<T, FEN> {
1204 #[inline]
1205 fn deref_mut(&mut self) -> &mut Self::Target {
1206 &mut self.raw
1207 }
1208}
1209
1210fn convert_executed_result<FEN: FoundryEvmNetwork>(
1212 evm_env: EvmEnvFor<FEN>,
1213 tx_env: TxEnvFor<FEN>,
1214 inspector: InspectorStack<FEN>,
1215 ResultAndState { result, state: state_changeset }: ResultAndState<HaltReasonFor<FEN>>,
1216 has_state_snapshot_failure: bool,
1217) -> eyre::Result<RawCallResult<FEN>> {
1218 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1219 ExecutionResult::Success { reason, gas, output, logs } => {
1220 (reason.into(), gas.final_refunded(), gas.tx_gas_used(), Some(output), logs)
1221 }
1222 ExecutionResult::Revert { gas, output, logs } => {
1223 (InstructionResult::Revert, 0_u64, gas.tx_gas_used(), Some(Output::Call(output)), logs)
1224 }
1225 ExecutionResult::Halt { reason, gas, logs } => {
1226 (reason.into_instruction_result(), 0_u64, gas.tx_gas_used(), None, logs)
1227 }
1228 };
1229 let gas = revm::interpreter::gas::calculate_initial_tx_gas_for_tx(
1230 &tx_env,
1231 evm_env.cfg_env.spec.into(),
1232 );
1233
1234 let result = match &out {
1235 Some(Output::Call(data)) => data.clone(),
1236 _ => Bytes::new(),
1237 };
1238
1239 let InspectorData {
1240 mut logs,
1241 labels,
1242 traces,
1243 line_coverage,
1244 edge_coverage,
1245 evm_cmp_values,
1246 cheatcodes,
1247 chisel_state,
1248 reverter,
1249 } = inspector.collect();
1250
1251 if logs.is_empty() {
1252 logs = exec_logs;
1253 }
1254
1255 let transactions = cheatcodes
1256 .as_ref()
1257 .map(|c| c.broadcastable_transactions.clone())
1258 .filter(|txs| !txs.is_empty());
1259
1260 Ok(RawCallResult {
1261 exit_reason: Some(exit_reason),
1262 reverted: !matches!(exit_reason, return_ok!()),
1263 has_state_snapshot_failure,
1264 result,
1265 gas_used,
1266 gas_refunded,
1267 stipend: gas.initial_total_gas,
1268 logs,
1269 labels,
1270 traces,
1271 line_coverage,
1272 edge_coverage,
1273 evm_cmp_values,
1274 sancov_coverage: None,
1275 sancov_cmp_values: None,
1276 transactions,
1277 state_changeset,
1278 evm_env,
1279 tx_env,
1280 cheatcodes,
1281 out,
1282 chisel_state,
1283 reverter,
1284 })
1285}
1286
1287pub struct FuzzTestTimer {
1289 inner: Option<(Instant, Duration)>,
1291}
1292
1293impl FuzzTestTimer {
1294 pub fn new(timeout: Option<u32>) -> Self {
1295 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1296 }
1297
1298 pub const fn is_enabled(&self) -> bool {
1300 self.inner.is_some()
1301 }
1302
1303 pub fn is_timed_out(&self) -> bool {
1305 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1306 }
1307}
1308
1309#[derive(Clone, Debug)]
1312pub struct EarlyExit {
1313 inner: Arc<AtomicBool>,
1315 fail_fast: bool,
1317}
1318
1319impl EarlyExit {
1320 pub fn new(fail_fast: bool) -> Self {
1321 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1322 }
1323
1324 pub fn record_failure(&self) {
1326 if self.fail_fast {
1327 self.inner.store(true, Ordering::Relaxed);
1328 }
1329 }
1330
1331 pub fn record_ctrl_c(&self) {
1333 self.inner.store(true, Ordering::Relaxed);
1334 }
1335
1336 pub fn should_stop(&self) -> bool {
1338 self.inner.load(Ordering::Relaxed)
1339 }
1340}
1341
1342#[cfg(test)]
1343mod tests {
1344 use super::*;
1345 use foundry_evm_core::constants::MAGIC_SKIP;
1346 use revm::{context::Cfg, primitives::hardfork::SpecId};
1347
1348 #[test]
1349 fn cheatcode_skip_payload_is_classified_as_skip() {
1350 let raw = RawCallResult::<EthEvmNetwork> {
1351 result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"),
1352 reverter: Some(CHEATCODE_ADDRESS),
1353 ..Default::default()
1354 };
1355
1356 let err = raw.into_evm_error(None);
1357 assert!(matches!(err, EvmError::Skip(_)));
1358 }
1359
1360 #[test]
1361 fn forged_skip_payload_from_non_cheatcode_is_execution_error() {
1362 let raw = RawCallResult::<EthEvmNetwork> {
1363 result: Bytes::from_static(MAGIC_SKIP),
1364 reverter: Some(CALLER),
1365 ..Default::default()
1366 };
1367
1368 let err = raw.into_evm_error(None);
1369 assert!(matches!(err, EvmError::Execution(_)));
1370 }
1371
1372 #[test]
1373 fn skip_payload_without_reverter_is_execution_error() {
1374 let raw = RawCallResult::<EthEvmNetwork> {
1375 result: Bytes::from_static(MAGIC_SKIP),
1376 reverter: None,
1377 ..Default::default()
1378 };
1379
1380 let err = raw.into_evm_error(None);
1381 assert!(matches!(err, EvmError::Execution(_)));
1382 }
1383
1384 #[test]
1385 fn set_spec_id_updates_spec_dependent_cfg_state() {
1386 let backend = Backend::<EthEvmNetwork>::spawn(None).unwrap();
1387 let mut executor = ExecutorBuilder::default().build(
1388 EvmEnvFor::<EthEvmNetwork>::default(),
1389 TxEnvFor::<EthEvmNetwork>::default(),
1390 backend,
1391 );
1392
1393 executor.evm_env_mut().cfg_env.set_spec_and_mainnet_gas_params(SpecId::HOMESTEAD);
1394 assert_eq!(
1395 executor.evm_env().cfg_env.gas_params(),
1396 &revm::context_interface::cfg::GasParams::new_spec(SpecId::HOMESTEAD),
1397 );
1398 assert!(!executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1399
1400 executor.set_spec_id(SpecId::AMSTERDAM);
1401
1402 assert_eq!(executor.spec_id(), SpecId::AMSTERDAM);
1403 assert_eq!(
1404 executor.evm_env().cfg_env.gas_params(),
1405 &revm::context_interface::cfg::GasParams::new_spec(SpecId::AMSTERDAM),
1406 );
1407 assert!(executor.evm_env().cfg_env.is_amsterdam_eip8037_enabled());
1408 }
1409}