foundry_evm/executors/invariant/
replay.rsuse super::{
call_after_invariant_function, call_invariant_function, error::FailedInvariantCaseData,
shrink_sequence,
};
use crate::executors::Executor;
use alloy_dyn_abi::JsonAbiExt;
use alloy_primitives::{map::HashMap, Log};
use eyre::Result;
use foundry_common::{ContractsByAddress, ContractsByArtifact};
use foundry_evm_coverage::HitMaps;
use foundry_evm_fuzz::{
invariant::{BasicTxDetails, InvariantContract},
BaseCounterExample,
};
use foundry_evm_traces::{load_contracts, TraceKind, TraceMode, Traces};
use indicatif::ProgressBar;
use parking_lot::RwLock;
use proptest::test_runner::TestError;
use revm::primitives::U256;
use std::sync::Arc;
#[allow(clippy::too_many_arguments)]
pub fn replay_run(
invariant_contract: &InvariantContract<'_>,
mut executor: Executor,
known_contracts: &ContractsByArtifact,
mut ided_contracts: ContractsByAddress,
logs: &mut Vec<Log>,
traces: &mut Traces,
coverage: &mut Option<HitMaps>,
deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
inputs: &[BasicTxDetails],
) -> Result<Vec<BaseCounterExample>> {
if executor.inspector().tracer.is_none() {
executor.set_tracing(TraceMode::Call);
}
let mut counterexample_sequence = vec![];
for tx in inputs {
let call_result = executor.transact_raw(
tx.sender,
tx.call_details.target,
tx.call_details.calldata.clone(),
U256::ZERO,
)?;
logs.extend(call_result.logs);
traces.push((TraceKind::Execution, call_result.traces.clone().unwrap()));
if let Some(new_coverage) = call_result.coverage {
if let Some(old_coverage) = coverage {
*coverage = Some(std::mem::take(old_coverage).merged(new_coverage));
} else {
*coverage = Some(new_coverage);
}
}
ided_contracts
.extend(load_contracts(call_result.traces.iter().map(|a| &a.arena), known_contracts));
counterexample_sequence.push(BaseCounterExample::from_invariant_call(
tx.sender,
tx.call_details.target,
&tx.call_details.calldata,
&ided_contracts,
call_result.traces,
));
}
let (invariant_result, invariant_success) = call_invariant_function(
&executor,
invariant_contract.address,
invariant_contract.invariant_function.abi_encode_input(&[])?.into(),
)?;
traces.push((TraceKind::Execution, invariant_result.traces.clone().unwrap()));
logs.extend(invariant_result.logs);
deprecated_cheatcodes.extend(
invariant_result
.cheatcodes
.as_ref()
.map_or_else(Default::default, |cheats| cheats.deprecated.clone()),
);
if invariant_contract.call_after_invariant && invariant_success {
let (after_invariant_result, _) =
call_after_invariant_function(&executor, invariant_contract.address)?;
traces.push((TraceKind::Execution, after_invariant_result.traces.clone().unwrap()));
logs.extend(after_invariant_result.logs);
}
Ok(counterexample_sequence)
}
#[allow(clippy::too_many_arguments)]
pub fn replay_error(
failed_case: &FailedInvariantCaseData,
invariant_contract: &InvariantContract<'_>,
mut executor: Executor,
known_contracts: &ContractsByArtifact,
ided_contracts: ContractsByAddress,
logs: &mut Vec<Log>,
traces: &mut Traces,
coverage: &mut Option<HitMaps>,
deprecated_cheatcodes: &mut HashMap<&'static str, Option<&'static str>>,
progress: Option<&ProgressBar>,
) -> Result<Vec<BaseCounterExample>> {
match failed_case.test_error {
TestError::Abort(_) => Ok(vec![]),
TestError::Fail(_, ref calls) => {
let calls = shrink_sequence(
failed_case,
calls,
&executor,
invariant_contract.call_after_invariant,
progress,
)?;
set_up_inner_replay(&mut executor, &failed_case.inner_sequence);
replay_run(
invariant_contract,
executor,
known_contracts,
ided_contracts,
logs,
traces,
coverage,
deprecated_cheatcodes,
&calls,
)
}
}
}
fn set_up_inner_replay(executor: &mut Executor, inner_sequence: &[Option<BasicTxDetails>]) {
if let Some(fuzzer) = &mut executor.inspector_mut().fuzzer {
if let Some(call_generator) = &mut fuzzer.call_generator {
call_generator.last_sequence = Arc::new(RwLock::new(inner_sequence.to_owned()));
call_generator.set_replay(true);
}
}
}