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::{CALLER, 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, Cfg, 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};
33use std::{
34 collections::VecDeque,
35 fmt::{self, Write as _},
36 io::Write,
37 ops::{ControlFlow, Deref, DerefMut},
38 path::{Path, PathBuf},
39 process::{Command, Stdio},
40 sync::{
41 Arc,
42 atomic::{AtomicBool, Ordering},
43 mpsc,
44 },
45 thread,
46 time::{Duration, Instant, SystemTime, UNIX_EPOCH},
47};
48use thiserror::Error;
49use tracing::{debug, trace, trace_span, warn};
50
51#[cfg(test)]
52use std::collections::BTreeMap;
53
54mod abi;
55mod consts;
56mod executor;
57mod runtime;
58
59pub use consts::BUILTIN_SYMBOLIC_SOLVERS;
60pub(crate) use consts::*;
61pub use runtime::{PortfolioDiagnostics, SymbolicBranchTarget, SymbolicError, SymbolicRunInput};
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64enum SymbolicVmCheatcode {
65 CreateAddress,
66 CreateBool,
67 CreateBytes,
68 CreateBytesSized,
69 CreateBytesFixed(usize),
70 CreateCalldata,
71 CreateInt,
72 CreateIntBits(usize),
73 CreateString,
74 CreateStringSized,
75 CreateUint,
76 CreateUintBits(usize),
77 EnableSymbolicStorage,
78 SnapshotStorage,
79 SnapshotState,
80}
81
82impl SymbolicVmCheatcode {
83 fn from_selector(selector: [u8; 4]) -> Option<Self> {
84 match selector {
85 SymbolicVm::createAddressCall::SELECTOR => Some(Self::CreateAddress),
86 SymbolicVm::createBoolCall::SELECTOR => Some(Self::CreateBool),
87 SymbolicVm::createBytes_0Call::SELECTOR => Some(Self::CreateBytes),
88 SymbolicVm::createBytes_1Call::SELECTOR => Some(Self::CreateBytesSized),
89 SymbolicVm::createCalldataCall::SELECTOR => Some(Self::CreateCalldata),
90 SymbolicVm::createIntCall::SELECTOR => Some(Self::CreateInt),
91 SymbolicVm::createString_0Call::SELECTOR => Some(Self::CreateString),
92 SymbolicVm::createString_1Call::SELECTOR => Some(Self::CreateStringSized),
93 SymbolicVm::createUintCall::SELECTOR => Some(Self::CreateUint),
94 SymbolicVm::enableSymbolicStorageCall::SELECTOR
95 | Vm::setArbitraryStorage_0Call::SELECTOR => Some(Self::EnableSymbolicStorage),
96 SymbolicVm::snapshotStorageCall::SELECTOR => Some(Self::SnapshotStorage),
97 Vm::snapshotStateCall::SELECTOR => Some(Self::SnapshotState),
98 _ => {
99 for &(bits, candidate) in symbolic_create_uint_selectors() {
100 if selector == candidate {
101 return Some(Self::CreateUintBits(bits));
102 }
103 }
104 for &(bits, candidate) in symbolic_create_int_selectors() {
105 if selector == candidate {
106 return Some(Self::CreateIntBits(bits));
107 }
108 }
109 for &(bytes, candidate) in symbolic_create_bytes_selectors() {
110 if selector == candidate {
111 return Some(Self::CreateBytesFixed(bytes));
112 }
113 }
114 None
115 }
116 }
117 }
118
119 const fn min_input_words(self) -> usize {
120 match self {
121 Self::CreateUint
122 | Self::CreateInt
123 | Self::CreateBytesSized
124 | Self::CreateStringSized
125 | Self::EnableSymbolicStorage
126 | Self::SnapshotStorage => 1,
127 Self::CreateAddress
128 | Self::CreateBool
129 | Self::CreateBytes
130 | Self::CreateBytesFixed(_)
131 | Self::CreateCalldata
132 | Self::CreateIntBits(_)
133 | Self::CreateString
134 | Self::CreateUintBits(_)
135 | Self::SnapshotState => 0,
136 }
137 }
138}
139
140#[derive(Clone, Debug)]
147pub enum SymbolicRunResult {
148 Safe {
150 stats: SymbolicStats,
152 success_input: Option<SymbolicConcreteInput>,
154 },
155 Counterexample {
157 args: Vec<DynSolValue>,
159 calldata: Bytes,
161 stats: SymbolicStats,
163 },
164 Incomplete {
166 kind: SymbolicStopReason,
168 reason: String,
170 stats: SymbolicStats,
172 },
173}
174
175#[derive(Clone, Debug)]
177pub struct SymbolicConcreteInput {
178 pub args: Vec<DynSolValue>,
180 pub calldata: Bytes,
182}
183
184#[derive(Debug)]
186pub struct SymbolicBranchTargetSearchResult {
187 pub candidates: Vec<SymbolicConcreteInput>,
189 pub execution: SymbolicRunResult,
191}
192
193#[derive(Clone, Debug)]
195pub struct SymbolicInvariantTarget {
196 pub address: Address,
198 pub contract_name: Option<String>,
200 pub function: Function,
202}
203
204pub struct SymbolicInvariantCandidateInput<'a, FEN: FoundryEvmNetwork> {
206 pub executor: &'a Executor<FEN>,
208 pub invariant_address: Address,
210 pub invariants: &'a [&'a Function],
212 pub after_invariant: Option<&'a Function>,
214 pub target: &'a SymbolicInvariantTarget,
216 pub handler_sender: Address,
218 pub ffi_enabled: bool,
220}
221
222#[derive(Clone, Debug)]
224pub struct SymbolicInvariantCandidate {
225 pub invariant_idx: usize,
227 pub step: SymbolicInvariantStep,
229 pub storage: Vec<SymbolicStorageAssignment>,
231}
232
233#[derive(Clone, Debug, PartialEq, Eq)]
235pub struct SymbolicInvariantSearchLimitation {
236 pub kind: SymbolicStopReason,
238 pub reason: String,
240}
241
242impl From<SymbolicError> for SymbolicInvariantSearchLimitation {
243 fn from(error: SymbolicError) -> Self {
244 Self { kind: error.stop_reason(), reason: error.to_string() }
245 }
246}
247
248#[derive(Clone, Debug)]
250pub struct SymbolicInvariantCandidateSearchResult {
251 pub candidates: Vec<SymbolicInvariantCandidate>,
253 pub limitation: Option<SymbolicInvariantSearchLimitation>,
255}
256
257pub struct SymbolicInvariantRunInput<'a, FEN: FoundryEvmNetwork> {
259 pub executor: &'a Executor<FEN>,
261 pub invariant_address: Address,
263 pub sender: Address,
265 pub invariant: &'a Function,
267 pub after_invariant: Option<&'a Function>,
269 pub targets: Vec<SymbolicInvariantTarget>,
271 pub senders: Vec<Address>,
273 pub excluded_senders: Vec<Address>,
275 pub depth: usize,
277 pub check_interval: u32,
279 pub fail_on_revert: bool,
281 pub ffi_enabled: bool,
283}
284
285#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
287pub struct SymbolicStorageAssignment {
288 pub address: Address,
290 pub slot: U256,
292 pub value: U256,
294}
295
296#[derive(Clone, Debug)]
298pub enum SymbolicInvariantRunResult {
299 Safe(SymbolicStats),
301 Counterexample {
303 kind: SymbolicInvariantCounterexampleKind,
305 sequence: Vec<SymbolicInvariantStep>,
307 storage: Vec<SymbolicStorageAssignment>,
309 stats: SymbolicStats,
311 },
312 Incomplete {
314 kind: SymbolicStopReason,
316 reason: String,
318 stats: SymbolicStats,
320 },
321}
322
323#[derive(Clone, Copy, Debug, PartialEq, Eq)]
325pub enum SymbolicInvariantCounterexampleKind {
326 Predicate,
328 Handler,
330}
331
332#[derive(Clone, Debug)]
334pub struct SymbolicInvariantStep {
335 pub sender: Address,
337 pub address: Address,
339 pub contract_name: Option<String>,
341 pub function_name: String,
343 pub signature: String,
345 pub args: Vec<DynSolValue>,
347 pub calldata: Bytes,
349}
350
351#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
353pub enum SymbolicStopReason {
354 Stuck,
356 RevertAll,
358 Timeout,
360 Error,
362}
363
364#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
366pub struct SymbolicStats {
367 pub paths: usize,
369 pub solver_queries: usize,
371 #[serde(default)]
373 pub smt_queries: usize,
374 #[serde(default)]
376 pub sat_queries: usize,
377 #[serde(default)]
379 pub model_queries: usize,
380 #[serde(default)]
382 pub sat_cache_hits: usize,
383 #[serde(default)]
385 pub model_cache_hits: usize,
386 #[serde(default)]
388 pub heuristic_witnesses: usize,
389 #[serde(default)]
391 pub solver_time_ms: u64,
392 #[serde(default)]
394 pub smt_input_bytes: u64,
395 #[serde(default)]
397 pub smt_max_query_bytes: u64,
398 #[serde(default)]
400 pub smt_build_time_ms: u64,
401 #[serde(default)]
403 pub smt_max_query_time_ms: u64,
404}
405
406pub struct SymbolicExecutor {
413 config: SymbolicConfig,
414 cx: runtime::SymCx,
415 solver: runtime::SmtLibSubprocessSolver,
416 deferred_incomplete: Option<DeferredIncomplete>,
417 deadline: Option<Instant>,
418 nested_deferred_mode: DeferredPathMode,
419 stateless_retry_safe: bool,
420}
421
422#[derive(Clone, Copy, Debug, PartialEq, Eq)]
423enum DeferredPathMode {
424 Skip,
425 Yield,
426 Drain,
427}
428
429#[derive(Clone, Copy, Debug, PartialEq, Eq)]
430enum DeferredIncomplete {
431 Unsupported(&'static str),
432 SolverUnknown,
433 HardArithmetic,
434}
435
436fn symbolic_create_uint_selectors() -> &'static [(usize, [u8; 4]); 32] {
437 static SELECTORS: [(usize, [u8; 4]); 32] = [
438 (8, SymbolicVm::createUint8Call::SELECTOR),
439 (16, SymbolicVm::createUint16Call::SELECTOR),
440 (24, SymbolicVm::createUint24Call::SELECTOR),
441 (32, SymbolicVm::createUint32Call::SELECTOR),
442 (40, SymbolicVm::createUint40Call::SELECTOR),
443 (48, SymbolicVm::createUint48Call::SELECTOR),
444 (56, SymbolicVm::createUint56Call::SELECTOR),
445 (64, SymbolicVm::createUint64Call::SELECTOR),
446 (72, SymbolicVm::createUint72Call::SELECTOR),
447 (80, SymbolicVm::createUint80Call::SELECTOR),
448 (88, SymbolicVm::createUint88Call::SELECTOR),
449 (96, SymbolicVm::createUint96Call::SELECTOR),
450 (104, SymbolicVm::createUint104Call::SELECTOR),
451 (112, SymbolicVm::createUint112Call::SELECTOR),
452 (120, SymbolicVm::createUint120Call::SELECTOR),
453 (128, SymbolicVm::createUint128Call::SELECTOR),
454 (136, SymbolicVm::createUint136Call::SELECTOR),
455 (144, SymbolicVm::createUint144Call::SELECTOR),
456 (152, SymbolicVm::createUint152Call::SELECTOR),
457 (160, SymbolicVm::createUint160Call::SELECTOR),
458 (168, SymbolicVm::createUint168Call::SELECTOR),
459 (176, SymbolicVm::createUint176Call::SELECTOR),
460 (184, SymbolicVm::createUint184Call::SELECTOR),
461 (192, SymbolicVm::createUint192Call::SELECTOR),
462 (200, SymbolicVm::createUint200Call::SELECTOR),
463 (208, SymbolicVm::createUint208Call::SELECTOR),
464 (216, SymbolicVm::createUint216Call::SELECTOR),
465 (224, SymbolicVm::createUint224Call::SELECTOR),
466 (232, SymbolicVm::createUint232Call::SELECTOR),
467 (240, SymbolicVm::createUint240Call::SELECTOR),
468 (248, SymbolicVm::createUint248Call::SELECTOR),
469 (256, SymbolicVm::createUint256Call::SELECTOR),
470 ];
471 &SELECTORS
472}
473
474fn symbolic_create_int_selectors() -> &'static [(usize, [u8; 4]); 32] {
475 static SELECTORS: [(usize, [u8; 4]); 32] = [
476 (8, SymbolicVm::createInt8Call::SELECTOR),
477 (16, SymbolicVm::createInt16Call::SELECTOR),
478 (24, SymbolicVm::createInt24Call::SELECTOR),
479 (32, SymbolicVm::createInt32Call::SELECTOR),
480 (40, SymbolicVm::createInt40Call::SELECTOR),
481 (48, SymbolicVm::createInt48Call::SELECTOR),
482 (56, SymbolicVm::createInt56Call::SELECTOR),
483 (64, SymbolicVm::createInt64Call::SELECTOR),
484 (72, SymbolicVm::createInt72Call::SELECTOR),
485 (80, SymbolicVm::createInt80Call::SELECTOR),
486 (88, SymbolicVm::createInt88Call::SELECTOR),
487 (96, SymbolicVm::createInt96Call::SELECTOR),
488 (104, SymbolicVm::createInt104Call::SELECTOR),
489 (112, SymbolicVm::createInt112Call::SELECTOR),
490 (120, SymbolicVm::createInt120Call::SELECTOR),
491 (128, SymbolicVm::createInt128Call::SELECTOR),
492 (136, SymbolicVm::createInt136Call::SELECTOR),
493 (144, SymbolicVm::createInt144Call::SELECTOR),
494 (152, SymbolicVm::createInt152Call::SELECTOR),
495 (160, SymbolicVm::createInt160Call::SELECTOR),
496 (168, SymbolicVm::createInt168Call::SELECTOR),
497 (176, SymbolicVm::createInt176Call::SELECTOR),
498 (184, SymbolicVm::createInt184Call::SELECTOR),
499 (192, SymbolicVm::createInt192Call::SELECTOR),
500 (200, SymbolicVm::createInt200Call::SELECTOR),
501 (208, SymbolicVm::createInt208Call::SELECTOR),
502 (216, SymbolicVm::createInt216Call::SELECTOR),
503 (224, SymbolicVm::createInt224Call::SELECTOR),
504 (232, SymbolicVm::createInt232Call::SELECTOR),
505 (240, SymbolicVm::createInt240Call::SELECTOR),
506 (248, SymbolicVm::createInt248Call::SELECTOR),
507 (256, SymbolicVm::createInt256Call::SELECTOR),
508 ];
509 &SELECTORS
510}
511
512fn symbolic_create_bytes_selectors() -> &'static [(usize, [u8; 4]); 32] {
513 static SELECTORS: [(usize, [u8; 4]); 32] = [
514 (1, SymbolicVm::createBytes1Call::SELECTOR),
515 (2, SymbolicVm::createBytes2Call::SELECTOR),
516 (3, SymbolicVm::createBytes3Call::SELECTOR),
517 (4, SymbolicVm::createBytes4Call::SELECTOR),
518 (5, SymbolicVm::createBytes5Call::SELECTOR),
519 (6, SymbolicVm::createBytes6Call::SELECTOR),
520 (7, SymbolicVm::createBytes7Call::SELECTOR),
521 (8, SymbolicVm::createBytes8Call::SELECTOR),
522 (9, SymbolicVm::createBytes9Call::SELECTOR),
523 (10, SymbolicVm::createBytes10Call::SELECTOR),
524 (11, SymbolicVm::createBytes11Call::SELECTOR),
525 (12, SymbolicVm::createBytes12Call::SELECTOR),
526 (13, SymbolicVm::createBytes13Call::SELECTOR),
527 (14, SymbolicVm::createBytes14Call::SELECTOR),
528 (15, SymbolicVm::createBytes15Call::SELECTOR),
529 (16, SymbolicVm::createBytes16Call::SELECTOR),
530 (17, SymbolicVm::createBytes17Call::SELECTOR),
531 (18, SymbolicVm::createBytes18Call::SELECTOR),
532 (19, SymbolicVm::createBytes19Call::SELECTOR),
533 (20, SymbolicVm::createBytes20Call::SELECTOR),
534 (21, SymbolicVm::createBytes21Call::SELECTOR),
535 (22, SymbolicVm::createBytes22Call::SELECTOR),
536 (23, SymbolicVm::createBytes23Call::SELECTOR),
537 (24, SymbolicVm::createBytes24Call::SELECTOR),
538 (25, SymbolicVm::createBytes25Call::SELECTOR),
539 (26, SymbolicVm::createBytes26Call::SELECTOR),
540 (27, SymbolicVm::createBytes27Call::SELECTOR),
541 (28, SymbolicVm::createBytes28Call::SELECTOR),
542 (29, SymbolicVm::createBytes29Call::SELECTOR),
543 (30, SymbolicVm::createBytes30Call::SELECTOR),
544 (31, SymbolicVm::createBytes31Call::SELECTOR),
545 (32, SymbolicVm::createBytes32Call::SELECTOR),
546 ];
547 &SELECTORS
548}
549
550pub fn symbolic_solver_is_builtin(solver: &str) -> bool {
552 BUILTIN_SYMBOLIC_SOLVERS.contains(&solver)
553}
554
555pub fn symbolic_solver_portfolio_availability_warning(config: &SymbolicConfig) -> Option<String> {
557 runtime::solver_portfolio_availability_warning(config)
558}
559
560#[cfg(test)]
561mod tests;