Skip to main content

foundry_evm_symbolic/runtime/
address.rs

1use super::*;
2
3impl SymExpr {
4    pub(crate) fn representative_symbolic_address(&self) -> Address {
5        let digest = keccak256(self.symbolic_address_key());
6        let mut bytes = [0u8; 20];
7        bytes[0] = 0xfe;
8        bytes[1..].copy_from_slice(&digest[..19]);
9        Address::from(bytes)
10    }
11
12    pub(crate) fn symbolic_address_key(&self) -> String {
13        if let Some(value) = self.as_const() {
14            format!("concrete-address:{:?}", word_to_address(value))
15        } else {
16            let expr = self.symbolic_address_canonical();
17            let bytes = expr
18                .address_byte_terms_for_equivalence()
19                .map(|bytes| format!("{bytes:?}"))
20                .unwrap_or_else(|| format!("{expr:?}"));
21            format!("symbolic-address:{bytes}")
22        }
23    }
24
25    pub(crate) fn address_match_condition(&self, cx: &mut SymCx, address: Address) -> SymBoolExpr {
26        if let Some(word) = self.as_const() {
27            return SymBoolExpr::constant(cx, word == address_word(address));
28        }
29        let Some(terms) = self.address_byte_terms(cx) else {
30            let address = Self::constant(cx, address_word(address));
31            return SymBoolExpr::eq(cx, self.clone(), address);
32        };
33        let bytes = address.as_slice();
34        let conditions = terms
35            .into_iter()
36            .enumerate()
37            .map(|(index, term)| {
38                let byte = Self::constant(cx, U256::from(bytes[index]));
39                SymBoolExpr::eq(cx, term, byte)
40            })
41            .collect();
42        SymBoolExpr::and(cx, conditions)
43    }
44
45    pub(crate) fn symbolic_address_equivalent(&self, alias: &Self) -> bool {
46        match (self.as_const(), alias.as_const()) {
47            (Some(left), Some(right)) => word_to_address(left) == word_to_address(right),
48            (None, None) => self.address_expr_equivalent(alias),
49            _ => false,
50        }
51    }
52
53    fn address_expr_equivalent(&self, alias: &Self) -> bool {
54        let this = self.symbolic_address_canonical();
55        let alias = alias.symbolic_address_canonical();
56
57        if this == alias {
58            return true;
59        }
60
61        if let (Some(candidate), Some(alias)) =
62            (this.address_byte_terms_for_equivalence(), alias.address_byte_terms_for_equivalence())
63        {
64            return candidate == alias;
65        }
66
67        match this.kind() {
68            SymExprKind::BinOp(SymBinOp::And, left, right) if right.is_address_mask() => {
69                left.address_expr_equivalent(alias)
70            }
71            SymExprKind::BinOp(SymBinOp::Shr, value, shift) if shift.is_shift_96() => {
72                match value.kind() {
73                    SymExprKind::BinOp(SymBinOp::Shl, inner, inner_shift)
74                        if inner_shift.is_shift_96() =>
75                    {
76                        inner.address_expr_equivalent(alias)
77                    }
78                    _ => false,
79                }
80            }
81            _ => false,
82        }
83    }
84
85    fn symbolic_address_canonical(&self) -> &Self {
86        match self.kind() {
87            SymExprKind::BinOp(SymBinOp::And, left, right) if right.is_address_mask() => {
88                left.symbolic_address_canonical()
89            }
90            SymExprKind::BinOp(SymBinOp::Shr, value, shift) if shift.is_shift_96() => {
91                match value.kind() {
92                    SymExprKind::BinOp(SymBinOp::Shl, inner, inner_shift)
93                        if inner_shift.is_shift_96() =>
94                    {
95                        inner.symbolic_address_canonical()
96                    }
97                    _ => self,
98                }
99            }
100            _ => self,
101        }
102    }
103
104    fn address_byte_terms(&self, cx: &mut SymCx) -> Option<Vec<Self>> {
105        (12..32).map(|index| self.byte_term(cx, index)).collect()
106    }
107
108    fn address_byte_terms_for_equivalence(&self) -> Option<Vec<Self>> {
109        (12..32).map(|index| self.extracted_byte_source(index)).collect()
110    }
111
112    fn is_address_mask(&self) -> bool {
113        self.as_const() == Some((U256::from(1) << 160) - U256::from(1))
114    }
115
116    fn is_shift_96(&self) -> bool {
117        self.as_const() == Some(U256::from(96))
118    }
119}
120
121pub(crate) fn mask_bits(value: U256, bits: usize) -> U256 {
122    if bits >= 256 {
123        value
124    } else {
125        let mask = (U256::from(1) << bits) - U256::from(1);
126        value & mask
127    }
128}
129
130pub(crate) fn address_word(address: Address) -> U256 {
131    U256::from_be_bytes(address.into_word().0)
132}
133
134pub(crate) fn word_to_address(value: U256) -> Address {
135    Address::from_word(value.to_be_bytes::<32>().into())
136}
137
138pub(crate) fn stable_symbol(cx: &mut SymCx, prefix: &'static str, input: &[u8]) -> Symbol {
139    let digest = keccak256(input);
140    let mut symbol = String::with_capacity(prefix.len() + 17);
141    symbol.push_str(prefix);
142    symbol.push('_');
143    for byte in &digest[..8] {
144        let _ = write!(symbol, "{byte:02x}");
145    }
146    cx.intern(&symbol)
147}