1use super::{runtime::*, *};
2
3#[derive(Clone, Debug)]
4pub(super) struct SymbolicCalldata {
5 bytes: SymBytes,
6 inputs: Vec<SymbolicInput>,
7 constraints: Vec<SymBoolExpr>,
8}
9
10impl SymbolicCalldata {
11 pub(super) fn variants(
12 function: &Function,
13 config: &SymbolicConfig,
14 cx: &mut SymCx,
15 ) -> Result<Vec<Self>, SymbolicError> {
16 Self::variants_with_prefix(function, config, cx, "calldata")
17 }
18
19 pub(super) fn selector_only(
20 cx: &mut SymCx,
21 function: &Function,
22 ) -> Result<Self, SymbolicError> {
23 if !function.inputs.is_empty() {
24 return Err(SymbolicError::UnsupportedAbi(format!(
25 "symbolic invariant `{}` must take no parameters",
26 function.name
27 )));
28 }
29 Ok(Self {
30 bytes: SymBytes::concrete(cx, function.selector().to_vec()),
31 inputs: Vec::new(),
32 constraints: Vec::new(),
33 })
34 }
35
36 pub(super) fn variants_with_prefix(
37 function: &Function,
38 config: &SymbolicConfig,
39 cx: &mut SymCx,
40 prefix: &str,
41 ) -> Result<Vec<Self>, SymbolicError> {
42 let variant_limit = calldata_variant_limit(config);
43 let mut builder = SymbolicAbiBuilder { config, cx };
44 let mut variants = vec![(SymbolicAbiState::default(), Vec::new())];
45 for (idx, input) in function.inputs.iter().enumerate() {
46 let ty = input.selector_type();
47 let mut next_variants = Vec::new();
48 for (state, inputs) in variants {
49 for (state, input) in SymbolicInput::variants(
50 &mut builder,
51 state,
52 prefix,
53 idx,
54 Some(input.name.as_str()),
55 ty.as_ref(),
56 )? {
57 let mut inputs = inputs.clone();
58 inputs.push(input);
59 push_variant(&mut next_variants, (state, inputs), variant_limit)?;
60 }
61 }
62 variants = next_variants;
63 }
64
65 validate_positional_dynamic_lengths(
66 config,
67 variants.iter().map(|(state, _)| state.positional_dynamic_index).max().unwrap_or(0),
68 )?;
69
70 let mut partitioned = Vec::new();
72 for (state, inputs) in variants {
73 let address_indices = inputs
74 .iter()
75 .enumerate()
76 .filter_map(|(idx, input)| match input.value {
77 SymbolicAbiValue::Address { .. } => Some(idx),
78 _ => None,
79 })
80 .collect::<Vec<_>>();
81 for partition in set_partitions(address_indices.len(), variant_limit)? {
82 let mut state = state.clone();
83 let mut inputs = inputs.clone();
84 let mut representatives = Vec::with_capacity(partition.len());
85 for block in partition {
86 let representative = match &inputs[address_indices[block[0]]].value {
87 SymbolicAbiValue::Address { word } => word.clone(),
88 _ => unreachable!("address index must refer to an address input"),
89 };
90 for member in block {
91 let replaced = match &mut inputs[address_indices[member]].value {
92 SymbolicAbiValue::Address { word } => {
93 let replaced = word.clone();
94 *word = representative.clone();
95 replaced
96 }
97 _ => unreachable!("address index must refer to an address input"),
98 };
99 if replaced != representative {
100 for constraint in &mut state.constraints {
101 *constraint = constraint.fold_exprs(builder.cx, &mut |_, expr| {
102 if expr == replaced { representative.clone() } else { expr }
103 });
104 }
105 }
106 }
107 representatives.push(representative);
108 }
109 for (idx, representative) in representatives.iter().enumerate() {
110 for other in &representatives[idx + 1..] {
111 let eq = SymBoolExpr::eq(builder.cx, representative.clone(), other.clone());
112 state.constraints.push(eq.not(builder.cx));
113 }
114 }
115 push_variant(&mut partitioned, (state, inputs), variant_limit)?;
116 }
117 }
118
119 let mut out = Vec::with_capacity(partitioned.len());
120 for (state, inputs) in partitioned {
121 let selector = SymBytes::concrete(builder.cx, function.selector().to_vec());
122 let encoded = builder.encode_sequence(inputs.iter().map(|input| &input.value));
123 let bytes = SymBytes::concat(builder.cx, [selector, encoded]);
124 if bytes.len() > config.max_calldata_bytes as usize {
125 return Err(SymbolicError::Unsupported(
126 "symbolic calldata size exceeds configured max",
127 ));
128 }
129
130 out.push(Self { bytes, inputs, constraints: state.constraints });
131 }
132 Ok(out)
133 }
134
135 pub(super) fn call_data(&self, cx: &mut SymCx) -> SymCalldata {
136 SymCalldata::from_bytes(cx, self.bytes.clone())
137 }
138
139 pub(super) fn constraints(&self) -> &[SymBoolExpr] {
141 &self.constraints
142 }
143
144 pub(super) fn into_constraints(self) -> Vec<SymBoolExpr> {
146 self.constraints
147 }
148
149 pub(super) fn model_to_args(
150 &self,
151 cx: &mut SymCx,
152 model: &(impl SymbolicModelLookup + ?Sized),
153 ) -> Result<Vec<DynSolValue>, SymbolicError> {
154 self.inputs.iter().map(|input| input.value.model_value(cx, model)).collect()
155 }
156
157 pub(super) fn seed_model(
158 &self,
159 cx: &mut SymCx,
160 seed: &SymbolicConcreteInput,
161 ) -> Option<SymbolicModel> {
162 if seed.args.len() != self.inputs.len() {
163 return None;
164 }
165
166 let mut model = SymbolicModel::default();
167 for (input, arg) in self.inputs.iter().zip(&seed.args) {
168 if !input.value.seed_model_value(cx, &mut model, arg) {
169 return None;
170 }
171 }
172
173 for constraint in &self.constraints {
174 if constraint.eval_model_if_complete(&model).ok().flatten() != Some(true) {
175 return None;
176 }
177 }
178
179 let calldata = self.bytes.eval_model(cx, &model).ok()?;
180 (calldata.as_slice() == seed.calldata.as_ref()).then_some(model)
181 }
182}
183
184#[derive(Clone, Debug)]
185pub(super) struct SymbolicInput {
186 value: SymbolicAbiValue,
187}
188
189impl SymbolicInput {
190 pub(super) fn variants<'a, 'cx>(
191 builder: &mut SymbolicAbiBuilder<'a, 'cx>,
192 state: SymbolicAbiState,
193 prefix: &str,
194 idx: usize,
195 abi_name: Option<&str>,
196 ty: &str,
197 ) -> Result<Vec<(SymbolicAbiState, Self)>, SymbolicError> {
198 let ty =
199 DynSolType::parse(ty).map_err(|_| SymbolicError::UnsupportedAbi(ty.to_string()))?;
200 let name = format!("{prefix}_{idx}");
201 let aliases =
202 abi_name.filter(|name| !name.is_empty()).map(str::to_string).into_iter().collect();
203 builder.value_variants(state, name, aliases, &ty).map(|variants| {
204 variants.into_iter().map(|(state, value)| (state, Self { value })).collect()
205 })
206 }
207}
208
209#[derive(Clone, Debug, Default)]
210pub(super) struct SymbolicAbiState {
211 constraints: Vec<SymBoolExpr>,
212 positional_dynamic_index: usize,
213}
214
215#[derive(Debug)]
216pub(super) struct SymbolicAbiBuilder<'a, 'cx> {
217 config: &'a SymbolicConfig,
218 cx: &'cx mut SymCx,
219}
220
221impl<'a, 'cx> SymbolicAbiBuilder<'a, 'cx> {
222 pub(super) fn value(
223 &mut self,
224 state: &mut SymbolicAbiState,
225 name: String,
226 aliases: Vec<String>,
227 ty: &DynSolType,
228 ) -> Result<SymbolicAbiValue, SymbolicError> {
229 Ok(match ty {
230 DynSolType::Bool => {
231 let word = self.fresh_word(&name);
232 state.constraints.push(SymBoolExpr::cmp_word_const(
233 self.cx,
234 SymCmpOp::Ult,
235 &word,
236 U256::from(2),
237 ));
238 SymbolicAbiValue::Bool { word }
239 }
240 DynSolType::Uint(bits) => {
241 let word = self.fresh_word(&name);
242 self.constrain_uint(state, &word, *bits);
243 SymbolicAbiValue::Uint { bits: *bits, word }
244 }
245 DynSolType::Int(bits) => {
246 let word = self.fresh_word(&name);
247 self.constrain_int(state, &word, *bits);
248 SymbolicAbiValue::Int { bits: *bits, word }
249 }
250 DynSolType::FixedBytes(size) => {
251 let bytes = (0..*size)
252 .map(|idx| self.fresh_byte(state, &format!("{name}_{idx}"), false))
253 .collect();
254 SymbolicAbiValue::FixedBytes { bytes: SymBytes::exprs(self.cx, bytes), size: *size }
255 }
256 DynSolType::Address => {
257 let word = self.fresh_word(&name);
258 self.constrain_uint(state, &word, 160);
259 SymbolicAbiValue::Address { word }
260 }
261 DynSolType::Function => {
262 return Err(SymbolicError::UnsupportedAbi("function".to_string()));
263 }
264 DynSolType::Bytes => {
265 let len = self.next_dynamic_length(state, &name, &aliases, DynamicKind::Bytes)?;
266 let bytes = (0..len)
267 .map(|idx| self.fresh_byte(state, &format!("{name}_{idx}"), false))
268 .collect();
269 SymbolicAbiValue::Bytes {
270 len: SymExpr::constant(self.cx, U256::from(len)),
271 bytes: SymBytes::exprs(self.cx, bytes),
272 }
273 }
274 DynSolType::String => {
275 let len = self.next_dynamic_length(state, &name, &aliases, DynamicKind::String)?;
276 let bytes = (0..len)
277 .map(|idx| self.fresh_byte(state, &format!("{name}_{idx}"), true))
278 .collect();
279 SymbolicAbiValue::String { bytes: SymBytes::exprs(self.cx, bytes) }
280 }
281 DynSolType::Array(inner) => {
282 let len = self.next_dynamic_length(state, &name, &aliases, DynamicKind::Array)?;
283 SymbolicAbiValue::Array {
284 elements: (0..len)
285 .map(|idx| {
286 self.value(
287 state,
288 format!("{name}_{idx}"),
289 child_aliases(&aliases, idx),
290 inner,
291 )
292 })
293 .collect::<Result<Vec<_>, _>>()?,
294 }
295 }
296 DynSolType::FixedArray(inner, len) => SymbolicAbiValue::FixedArray {
297 elements: (0..*len)
298 .map(|idx| {
299 self.value(
300 state,
301 format!("{name}_{idx}"),
302 child_aliases(&aliases, idx),
303 inner,
304 )
305 })
306 .collect::<Result<Vec<_>, _>>()?,
307 },
308 DynSolType::Tuple(types) => SymbolicAbiValue::Tuple {
309 elements: types
310 .iter()
311 .enumerate()
312 .map(|(idx, ty)| {
313 self.value(state, format!("{name}_{idx}"), child_aliases(&aliases, idx), ty)
314 })
315 .collect::<Result<Vec<_>, _>>()?,
316 },
317 DynSolType::CustomStruct { tuple, .. } => SymbolicAbiValue::Tuple {
318 elements: tuple
319 .iter()
320 .enumerate()
321 .map(|(idx, ty)| {
322 self.value(state, format!("{name}_{idx}"), child_aliases(&aliases, idx), ty)
323 })
324 .collect::<Result<Vec<_>, _>>()?,
325 },
326 })
327 }
328
329 pub(super) fn value_variants(
330 &mut self,
331 state: SymbolicAbiState,
332 name: String,
333 aliases: Vec<String>,
334 ty: &DynSolType,
335 ) -> Result<Vec<(SymbolicAbiState, SymbolicAbiValue)>, SymbolicError> {
336 Ok(match ty {
337 DynSolType::Bytes => {
338 let mut state = state;
339 let lengths = self.next_dynamic_length_options(
340 &mut state,
341 &name,
342 &aliases,
343 DynamicKind::Bytes,
344 )?;
345 let limit = calldata_variant_limit(self.config);
346 let mut variants = Vec::new();
347 for len in lengths {
348 let mut state = state.clone();
349 let bytes = (0..len as usize)
350 .map(|idx| self.fresh_byte(&mut state, &format!("{name}_{idx}"), false))
351 .collect();
352 let value = SymbolicAbiValue::Bytes {
353 len: SymExpr::constant(self.cx, U256::from(len)),
354 bytes: SymBytes::exprs(self.cx, bytes),
355 };
356 push_variant(&mut variants, (state, value), limit)?;
357 }
358 variants
359 }
360 DynSolType::String => {
361 let mut state = state;
362 let lengths = self.next_dynamic_length_options(
363 &mut state,
364 &name,
365 &aliases,
366 DynamicKind::String,
367 )?;
368 let limit = calldata_variant_limit(self.config);
369 let mut variants = Vec::new();
370 for len in lengths {
371 let mut state = state.clone();
372 let bytes = (0..len as usize)
373 .map(|idx| self.fresh_byte(&mut state, &format!("{name}_{idx}"), true))
374 .collect();
375 let value = SymbolicAbiValue::String { bytes: SymBytes::exprs(self.cx, bytes) };
376 push_variant(&mut variants, (state, value), limit)?;
377 }
378 variants
379 }
380 DynSolType::Array(inner) => {
381 let mut state = state;
382 let lengths = self.next_dynamic_length_options(
383 &mut state,
384 &name,
385 &aliases,
386 DynamicKind::Array,
387 )?;
388 let limit = calldata_variant_limit(self.config);
389 let mut variants = Vec::new();
390 for len in lengths {
391 for (state, elements) in self.array_elements_variants(
392 state.clone(),
393 &name,
394 &aliases,
395 inner,
396 len as usize,
397 )? {
398 push_variant(
399 &mut variants,
400 (state, SymbolicAbiValue::Array { elements }),
401 limit,
402 )?;
403 }
404 }
405 variants
406 }
407 DynSolType::FixedArray(inner, len) => self
408 .array_elements_variants(state, &name, &aliases, inner, *len)
409 .map(|variants| {
410 variants
411 .into_iter()
412 .map(|(state, elements)| (state, SymbolicAbiValue::FixedArray { elements }))
413 .collect()
414 })?,
415 DynSolType::Tuple(types) => self
416 .tuple_elements_variants(state, &name, &aliases, types)?
417 .into_iter()
418 .map(|(state, elements)| (state, SymbolicAbiValue::Tuple { elements }))
419 .collect(),
420 DynSolType::CustomStruct { tuple, .. } => self
421 .tuple_elements_variants(state, &name, &aliases, tuple)?
422 .into_iter()
423 .map(|(state, elements)| (state, SymbolicAbiValue::Tuple { elements }))
424 .collect(),
425 _ => {
426 let mut state = state;
427 let value = self.value(&mut state, name, aliases, ty)?;
428 vec![(state, value)]
429 }
430 })
431 }
432
433 pub(super) fn array_elements_variants(
434 &mut self,
435 state: SymbolicAbiState,
436 name: &str,
437 aliases: &[String],
438 inner: &DynSolType,
439 len: usize,
440 ) -> Result<Vec<(SymbolicAbiState, Vec<SymbolicAbiValue>)>, SymbolicError> {
441 let limit = calldata_variant_limit(self.config);
442 let mut variants = vec![(state, Vec::with_capacity(len))];
443 for idx in 0..len {
444 let mut next_variants = Vec::new();
445 for (state, elements) in variants {
446 for (state, value) in self.value_variants(
447 state,
448 format!("{name}_{idx}"),
449 child_aliases(aliases, idx),
450 inner,
451 )? {
452 let mut elements = elements.clone();
453 elements.push(value);
454 push_variant(&mut next_variants, (state, elements), limit)?;
455 }
456 }
457 variants = next_variants;
458 }
459 Ok(variants)
460 }
461
462 pub(super) fn tuple_elements_variants(
463 &mut self,
464 state: SymbolicAbiState,
465 name: &str,
466 aliases: &[String],
467 types: &[DynSolType],
468 ) -> Result<Vec<(SymbolicAbiState, Vec<SymbolicAbiValue>)>, SymbolicError> {
469 let limit = calldata_variant_limit(self.config);
470 let mut variants = vec![(state, Vec::with_capacity(types.len()))];
471 for (idx, ty) in types.iter().enumerate() {
472 let mut next_variants = Vec::new();
473 for (state, elements) in variants {
474 for (state, value) in self.value_variants(
475 state,
476 format!("{name}_{idx}"),
477 child_aliases(aliases, idx),
478 ty,
479 )? {
480 let mut elements = elements.clone();
481 elements.push(value);
482 push_variant(&mut next_variants, (state, elements), limit)?;
483 }
484 }
485 variants = next_variants;
486 }
487 Ok(variants)
488 }
489
490 pub(super) fn fresh_word(&mut self, name: &str) -> SymExpr {
491 let symbol = self.cx.intern(name);
492 self.cx.mark_replayable_input(symbol);
493 SymExpr::get_var(self.cx, symbol)
494 }
495
496 pub(super) fn fresh_byte(
497 &mut self,
498 state: &mut SymbolicAbiState,
499 name: &str,
500 printable: bool,
501 ) -> SymExpr {
502 let word = self.fresh_word(name);
503 state.constraints.push(SymBoolExpr::cmp_word_const(
504 self.cx,
505 SymCmpOp::Ult,
506 &word,
507 U256::from(256),
508 ));
509 if printable {
510 state.constraints.push(SymBoolExpr::cmp_word_const(
511 self.cx,
512 SymCmpOp::Uge,
513 &word,
514 U256::from(0x20),
515 ));
516 state.constraints.push(SymBoolExpr::cmp_word_const(
517 self.cx,
518 SymCmpOp::Ule,
519 &word,
520 U256::from(0x7e),
521 ));
522 }
523 word
524 }
525
526 pub(super) fn next_dynamic_length(
527 &self,
528 state: &mut SymbolicAbiState,
529 name: &str,
530 aliases: &[String],
531 kind: DynamicKind,
532 ) -> Result<usize, SymbolicError> {
533 Ok(first_dynamic_length(
534 &self.next_dynamic_length_options(state, name, aliases, kind)?,
535 "symbolic dynamic length",
536 )? as usize)
537 }
538
539 pub(super) fn next_dynamic_length_options(
540 &self,
541 state: &mut SymbolicAbiState,
542 name: &str,
543 aliases: &[String],
544 kind: DynamicKind,
545 ) -> Result<Vec<u32>, SymbolicError> {
546 let named_lengths = std::iter::once(name)
547 .chain(aliases.iter().map(String::as_str))
548 .find_map(|name| self.config.dynamic_lengths.get(name));
549
550 let lengths = if let Some(lengths) = named_lengths {
551 lengths.clone()
552 } else if let Some(lengths) = kind.default_lengths(self.config) {
553 lengths.to_vec()
554 } else if let Some(len) =
555 self.config.array_lengths.get(state.positional_dynamic_index).copied()
556 {
557 state.positional_dynamic_index += 1;
558 vec![len]
559 } else {
560 vec![self.config.default_dynamic_length]
561 };
562
563 if lengths.is_empty() {
564 return Err(SymbolicError::UnsupportedAbi(
565 "symbolic dynamic length set must not be empty".to_string(),
566 ));
567 }
568 for len in &lengths {
569 if *len > self.config.max_dynamic_length {
570 return Err(SymbolicError::UnsupportedAbi(format!(
571 "symbolic {} length {len} exceeds max_dynamic_length {}",
572 kind.name(),
573 self.config.max_dynamic_length
574 )));
575 }
576 }
577 Ok(lengths)
578 }
579
580 pub(super) fn constrain_uint(
581 &mut self,
582 state: &mut SymbolicAbiState,
583 word: &SymExpr,
584 bits: usize,
585 ) {
586 if bits < 256 {
587 state.constraints.push(SymBoolExpr::cmp_word_const(
588 self.cx,
589 SymCmpOp::Ult,
590 word,
591 U256::ONE << bits,
592 ));
593 }
594 }
595
596 pub(super) fn constrain_int(
597 &mut self,
598 state: &mut SymbolicAbiState,
599 word: &SymExpr,
600 bits: usize,
601 ) {
602 if bits < 256 {
603 let byte_index = U256::from(bits / 8 - 1);
604 let signextended = signextend_word(self.cx, byte_index, word.clone());
605 state.constraints.push(SymBoolExpr::eq(self.cx, word.clone(), signextended));
606 }
607 }
608
609 pub(super) fn encode_sequence<'v>(
610 &mut self,
611 values: impl IntoIterator<Item = &'v SymbolicAbiValue>,
612 ) -> SymBytes {
613 encode_sequence(self.cx, values)
614 }
615}
616
617fn validate_positional_dynamic_lengths(
619 config: &SymbolicConfig,
620 max_positional_dynamic_index: usize,
621) -> Result<(), SymbolicError> {
622 if config.array_lengths.len() > max_positional_dynamic_index {
623 return Err(SymbolicError::UnsupportedAbi(format!(
624 "symbolic.array_lengths has {} entries but ABI used at most {} positional dynamic leaves",
625 config.array_lengths.len(),
626 max_positional_dynamic_index
627 )));
628 }
629 Ok(())
630}
631
632fn set_partitions(len: usize, limit: usize) -> Result<Vec<Vec<Vec<usize>>>, SymbolicError> {
634 let mut out = Vec::new();
635 let mut blocks = Vec::<Vec<usize>>::new();
636 fn go(
637 idx: usize,
638 len: usize,
639 blocks: &mut Vec<Vec<usize>>,
640 out: &mut Vec<Vec<Vec<usize>>>,
641 limit: usize,
642 ) -> Result<(), SymbolicError> {
643 if idx == len {
644 return push_variant(out, blocks.clone(), limit);
645 }
646 blocks.push(vec![idx]);
647 go(idx + 1, len, blocks, out, limit)?;
648 blocks.pop();
649 for block in 0..blocks.len() {
650 blocks[block].push(idx);
651 go(idx + 1, len, blocks, out, limit)?;
652 blocks[block].pop();
653 }
654 Ok(())
655 }
656 go(0, len, &mut blocks, &mut out, limit)?;
657 Ok(out)
658}
659
660fn calldata_variant_limit(config: &SymbolicConfig) -> usize {
662 config.path_width().max(1) as usize
663}
664
665fn push_variant<T>(variants: &mut Vec<T>, variant: T, limit: usize) -> Result<(), SymbolicError> {
667 if variants.len() >= limit {
668 return Err(SymbolicError::CalldataVariantLimit(limit));
669 }
670 variants.push(variant);
671 Ok(())
672}
673
674#[derive(Clone, Copy)]
675pub(super) enum DynamicKind {
676 Array,
677 Bytes,
678 String,
679}
680
681impl DynamicKind {
682 pub(super) const fn name(self) -> &'static str {
683 match self {
684 Self::Array => "array",
685 Self::Bytes => "bytes",
686 Self::String => "string",
687 }
688 }
689
690 pub(super) fn default_lengths(self, config: &SymbolicConfig) -> Option<&[u32]> {
691 match self {
692 Self::Array if !config.default_array_lengths.is_empty() => {
693 Some(&config.default_array_lengths)
694 }
695 Self::Bytes | Self::String if !config.default_bytes_lengths.is_empty() => {
696 Some(&config.default_bytes_lengths)
697 }
698 _ => None,
699 }
700 }
701}
702
703pub(super) fn first_dynamic_length(lengths: &[u32], field: &str) -> Result<u32, SymbolicError> {
704 lengths
705 .first()
706 .copied()
707 .ok_or_else(|| SymbolicError::UnsupportedAbi(format!("{field} must not be empty")))
708}
709
710pub(super) fn child_aliases(aliases: &[String], idx: usize) -> Vec<String> {
711 aliases.iter().map(|alias| format!("{alias}_{idx}")).collect()
712}
713
714#[derive(Clone, Debug)]
715pub(super) enum SymbolicAbiValue {
716 Bool { word: SymExpr },
717 Uint { bits: usize, word: SymExpr },
718 Int { bits: usize, word: SymExpr },
719 FixedBytes { bytes: SymBytes, size: usize },
720 Address { word: SymExpr },
721 Bytes { len: SymExpr, bytes: SymBytes },
722 String { bytes: SymBytes },
723 Array { elements: Vec<Self> },
724 FixedArray { elements: Vec<Self> },
725 Tuple { elements: Vec<Self> },
726}
727
728impl SymbolicAbiValue {
729 pub(super) fn is_dynamic(&self) -> bool {
731 match self {
732 Self::Bool { .. }
733 | Self::Uint { .. }
734 | Self::Int { .. }
735 | Self::FixedBytes { .. }
736 | Self::Address { .. } => false,
737 Self::Bytes { .. } | Self::String { .. } | Self::Array { .. } => true,
738 Self::FixedArray { elements } | Self::Tuple { elements } => {
739 elements.iter().any(Self::is_dynamic)
740 }
741 }
742 }
743
744 pub(super) fn head_size(&self) -> usize {
745 if self.is_dynamic() {
746 32
747 } else {
748 match self {
749 Self::Bool { .. }
750 | Self::Uint { .. }
751 | Self::Int { .. }
752 | Self::FixedBytes { .. }
753 | Self::Address { .. } => 32,
754 Self::FixedArray { elements } | Self::Tuple { elements } => {
755 elements.iter().map(Self::head_size).sum()
756 }
757 Self::Bytes { .. } | Self::String { .. } | Self::Array { .. } => 32,
758 }
759 }
760 }
761
762 pub(super) fn model_value(
763 &self,
764 cx: &mut SymCx,
765 model: &(impl SymbolicModelLookup + ?Sized),
766 ) -> Result<DynSolValue, SymbolicError> {
767 Ok(match self {
768 Self::Bool { word } => DynSolValue::Bool(!word.eval_model(model)?.is_zero()),
769 Self::Uint { bits, word } => {
770 DynSolValue::Uint(mask_bits(word.eval_model(model)?, *bits), *bits)
771 }
772 Self::Int { bits, word } => {
773 DynSolValue::Int(I256::from_raw(word.eval_model(model)?), *bits)
774 }
775 Self::FixedBytes { bytes, size } => {
776 let mut word = [0u8; 32];
777 for (idx, out) in word.iter_mut().enumerate().take(bytes.len()) {
778 *out = bytes.byte(cx, idx).eval_model(model)?.to::<u8>();
779 }
780 DynSolValue::FixedBytes(B256::from(word), *size)
781 }
782 Self::Address { word } => {
783 DynSolValue::Address(word_to_address(word.eval_model(model)?))
784 }
785 Self::Bytes { len, bytes } => {
786 let len = len.eval_model(model)?;
787 let len = usize::try_from(len)
788 .ok()
789 .filter(|len| *len <= bytes.len())
790 .ok_or_else(|| SymbolicError::Solver("invalid symbolic bytes length".into()))?;
791 let mut bytes = bytes.eval_model(cx, model)?;
792 bytes.truncate(len);
793 DynSolValue::Bytes(bytes)
794 }
795 Self::String { bytes } => {
796 let bytes = bytes.eval_model(cx, model)?;
797 let value = String::from_utf8(bytes).map_err(|err| {
798 SymbolicError::Solver(format!("invalid symbolic string model: {err}"))
799 })?;
800 DynSolValue::String(value)
801 }
802 Self::Array { elements } => DynSolValue::Array(
803 elements
804 .iter()
805 .map(|value| value.model_value(cx, model))
806 .collect::<Result<Vec<_>, _>>()?,
807 ),
808 Self::FixedArray { elements } => DynSolValue::FixedArray(
809 elements
810 .iter()
811 .map(|value| value.model_value(cx, model))
812 .collect::<Result<Vec<_>, _>>()?,
813 ),
814 Self::Tuple { elements } => DynSolValue::Tuple(
815 elements
816 .iter()
817 .map(|value| value.model_value(cx, model))
818 .collect::<Result<Vec<_>, _>>()?,
819 ),
820 })
821 }
822
823 pub(super) fn seed_model_value(
824 &self,
825 cx: &mut SymCx,
826 model: &mut SymbolicModel,
827 value: &DynSolValue,
828 ) -> bool {
829 match (self, value) {
830 (Self::Bool { word }, DynSolValue::Bool(value)) => {
831 word.assign_model_value(model, U256::from(*value as u8))
832 }
833 (Self::Uint { bits, word }, DynSolValue::Uint(value, value_bits))
834 if bits == value_bits =>
835 {
836 word.assign_model_value(model, *value)
837 }
838 (Self::Int { bits, word }, DynSolValue::Int(value, value_bits))
839 if bits == value_bits =>
840 {
841 word.assign_model_value(model, value.into_raw())
842 }
843 (Self::FixedBytes { bytes, size }, DynSolValue::FixedBytes(value, value_size))
844 if size == value_size =>
845 {
846 seed_model_bytes(cx, model, bytes, &value.as_slice()[..*size])
847 }
848 (Self::Address { word }, DynSolValue::Address(value)) => {
849 word.assign_model_value(model, address_word(*value))
850 }
851 (Self::Bytes { len, bytes }, DynSolValue::Bytes(value)) => {
852 len.assign_model_value(model, U256::from(value.len()))
853 && seed_model_bytes(cx, model, bytes, value)
854 }
855 (Self::String { bytes }, DynSolValue::String(value)) => {
856 seed_model_bytes(cx, model, bytes, value.as_bytes())
857 }
858 (Self::Array { elements }, DynSolValue::Array(values))
859 | (Self::FixedArray { elements }, DynSolValue::FixedArray(values))
860 | (Self::Tuple { elements }, DynSolValue::Tuple(values)) => {
861 seed_model_elements(cx, model, elements, values)
862 }
863 (Self::Tuple { elements }, DynSolValue::CustomStruct { tuple, .. }) => {
864 seed_model_elements(cx, model, elements, tuple)
865 }
866 _ => false,
867 }
868 }
869}
870
871fn seed_model_elements(
872 cx: &mut SymCx,
873 model: &mut SymbolicModel,
874 elements: &[SymbolicAbiValue],
875 values: &[DynSolValue],
876) -> bool {
877 elements.len() == values.len()
878 && elements
879 .iter()
880 .zip(values)
881 .all(|(element, value)| element.seed_model_value(cx, model, value))
882}
883
884fn seed_model_bytes(
885 cx: &mut SymCx,
886 model: &mut SymbolicModel,
887 bytes: &SymBytes,
888 value: &[u8],
889) -> bool {
890 bytes.len() == value.len()
891 && value
892 .iter()
893 .enumerate()
894 .all(|(idx, byte)| bytes.byte(cx, idx).assign_model_value(model, U256::from(*byte)))
895}
896
897pub(super) fn encode_sequence<'a>(
898 cx: &mut SymCx,
899 values: impl IntoIterator<Item = &'a SymbolicAbiValue>,
900) -> SymBytes {
901 let values = values.into_iter().collect::<Vec<_>>();
902 let head_size = values.iter().map(|value| value.head_size()).sum::<usize>();
903 let mut head = Vec::with_capacity(values.len());
904 let mut tail = Vec::new();
905 let mut tail_len = 0usize;
906
907 for value in values {
908 if value.is_dynamic() {
909 let offset = SymExpr::constant(cx, U256::from(head_size + tail_len));
910 head.push(offset.into_bytes(cx));
911 let body = encode_dynamic_body(cx, value);
912 tail_len += body.len();
913 tail.push(body);
914 } else {
915 head.push(encode_static(cx, value));
916 }
917 }
918
919 SymBytes::concat(cx, head.into_iter().chain(tail))
920}
921
922fn encode_static(cx: &mut SymCx, value: &SymbolicAbiValue) -> SymBytes {
923 match value {
924 SymbolicAbiValue::Bool { word }
925 | SymbolicAbiValue::Uint { word, .. }
926 | SymbolicAbiValue::Int { word, .. }
927 | SymbolicAbiValue::Address { word } => word.clone().into_bytes(cx),
928 SymbolicAbiValue::FixedBytes { bytes, .. } => {
929 let padding = SymBytes::concrete(cx, vec![0; 32usize.saturating_sub(bytes.len())]);
930 SymBytes::concat(cx, [bytes.clone(), padding])
931 }
932 SymbolicAbiValue::FixedArray { elements } | SymbolicAbiValue::Tuple { elements } => {
933 encode_sequence(cx, elements.iter())
934 }
935 SymbolicAbiValue::Bytes { .. }
936 | SymbolicAbiValue::String { .. }
937 | SymbolicAbiValue::Array { .. } => unreachable!("dynamic ABI value encoded as static"),
938 }
939}
940
941fn encode_dynamic_body(cx: &mut SymCx, value: &SymbolicAbiValue) -> SymBytes {
942 match value {
943 SymbolicAbiValue::Bytes { len, bytes } => {
944 encode_packed_bytes_with_len(cx, len.clone(), bytes)
945 }
946 SymbolicAbiValue::String { bytes } => {
947 let len = SymExpr::constant(cx, U256::from(bytes.len()));
948 encode_packed_bytes_with_len(cx, len, bytes)
949 }
950 SymbolicAbiValue::Array { elements } => {
951 let len = SymExpr::constant(cx, U256::from(elements.len()));
952 let len = len.into_bytes(cx);
953 let elements = encode_sequence(cx, elements.iter());
954 SymBytes::concat(cx, [len, elements])
955 }
956 SymbolicAbiValue::FixedArray { elements } | SymbolicAbiValue::Tuple { elements } => {
957 encode_sequence(cx, elements.iter())
958 }
959 SymbolicAbiValue::Bool { .. }
960 | SymbolicAbiValue::Uint { .. }
961 | SymbolicAbiValue::Int { .. }
962 | SymbolicAbiValue::FixedBytes { .. }
963 | SymbolicAbiValue::Address { .. } => unreachable!("static ABI value encoded as dynamic"),
964 }
965}
966
967pub(super) fn encode_packed_bytes_with_len(
968 cx: &mut SymCx,
969 len: SymExpr,
970 bytes: &SymBytes,
971) -> SymBytes {
972 let padded_len = bytes.len().next_multiple_of(32);
973 let len = len.into_bytes(cx);
974 let padding = SymBytes::concrete(cx, vec![0; padded_len - bytes.len()]);
975 SymBytes::concat(cx, [len, bytes.clone(), padding])
976}