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