foundry_evm/executors/invariant/
replay.rs1use super::{call_after_invariant_function, call_invariant_function, execute_tx};
2use crate::executors::{EarlyExit, Executor, invariant::shrink::shrink_sequence};
3use alloy_dyn_abi::JsonAbiExt;
4use alloy_primitives::{Log, map::HashMap};
5use eyre::Result;
6use foundry_common::{ContractsByAddress, ContractsByArtifact};
7use foundry_config::InvariantConfig;
8use foundry_evm_coverage::HitMaps;
9use foundry_evm_fuzz::{BaseCounterExample, BasicTxDetails, invariant::InvariantContract};
10use foundry_evm_traces::{TraceKind, TraceMode, Traces, load_contracts};
11use indicatif::ProgressBar;
12use parking_lot::RwLock;
13use std::sync::Arc;
14
15#[expect(clippy::too_many_arguments)]
18pub fn replay_run(
19 invariant_contract: &InvariantContract<'_>,
20 mut executor: Executor,
21 known_contracts: &ContractsByArtifact,
22 mut ided_contracts: ContractsByAddress,
23 logs: &mut Vec<Log>,
24 traces: &mut Traces,
25 line_coverage: &mut Option<HitMaps>,
26 deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
27 inputs: &[BasicTxDetails],
28 show_solidity: bool,
29) -> Result<Vec<BaseCounterExample>> {
30 if executor.inspector().tracer.is_none() {
32 executor.set_tracing(TraceMode::Call);
33 }
34
35 let mut counterexample_sequence = vec![];
36
37 for tx in inputs {
39 let mut call_result = execute_tx(&mut executor, tx)?;
40 logs.extend(call_result.logs.clone());
41 traces.push((TraceKind::Execution, call_result.traces.clone().unwrap()));
42 HitMaps::merge_opt(line_coverage, call_result.line_coverage.clone());
43
44 executor.commit(&mut call_result);
46
47 ided_contracts
49 .extend(load_contracts(call_result.traces.iter().map(|a| &a.arena), known_contracts));
50
51 counterexample_sequence.push(BaseCounterExample::from_invariant_call(
53 tx,
54 &ided_contracts,
55 call_result.traces,
56 show_solidity,
57 ));
58 }
59
60 let (invariant_result, invariant_success) = call_invariant_function(
66 &executor,
67 invariant_contract.address,
68 invariant_contract.invariant_function.abi_encode_input(&[])?.into(),
69 )?;
70 traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap()));
71 logs.extend(invariant_result.logs);
72 deprecated_cheatcodes.extend(
73 invariant_result
74 .cheatcodes
75 .as_ref()
76 .map_or_else(Default::default, |cheats| cheats.deprecated.clone()),
77 );
78
79 if invariant_contract.call_after_invariant && invariant_success {
81 let (after_invariant_result, _) =
82 call_after_invariant_function(&executor, invariant_contract.address)?;
83 traces.push((TraceKind::Execution, after_invariant_result.traces.clone().unwrap()));
84 logs.extend(after_invariant_result.logs);
85 }
86
87 Ok(counterexample_sequence)
88}
89
90#[expect(clippy::too_many_arguments)]
92pub fn replay_error(
93 config: InvariantConfig,
94 mut executor: Executor,
95 calls: &[BasicTxDetails],
96 inner_sequence: Option<Vec<Option<BasicTxDetails>>>,
97 invariant_contract: &InvariantContract<'_>,
98 known_contracts: &ContractsByArtifact,
99 ided_contracts: ContractsByAddress,
100 logs: &mut Vec<Log>,
101 traces: &mut Traces,
102 line_coverage: &mut Option<HitMaps>,
103 deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
104 progress: Option<&ProgressBar>,
105 early_exit: &EarlyExit,
106) -> Result<Vec<BaseCounterExample>> {
107 let calls =
109 shrink_sequence(&config, invariant_contract, calls, &executor, progress, early_exit)?;
110
111 if let Some(sequence) = inner_sequence {
112 set_up_inner_replay(&mut executor, &sequence);
113 }
114
115 replay_run(
117 invariant_contract,
118 executor,
119 known_contracts,
120 ided_contracts,
121 logs,
122 traces,
123 line_coverage,
124 deprecated_cheatcodes,
125 &calls,
126 config.show_solidity,
127 )
128}
129
130fn set_up_inner_replay(executor: &mut Executor, inner_sequence: &[Option<BasicTxDetails>]) {
132 if let Some(fuzzer) = &mut executor.inspector_mut().fuzzer
133 && let Some(call_generator) = &mut fuzzer.call_generator
134 {
135 call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned()));
136 call_generator.set_replay(true);
137 }
138}