foundry_evm/executors/invariant/
replay.rs1use super::{call_after_invariant_function, call_invariant_function, execute_tx};
2use crate::executors::{
3 EarlyExit, Executor,
4 invariant::shrink::{reset_shrink_progress, shrink_sequence, shrink_sequence_value},
5};
6use alloy_dyn_abi::JsonAbiExt;
7use alloy_json_abi::Function;
8use alloy_primitives::{
9 Bytes, I256, Log,
10 map::{AddressHashMap, HashMap},
11};
12use eyre::Result;
13use foundry_common::{ContractsByAddress, ContractsByArtifact};
14use foundry_config::InvariantConfig;
15use foundry_evm_core::evm::FoundryEvmNetwork;
16use foundry_evm_coverage::HitMaps;
17use foundry_evm_fuzz::{BaseCounterExample, BasicTxDetails, invariant::InvariantContract};
18use foundry_evm_traces::{TraceKind, TraceMode, Traces, load_contracts};
19use indicatif::ProgressBar;
20use parking_lot::RwLock;
21use std::sync::Arc;
22
23#[expect(clippy::too_many_arguments)]
26pub fn replay_run<FEN: FoundryEvmNetwork>(
27 invariant_contract: &InvariantContract<'_>,
28 target_invariant: &Function,
29 mut executor: Executor<FEN>,
30 known_contracts: &ContractsByArtifact,
31 mut ided_contracts: ContractsByAddress,
32 logs: &mut Vec<Log>,
33 traces: &mut Traces,
34 debug_bytecodes: &mut AddressHashMap<Bytes>,
35 line_coverage: &mut Option<HitMaps>,
36 deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
37 inputs: &[BasicTxDetails],
38 show_solidity: bool,
39) -> Result<Vec<BaseCounterExample>> {
40 if executor.inspector().tracer.is_none() {
42 executor.set_tracing(TraceMode::Call);
43 }
44
45 let mut counterexample_sequence = vec![];
46
47 for tx in inputs {
49 let mut call_result = execute_tx(&mut executor, tx)?;
50 logs.extend(call_result.logs.clone());
51 debug_bytecodes.extend(call_result.debug_bytecodes.clone());
52 traces.push((TraceKind::Execution, call_result.traces.clone().unwrap()));
53 HitMaps::merge_opt(line_coverage, call_result.line_coverage.clone());
54
55 executor.commit(&mut call_result);
57
58 ided_contracts
60 .extend(load_contracts(call_result.traces.iter().map(|a| &a.arena), known_contracts));
61
62 counterexample_sequence.push(BaseCounterExample::from_invariant_call(
64 tx,
65 &ided_contracts,
66 call_result.traces,
67 show_solidity,
68 ));
69 }
70
71 let (invariant_result, invariant_success) = call_invariant_function(
77 &executor,
78 invariant_contract.address,
79 target_invariant.abi_encode_input(&[])?.into(),
80 )?;
81 debug_bytecodes.extend(invariant_result.debug_bytecodes);
82 traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap()));
83 logs.extend(invariant_result.logs);
84 deprecated_cheatcodes.extend(
85 invariant_result
86 .cheatcodes
87 .as_ref()
88 .map_or_else(Default::default, |cheats| cheats.deprecated.clone()),
89 );
90
91 if invariant_contract.call_after_invariant && invariant_success {
93 let (after_invariant_result, _) =
94 call_after_invariant_function(&executor, invariant_contract.address)?;
95 debug_bytecodes.extend(after_invariant_result.debug_bytecodes);
96 traces.push((TraceKind::Execution, after_invariant_result.traces.clone().unwrap()));
97 logs.extend(after_invariant_result.logs);
98 }
99
100 Ok(counterexample_sequence)
101}
102
103#[expect(clippy::too_many_arguments)]
108pub fn replay_error<FEN: FoundryEvmNetwork>(
109 config: InvariantConfig,
110 mut executor: Executor<FEN>,
111 calls: &[BasicTxDetails],
112 inner_sequence: Option<Vec<Option<BasicTxDetails>>>,
113 expect_assertion_failure: bool,
114 target_value: Option<I256>,
115 invariant_contract: &InvariantContract<'_>,
116 target_invariant: &Function,
117 known_contracts: &ContractsByArtifact,
118 ided_contracts: ContractsByAddress,
119 logs: &mut Vec<Log>,
120 traces: &mut Traces,
121 debug_bytecodes: &mut AddressHashMap<Bytes>,
122 line_coverage: &mut Option<HitMaps>,
123 deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
124 progress: Option<&ProgressBar>,
125 early_exit: &EarlyExit,
126 position: Option<(usize, usize)>,
127) -> Result<Vec<BaseCounterExample>> {
128 reset_shrink_progress(&config, progress, &target_invariant.name, position);
132
133 let calls = if let Some(target) = target_value {
134 shrink_sequence_value(
135 &config,
136 invariant_contract,
137 target_invariant,
138 calls,
139 &executor,
140 target,
141 progress,
142 early_exit,
143 )?
144 } else {
145 shrink_sequence(
146 &config,
147 invariant_contract,
148 target_invariant,
149 calls,
150 expect_assertion_failure,
151 &executor,
152 progress,
153 early_exit,
154 )?
155 };
156
157 if let Some(sequence) = inner_sequence {
158 set_up_inner_replay(&mut executor, &sequence);
159 }
160
161 replay_run(
162 invariant_contract,
163 target_invariant,
164 executor,
165 known_contracts,
166 ided_contracts,
167 logs,
168 traces,
169 debug_bytecodes,
170 line_coverage,
171 deprecated_cheatcodes,
172 &calls,
173 config.show_solidity,
174 )
175}
176
177fn set_up_inner_replay<FEN: FoundryEvmNetwork>(
179 executor: &mut Executor<FEN>,
180 inner_sequence: &[Option<BasicTxDetails>],
181) {
182 if let Some(fuzzer) = &mut executor.inspector_mut().fuzzer
183 && let Some(call_generator) = &mut fuzzer.call_generator
184 {
185 call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned()));
186 call_generator.set_replay(true);
187 }
188}