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