Skip to main content

foundry_config/
fuzz.rs

1//! Configuration for fuzz testing.
2
3use alloy_primitives::U256;
4use serde::{Deserialize, Serialize};
5use std::path::PathBuf;
6
7/// Contains for fuzz testing
8#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
9pub struct FuzzConfig {
10    /// The number of test cases that must execute for each property test
11    pub runs: u32,
12    /// Fails the fuzzed test if a revert occurs.
13    pub fail_on_revert: bool,
14    /// The maximum number of test case rejections allowed by proptest, to be
15    /// encountered during usage of `vm.assume` cheatcode. This will be used
16    /// to set the `max_global_rejects` value in proptest test runner config.
17    /// `max_local_rejects` option isn't exposed here since we're not using
18    /// `prop_filter`.
19    pub max_test_rejects: u32,
20    /// Optional seed for the fuzzing RNG algorithm
21    pub seed: Option<U256>,
22    /// The fuzz dictionary configuration
23    #[serde(flatten)]
24    pub dictionary: FuzzDictionaryConfig,
25    /// Number of runs to execute and include in the gas report.
26    pub gas_report_samples: u32,
27    /// Path where fuzz failures are recorded and replayed.
28    pub failure_persist_dir: Option<PathBuf>,
29    /// Name of the file to record fuzz failures, defaults to `failures`.
30    pub failure_persist_file: Option<String>,
31    /// show `console.log` in fuzz test, defaults to `false`
32    pub show_logs: bool,
33    /// Optional timeout (in seconds) for each property test
34    pub timeout: Option<u32>,
35    /// Maximum value for fuzzed integers, used to simulate smaller integer types.
36    /// When set, unsigned integers are clamped to [0, max_fuzz_int] and signed integers
37    /// are clamped to [-(max_fuzz_int+1), max_fuzz_int] to match real signed type ranges.
38    /// Useful for Polkadot compatibility where balances are u128.
39    #[serde(default, skip_serializing_if = "Option::is_none")]
40    pub max_fuzz_int: Option<U256>,
41}
42
43impl Default for FuzzConfig {
44    fn default() -> Self {
45        Self {
46            runs: 256,
47            fail_on_revert: true,
48            max_test_rejects: 65536,
49            seed: None,
50            dictionary: FuzzDictionaryConfig::default(),
51            gas_report_samples: 256,
52            failure_persist_dir: None,
53            failure_persist_file: None,
54            show_logs: false,
55            timeout: None,
56            max_fuzz_int: None,
57        }
58    }
59}
60
61impl FuzzConfig {
62    /// Creates fuzz configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir.
63    pub fn new(cache_dir: PathBuf) -> Self {
64        Self {
65            failure_persist_dir: Some(cache_dir),
66            failure_persist_file: Some("failures".to_string()),
67            ..Default::default()
68        }
69    }
70}
71
72/// Contains for fuzz testing
73#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
74pub struct FuzzDictionaryConfig {
75    /// The weight of the dictionary
76    #[serde(deserialize_with = "crate::deserialize_stringified_percent")]
77    pub dictionary_weight: u32,
78    /// The flag indicating whether to include values from storage
79    pub include_storage: bool,
80    /// The flag indicating whether to include push bytes values
81    pub include_push_bytes: bool,
82    /// How many addresses to record at most.
83    /// Once the fuzzer exceeds this limit, it will start evicting random entries
84    ///
85    /// This limit is put in place to prevent memory blowup.
86    #[serde(deserialize_with = "crate::deserialize_usize_or_max")]
87    pub max_fuzz_dictionary_addresses: usize,
88    /// How many values to record at most.
89    /// Once the fuzzer exceeds this limit, it will start evicting random entries
90    #[serde(deserialize_with = "crate::deserialize_usize_or_max")]
91    pub max_fuzz_dictionary_values: usize,
92}
93
94impl Default for FuzzDictionaryConfig {
95    fn default() -> Self {
96        Self {
97            dictionary_weight: 40,
98            include_storage: true,
99            include_push_bytes: true,
100            // limit this to 300MB
101            max_fuzz_dictionary_addresses: (300 * 1024 * 1024) / 20,
102            // limit this to 200MB
103            max_fuzz_dictionary_values: (200 * 1024 * 1024) / 32,
104        }
105    }
106}