Skip to main content

foundry_evm_symbolic/runtime/
address.rs

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