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}