foundry_evm_symbolic/runtime/
address.rs1use 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}