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 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}