Skip to main content

foundry_evm_symbolic/runtime/
calldata.rs

1use super::*;
2
3#[derive(Clone, Debug)]
4pub(crate) struct SymCalldata {
5    pub(crate) size: usize,
6    pub(crate) size_word: SymWord,
7    pub(crate) bytes: Vec<SymWord>,
8}
9
10impl SymCalldata {
11    /// Constructs a new instance.
12    pub(crate) fn new(bytes: Vec<SymWord>) -> Self {
13        Self { size_word: SymWord::Concrete(U256::from(bytes.len())), size: bytes.len(), bytes }
14    }
15
16    /// Implements the `new_symbolic_size` symbolic calldata helper.
17    pub(crate) const fn new_symbolic_size(bytes: Vec<SymWord>, size_word: SymWord) -> Self {
18        Self { size: bytes.len(), size_word, bytes }
19    }
20
21    /// Returns the `load_word` symbolic calldata helper result.
22    pub(crate) fn load_word(&self, offset: SymWord) -> Result<SymWord, SymbolicError> {
23        match offset {
24            SymWord::Concrete(offset) => {
25                if offset > U256::from(usize::MAX) {
26                    return Ok(SymWord::zero());
27                }
28                self.load(offset.to::<usize>())
29            }
30            SymWord::Expr(offset) => self.load_dynamic(offset),
31        }
32    }
33
34    /// Implements the `load` symbolic calldata helper.
35    pub(crate) fn load(&self, offset: usize) -> Result<SymWord, SymbolicError> {
36        Ok(word_from_bytes((0..32).map(|idx| self.byte(offset + idx))))
37    }
38
39    /// Implements the `byte` symbolic calldata helper.
40    pub(crate) fn byte(&self, offset: usize) -> SymWord {
41        self.bytes.get(offset).cloned().unwrap_or_else(SymWord::zero)
42    }
43
44    /// Returns the `load_dynamic` symbolic calldata helper result.
45    pub(crate) fn load_dynamic(&self, offset: Expr) -> Result<SymWord, SymbolicError> {
46        let mut result = Expr::Const(U256::ZERO);
47        for candidate in (0..self.size).rev() {
48            result = Expr::Ite(
49                Box::new(BoolExpr::eq(offset.clone(), Expr::Const(U256::from(candidate)))),
50                Box::new(self.load(candidate)?.into_expr()),
51                Box::new(result),
52            );
53        }
54        Ok(SymWord::Expr(result))
55    }
56
57    /// Returns the `byte_dynamic_with_delta` symbolic calldata helper result.
58    pub(crate) fn byte_dynamic_with_delta(&self, offset: Expr, delta: usize) -> SymWord {
59        let mut result = Expr::Const(U256::ZERO);
60        for candidate in (delta..self.size).rev() {
61            result = Expr::Ite(
62                Box::new(BoolExpr::eq(offset.clone(), Expr::Const(U256::from(candidate - delta)))),
63                Box::new(self.byte(candidate).into_expr()),
64                Box::new(result),
65            );
66        }
67        SymWord::Expr(result)
68    }
69}
70
71/// Implements the `call_input_from_memory` symbolic calldata helper.
72pub(crate) fn call_input_from_memory(
73    memory: &SymMemory,
74    offset: SymWord,
75    size: &BoundedCopySize,
76) -> Vec<SymWord> {
77    match size {
78        BoundedCopySize::Concrete(size) => memory.read_bytes_offset(offset, *size),
79        BoundedCopySize::Symbolic { size, max_size } => {
80            memory.read_bytes_symbolic_size(offset, size.clone(), *max_size)
81        }
82    }
83}
84
85/// Implements the `bounded_copy_size_word` symbolic calldata helper.
86pub(crate) fn bounded_copy_size_word(size: &BoundedCopySize) -> SymWord {
87    match size {
88        BoundedCopySize::Concrete(size) => SymWord::Concrete(U256::from(*size)),
89        BoundedCopySize::Symbolic { size, .. } => size.clone(),
90    }
91}
92
93/// Implements the `bounded_copy_size_parts` symbolic calldata helper.
94pub(crate) fn bounded_copy_size_parts(size: &BoundedCopySize) -> (SymWord, usize, bool) {
95    match size {
96        BoundedCopySize::Concrete(size) => (SymWord::Concrete(U256::from(*size)), *size, false),
97        BoundedCopySize::Symbolic { size, max_size } => (size.clone(), *max_size, true),
98    }
99}
100
101/// Implements the `calldata_from_call_input` symbolic calldata helper.
102pub(crate) fn calldata_from_call_input(input: Vec<SymWord>, size: &BoundedCopySize) -> SymCalldata {
103    match size {
104        BoundedCopySize::Concrete(_) => SymCalldata::new(input),
105        BoundedCopySize::Symbolic { size, .. } => {
106            SymCalldata::new_symbolic_size(input, size.clone())
107        }
108    }
109}