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