1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
4
5use alloy_dyn_abi::{DynSolType, DynSolValue, JsonAbiExt};
6use alloy_json_abi::Function;
7use alloy_primitives::{
8 Address, B256, Bytes, I256, U256, hex, keccak256,
9 map::{HashMap, HashSet, IndexSet},
10};
11use alloy_signer::SignerSync;
12use alloy_signer_local::{
13 PrivateKeySigner,
14 coins_bip39::{English, Wordlist},
15};
16use alloy_sol_types::SolCall;
17use base64::prelude::*;
18use foundry_cheatcodes_spec::{SymbolicVm, Vm};
19use foundry_config::{SymbolicConfig, SymbolicExplorationOrder, SymbolicStorageLayout};
20use foundry_evm::{
21 constants::{CHEATCODE_ADDRESS, DEFAULT_CREATE2_DEPLOYER, HARDHAT_CONSOLE_ADDRESS},
22 core::{backend::DatabaseExt, evm::FoundryEvmNetwork},
23 executors::Executor,
24 revm::{
25 bytecode::{Bytecode, JumpTable, opcode},
26 context::{Block, Transaction},
27 database::DatabaseRef,
28 precompile::{blake2, bn254, hash, identity, kzg_point_evaluation, modexp, secp256k1},
29 primitives::hardfork::SpecId,
30 },
31};
32use serde::{Deserialize, Serialize};
33#[cfg(test)]
34use std::collections::BTreeMap;
35use std::{
36 collections::VecDeque,
37 fmt::{self, Write as _},
38 io::Write,
39 ops::{ControlFlow, Deref, DerefMut},
40 path::{Path, PathBuf},
41 process::{Command, Stdio},
42 sync::{
43 Arc,
44 atomic::{AtomicBool, Ordering},
45 mpsc,
46 },
47 thread,
48 time::{Duration, Instant, SystemTime, UNIX_EPOCH},
49};
50use thiserror::Error;
51use tracing::{debug, trace, trace_span, warn};
52
53mod consts;
54pub use consts::BUILTIN_SYMBOLIC_SOLVERS;
55pub(crate) use consts::*;
56
57mod abi;
58mod executor;
59mod runtime;
60
61pub use runtime::{PortfolioDiagnostics, SymbolicBranchTarget, SymbolicError, SymbolicRunInput};
62
63pub fn symbolic_solver_is_builtin(solver: &str) -> bool {
65 BUILTIN_SYMBOLIC_SOLVERS.contains(&solver)
66}
67
68pub fn symbolic_solver_portfolio_availability_warning(config: &SymbolicConfig) -> Option<String> {
70 runtime::solver_portfolio_availability_warning(config)
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq)]
74enum SymbolicVmCheatcode {
75 CreateAddress,
76 CreateBool,
77 CreateBytes,
78 CreateBytesSized,
79 CreateBytesFixed(usize),
80 CreateCalldata,
81 CreateInt,
82 CreateIntBits(usize),
83 CreateString,
84 CreateStringSized,
85 CreateUint,
86 CreateUintBits(usize),
87 EnableSymbolicStorage,
88 SnapshotStorage,
89 SnapshotState,
90}
91
92impl SymbolicVmCheatcode {
93 fn from_selector(selector: [u8; 4]) -> Option<Self> {
94 match selector {
95 SymbolicVm::createAddressCall::SELECTOR => Some(Self::CreateAddress),
96 SymbolicVm::createBoolCall::SELECTOR => Some(Self::CreateBool),
97 SymbolicVm::createBytes_0Call::SELECTOR => Some(Self::CreateBytes),
98 SymbolicVm::createBytes_1Call::SELECTOR => Some(Self::CreateBytesSized),
99 SymbolicVm::createCalldataCall::SELECTOR => Some(Self::CreateCalldata),
100 SymbolicVm::createIntCall::SELECTOR => Some(Self::CreateInt),
101 SymbolicVm::createString_0Call::SELECTOR => Some(Self::CreateString),
102 SymbolicVm::createString_1Call::SELECTOR => Some(Self::CreateStringSized),
103 SymbolicVm::createUintCall::SELECTOR => Some(Self::CreateUint),
104 SymbolicVm::enableSymbolicStorageCall::SELECTOR
105 | Vm::setArbitraryStorage_0Call::SELECTOR => Some(Self::EnableSymbolicStorage),
106 SymbolicVm::snapshotStorageCall::SELECTOR => Some(Self::SnapshotStorage),
107 Vm::snapshotStateCall::SELECTOR => Some(Self::SnapshotState),
108 _ => {
109 for &(bits, candidate) in symbolic_create_uint_selectors() {
110 if selector == candidate {
111 return Some(Self::CreateUintBits(bits));
112 }
113 }
114 for &(bits, candidate) in symbolic_create_int_selectors() {
115 if selector == candidate {
116 return Some(Self::CreateIntBits(bits));
117 }
118 }
119 for &(bytes, candidate) in symbolic_create_bytes_selectors() {
120 if selector == candidate {
121 return Some(Self::CreateBytesFixed(bytes));
122 }
123 }
124 None
125 }
126 }
127 }
128
129 const fn min_input_words(self) -> usize {
130 match self {
131 Self::CreateUint
132 | Self::CreateInt
133 | Self::CreateBytesSized
134 | Self::CreateStringSized
135 | Self::EnableSymbolicStorage
136 | Self::SnapshotStorage => 1,
137 Self::CreateAddress
138 | Self::CreateBool
139 | Self::CreateBytes
140 | Self::CreateBytesFixed(_)
141 | Self::CreateCalldata
142 | Self::CreateIntBits(_)
143 | Self::CreateString
144 | Self::CreateUintBits(_)
145 | Self::SnapshotState => 0,
146 }
147 }
148}
149
150fn symbolic_create_uint_selectors() -> &'static [(usize, [u8; 4]); 32] {
151 static SELECTORS: [(usize, [u8; 4]); 32] = [
152 (8, SymbolicVm::createUint8Call::SELECTOR),
153 (16, SymbolicVm::createUint16Call::SELECTOR),
154 (24, SymbolicVm::createUint24Call::SELECTOR),
155 (32, SymbolicVm::createUint32Call::SELECTOR),
156 (40, SymbolicVm::createUint40Call::SELECTOR),
157 (48, SymbolicVm::createUint48Call::SELECTOR),
158 (56, SymbolicVm::createUint56Call::SELECTOR),
159 (64, SymbolicVm::createUint64Call::SELECTOR),
160 (72, SymbolicVm::createUint72Call::SELECTOR),
161 (80, SymbolicVm::createUint80Call::SELECTOR),
162 (88, SymbolicVm::createUint88Call::SELECTOR),
163 (96, SymbolicVm::createUint96Call::SELECTOR),
164 (104, SymbolicVm::createUint104Call::SELECTOR),
165 (112, SymbolicVm::createUint112Call::SELECTOR),
166 (120, SymbolicVm::createUint120Call::SELECTOR),
167 (128, SymbolicVm::createUint128Call::SELECTOR),
168 (136, SymbolicVm::createUint136Call::SELECTOR),
169 (144, SymbolicVm::createUint144Call::SELECTOR),
170 (152, SymbolicVm::createUint152Call::SELECTOR),
171 (160, SymbolicVm::createUint160Call::SELECTOR),
172 (168, SymbolicVm::createUint168Call::SELECTOR),
173 (176, SymbolicVm::createUint176Call::SELECTOR),
174 (184, SymbolicVm::createUint184Call::SELECTOR),
175 (192, SymbolicVm::createUint192Call::SELECTOR),
176 (200, SymbolicVm::createUint200Call::SELECTOR),
177 (208, SymbolicVm::createUint208Call::SELECTOR),
178 (216, SymbolicVm::createUint216Call::SELECTOR),
179 (224, SymbolicVm::createUint224Call::SELECTOR),
180 (232, SymbolicVm::createUint232Call::SELECTOR),
181 (240, SymbolicVm::createUint240Call::SELECTOR),
182 (248, SymbolicVm::createUint248Call::SELECTOR),
183 (256, SymbolicVm::createUint256Call::SELECTOR),
184 ];
185 &SELECTORS
186}
187
188fn symbolic_create_int_selectors() -> &'static [(usize, [u8; 4]); 32] {
189 static SELECTORS: [(usize, [u8; 4]); 32] = [
190 (8, SymbolicVm::createInt8Call::SELECTOR),
191 (16, SymbolicVm::createInt16Call::SELECTOR),
192 (24, SymbolicVm::createInt24Call::SELECTOR),
193 (32, SymbolicVm::createInt32Call::SELECTOR),
194 (40, SymbolicVm::createInt40Call::SELECTOR),
195 (48, SymbolicVm::createInt48Call::SELECTOR),
196 (56, SymbolicVm::createInt56Call::SELECTOR),
197 (64, SymbolicVm::createInt64Call::SELECTOR),
198 (72, SymbolicVm::createInt72Call::SELECTOR),
199 (80, SymbolicVm::createInt80Call::SELECTOR),
200 (88, SymbolicVm::createInt88Call::SELECTOR),
201 (96, SymbolicVm::createInt96Call::SELECTOR),
202 (104, SymbolicVm::createInt104Call::SELECTOR),
203 (112, SymbolicVm::createInt112Call::SELECTOR),
204 (120, SymbolicVm::createInt120Call::SELECTOR),
205 (128, SymbolicVm::createInt128Call::SELECTOR),
206 (136, SymbolicVm::createInt136Call::SELECTOR),
207 (144, SymbolicVm::createInt144Call::SELECTOR),
208 (152, SymbolicVm::createInt152Call::SELECTOR),
209 (160, SymbolicVm::createInt160Call::SELECTOR),
210 (168, SymbolicVm::createInt168Call::SELECTOR),
211 (176, SymbolicVm::createInt176Call::SELECTOR),
212 (184, SymbolicVm::createInt184Call::SELECTOR),
213 (192, SymbolicVm::createInt192Call::SELECTOR),
214 (200, SymbolicVm::createInt200Call::SELECTOR),
215 (208, SymbolicVm::createInt208Call::SELECTOR),
216 (216, SymbolicVm::createInt216Call::SELECTOR),
217 (224, SymbolicVm::createInt224Call::SELECTOR),
218 (232, SymbolicVm::createInt232Call::SELECTOR),
219 (240, SymbolicVm::createInt240Call::SELECTOR),
220 (248, SymbolicVm::createInt248Call::SELECTOR),
221 (256, SymbolicVm::createInt256Call::SELECTOR),
222 ];
223 &SELECTORS
224}
225
226fn symbolic_create_bytes_selectors() -> &'static [(usize, [u8; 4]); 32] {
227 static SELECTORS: [(usize, [u8; 4]); 32] = [
228 (1, SymbolicVm::createBytes1Call::SELECTOR),
229 (2, SymbolicVm::createBytes2Call::SELECTOR),
230 (3, SymbolicVm::createBytes3Call::SELECTOR),
231 (4, SymbolicVm::createBytes4Call::SELECTOR),
232 (5, SymbolicVm::createBytes5Call::SELECTOR),
233 (6, SymbolicVm::createBytes6Call::SELECTOR),
234 (7, SymbolicVm::createBytes7Call::SELECTOR),
235 (8, SymbolicVm::createBytes8Call::SELECTOR),
236 (9, SymbolicVm::createBytes9Call::SELECTOR),
237 (10, SymbolicVm::createBytes10Call::SELECTOR),
238 (11, SymbolicVm::createBytes11Call::SELECTOR),
239 (12, SymbolicVm::createBytes12Call::SELECTOR),
240 (13, SymbolicVm::createBytes13Call::SELECTOR),
241 (14, SymbolicVm::createBytes14Call::SELECTOR),
242 (15, SymbolicVm::createBytes15Call::SELECTOR),
243 (16, SymbolicVm::createBytes16Call::SELECTOR),
244 (17, SymbolicVm::createBytes17Call::SELECTOR),
245 (18, SymbolicVm::createBytes18Call::SELECTOR),
246 (19, SymbolicVm::createBytes19Call::SELECTOR),
247 (20, SymbolicVm::createBytes20Call::SELECTOR),
248 (21, SymbolicVm::createBytes21Call::SELECTOR),
249 (22, SymbolicVm::createBytes22Call::SELECTOR),
250 (23, SymbolicVm::createBytes23Call::SELECTOR),
251 (24, SymbolicVm::createBytes24Call::SELECTOR),
252 (25, SymbolicVm::createBytes25Call::SELECTOR),
253 (26, SymbolicVm::createBytes26Call::SELECTOR),
254 (27, SymbolicVm::createBytes27Call::SELECTOR),
255 (28, SymbolicVm::createBytes28Call::SELECTOR),
256 (29, SymbolicVm::createBytes29Call::SELECTOR),
257 (30, SymbolicVm::createBytes30Call::SELECTOR),
258 (31, SymbolicVm::createBytes31Call::SELECTOR),
259 (32, SymbolicVm::createBytes32Call::SELECTOR),
260 ];
261 &SELECTORS
262}
263
264#[derive(Clone, Debug)]
271pub enum SymbolicRunResult {
272 Safe {
274 stats: SymbolicStats,
276 success_input: Option<SymbolicConcreteInput>,
278 },
279 Counterexample {
281 args: Vec<DynSolValue>,
283 calldata: Bytes,
285 stats: SymbolicStats,
287 },
288 Incomplete {
290 kind: SymbolicStopReason,
292 reason: String,
294 stats: SymbolicStats,
296 },
297}
298
299#[derive(Clone, Debug)]
301pub struct SymbolicConcreteInput {
302 pub args: Vec<DynSolValue>,
304 pub calldata: Bytes,
306}
307
308#[derive(Clone, Debug)]
310pub struct SymbolicInvariantTarget {
311 pub address: Address,
313 pub contract_name: Option<String>,
315 pub function: Function,
317}
318
319pub struct SymbolicInvariantRunInput<'a, FEN: FoundryEvmNetwork> {
321 pub executor: &'a Executor<FEN>,
323 pub invariant_address: Address,
325 pub sender: Address,
327 pub invariant: &'a Function,
329 pub after_invariant: Option<&'a Function>,
331 pub targets: Vec<SymbolicInvariantTarget>,
333 pub senders: Vec<Address>,
335 pub excluded_senders: Vec<Address>,
337 pub depth: usize,
339 pub check_interval: u32,
341 pub fail_on_revert: bool,
343 pub ffi_enabled: bool,
345}
346
347#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
349pub struct SymbolicStorageAssignment {
350 pub address: Address,
352 pub slot: U256,
354 pub value: U256,
356}
357
358#[derive(Clone, Debug)]
360pub enum SymbolicInvariantRunResult {
361 Safe(SymbolicStats),
363 Counterexample {
365 kind: SymbolicInvariantCounterexampleKind,
367 sequence: Vec<SymbolicInvariantStep>,
369 storage: Vec<SymbolicStorageAssignment>,
371 stats: SymbolicStats,
373 },
374 Incomplete {
376 kind: SymbolicStopReason,
378 reason: String,
380 stats: SymbolicStats,
382 },
383}
384
385#[derive(Clone, Copy, Debug, PartialEq, Eq)]
387pub enum SymbolicInvariantCounterexampleKind {
388 Predicate,
390 Handler,
392}
393
394#[derive(Clone, Debug)]
396pub struct SymbolicInvariantStep {
397 pub sender: Address,
399 pub address: Address,
401 pub contract_name: Option<String>,
403 pub function_name: String,
405 pub signature: String,
407 pub args: Vec<DynSolValue>,
409 pub calldata: Bytes,
411}
412
413#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
415pub enum SymbolicStopReason {
416 Stuck,
418 RevertAll,
420 Timeout,
422 Error,
424}
425
426#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
428pub struct SymbolicStats {
429 pub paths: usize,
431 pub solver_queries: usize,
433 #[serde(default)]
435 pub smt_queries: usize,
436 #[serde(default)]
438 pub sat_queries: usize,
439 #[serde(default)]
441 pub model_queries: usize,
442 #[serde(default)]
444 pub sat_cache_hits: usize,
445 #[serde(default)]
447 pub model_cache_hits: usize,
448 #[serde(default)]
450 pub heuristic_witnesses: usize,
451 #[serde(default)]
453 pub solver_time_ms: u64,
454 #[serde(default)]
456 pub smt_input_bytes: u64,
457 #[serde(default)]
459 pub smt_max_query_bytes: u64,
460 #[serde(default)]
462 pub smt_build_time_ms: u64,
463 #[serde(default)]
465 pub smt_max_query_time_ms: u64,
466}
467
468pub struct SymbolicExecutor {
475 config: SymbolicConfig,
476 cx: runtime::SymCx,
477 solver: Box<dyn runtime::SymbolicSolver>,
478 deferred_incomplete: Option<DeferredIncomplete>,
479 deadline: Option<Instant>,
480}
481
482#[derive(Clone, Copy, Debug)]
483enum DeferredIncomplete {
484 Unsupported(&'static str),
485 SolverUnknown,
486}
487
488#[cfg(test)]
489mod tests;