1use serde::{Deserialize, Serialize};
4use std::collections::BTreeMap;
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum SymbolicStorageLayout {
10 #[default]
12 Solidity,
13 Generic,
15 ZeroInit,
17}
18
19#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
21#[serde(rename_all = "snake_case")]
22pub enum SymbolicExplorationOrder {
23 #[default]
25 Bfs,
26 Dfs,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
32pub struct SymbolicConfig {
33 pub enabled: bool,
35 pub seed_corpus: bool,
37 pub use_fuzz_corpus: bool,
39 pub corpus_seed_limit: usize,
41 pub use_fuzz_frontiers: bool,
43 #[serde(default)]
45 pub check_invariant_frontiers: bool,
46 pub frontier_limit: usize,
48 #[serde(default, skip_serializing_if = "Vec::is_empty")]
50 pub frontier_ids: Vec<u64>,
51 #[serde(default, skip_serializing_if = "Vec::is_empty")]
53 pub frontier_pcs: Vec<usize>,
54 #[serde(default, skip_serializing_if = "Vec::is_empty")]
56 pub frontier_selectors: Vec<String>,
57 pub solver: String,
59 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub solver_command: Option<String>,
62 #[serde(default, skip_serializing_if = "Vec::is_empty")]
64 pub solver_portfolio: Vec<String>,
65 pub timeout: Option<u32>,
68 #[serde(default, rename = "loop", skip_serializing_if = "Option::is_none")]
70 pub loop_bound: Option<u32>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub depth: Option<u32>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub width: Option<u32>,
77 pub max_depth: u32,
79 pub max_paths: u32,
81 pub invariant_depth: u32,
83 #[serde(default)]
85 pub exploration_order: SymbolicExplorationOrder,
86 pub max_solver_queries: u32,
88 pub default_dynamic_length: u32,
90 pub max_dynamic_length: u32,
92 pub array_lengths: Vec<u32>,
94 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
96 pub dynamic_lengths: BTreeMap<String, Vec<u32>>,
97 #[serde(default, skip_serializing_if = "Vec::is_empty")]
99 pub default_array_lengths: Vec<u32>,
100 #[serde(default, skip_serializing_if = "Vec::is_empty")]
102 pub default_bytes_lengths: Vec<u32>,
103 pub max_calldata_bytes: u32,
105 pub symbolic_call_targets: bool,
107 pub dump_smt: bool,
109 pub storage_layout: SymbolicStorageLayout,
111}
112
113impl Default for SymbolicConfig {
114 fn default() -> Self {
115 Self {
116 enabled: false,
117 seed_corpus: false,
118 use_fuzz_corpus: false,
119 corpus_seed_limit: 32,
120 use_fuzz_frontiers: false,
121 check_invariant_frontiers: false,
122 frontier_limit: 256,
123 frontier_ids: Vec::new(),
124 frontier_pcs: Vec::new(),
125 frontier_selectors: Vec::new(),
126 solver: "z3".to_string(),
127 solver_command: None,
128 solver_portfolio: Vec::new(),
129 timeout: Some(30),
130 loop_bound: None,
131 depth: None,
132 width: None,
133 max_depth: 10_000,
134 max_paths: 1_024,
135 invariant_depth: 10,
136 exploration_order: SymbolicExplorationOrder::default(),
137 max_solver_queries: 10_000,
138 default_dynamic_length: 2,
139 max_dynamic_length: 256,
140 array_lengths: Vec::new(),
141 dynamic_lengths: BTreeMap::new(),
142 default_array_lengths: Vec::new(),
143 default_bytes_lengths: Vec::new(),
144 max_calldata_bytes: 4_096,
145 symbolic_call_targets: false,
146 dump_smt: false,
147 storage_layout: SymbolicStorageLayout::Solidity,
148 }
149 }
150}
151
152impl SymbolicConfig {
153 pub fn execution_depth(&self) -> u32 {
158 self.depth.unwrap_or(self.max_depth)
159 }
160
161 pub fn path_width(&self) -> u32 {
166 self.width.unwrap_or(self.max_paths)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn missing_exploration_order_defaults_to_bfs() {
176 let value = serde_json::json!({
177 "enabled": false,
178 "seed_corpus": false,
179 "use_fuzz_corpus": false,
180 "corpus_seed_limit": 32,
181 "use_fuzz_frontiers": false,
182 "frontier_limit": 256,
183 "frontier_ids": [],
184 "frontier_pcs": [],
185 "frontier_selectors": [],
186 "solver": "z3",
187 "timeout": 30,
188 "max_depth": 10000,
189 "max_paths": 1024,
190 "invariant_depth": 10,
191 "max_solver_queries": 10000,
192 "default_dynamic_length": 2,
193 "max_dynamic_length": 256,
194 "array_lengths": [],
195 "max_calldata_bytes": 4096,
196 "symbolic_call_targets": false,
197 "dump_smt": false,
198 "storage_layout": "solidity"
199 });
200
201 let config: SymbolicConfig = serde_json::from_value(value).unwrap();
202
203 assert_eq!(config.exploration_order, SymbolicExplorationOrder::Bfs);
204 }
205}