diff --git a/config/README.md b/config/README.md index 4d68412755a01..f1699c531668e 100644 --- a/config/README.md +++ b/config/README.md @@ -187,6 +187,7 @@ call_override = false dictionary_weight = 80 include_storage = true include_push_bytes = true +shrink_sequence = true [fmt] line_length = 100 diff --git a/config/src/invariant.rs b/config/src/invariant.rs index ad49e40fffb84..2e4ebe43bc1a1 100644 --- a/config/src/invariant.rs +++ b/config/src/invariant.rs @@ -24,6 +24,8 @@ pub struct InvariantConfig { /// The fuzz dictionary configuration #[serde(flatten)] pub dictionary: FuzzDictionaryConfig, + /// Attempt to shrink the failure case to its smallest sequence of calls + pub shrink_sequence: bool, } impl Default for InvariantConfig { @@ -34,6 +36,7 @@ impl Default for InvariantConfig { fail_on_revert: false, call_override: false, dictionary: FuzzDictionaryConfig { dictionary_weight: 80, ..Default::default() }, + shrink_sequence: true, } } } @@ -61,6 +64,7 @@ impl InlineConfigParser for InvariantConfig { "depth" => conf_clone.depth = parse_config_u32(key, value)?, "fail-on-revert" => conf_clone.fail_on_revert = parse_config_bool(key, value)?, "call-override" => conf_clone.call_override = parse_config_bool(key, value)?, + "shrink-sequence" => conf_clone.shrink_sequence = parse_config_bool(key, value)?, _ => Err(InlineConfigParserError::InvalidConfigProperty(key.to_string()))?, } } diff --git a/config/src/lib.rs b/config/src/lib.rs index 328869408c78e..990b4e20bcb4c 100644 --- a/config/src/lib.rs +++ b/config/src/lib.rs @@ -3377,6 +3377,7 @@ mod tests { depth = 15 fail_on_revert = false call_override = false + shrink_sequence = true "#, )?; diff --git a/evm/src/fuzz/invariant/error.rs b/evm/src/fuzz/invariant/error.rs index 31064331aca2a..619f5a40a4f7c 100644 --- a/evm/src/fuzz/invariant/error.rs +++ b/evm/src/fuzz/invariant/error.rs @@ -31,6 +31,8 @@ pub struct InvariantFuzzError { pub func: Option, /// Inner fuzzing Sequence coming from overriding calls. pub inner_sequence: Vec>, + /// Shrink the failed test case to the smallest sequence. + pub shrink: bool, } impl InvariantFuzzError { @@ -40,6 +42,7 @@ impl InvariantFuzzError { calldata: &[BasicTxDetails], call_result: RawCallResult, inner_sequence: &[Option], + shrink_sequence: bool, ) -> Self { let mut func = None; let origin: String; @@ -80,6 +83,7 @@ impl InvariantFuzzError { addr: invariant_contract.address, func, inner_sequence: inner_sequence.to_vec(), + shrink: shrink_sequence, } } @@ -99,7 +103,11 @@ impl InvariantFuzzError { TestError::Fail(_, ref calls) => calls, }; - let calls = self.try_shrinking(calls, &executor); + if self.shrink { + let _ = self.try_shrinking(calls, &executor); + } else { + trace!(target: "forge::test", "Shrinking disabled."); + } // We want traces for a failed case. executor.set_tracing(true); diff --git a/evm/src/fuzz/invariant/executor.rs b/evm/src/fuzz/invariant/executor.rs index f0658a862e4ff..c982e904398a3 100644 --- a/evm/src/fuzz/invariant/executor.rs +++ b/evm/src/fuzz/invariant/executor.rs @@ -187,6 +187,7 @@ impl<'a> InvariantExecutor<'a> { &inputs, &mut failures.borrow_mut(), self.config.fail_on_revert, + self.config.shrink_sequence, ); if !can_continue { @@ -558,6 +559,7 @@ fn can_continue( calldata: &[BasicTxDetails], failures: &mut InvariantFailures, fail_on_revert: bool, + shrink_sequence: bool, ) -> (bool, Option>) { let mut call_results = None; if !call_result.reverted { @@ -571,8 +573,14 @@ fn can_continue( // The user might want to stop all execution if a revert happens to // better bound their testing space. if fail_on_revert { - let error = - InvariantFuzzError::new(invariant_contract, None, calldata, call_result, &[]); + let error = InvariantFuzzError::new( + invariant_contract, + None, + calldata, + call_result, + &[], + shrink_sequence, + ); failures.revert_reason = Some(error.revert_reason.clone()); diff --git a/evm/src/fuzz/invariant/mod.rs b/evm/src/fuzz/invariant/mod.rs index b956f80742d56..79e428f87b2bf 100644 --- a/evm/src/fuzz/invariant/mod.rs +++ b/evm/src/fuzz/invariant/mod.rs @@ -95,6 +95,7 @@ pub fn assert_invariants( calldata, call_result, &inner_sequence, + true, )), ); found_case = true; diff --git a/forge/tests/it/config.rs b/forge/tests/it/config.rs index 8a0034a7364f3..d703738a2b0c7 100644 --- a/forge/tests/it/config.rs +++ b/forge/tests/it/config.rs @@ -126,6 +126,7 @@ pub fn test_opts() -> TestOptions { max_fuzz_dictionary_addresses: 10_000, max_fuzz_dictionary_values: 10_000, }, + shrink_sequence: true, }, inline_fuzz: Default::default(), inline_invariant: Default::default(), diff --git a/testdata/foundry.toml b/testdata/foundry.toml index 29dc2bf427822..da4c21696c2dd 100644 --- a/testdata/foundry.toml +++ b/testdata/foundry.toml @@ -16,6 +16,7 @@ ffi = false force = false invariant_fail_on_revert = false invariant_call_override = false +invariant_shrink_sequence = true gas_limit = 9223372036854775807 gas_price = 0 gas_reports = ['*']