Skip to main content

foundry_evm_symbolic/
abi.rs

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        // Explore equal-address cases as variants sharing a representative account.
71        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    /// Returns symbolic calldata constraints.
140    pub(super) fn constraints(&self) -> &[SymBoolExpr] {
141        &self.constraints
142    }
143
144    /// Consumes this symbolic calldata into its constraints.
145    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
617/// Validates that positional ABI length config can be consumed by at least one expanded variant.
618fn 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
632/// Enumerates address partitions within the variant budget, all-distinct first.
633fn 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
660/// Returns the maximum number of calldata variants allowed during ABI expansion.
661fn calldata_variant_limit(config: &SymbolicConfig) -> usize {
662    config.path_width().max(1) as usize
663}
664
665/// Adds one expansion variant while enforcing the configured symbolic path-width budget.
666fn 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    /// Returns whether `is_dynamic` holds.
730    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}