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 solver: String,
37 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub solver_command: Option<String>,
40 #[serde(default, skip_serializing_if = "Vec::is_empty")]
42 pub solver_portfolio: Vec<String>,
43 pub timeout: Option<u32>,
45 #[serde(default, rename = "loop", skip_serializing_if = "Option::is_none")]
47 pub loop_bound: Option<u32>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub depth: Option<u32>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub width: Option<u32>,
54 pub max_depth: u32,
56 pub max_paths: u32,
58 pub invariant_depth: u32,
60 #[serde(default)]
62 pub exploration_order: SymbolicExplorationOrder,
63 pub max_solver_queries: u32,
65 pub default_dynamic_length: u32,
67 pub max_dynamic_length: u32,
69 pub array_lengths: Vec<u32>,
71 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
73 pub dynamic_lengths: BTreeMap<String, Vec<u32>>,
74 #[serde(default, skip_serializing_if = "Vec::is_empty")]
76 pub default_array_lengths: Vec<u32>,
77 #[serde(default, skip_serializing_if = "Vec::is_empty")]
79 pub default_bytes_lengths: Vec<u32>,
80 pub max_calldata_bytes: u32,
82 pub symbolic_call_targets: bool,
84 pub dump_smt: bool,
86 pub storage_layout: SymbolicStorageLayout,
88}
89
90impl Default for SymbolicConfig {
91 fn default() -> Self {
92 Self {
93 enabled: false,
94 solver: "z3".to_string(),
95 solver_command: None,
96 solver_portfolio: Vec::new(),
97 timeout: Some(30),
98 loop_bound: None,
99 depth: None,
100 width: None,
101 max_depth: 10_000,
102 max_paths: 1_024,
103 invariant_depth: 10,
104 exploration_order: SymbolicExplorationOrder::default(),
105 max_solver_queries: 10_000,
106 default_dynamic_length: 2,
107 max_dynamic_length: 256,
108 array_lengths: Vec::new(),
109 dynamic_lengths: BTreeMap::new(),
110 default_array_lengths: Vec::new(),
111 default_bytes_lengths: Vec::new(),
112 max_calldata_bytes: 4_096,
113 symbolic_call_targets: false,
114 dump_smt: false,
115 storage_layout: SymbolicStorageLayout::Solidity,
116 }
117 }
118}
119
120impl SymbolicConfig {
121 pub fn execution_depth(&self) -> u32 {
126 self.depth.unwrap_or(self.max_depth)
127 }
128
129 pub fn path_width(&self) -> u32 {
134 self.width.unwrap_or(self.max_paths)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn missing_exploration_order_defaults_to_bfs() {
144 let value = serde_json::json!({
145 "enabled": false,
146 "solver": "z3",
147 "timeout": 30,
148 "max_depth": 10000,
149 "max_paths": 1024,
150 "invariant_depth": 10,
151 "max_solver_queries": 10000,
152 "default_dynamic_length": 2,
153 "max_dynamic_length": 256,
154 "array_lengths": [],
155 "max_calldata_bytes": 4096,
156 "symbolic_call_targets": false,
157 "dump_smt": false,
158 "storage_layout": "solidity"
159 });
160
161 let config: SymbolicConfig = serde_json::from_value(value).unwrap();
162
163 assert_eq!(config.exploration_order, SymbolicExplorationOrder::Bfs);
164 }
165}