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}
36
37impl Default for FuzzConfig {
38    fn default() -> Self {
39        Self {
40            runs: 256,
41            fail_on_revert: true,
42            max_test_rejects: 65536,
43            seed: None,
44            dictionary: FuzzDictionaryConfig::default(),
45            gas_report_samples: 256,
46            failure_persist_dir: None,
47            failure_persist_file: None,
48            show_logs: false,
49            timeout: None,
50        }
51    }
52}
53
54impl FuzzConfig {
55    /// Creates fuzz configuration to write failures in `{PROJECT_ROOT}/cache/fuzz` dir.
56    pub fn new(cache_dir: PathBuf) -> Self {
57        Self {
58            failure_persist_dir: Some(cache_dir),
59            failure_persist_file: Some("failures".to_string()),
60            ..Default::default()
61        }
62    }
63}
64
65/// Contains for fuzz testing
66#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
67pub struct FuzzDictionaryConfig {
68    /// The weight of the dictionary
69    #[serde(deserialize_with = "crate::deserialize_stringified_percent")]
70    pub dictionary_weight: u32,
71    /// The flag indicating whether to include values from storage
72    pub include_storage: bool,
73    /// The flag indicating whether to include push bytes values
74    pub include_push_bytes: bool,
75    /// How many addresses to record at most.
76    /// Once the fuzzer exceeds this limit, it will start evicting random entries
77    ///
78    /// This limit is put in place to prevent memory blowup.
79    #[serde(deserialize_with = "crate::deserialize_usize_or_max")]
80    pub max_fuzz_dictionary_addresses: usize,
81    /// How many values to record at most.
82    /// Once the fuzzer exceeds this limit, it will start evicting random entries
83    #[serde(deserialize_with = "crate::deserialize_usize_or_max")]
84    pub max_fuzz_dictionary_values: usize,
85}
86
87impl Default for FuzzDictionaryConfig {
88    fn default() -> Self {
89        Self {
90            dictionary_weight: 40,
91            include_storage: true,
92            include_push_bytes: true,
93            // limit this to 300MB
94            max_fuzz_dictionary_addresses: (300 * 1024 * 1024) / 20,
95            // limit this to 200MB
96            max_fuzz_dictionary_values: (200 * 1024 * 1024) / 32,
97        }
98    }
99}