Skip to content

Commit d20285a

Browse files
committed
Merge foundry-rs#12587 into aviggiano/master
2 parents a835a49 + 98b7cda commit d20285a

14 files changed

Lines changed: 454 additions & 499 deletions

File tree

crates/config/src/invariant.rs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,8 @@ pub struct InvariantConfig {
3737
pub timeout: Option<u32>,
3838
/// Display counterexample as solidity calls.
3939
pub show_solidity: bool,
40-
/// Maximum time (in seconds) between generated txs.
41-
pub max_time_delay: Option<u32>,
42-
/// Maximum number of blocks elapsed between generated txs.
43-
pub max_block_delay: Option<u32>,
44-
/// Number of calls to execute between invariant assertions.
45-
///
46-
/// - `0`: Only assert on the last call of each run (fastest, but may miss exact breaking call)
47-
/// - `1` (default): Assert after every call (current behavior, most precise)
48-
/// - `N`: Assert every N calls AND always on the last call
49-
///
50-
/// Example: `check_interval = 10` means assert after calls 10, 20, 30, ... and the last call.
51-
pub check_interval: u32,
40+
/// Continue invariant run until all invariants declared in current test suite breaks.
41+
pub continuous_run: bool,
5242
}
5343

5444
impl Default for InvariantConfig {
@@ -67,9 +57,7 @@ impl Default for InvariantConfig {
6757
show_metrics: true,
6858
timeout: None,
6959
show_solidity: false,
70-
max_time_delay: None,
71-
max_block_delay: None,
72-
check_interval: 1,
60+
continuous_run: false,
7361
}
7462
}
7563
}

crates/evm/evm/src/executors/corpus.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ pub(crate) struct CorpusMetrics {
210210
impl fmt::Display for CorpusMetrics {
211211
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212212
writeln!(f)?;
213+
writeln!(f, " Edge coverage metrics:")?;
213214
writeln!(f, " - cumulative edges seen: {}", self.cumulative_edges_seen)?;
214215
writeln!(f, " - cumulative features seen: {}", self.cumulative_features_seen)?;
215216
writeln!(f, " - corpus count: {}", self.corpus_count)?;

crates/evm/evm/src/executors/invariant/error.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use super::InvariantContract;
22
use crate::executors::RawCallResult;
3+
use alloy_json_abi::Function;
34
use alloy_primitives::{Address, Bytes};
4-
use foundry_config::InvariantConfig;
55
use foundry_evm_core::decode::RevertDecoder;
66
use foundry_evm_fuzz::{BasicTxDetails, Reason, invariant::FuzzRunIdentifiedContracts};
77
use proptest::test_runner::TestError;
8+
use std::{collections::HashMap, fmt};
89

910
/// Stores information about failures and reverts of the invariant tests.
1011
#[derive(Clone, Default)]
@@ -14,16 +15,40 @@ pub struct InvariantFailures {
1415
/// The latest revert reason of a run.
1516
pub revert_reason: Option<String>,
1617
/// Maps a broken invariant to its specific error.
17-
pub error: Option<InvariantFuzzError>,
18+
pub errors: HashMap<String, InvariantFuzzError>,
1819
}
1920

2021
impl InvariantFailures {
2122
pub fn new() -> Self {
2223
Self::default()
2324
}
2425

25-
pub fn into_inner(self) -> (usize, Option<InvariantFuzzError>) {
26-
(self.reverts, self.error)
26+
pub fn into_inner(self) -> (usize, HashMap<String, InvariantFuzzError>) {
27+
(self.reverts, self.errors)
28+
}
29+
30+
pub fn record_failure(&mut self, invariant: &Function, failure: InvariantFuzzError) {
31+
self.errors.insert(invariant.name.clone(), failure);
32+
}
33+
34+
pub fn has_failure(&self, invariant: &Function) -> bool {
35+
self.errors.contains_key(&invariant.name)
36+
}
37+
38+
pub fn get_failure(&self, invariant: &Function) -> Option<&InvariantFuzzError> {
39+
self.errors.get(&invariant.name)
40+
}
41+
42+
pub fn can_continue(&self, invariants: usize) -> bool {
43+
self.errors.len() < invariants
44+
}
45+
}
46+
47+
impl fmt::Display for InvariantFailures {
48+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49+
writeln!(f)?;
50+
writeln!(f, " ❌ Failures: {}", self.errors.len())?;
51+
Ok(())
2752
}
2853
}
2954

@@ -70,10 +95,11 @@ pub struct FailedInvariantCaseData {
7095
impl FailedInvariantCaseData {
7196
pub fn new(
7297
invariant_contract: &InvariantContract<'_>,
73-
invariant_config: &InvariantConfig,
98+
shrink_run_limit: u32,
99+
fail_on_revert: bool,
74100
targeted_contracts: &FuzzRunIdentifiedContracts,
75101
calldata: &[BasicTxDetails],
76-
call_result: RawCallResult,
102+
call_result: &RawCallResult,
77103
inner_sequence: &[Option<BasicTxDetails>],
78104
) -> Self {
79105
// Collect abis of fuzzed and invariant contracts to decode custom error.
@@ -82,7 +108,7 @@ impl FailedInvariantCaseData {
82108
.with_abi(invariant_contract.abi)
83109
.decode(call_result.result.as_ref(), call_result.exit_reason);
84110

85-
let func = invariant_contract.invariant_function;
111+
let func = invariant_contract.invariant_fn;
86112
debug_assert!(func.inputs.is_empty());
87113
let origin = func.name.as_str();
88114
Self {
@@ -95,8 +121,8 @@ impl FailedInvariantCaseData {
95121
addr: invariant_contract.address,
96122
calldata: func.selector().to_vec().into(),
97123
inner_sequence: inner_sequence.to_vec(),
98-
shrink_run_limit: invariant_config.shrink_run_limit,
99-
fail_on_revert: invariant_config.fail_on_revert,
124+
shrink_run_limit,
125+
fail_on_revert,
100126
}
101127
}
102128
}

0 commit comments

Comments
 (0)