1use crate::inspectors::{
10 Cheatcodes, 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 const fn set_spec_id(&mut self, spec_id: SpecFor<FEN>) {
212 self.evm_env.cfg_env.spec = 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_success(
688 &self,
689 address: Address,
690 reverted: bool,
691 state_changeset: Cow<'_, StateChangeset>,
692 should_fail: bool,
693 ) -> bool {
694 let success = self.is_success_raw(address, reverted, state_changeset);
695 should_fail ^ success
696 }
697
698 #[instrument(name = "is_success", level = "debug", skip_all)]
699 fn is_success_raw(
700 &self,
701 address: Address,
702 reverted: bool,
703 state_changeset: Cow<'_, StateChangeset>,
704 ) -> bool {
705 if reverted {
707 return false;
708 }
709
710 if self.backend().has_state_snapshot_failure() {
712 return false;
713 }
714
715 if self.has_global_failure(&state_changeset) {
717 return false;
718 }
719
720 if !self.legacy_assertions {
721 return true;
722 }
723
724 {
726 let mut backend = self.backend().clone_empty();
728
729 for address in [address, CHEATCODE_ADDRESS] {
732 let Ok(acc) = self.backend().basic_ref(address) else { return false };
733 backend.insert_account_info(address, acc.unwrap_or_default());
734 }
735
736 backend.commit(state_changeset.into_owned());
741
742 let executor = self.clone_with_backend(backend);
744 let call = executor.call_sol(CALLER, address, &ITest::failedCall {}, U256::ZERO, None);
745 match call {
746 Ok(CallResult { raw: _, decoded_result: failed }) => {
747 trace!(failed, "DSTest::failed()");
748 !failed
749 }
750 Err(err) => {
751 trace!(%err, "failed to call DSTest::failed()");
752 true
753 }
754 }
755 }
756 }
757
758 pub fn has_pending_global_failure(state_changeset: &StateChangeset) -> bool {
761 if let Some(acc) = state_changeset.get(&CHEATCODE_ADDRESS)
762 && let Some(failed_slot) = acc.storage.get(&GLOBAL_FAIL_SLOT)
763 && !failed_slot.present_value().is_zero()
764 {
765 return true;
766 }
767
768 false
769 }
770
771 pub fn has_global_failure(&self, state_changeset: &StateChangeset) -> bool {
774 if Self::has_pending_global_failure(state_changeset) {
775 return true;
776 }
777
778 self.backend()
779 .storage_ref(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT)
780 .is_ok_and(|failed_slot| !failed_slot.is_zero())
781 }
782
783 pub fn clear_global_failure(
786 &mut self,
787 state_changeset: Option<&mut StateChangeset>,
788 ) -> BackendResult<()> {
789 if let Some(state_changeset) = state_changeset
790 && let Some(acc) = state_changeset.get_mut(&CHEATCODE_ADDRESS)
791 && let Some(failed_slot) = acc.storage.get_mut(&GLOBAL_FAIL_SLOT)
792 {
793 failed_slot.present_value = U256::ZERO;
794 }
795
796 self.set_storage_slot(CHEATCODE_ADDRESS, GLOBAL_FAIL_SLOT, U256::ZERO)
797 }
798
799 fn build_test_env(
804 &self,
805 caller: Address,
806 kind: TxKind,
807 data: Bytes,
808 value: U256,
809 ) -> (EvmEnvFor<FEN>, TxEnvFor<FEN>) {
810 let mut cfg_env = self.evm_env.cfg_env.clone();
811 cfg_env.spec = self.spec_id();
812
813 let mut block_env = self.evm_env.block_env.clone();
817 block_env.set_basefee(0);
818 block_env.set_gas_limit(self.gas_limit);
819
820 let mut tx_env = self.tx_env.clone();
821 tx_env.set_caller(caller);
822 tx_env.set_kind(kind);
823 tx_env.set_data(data);
824 tx_env.set_value(value);
825 tx_env.set_gas_price(0);
827 tx_env.set_gas_priority_fee(None);
828 tx_env.set_gas_limit(self.gas_limit);
829 tx_env.set_chain_id(Some(self.evm_env.cfg_env.chain_id));
830
831 (EvmEnv { cfg_env, block_env }, tx_env)
832 }
833
834 pub fn call_sol_default<C: SolCall>(&self, to: Address, args: &C) -> C::Return
835 where
836 C::Return: Default,
837 {
838 self.call_sol(CALLER, to, args, U256::ZERO, None)
839 .map(|c| c.decoded_result)
840 .inspect_err(|e| warn!(target: "forge::test", "failed calling {:?}: {e}", C::SIGNATURE))
841 .unwrap_or_default()
842 }
843}
844
845#[derive(Debug, thiserror::Error)]
847#[error("execution reverted: {reason} (gas: {})", raw.gas_used)]
848pub struct ExecutionErr<FEN: FoundryEvmNetwork = EthEvmNetwork> {
849 pub raw: RawCallResult<FEN>,
851 pub reason: String,
853}
854
855impl<FEN: FoundryEvmNetwork> std::ops::Deref for ExecutionErr<FEN> {
856 type Target = RawCallResult<FEN>;
857
858 #[inline]
859 fn deref(&self) -> &Self::Target {
860 &self.raw
861 }
862}
863
864impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for ExecutionErr<FEN> {
865 #[inline]
866 fn deref_mut(&mut self) -> &mut Self::Target {
867 &mut self.raw
868 }
869}
870
871#[derive(Debug, thiserror::Error)]
872pub enum EvmError<FEN: FoundryEvmNetwork = EthEvmNetwork> {
873 #[error(transparent)]
875 Execution(Box<ExecutionErr<FEN>>),
876 #[error(transparent)]
878 Abi(#[from] alloy_dyn_abi::Error),
879 #[error("{0}")]
881 Skip(SkipReason),
882 #[error("{0}")]
884 Eyre(
885 #[from]
886 #[source]
887 eyre::Report,
888 ),
889}
890
891impl<FEN: FoundryEvmNetwork> From<ExecutionErr<FEN>> for EvmError<FEN> {
892 fn from(err: ExecutionErr<FEN>) -> Self {
893 Self::Execution(Box::new(err))
894 }
895}
896
897impl<FEN: FoundryEvmNetwork> From<alloy_sol_types::Error> for EvmError<FEN> {
898 fn from(err: alloy_sol_types::Error) -> Self {
899 Self::Abi(err.into())
900 }
901}
902
903#[derive(Debug)]
905pub struct DeployResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
906 pub raw: RawCallResult<FEN>,
908 pub address: Address,
910}
911
912impl<FEN: FoundryEvmNetwork> std::ops::Deref for DeployResult<FEN> {
913 type Target = RawCallResult<FEN>;
914
915 #[inline]
916 fn deref(&self) -> &Self::Target {
917 &self.raw
918 }
919}
920
921impl<FEN: FoundryEvmNetwork> std::ops::DerefMut for DeployResult<FEN> {
922 #[inline]
923 fn deref_mut(&mut self) -> &mut Self::Target {
924 &mut self.raw
925 }
926}
927
928impl<FEN: FoundryEvmNetwork> From<DeployResult<FEN>> for RawCallResult<FEN> {
929 fn from(d: DeployResult<FEN>) -> Self {
930 d.raw
931 }
932}
933
934#[derive(Debug)]
936pub struct RawCallResult<FEN: FoundryEvmNetwork = EthEvmNetwork> {
937 pub exit_reason: Option<InstructionResult>,
939 pub reverted: bool,
941 pub has_state_snapshot_failure: bool,
946 pub result: Bytes,
948 pub gas_used: u64,
950 pub gas_refunded: u64,
952 pub stipend: u64,
954 pub logs: Vec<Log>,
956 pub labels: AddressHashMap<String>,
958 pub traces: Option<SparsedTraceArena>,
960 pub line_coverage: Option<HitMaps>,
962 pub edge_coverage: Option<Vec<u8>>,
964 pub sancov_coverage: Option<Vec<u8>>,
967 pub sancov_cmp_values: Option<Vec<foundry_evm_sancov::CmpSample>>,
969 pub transactions: Option<BroadcastableTransactions<FEN::Network>>,
971 pub state_changeset: StateChangeset,
973 pub evm_env: EvmEnvFor<FEN>,
975 pub tx_env: TxEnvFor<FEN>,
977 pub cheatcodes: Option<Box<Cheatcodes<FEN>>>,
979 pub out: Option<Output>,
981 pub chisel_state: Option<(Vec<U256>, Vec<u8>)>,
983 pub reverter: Option<Address>,
984}
985
986impl<FEN: FoundryEvmNetwork> Default for RawCallResult<FEN> {
987 fn default() -> Self {
988 Self {
989 exit_reason: None,
990 reverted: false,
991 has_state_snapshot_failure: false,
992 result: Bytes::new(),
993 gas_used: 0,
994 gas_refunded: 0,
995 stipend: 0,
996 logs: Vec::new(),
997 labels: HashMap::default(),
998 traces: None,
999 line_coverage: None,
1000 edge_coverage: None,
1001 sancov_coverage: None,
1002 sancov_cmp_values: None,
1003 transactions: None,
1004 state_changeset: HashMap::default(),
1005 evm_env: EvmEnv::default(),
1006 tx_env: TxEnvFor::<FEN>::default(),
1007 cheatcodes: Default::default(),
1008 out: None,
1009 chisel_state: None,
1010 reverter: None,
1011 }
1012 }
1013}
1014
1015impl<FEN: FoundryEvmNetwork> RawCallResult<FEN> {
1016 pub fn from_evm_result(r: Result<Self, EvmError<FEN>>) -> eyre::Result<(Self, Option<String>)> {
1018 match r {
1019 Ok(r) => Ok((r, None)),
1020 Err(EvmError::Execution(e)) => Ok((e.raw, Some(e.reason))),
1021 Err(e) => Err(e.into()),
1022 }
1023 }
1024
1025 pub fn into_evm_error(self, rd: Option<&RevertDecoder>) -> EvmError<FEN> {
1027 if self.reverter == Some(CHEATCODE_ADDRESS)
1028 && let Some(reason) = SkipReason::decode(&self.result)
1029 {
1030 return EvmError::Skip(reason);
1031 }
1032 let reason = rd.unwrap_or_default().decode(&self.result, self.exit_reason);
1033 EvmError::Execution(Box::new(self.into_execution_error(reason)))
1034 }
1035
1036 pub const fn into_execution_error(self, reason: String) -> ExecutionErr<FEN> {
1038 ExecutionErr { raw: self, reason }
1039 }
1040
1041 pub fn into_result(self, rd: Option<&RevertDecoder>) -> Result<Self, EvmError<FEN>> {
1043 if let Some(reason) = self.exit_reason
1044 && reason.is_ok()
1045 {
1046 Ok(self)
1047 } else {
1048 Err(self.into_evm_error(rd))
1049 }
1050 }
1051
1052 pub fn into_decoded_result(
1054 mut self,
1055 func: &Function,
1056 rd: Option<&RevertDecoder>,
1057 ) -> Result<CallResult<DynSolValue, FEN>, EvmError<FEN>> {
1058 self = self.into_result(rd)?;
1059 let mut result = func.abi_decode_output(&self.result)?;
1060 let decoded_result =
1061 if result.len() == 1 { result.pop().unwrap() } else { DynSolValue::Tuple(result) };
1062 Ok(CallResult { raw: self, decoded_result })
1063 }
1064
1065 pub fn transactions(&self) -> Option<&BroadcastableTransactions<FEN::Network>> {
1067 self.cheatcodes.as_ref().map(|c| &c.broadcastable_transactions)
1068 }
1069
1070 pub fn merge_edge_coverage(&mut self, history_map: &mut [u8]) -> (bool, bool) {
1073 let mut new_coverage = false;
1074 let mut is_edge = false;
1075 if let Some(x) = &mut self.edge_coverage {
1076 for (curr, hist) in std::iter::zip(x, history_map) {
1079 if *curr > 0 {
1081 let bucket = match *curr {
1083 0 => 0,
1084 1 => 1,
1085 2 => 2,
1086 3 => 4,
1087 4..=7 => 8,
1088 8..=15 => 16,
1089 16..=31 => 32,
1090 32..=127 => 64,
1091 128..=255 => 128,
1092 };
1093
1094 if *hist < bucket {
1096 if *hist == 0 {
1097 is_edge = true;
1099 }
1100 *hist = bucket;
1101 new_coverage = true;
1102 }
1103
1104 *curr = 0;
1106 }
1107 }
1108 }
1109 (new_coverage, is_edge)
1110 }
1111
1112 pub fn merge_sancov_coverage(&mut self, history_map: &mut Vec<u8>) -> (bool, bool) {
1115 let mut new_coverage = false;
1116 let mut is_edge = false;
1117 if let Some(x) = &mut self.sancov_coverage {
1118 if history_map.len() < x.len() {
1119 history_map.resize(x.len(), 0);
1120 }
1121 for (curr, hist) in std::iter::zip(x.iter_mut(), history_map.iter_mut()) {
1122 if *curr > 0 {
1123 let bucket = match *curr {
1124 0 => 0,
1125 1 => 1,
1126 2 => 2,
1127 3 => 4,
1128 4..=7 => 8,
1129 8..=15 => 16,
1130 16..=31 => 32,
1131 32..=127 => 64,
1132 128..=255 => 128,
1133 };
1134 if *hist < bucket {
1135 if *hist == 0 {
1136 is_edge = true;
1137 }
1138 *hist = bucket;
1139 new_coverage = true;
1140 }
1141 *curr = 0;
1142 }
1143 }
1144 }
1145 (new_coverage, is_edge)
1146 }
1147
1148 pub fn merge_all_coverage(
1151 &mut self,
1152 evm_history: &mut [u8],
1153 sancov_history: &mut Vec<u8>,
1154 ) -> (bool, bool) {
1155 let (new_evm, edge_evm) = self.merge_edge_coverage(evm_history);
1156 let (new_san, edge_san) = self.merge_sancov_coverage(sancov_history);
1157 (new_evm || new_san, edge_evm || edge_san)
1158 }
1159}
1160
1161pub struct CallResult<T = DynSolValue, FEN: FoundryEvmNetwork = EthEvmNetwork> {
1163 pub raw: RawCallResult<FEN>,
1165 pub decoded_result: T,
1167}
1168
1169impl<T, FEN: FoundryEvmNetwork> std::ops::Deref for CallResult<T, FEN> {
1170 type Target = RawCallResult<FEN>;
1171
1172 #[inline]
1173 fn deref(&self) -> &Self::Target {
1174 &self.raw
1175 }
1176}
1177
1178impl<T, FEN: FoundryEvmNetwork> std::ops::DerefMut for CallResult<T, FEN> {
1179 #[inline]
1180 fn deref_mut(&mut self) -> &mut Self::Target {
1181 &mut self.raw
1182 }
1183}
1184
1185fn convert_executed_result<FEN: FoundryEvmNetwork>(
1187 evm_env: EvmEnvFor<FEN>,
1188 tx_env: TxEnvFor<FEN>,
1189 inspector: InspectorStack<FEN>,
1190 ResultAndState { result, state: state_changeset }: ResultAndState<HaltReasonFor<FEN>>,
1191 has_state_snapshot_failure: bool,
1192) -> eyre::Result<RawCallResult<FEN>> {
1193 let (exit_reason, gas_refunded, gas_used, out, exec_logs) = match result {
1194 ExecutionResult::Success { reason, gas, output, logs } => {
1195 (reason.into(), gas.final_refunded(), gas.tx_gas_used(), Some(output), logs)
1196 }
1197 ExecutionResult::Revert { gas, output, logs } => {
1198 (InstructionResult::Revert, 0_u64, gas.tx_gas_used(), Some(Output::Call(output)), logs)
1199 }
1200 ExecutionResult::Halt { reason, gas, logs } => {
1201 (reason.into_instruction_result(), 0_u64, gas.tx_gas_used(), None, logs)
1202 }
1203 };
1204 let gas = revm::interpreter::gas::calculate_initial_tx_gas_for_tx(
1205 &tx_env,
1206 evm_env.cfg_env.spec.into(),
1207 );
1208
1209 let result = match &out {
1210 Some(Output::Call(data)) => data.clone(),
1211 _ => Bytes::new(),
1212 };
1213
1214 let InspectorData {
1215 mut logs,
1216 labels,
1217 traces,
1218 line_coverage,
1219 edge_coverage,
1220 cheatcodes,
1221 chisel_state,
1222 reverter,
1223 } = inspector.collect();
1224
1225 if logs.is_empty() {
1226 logs = exec_logs;
1227 }
1228
1229 let transactions = cheatcodes
1230 .as_ref()
1231 .map(|c| c.broadcastable_transactions.clone())
1232 .filter(|txs| !txs.is_empty());
1233
1234 Ok(RawCallResult {
1235 exit_reason: Some(exit_reason),
1236 reverted: !matches!(exit_reason, return_ok!()),
1237 has_state_snapshot_failure,
1238 result,
1239 gas_used,
1240 gas_refunded,
1241 stipend: gas.initial_total_gas,
1242 logs,
1243 labels,
1244 traces,
1245 line_coverage,
1246 edge_coverage,
1247 sancov_coverage: None,
1248 sancov_cmp_values: None,
1249 transactions,
1250 state_changeset,
1251 evm_env,
1252 tx_env,
1253 cheatcodes,
1254 out,
1255 chisel_state,
1256 reverter,
1257 })
1258}
1259
1260pub struct FuzzTestTimer {
1262 inner: Option<(Instant, Duration)>,
1264}
1265
1266impl FuzzTestTimer {
1267 pub fn new(timeout: Option<u32>) -> Self {
1268 Self { inner: timeout.map(|timeout| (Instant::now(), Duration::from_secs(timeout.into()))) }
1269 }
1270
1271 pub const fn is_enabled(&self) -> bool {
1273 self.inner.is_some()
1274 }
1275
1276 pub fn is_timed_out(&self) -> bool {
1278 self.inner.is_some_and(|(start, duration)| start.elapsed() > duration)
1279 }
1280}
1281
1282#[derive(Clone, Debug)]
1285pub struct EarlyExit {
1286 inner: Arc<AtomicBool>,
1288 fail_fast: bool,
1290}
1291
1292impl EarlyExit {
1293 pub fn new(fail_fast: bool) -> Self {
1294 Self { inner: Arc::new(AtomicBool::new(false)), fail_fast }
1295 }
1296
1297 pub fn record_failure(&self) {
1299 if self.fail_fast {
1300 self.inner.store(true, Ordering::Relaxed);
1301 }
1302 }
1303
1304 pub fn record_ctrl_c(&self) {
1306 self.inner.store(true, Ordering::Relaxed);
1307 }
1308
1309 pub fn should_stop(&self) -> bool {
1311 self.inner.load(Ordering::Relaxed)
1312 }
1313}
1314
1315#[cfg(test)]
1316mod tests {
1317 use super::*;
1318 use foundry_evm_core::constants::MAGIC_SKIP;
1319
1320 #[test]
1321 fn cheatcode_skip_payload_is_classified_as_skip() {
1322 let raw = RawCallResult::<EthEvmNetwork> {
1323 result: Bytes::from_static(b"FOUNDRY::SKIPwith reason"),
1324 reverter: Some(CHEATCODE_ADDRESS),
1325 ..Default::default()
1326 };
1327
1328 let err = raw.into_evm_error(None);
1329 assert!(matches!(err, EvmError::Skip(_)));
1330 }
1331
1332 #[test]
1333 fn forged_skip_payload_from_non_cheatcode_is_execution_error() {
1334 let raw = RawCallResult::<EthEvmNetwork> {
1335 result: Bytes::from_static(MAGIC_SKIP),
1336 reverter: Some(CALLER),
1337 ..Default::default()
1338 };
1339
1340 let err = raw.into_evm_error(None);
1341 assert!(matches!(err, EvmError::Execution(_)));
1342 }
1343
1344 #[test]
1345 fn skip_payload_without_reverter_is_execution_error() {
1346 let raw = RawCallResult::<EthEvmNetwork> {
1347 result: Bytes::from_static(MAGIC_SKIP),
1348 reverter: None,
1349 ..Default::default()
1350 };
1351
1352 let err = raw.into_evm_error(None);
1353 assert!(matches!(err, EvmError::Execution(_)));
1354 }
1355}