foundry_evm_symbolic/runtime/
address.rs1use super::*;
2
3pub(crate) fn mask_bits(value: U256, bits: usize) -> U256 {
5 if bits >= 256 {
6 value
7 } else {
8 let mask = (U256::from(1) << bits) - U256::from(1);
9 value & mask
10 }
11}
12
13pub(crate) fn address_word(address: Address) -> U256 {
15 U256::from_be_bytes(address.into_word().0)
16}
17
18pub(crate) fn word_to_address(value: U256) -> Address {
20 Address::from_word(value.to_be_bytes::<32>().into())
21}
22
23pub(crate) fn representative_symbolic_address(word: &SymWord) -> Address {
25 let digest = keccak256(symbolic_address_key(word));
26 let mut bytes = [0u8; 20];
27 bytes[0] = 0xfe;
28 bytes[1..].copy_from_slice(&digest[..19]);
29 Address::from(bytes)
30}
31
32pub(crate) fn symbolic_address_key(word: &SymWord) -> String {
34 match word {
35 SymWord::Concrete(value) => format!("concrete-address:{:?}", word_to_address(*value)),
36 SymWord::Expr(expr) => {
37 let bytes = address_byte_terms(expr)
38 .map(|bytes| format!("{bytes:?}"))
39 .unwrap_or_else(|| format!("{expr:?}"));
40 format!("symbolic-address:{bytes}")
41 }
42 }
43}
44
45pub(crate) fn address_match_condition(word: &SymWord, address: Address) -> BoolExpr {
47 let expr = word.clone().into_expr();
48 let Some(terms) = address_byte_terms(&expr) else {
49 return BoolExpr::eq(expr, Expr::Const(address_word(address)));
50 };
51 let bytes = address.as_slice();
52 BoolExpr::and(
53 terms
54 .into_iter()
55 .enumerate()
56 .map(|(index, term)| BoolExpr::eq(term, Expr::Const(U256::from(bytes[index]))))
57 .collect(),
58 )
59}
60
61pub(crate) fn symbolic_address_equivalent(candidate: &SymWord, alias: &SymWord) -> bool {
63 match (candidate, alias) {
64 (SymWord::Concrete(left), SymWord::Concrete(right)) => {
65 word_to_address(*left) == word_to_address(*right)
66 }
67 (SymWord::Expr(candidate), SymWord::Expr(alias)) => {
68 address_expr_equivalent(candidate, alias)
69 }
70 _ => false,
71 }
72}
73
74pub(crate) fn address_expr_equivalent(candidate: &Expr, alias: &Expr) -> bool {
76 if candidate == alias {
77 return true;
78 }
79
80 if let (Some(candidate), Some(alias)) =
81 (address_byte_terms(candidate), address_byte_terms(alias))
82 {
83 return candidate == alias;
84 }
85
86 match candidate {
87 Expr::Op(ExprOp::And, left, right) => {
88 (is_address_mask(right) && address_expr_equivalent(left, alias))
89 || (is_address_mask(left) && address_expr_equivalent(right, alias))
90 }
91 Expr::Op(ExprOp::Shr, value, shift) if is_shift_96(shift) => match value.as_ref() {
92 Expr::Op(ExprOp::Shl, inner, inner_shift) if is_shift_96(inner_shift) => {
93 address_expr_equivalent(inner, alias)
94 }
95 _ => false,
96 },
97 _ => false,
98 }
99}
100
101pub(crate) fn address_byte_terms(expr: &Expr) -> Option<Vec<Expr>> {
103 (12..32).map(|index| expr_byte_term(expr, index)).collect()
104}
105
106pub(crate) fn expr_byte_term(expr: &Expr, index: usize) -> Option<Expr> {
108 debug_assert!(index < 32);
109
110 match expr {
111 Expr::Const(value) => Some(Expr::Const(U256::from(value.to_be_bytes::<32>()[index]))),
112 Expr::Var(_) | Expr::GasLeft(_) | Expr::Keccak { .. } | Expr::Hash { .. } => {
113 Some(extracted_byte_expr(expr, index))
114 }
115 Expr::Not(value) => Some(Expr::Not(Box::new(expr_byte_term(value, index)?))),
116 Expr::Ite(cond, then_expr, else_expr) => Some(Expr::Ite(
117 cond.clone(),
118 Box::new(expr_byte_term(then_expr, index)?),
119 Box::new(expr_byte_term(else_expr, index)?),
120 )),
121 Expr::Op(op, left, right) => match op {
122 ExprOp::And => expr_binary_byte_term(
123 left,
124 right,
125 index,
126 ExprOp::And,
127 |byte| byte == 0xff,
128 |byte| byte == 0,
129 ),
130 ExprOp::Or => {
131 expr_binary_byte_term(left, right, index, ExprOp::Or, |byte| byte == 0, |_| false)
132 }
133 ExprOp::Xor => {
134 expr_binary_byte_term(left, right, index, ExprOp::Xor, |byte| byte == 0, |_| false)
135 }
136 ExprOp::Shl => {
137 let shift = expr_const_value(right)?;
138 if shift >= U256::from(256) {
139 return Some(Expr::Const(U256::ZERO));
140 }
141 let shift = shift.to::<usize>();
142 if shift % 8 != 0 {
143 return None;
144 }
145 let source_index = index + shift / 8;
146 if source_index >= 32 {
147 Some(Expr::Const(U256::ZERO))
148 } else {
149 expr_byte_term(left, source_index)
150 }
151 }
152 ExprOp::Shr => {
153 let shift = expr_const_value(right)?;
154 if shift >= U256::from(256) {
155 return Some(Expr::Const(U256::ZERO));
156 }
157 let shift = shift.to::<usize>();
158 if shift % 8 != 0 {
159 return None;
160 }
161 let byte_shift = shift / 8;
162 if index < byte_shift {
163 Some(Expr::Const(U256::ZERO))
164 } else {
165 expr_byte_term(left, index - byte_shift)
166 }
167 }
168 ExprOp::Add
169 | ExprOp::Sub
170 | ExprOp::Mul
171 | ExprOp::UDiv
172 | ExprOp::URem
173 | ExprOp::SDiv
174 | ExprOp::SRem
175 | ExprOp::Sar => None,
176 },
177 }
178}
179
180pub(crate) fn expr_binary_byte_term(
182 left: &Expr,
183 right: &Expr,
184 index: usize,
185 op: ExprOp,
186 identity: impl Fn(u8) -> bool,
187 absorbing: impl Fn(u8) -> bool,
188) -> Option<Expr> {
189 let left = expr_byte_term(left, index)?;
190 let right = expr_byte_term(right, index)?;
191 match (expr_byte_const(&left), expr_byte_const(&right)) {
192 (Some(left), _) if absorbing(left) => Some(Expr::Const(U256::from(left))),
193 (_, Some(right)) if absorbing(right) => Some(Expr::Const(U256::from(right))),
194 (Some(left), _) if identity(left) => Some(right),
195 (_, Some(right)) if identity(right) => Some(left),
196 _ => Some(Expr::op(op, left, right)),
197 }
198}
199
200pub(crate) fn expr_byte_const(expr: &Expr) -> Option<u8> {
202 let Expr::Const(value) = expr else { return None };
203 Some(value.to::<u8>())
204}
205
206pub(crate) fn extracted_byte_expr(expr: &Expr, index: usize) -> Expr {
208 Expr::op(
209 ExprOp::And,
210 Expr::op(ExprOp::Shr, expr.clone(), Expr::Const(U256::from((31 - index) * 8))),
211 Expr::Const(U256::from(0xff)),
212 )
213}
214
215pub(crate) fn is_address_mask(expr: &Expr) -> bool {
217 matches!(expr, Expr::Const(value) if *value == ((U256::from(1) << 160) - U256::from(1)))
218}
219
220pub(crate) fn is_shift_96(expr: &Expr) -> bool {
222 matches!(expr, Expr::Const(value) if *value == U256::from(96))
223}
224
225pub(crate) fn stable_symbol(prefix: &'static str, input: impl AsRef<[u8]>) -> String {
227 let digest = keccak256(input.as_ref());
228 let mut symbol = String::with_capacity(prefix.len() + 17);
229 symbol.push_str(prefix);
230 symbol.push('_');
231 for byte in &digest[..8] {
232 let _ = write!(symbol, "{byte:02x}");
233 }
234 symbol
235}