1use super::{hashcons::HashConsed, *};
2
3pub(crate) fn keccak_word(cx: &mut SymCx, bytes: Vec<SymExpr>) -> SymExpr {
4 let len = bytes.len();
5 let len = SymExpr::constant(cx, U256::from(len));
6 keccak_word_with_len(cx, bytes, len)
7}
8
9pub(crate) fn keccak_word_with_len(cx: &mut SymCx, bytes: Vec<SymExpr>, len: SymExpr) -> SymExpr {
10 if let Some(len) = len.as_const()
11 && let Ok(len) = usize::try_from(len)
12 && len <= bytes.len()
13 && let Ok(concrete) = concrete_expr_bytes(&bytes[..len], "symbolic keccak input")
14 {
15 let hash = U256::from_be_bytes(keccak256(concrete).0);
16 if len == 64 {
17 cx.record_concrete_keccak_preimage(hash, bytes[..len].to_vec().into());
18 }
19 return SymExpr::constant(cx, hash);
20 }
21
22 let exprs = bytes;
23 let name = stable_symbol(cx, "keccak", format!("{len:?}:{exprs:?}").as_bytes());
24 SymExpr::keccak_symbol(cx, name, len, exprs)
25}
26
27pub(crate) fn symbolic_hash_word_with_len(
28 cx: &mut SymCx,
29 algorithm: &'static str,
30 bytes: Vec<SymExpr>,
31 len: SymExpr,
32) -> SymExpr {
33 let exprs = bytes;
34 let name = stable_symbol(cx, algorithm, format!("{len:?}:{exprs:?}").as_bytes());
35 let mut identity = Vec::with_capacity(exprs.len() + 1);
36 identity.push(len);
37 identity.extend(exprs);
38 SymExpr::hash_symbol(cx, name, algorithm, identity)
39}
40
41pub(crate) fn create2_address_word(
42 cx: &mut SymCx,
43 state: &mut PathState,
44 creator: Address,
45 salt: SymExpr,
46 initcode: &SymCode,
47) -> Result<(SymExpr, Address), SymbolicError> {
48 match (salt.as_const(), initcode.concrete_bytes(cx, "symbolic CREATE2 initcode")) {
49 (Some(salt), Ok(initcode)) => {
50 let address = creator.create2_from_code(salt.to_be_bytes::<32>(), &initcode);
51 Ok((SymExpr::constant(cx, address_word(address)), address))
52 }
53 (None, Ok(initcode)) => {
54 let initcode_hash = keccak256(&initcode);
55 let word = symbolic_create2_address_word(
56 cx,
57 state,
58 format!("{creator:?}"),
59 salt,
60 format!("{initcode_hash:?}"),
61 );
62 let address = state.world.symbolic_address_slot(word.clone());
63 Ok((word, address))
64 }
65 (_, Err(SymbolicError::Unsupported("symbolic CREATE2 initcode"))) => {
66 let initcode_bytes = initcode.read_byte_exprs(cx, 0, initcode.len());
67 let word = symbolic_create2_address_word(
68 cx,
69 state,
70 format!("{creator:?}"),
71 salt,
72 format!("{initcode_bytes:?}"),
73 );
74 let address = state.world.symbolic_address_slot(word.clone());
75 Ok((word, address))
76 }
77 (_, Err(err)) => Err(err),
78 }
79}
80
81pub(crate) fn compute_create2_address_word(
82 cx: &mut SymCx,
83 state: &mut PathState,
84 deployer: SymExpr,
85 salt: SymExpr,
86 init_code_hash: SymExpr,
87) -> Result<SymExpr, SymbolicError> {
88 let deployer_concrete = state.constrained_word(cx, &deployer).map(word_to_address);
89 let salt_concrete = state.constrained_word(cx, &salt);
90 let init_code_hash_concrete = state.constrained_word(cx, &init_code_hash);
91
92 if let (Some(deployer), Some(salt), Some(init_code_hash)) =
93 (deployer_concrete, salt_concrete, init_code_hash_concrete)
94 {
95 let init_code_hash = B256::from(init_code_hash.to_be_bytes::<32>());
96 let address = deployer.create2(B256::from(salt.to_be_bytes::<32>()), init_code_hash);
97 return Ok(SymExpr::constant(cx, address_word(address)));
98 }
99
100 let deployer_identity = deployer_concrete
101 .map(|deployer| format!("{deployer:?}"))
102 .unwrap_or_else(|| format!("{deployer:?}"));
103 let init_code_hash_identity = init_code_hash_concrete
104 .map(|init_code_hash| {
105 let init_code_hash = B256::from(init_code_hash.to_be_bytes::<32>());
106 format!("{init_code_hash:?}")
107 })
108 .unwrap_or_else(|| format!("{init_code_hash:?}"));
109
110 Ok(symbolic_create2_address_word(cx, state, deployer_identity, salt, init_code_hash_identity))
111}
112
113pub(crate) fn compute_create_address_word(
114 cx: &mut SymCx,
115 state: &mut PathState,
116 deployer: SymExpr,
117 nonce: SymExpr,
118) -> Result<SymExpr, SymbolicError> {
119 let deployer_concrete = state.constrained_word(cx, &deployer).map(word_to_address);
120 let nonce_concrete = state.constrained_word(cx, &nonce);
121
122 if let (Some(deployer), Some(nonce)) = (deployer_concrete, nonce_concrete) {
123 let Ok(nonce) = u64::try_from(nonce) else {
124 return Err(SymbolicError::Unsupported("symbolic vm.computeCreateAddress nonce"));
125 };
126 return Ok(SymExpr::constant(cx, address_word(deployer.create(nonce))));
127 }
128
129 let deployer_identity = deployer_concrete
130 .map(|deployer| format!("{deployer:?}"))
131 .unwrap_or_else(|| format!("{deployer:?}"));
132 Ok(symbolic_create_address_word(cx, state, deployer_identity, nonce))
133}
134
135pub(crate) fn symbolic_create_address_word(
136 cx: &mut SymCx,
137 state: &mut PathState,
138 creator_identity: String,
139 nonce: SymExpr,
140) -> SymExpr {
141 let name =
142 stable_symbol(cx, "create_address", format!("{creator_identity}:{nonce:?}").as_bytes());
143 let word = SymExpr::get_var(cx, name);
144 state.constraints.push(SymBoolExpr::cmp_word_const(cx, SymCmpOp::Ult, &word, U256::ONE << 160));
145 word
146}
147
148pub(crate) fn symbolic_create2_address_word(
149 cx: &mut SymCx,
150 state: &mut PathState,
151 creator_identity: String,
152 salt: SymExpr,
153 initcode_identity: String,
154) -> SymExpr {
155 let name = stable_symbol(
156 cx,
157 "create2_address",
158 format!("{creator_identity}:{salt:?}:{initcode_identity}").as_bytes(),
159 );
160 let word = SymExpr::get_var(cx, name);
161 state.constraints.push(SymBoolExpr::cmp_word_const(cx, SymCmpOp::Ult, &word, U256::ONE << 160));
162 word
163}
164
165impl SymExpr {
166 pub(crate) fn select_storage_write(
167 self,
168 cx: &mut SymCx,
169 write_key: Self,
170 write_value: Self,
171 base: Self,
172 ) -> Self {
173 if write_value == base {
174 return base;
175 }
176 let condition = self.storage_key_eq(cx, &write_key);
177 match condition.as_const() {
178 Some(true) => write_value,
179 Some(false) => base,
180 None => Self::ite(cx, condition, write_value, base),
181 }
182 }
183
184 pub(crate) fn storage_key_eq(&self, cx: &mut SymCx, write_key: &Self) -> SymBoolExpr {
185 if let (Some(read_root), Some(write_root)) =
186 (self.storage_mapping_root_slot(cx), write_key.storage_mapping_root_slot(cx))
187 && read_root != write_root
188 {
189 return SymBoolExpr::constant(cx, false);
190 }
191 match (self.storage_layout_key(cx), write_key.storage_layout_key(cx)) {
192 (Some((read_base, read_offset)), Some((write_base, write_offset))) => {
193 let read_base = read_base
194 .storage_base_eq(cx, &write_base)
195 .unwrap_or_else(|| SymBoolExpr::eq(cx, read_base, write_base));
196 let read_offset = SymBoolExpr::eq(cx, read_offset, write_offset);
197 SymBoolExpr::and(cx, vec![read_base, read_offset])
198 }
199 (Some(_), None) if write_key.as_const().is_some() => SymBoolExpr::constant(cx, false),
200 (None, Some(_)) if self.as_const().is_some() => SymBoolExpr::constant(cx, false),
201 _ => SymBoolExpr::eq(cx, self.clone(), write_key.clone()),
202 }
203 }
204
205 fn storage_base_eq(&self, cx: &mut SymCx, other: &Self) -> Option<SymBoolExpr> {
206 let read = self.storage_mapping_key(cx)?;
207 let write = other.storage_mapping_key(cx)?;
208
209 let key_eq = storage_mapping_key_eq(cx, &read, &write);
210 let slot_eq = read
211 .slot
212 .storage_base_eq(cx, &write.slot)
213 .unwrap_or_else(|| SymBoolExpr::eq(cx, read.slot, write.slot));
214 Some(SymBoolExpr::and(cx, vec![key_eq, slot_eq]))
215 }
216
217 pub(crate) fn storage_mapping_key(&self, cx: &mut SymCx) -> Option<StorageMappingKey> {
218 let bytes = self.storage_mapping_key_bytes(cx)?;
219 let key_bytes = &bytes[..32];
220 let preserve_key_bytes =
221 (!storage_mapping_key_bytes_form_compact_word(key_bytes)).then(|| key_bytes.to_vec());
222 let key = Self::from_bytes(cx, key_bytes.iter().cloned());
223 let slot = Self::from_bytes(cx, bytes[32..64].iter().cloned());
224 Some(StorageMappingKey { key, key_bytes: preserve_key_bytes, slot })
225 }
226
227 pub(crate) fn storage_mapping_provenance_observed_with(
228 &self,
229 cx: &mut SymCx,
230 mut observed_preimage: impl FnMut(&Self) -> Option<Arc<[Self]>>,
231 ) -> Option<SymbolicMappingProvenance> {
232 let mut current = self.clone();
233 let mut keys = Vec::new();
234 let mut visited = Vec::new();
235 loop {
236 if visited.contains(¤t) {
237 return None;
238 }
239 visited.push(current.clone());
240 let bytes = observed_preimage(¤t)?;
241 if bytes.len() != 64 {
242 return None;
243 }
244 let key = Self::from_bytes(cx, bytes[..32].iter().cloned());
245 keys.push(key);
246 current = Self::from_bytes(cx, bytes[32..64].iter().cloned());
247 match current.kind() {
248 SymExprKind::Const(root_slot) if observed_preimage(¤t).is_none() => {
249 keys.reverse();
250 return Some(SymbolicMappingProvenance { root_slot: *root_slot, keys });
251 }
252 SymExprKind::Const(_) | SymExprKind::Keccak { .. } => {}
253 _ => return None,
254 }
255 }
256 }
257
258 fn storage_mapping_root_slot(&self, cx: &mut SymCx) -> Option<U256> {
259 let bytes = self.storage_mapping_key_bytes(cx)?;
260 let slot = Self::from_bytes(cx, bytes[32..64].iter().cloned());
261 match slot.kind() {
262 SymExprKind::Const(value) if cx.concrete_keccak_preimage(*value).is_some() => {
263 slot.storage_mapping_root_slot(cx)
264 }
265 SymExprKind::Const(slot) => Some(*slot),
266 SymExprKind::Keccak { .. } => slot.storage_mapping_root_slot(cx),
267 _ => None,
268 }
269 }
270
271 fn storage_mapping_key_bytes(&self, cx: &SymCx) -> Option<Arc<[Self]>> {
272 match self.kind() {
273 SymExprKind::Keccak { len, bytes, .. }
274 if len.as_const() == Some(U256::from(64)) && bytes.len() >= 64 =>
275 {
276 Some(bytes.clone())
277 }
278 SymExprKind::Const(hash) => cx.concrete_keccak_preimage(*hash),
279 _ => None,
280 }
281 }
282
283 fn storage_layout_key(&self, cx: &mut SymCx) -> Option<(Self, Self)> {
284 match self.kind() {
285 SymExprKind::Keccak { .. } => Some((self.clone(), Self::zero(cx))),
286 SymExprKind::Const(hash) if cx.concrete_keccak_preimage(*hash).is_some() => {
287 Some((self.clone(), Self::zero(cx)))
288 }
289 SymExprKind::BinOp(SymBinOp::Add, left, right) => {
290 if let Some((base, offset)) = left.storage_layout_key(cx)
291 && !right.contains_keccak()
292 {
293 let offset = Self::binop(cx, SymBinOp::Add, offset, right.clone());
294 return Some((base, offset));
295 }
296 if let Some((base, offset)) = right.storage_layout_key(cx)
297 && !left.contains_keccak()
298 {
299 let offset = Self::binop(cx, SymBinOp::Add, offset, left.clone());
300 return Some((base, offset));
301 }
302 None
303 }
304 _ => None,
305 }
306 }
307}
308
309pub(crate) struct StorageMappingKey {
310 key: SymExpr,
311 key_bytes: Option<Vec<SymExpr>>,
312 slot: SymExpr,
313}
314
315#[derive(Clone, Debug, PartialEq, Eq)]
316pub(crate) struct SymbolicMappingProvenance {
317 pub(crate) root_slot: U256,
318 pub(crate) keys: Vec<SymExpr>,
319}
320
321fn storage_mapping_key_eq(
322 cx: &mut SymCx,
323 read: &StorageMappingKey,
324 write: &StorageMappingKey,
325) -> SymBoolExpr {
326 if read.key_bytes.is_some() || write.key_bytes.is_some() {
327 let read_owned;
328 let read_bytes = if let Some(bytes) = read.key_bytes.as_deref() {
329 bytes
330 } else {
331 read_owned = read.key.clone().into_byte_exprs(cx);
332 &read_owned
333 };
334 let write_owned;
335 let write_bytes = if let Some(bytes) = write.key_bytes.as_deref() {
336 bytes
337 } else {
338 write_owned = write.key.clone().into_byte_exprs(cx);
339 &write_owned
340 };
341 let byte_equalities = read_bytes
342 .iter()
343 .zip(write_bytes)
344 .map(|(read, write)| {
345 let read = read.byte_term(cx, 31).unwrap_or_else(|| read.clone().low_byte(cx));
346 let write = write.byte_term(cx, 31).unwrap_or_else(|| write.clone().low_byte(cx));
347 SymBoolExpr::eq(cx, read, write)
348 })
349 .collect();
350 SymBoolExpr::and(cx, byte_equalities)
351 } else {
352 SymBoolExpr::eq(cx, read.key.clone(), write.key.clone())
353 }
354}
355
356fn storage_mapping_key_bytes_form_compact_word(bytes: &[SymExpr]) -> bool {
357 bytes.iter().all(|byte| byte.as_const().is_some()) || word_from_extracted_bytes(bytes).is_some()
358}
359
360fn masked_expr_matches(candidate: &SymExprKind, target: &SymExpr) -> Option<U256> {
361 match candidate {
362 SymExprKind::BinOp(SymBinOp::And, left, right) if left == target => right.eval(),
363 SymExprKind::BinOp(SymBinOp::And, left, right) if right == target => left.eval(),
364 _ => None,
365 }
366}
367
368fn context_forces_masked_expr(context: &[SymBoolExpr], target: &SymExpr, mask: U256) -> bool {
369 context.iter().any(|condition| match condition.kind() {
370 SymBoolExprKind::Cmp(SymCmpOp::Eq, left, right) => {
371 (left == target && masked_expr_matches(right.kind(), target) == Some(mask))
372 || (right == target && masked_expr_matches(left.kind(), target) == Some(mask))
373 }
374 SymBoolExprKind::And(values) => context_forces_masked_expr(values, target, mask),
375 _ => false,
376 })
377}
378
379pub(crate) fn concrete_expr_bytes(
380 bytes: &[SymExpr],
381 reason: &'static str,
382) -> Result<Vec<u8>, SymbolicError> {
383 bytes
384 .iter()
385 .map(|byte| match byte.as_const() {
386 Some(value) => Ok(value.to::<u8>()),
387 None => Err(SymbolicError::Unsupported(reason)),
388 })
389 .collect()
390}
391
392pub(crate) fn mask_low_bits(mask: U256) -> Option<usize> {
393 let bits = mask.bit_len();
394 (mask == mask_bits(U256::MAX, bits)).then_some(bits)
395}
396
397fn power_of_two_shift(value: U256) -> Option<usize> {
398 if value <= U256::ONE || !value.is_power_of_two() {
399 return None;
400 }
401 Some(value.bit_len() - 1)
402}
403
404pub(in crate::runtime::expr) fn low_masked_source(expr: &SymExpr, bits: usize) -> Option<&SymExpr> {
405 match expr.kind() {
406 SymExprKind::BinOp(SymBinOp::And, left, right)
408 if right.as_const().and_then(mask_low_bits) == Some(bits) =>
409 {
410 Some(left)
411 }
412 _ => None,
413 }
414}
415
416pub(in crate::runtime::expr) fn low_masked_source_any(expr: &SymExpr) -> Option<&SymExpr> {
417 match expr.kind() {
418 SymExprKind::BinOp(SymBinOp::And, left, right)
420 if right.as_const().and_then(mask_low_bits).is_some() =>
421 {
422 Some(left)
423 }
424 _ => None,
425 }
426}
427
428fn word_from_extracted_bytes(bytes: &[SymExpr]) -> Option<SymExpr> {
429 if bytes.len() < 32 {
430 return None;
431 }
432
433 let source = bytes
434 .iter()
435 .take(32)
436 .enumerate()
437 .find_map(|(idx, byte)| byte.extracted_byte_source(idx))?;
438
439 for (idx, byte) in bytes.iter().take(32).enumerate() {
440 if let Some(byte_source) = byte.extracted_byte_source(idx) {
441 if byte_source != source {
442 return None;
443 }
444 continue;
445 }
446
447 let byte = byte.as_const()?;
448 if source.known_byte(idx) != Some(byte.to::<u8>()) {
449 return None;
450 }
451 }
452 Some(source)
453}
454
455#[derive(Clone, PartialEq, Eq, Hash)]
456pub(crate) struct SymExpr {
457 pub(in crate::runtime::expr) kind: HashConsed<SymExprKind>,
458}
459
460impl fmt::Debug for SymExpr {
461 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
462 self.kind().fmt(f)
463 }
464}
465
466#[derive(Clone, Debug, PartialEq, Eq, Hash)]
467pub(in crate::runtime) enum SymExprKind {
468 Const(U256),
469 Var(Symbol),
470 GasLeft(Symbol),
471 Keccak { name: Symbol, len: SymExpr, bytes: Arc<[SymExpr]> },
472 Hash { name: Symbol, algorithm: &'static str, bytes: Arc<[SymExpr]> },
473 Not(SymExpr),
474 BinOp(SymBinOp, SymExpr, SymExpr),
475 TernOp(SymTernOp, SymExpr, SymExpr, SymExpr),
476 Ite(SymBoolExpr, SymExpr, SymExpr),
477}
478
479impl SymExprKind {
480 pub(in crate::runtime) const fn get_var(&self) -> Option<Symbol> {
481 match self {
482 Self::Var(symbol)
483 | Self::GasLeft(symbol)
484 | Self::Keccak { name: symbol, .. }
485 | Self::Hash { name: symbol, .. } => Some(*symbol),
486 _ => None,
487 }
488 }
489
490 pub(in crate::runtime) const fn get_eval_var(&self) -> Option<Symbol> {
491 match self {
492 Self::Var(symbol) | Self::GasLeft(symbol) | Self::Hash { name: symbol, .. } => {
493 Some(*symbol)
494 }
495 _ => None,
496 }
497 }
498}
499
500impl SymExpr {
501 pub(in crate::runtime) fn kind(&self) -> &SymExprKind {
502 self.kind.value()
503 }
504
505 #[cfg(test)]
506 pub(crate) fn get_var_name<'a>(&self, cx: &'a SymCx) -> Option<&'a str> {
507 self.kind().get_var().map(|symbol| cx.symbol_name(symbol))
508 }
509
510 #[cfg(test)]
511 pub(crate) fn is_keccak(&self) -> bool {
512 matches!(self.kind(), SymExprKind::Keccak { .. })
513 }
514
515 #[cfg(test)]
516 pub(crate) fn keccak_len_and_byte_count(&self) -> Option<(&Self, usize)> {
517 match self.kind() {
518 SymExprKind::Keccak { len, bytes, .. } => Some((len, bytes.len())),
519 _ => None,
520 }
521 }
522
523 #[cfg(test)]
524 pub(crate) fn hash_algorithm(&self) -> Option<&'static str> {
525 match self.kind() {
526 SymExprKind::Hash { algorithm, .. } => Some(algorithm),
527 _ => None,
528 }
529 }
530
531 pub(in crate::runtime) fn into_kind(self) -> SymExprKind {
532 self.kind.into_value()
533 }
534
535 pub(in crate::runtime) fn from_kind(cx: &mut SymCx, kind: SymExprKind) -> Self {
536 cx.mk_expr_kind(kind)
537 }
538
539 pub(crate) fn zero(cx: &mut SymCx) -> Self {
540 Self::constant(cx, U256::ZERO)
541 }
542
543 pub(crate) fn one(cx: &mut SymCx) -> Self {
544 Self::constant(cx, U256::ONE)
545 }
546
547 pub(crate) fn constant(cx: &mut SymCx, value: U256) -> Self {
548 if value.is_zero() {
549 return cx.cached_zero();
550 }
551 if value == U256::ONE {
552 return cx.cached_one();
553 }
554 Self::from_kind(cx, SymExprKind::Const(value))
555 }
556
557 pub(crate) fn var(cx: &mut SymCx, name: &str) -> Self {
558 let symbol = cx.intern(name);
559 Self::get_var(cx, symbol)
560 }
561
562 pub(crate) fn get_var(cx: &mut SymCx, symbol: Symbol) -> Self {
563 Self::from_kind(cx, SymExprKind::Var(symbol))
564 }
565
566 pub(crate) fn gas_left(cx: &mut SymCx, id: usize) -> Self {
567 let symbol = cx.intern(&format!("gasleft_{id}"));
568 Self::from_kind(cx, SymExprKind::GasLeft(symbol))
569 }
570
571 pub(crate) fn not(cx: &mut SymCx, value: Self) -> Self {
572 match value.kind() {
573 SymExprKind::Const(value) => Self::constant(cx, !*value),
574 SymExprKind::Not(value) => value.clone(),
575 _ => Self::from_kind(cx, SymExprKind::Not(value)),
576 }
577 }
578
579 pub(crate) fn binop(cx: &mut SymCx, binop: SymBinOp, left: Self, right: Self) -> Self {
580 match binop {
581 SymBinOp::Add => match (left.kind(), right.kind()) {
582 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
583 Self::constant(cx, binop.eval(*left_value, *right_value))
585 }
586 (SymExprKind::Const(value), _) if value.is_zero() => right,
588 (_, SymExprKind::Const(value)) if value.is_zero() => left,
590 _ => Self::commutative_binop(cx, binop, left, right),
591 },
592 SymBinOp::Sub => match (left.kind(), right.kind()) {
593 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
594 Self::constant(cx, binop.eval(*left_value, *right_value))
596 }
597 (_, SymExprKind::Const(value)) if value.is_zero() => left,
599 _ if left == right => Self::zero(cx),
601 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
602 },
603 SymBinOp::Mul => match (left.kind(), right.kind()) {
604 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
605 Self::constant(cx, binop.eval(*left_value, *right_value))
607 }
608 (SymExprKind::Const(value), _) | (_, SymExprKind::Const(value))
610 if value.is_zero() =>
611 {
612 Self::zero(cx)
613 }
614 (SymExprKind::Const(value), _) if *value == U256::ONE => right,
616 (_, SymExprKind::Const(value)) if *value == U256::ONE => left,
618 (SymExprKind::Const(value), _) if let Some(shift) = power_of_two_shift(*value) => {
620 let shift = Self::constant(cx, U256::from(shift));
621 Self::binop(cx, SymBinOp::Shl, right, shift)
622 }
623 (_, SymExprKind::Const(value)) if let Some(shift) = power_of_two_shift(*value) => {
625 let shift = Self::constant(cx, U256::from(shift));
626 Self::binop(cx, SymBinOp::Shl, left, shift)
627 }
628 _ => Self::commutative_binop(cx, binop, left, right),
629 },
630 SymBinOp::UDiv | SymBinOp::SDiv => match (left.kind(), right.kind()) {
631 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
632 Self::constant(cx, binop.eval(*left_value, *right_value))
634 }
635 (_, SymExprKind::Const(value)) if value.is_zero() => Self::zero(cx),
637 (_, SymExprKind::Const(value)) if *value == U256::ONE => left,
639 (
641 SymExprKind::BinOp(SymBinOp::Sub, value, low_bits),
642 SymExprKind::Const(divisor),
643 ) if binop == SymBinOp::UDiv
644 && let Some(shift) = power_of_two_shift(*divisor)
645 && low_masked_source(low_bits, shift) == Some(value) =>
646 {
647 let shift = Self::constant(cx, U256::from(shift));
648 Self::binop(cx, SymBinOp::Shr, value.clone(), shift)
649 }
650 (_, SymExprKind::Const(divisor))
652 if binop == SymBinOp::UDiv
653 && let Some(shift) = power_of_two_shift(*divisor) =>
654 {
655 let shift = Self::constant(cx, U256::from(shift));
656 Self::binop(cx, SymBinOp::Shr, left, shift)
657 }
658 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
659 },
660 SymBinOp::URem | SymBinOp::SRem => match (left.kind(), right.kind()) {
661 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
662 Self::constant(cx, binop.eval(*left_value, *right_value))
664 }
665 (_, SymExprKind::Const(value)) if value.is_zero() => Self::zero(cx),
667 (_, SymExprKind::Const(value)) if *value == U256::ONE => Self::zero(cx),
669 (_, SymExprKind::Const(divisor))
671 if binop == SymBinOp::URem
672 && let Some(bits) = power_of_two_shift(*divisor) =>
673 {
674 Self::and_const(cx, left, mask_bits(U256::MAX, bits))
675 }
676 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
677 },
678 SymBinOp::And => match (left.kind(), right.kind()) {
679 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
680 Self::constant(cx, binop.eval(*left_value, *right_value))
682 }
683 (SymExprKind::Const(value), _) | (_, SymExprKind::Const(value))
685 if value.is_zero() =>
686 {
687 Self::zero(cx)
688 }
689 (SymExprKind::Const(value), _) if *value == U256::MAX => right,
691 (_, SymExprKind::Const(value)) if *value == U256::MAX => left,
693 _ if left == right => left,
695 (SymExprKind::Const(mask), _) => Self::and_const(cx, right, *mask),
696 (_, SymExprKind::Const(mask)) => Self::and_const(cx, left, *mask),
697 _ => Self::commutative_binop(cx, binop, left, right),
698 },
699 SymBinOp::Or => match (left.kind(), right.kind()) {
700 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
701 Self::constant(cx, binop.eval(*left_value, *right_value))
703 }
704 (SymExprKind::Const(value), _) if value.is_zero() => right,
706 (_, SymExprKind::Const(value)) if value.is_zero() => left,
708 _ if left == right => left,
710 _ => Self::or(cx, left, right),
711 },
712 SymBinOp::Xor => match (left.kind(), right.kind()) {
713 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
714 Self::constant(cx, binop.eval(*left_value, *right_value))
716 }
717 (SymExprKind::Const(value), _) if value.is_zero() => right,
719 (_, SymExprKind::Const(value)) if value.is_zero() => left,
721 _ if left == right => Self::zero(cx),
723 _ => Self::commutative_binop(cx, binop, left, right),
724 },
725 SymBinOp::Shl => match (left.kind(), right.kind()) {
726 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
727 Self::constant(cx, binop.eval(*left_value, *right_value))
729 }
730 (_, SymExprKind::Const(value)) if value.is_zero() => left,
732 (SymExprKind::Const(value), _) if value.is_zero() => Self::zero(cx),
734 (_, SymExprKind::Const(value)) if *value >= U256::from(256) => Self::zero(cx),
736 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
737 },
738 SymBinOp::Shr => match (left.kind(), right.kind()) {
739 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
740 Self::constant(cx, binop.eval(*left_value, *right_value))
742 }
743 (_, SymExprKind::Const(value)) if value.is_zero() => left,
745 (SymExprKind::Const(value), _) if value.is_zero() => Self::zero(cx),
747 (_, SymExprKind::Const(value)) => Self::shr_const(cx, left, *value),
748 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
749 },
750 SymBinOp::Sar => match (left.kind(), right.kind()) {
751 (SymExprKind::Const(left_value), SymExprKind::Const(right_value)) => {
752 Self::constant(cx, binop.eval(*left_value, *right_value))
754 }
755 (_, SymExprKind::Const(value)) if value.is_zero() => left,
757 _ => Self::from_kind(cx, SymExprKind::BinOp(binop, left, right)),
758 },
759 }
760 }
761
762 pub(crate) fn ternop(
763 cx: &mut SymCx,
764 ternop: SymTernOp,
765 left: Self,
766 right: Self,
767 modulus: Self,
768 ) -> Self {
769 match (left.kind(), right.kind(), modulus.kind()) {
770 (_, _, SymExprKind::Const(modulus)) if modulus.is_zero() || *modulus == U256::ONE => {
771 Self::zero(cx)
773 }
774 (SymExprKind::Const(left), SymExprKind::Const(right), SymExprKind::Const(modulus)) => {
775 Self::constant(cx, ternop.eval(*left, *right, *modulus))
777 }
778 (_, _, SymExprKind::Const(modulus))
780 if let Some(bits) = power_of_two_shift(*modulus) =>
781 {
782 let binop = match ternop {
783 SymTernOp::AddMod => SymBinOp::Add,
784 SymTernOp::MulMod => SymBinOp::Mul,
785 };
786 let value = Self::binop(cx, binop, left, right);
787 Self::and_const(cx, value, mask_bits(U256::MAX, bits))
788 }
789 _ => {
790 let (left, right) = Self::ordered_commutative_operands(left, right);
792 Self::from_kind(cx, SymExprKind::TernOp(ternop, left, right, modulus))
793 }
794 }
795 }
796
797 pub(crate) fn ite(
798 cx: &mut SymCx,
799 condition: SymBoolExpr,
800 then_expr: Self,
801 else_expr: Self,
802 ) -> Self {
803 match condition.as_const() {
804 Some(true) => then_expr,
806 Some(false) => else_expr,
808 None if then_expr == else_expr => then_expr,
810 None if then_expr.as_const().is_some_and(|value| value.is_zero())
812 && Self::self_div_expr_matches_zero_check(&condition, &else_expr) =>
813 {
814 let condition = condition.not(cx);
815 Self::bool_word(cx, condition)
816 }
817 None if then_expr.as_const() == Some(U256::ONE)
819 && else_expr.bool_word_condition().as_ref() == Some(&condition) =>
820 {
821 else_expr
822 }
823 None if else_expr.as_const().is_some_and(|value| value.is_zero())
825 && then_expr.bool_word_condition().as_ref() == Some(&condition) =>
826 {
827 then_expr
828 }
829 None => Self::from_kind(cx, SymExprKind::Ite(condition, then_expr, else_expr)),
830 }
831 }
832
833 pub(crate) fn bool_word(cx: &mut SymCx, value: SymBoolExpr) -> Self {
834 let one = Self::one(cx);
835 let zero = Self::zero(cx);
836 Self::ite(cx, value, one, zero)
837 }
838
839 fn self_div_expr_matches_zero_check(cond: &SymBoolExpr, expr: &Self) -> bool {
840 let Some(zero_operand) = cond.zero_check_operand() else { return false };
841 let Some((numerator, denominator)) = expr.udiv_operands() else { return false };
842 numerator == zero_operand && denominator == zero_operand
843 }
844
845 pub(crate) fn keccak_symbol(cx: &mut SymCx, name: Symbol, len: Self, bytes: Vec<Self>) -> Self {
846 Self::from_kind(cx, SymExprKind::Keccak { name, len, bytes: bytes.into() })
847 }
848
849 pub(crate) fn hash_symbol(
850 cx: &mut SymCx,
851 name: Symbol,
852 algorithm: &'static str,
853 bytes: Vec<Self>,
854 ) -> Self {
855 Self::from_kind(cx, SymExprKind::Hash { name, algorithm, bytes: bytes.into() })
856 }
857
858 fn or(cx: &mut SymCx, left: Self, right: Self) -> Self {
859 if let Some(rebuilt) = Self::rebuild_from_or_terms(&left, &right) {
860 return rebuilt;
862 }
863 Self::commutative_binop(cx, SymBinOp::Or, left, right)
864 }
865
866 fn commutative_binop(cx: &mut SymCx, op: SymBinOp, left: Self, right: Self) -> Self {
867 let (left, right) = Self::ordered_commutative_operands(left, right);
869 Self::from_kind(cx, SymExprKind::BinOp(op, left, right))
870 }
871
872 pub(in crate::runtime::expr) fn ordered_commutative_operands(
873 left: Self,
874 right: Self,
875 ) -> (Self, Self) {
876 match left.complexity().cmp(&right.complexity()) {
877 std::cmp::Ordering::Less => (right, left),
879 std::cmp::Ordering::Greater => (left, right),
880 std::cmp::Ordering::Equal if right.kind.stable_hash_cmp(&left.kind).is_lt() => {
881 (right, left)
882 }
883 std::cmp::Ordering::Equal => (left, right),
884 }
885 }
886
887 fn complexity(&self) -> usize {
888 match self.kind() {
889 SymExprKind::Const(_) => 0,
890 SymExprKind::Not(_) => 1,
891 SymExprKind::BinOp(..) => 2,
892 SymExprKind::TernOp(..) => 3,
893 _ => 4,
894 }
895 }
896
897 fn and_const(cx: &mut SymCx, expr: Self, mask: U256) -> Self {
898 if mask.is_zero() {
899 return Self::zero(cx);
901 }
902 if mask == U256::MAX {
903 return expr;
905 }
906
907 match expr.kind() {
908 SymExprKind::Const(value) => Self::constant(cx, *value & mask),
910 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
911 let left = Self::and_const(cx, left.clone(), mask);
913 let right = Self::and_const(cx, right.clone(), mask);
914 Self::binop(cx, SymBinOp::Or, left, right)
915 }
916 SymExprKind::BinOp(SymBinOp::Shl, _, shift)
917 if mask_low_bits(mask).is_some_and(|bits| {
918 shift
919 .as_const()
920 .and_then(|shift| usize::try_from(shift).ok())
921 .is_some_and(|shift| bits <= shift)
922 }) =>
923 {
924 Self::zero(cx)
926 }
927 SymExprKind::BinOp(SymBinOp::And, left, right) => {
928 if right.as_const() == Some(mask) {
929 Self::and_const(cx, left.clone(), mask)
931 } else if left == right {
932 Self::and_const(cx, left.clone(), mask)
934 } else {
935 let mask = Self::constant(cx, mask);
936 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::And, expr, mask))
937 }
938 }
939 _ => {
940 let mask = Self::constant(cx, mask);
941 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::And, expr, mask))
942 }
943 }
944 }
945
946 fn shr_const(cx: &mut SymCx, expr: Self, shift: U256) -> Self {
947 if shift.is_zero() {
948 return expr;
950 }
951 if shift >= U256::from(256) {
952 return Self::zero(cx);
954 }
955
956 let shift = usize::try_from(shift).expect("shift is less than 256");
957 if expr.unsigned_bits() <= shift {
958 return Self::zero(cx);
960 }
961
962 if let SymExprKind::BinOp(SymBinOp::Shl, inner, left_shift) = expr.kind()
963 && left_shift.as_const() == Some(U256::from(shift))
964 && inner.unsigned_bits() <= 256 - shift
965 {
966 return inner.clone();
968 }
969
970 if let SymExprKind::BinOp(SymBinOp::Or, left, right) = expr.kind() {
971 let left = Self::shr_const(cx, left.clone(), U256::from(shift));
974 let right = Self::shr_const(cx, right.clone(), U256::from(shift));
975 if left.as_const().is_some_and(|value| value.is_zero()) {
976 return right;
977 }
978 if right.as_const().is_some_and(|value| value.is_zero()) {
979 return left;
980 }
981 }
982
983 let shift = Self::constant(cx, U256::from(shift));
984 Self::from_kind(cx, SymExprKind::BinOp(SymBinOp::Shr, expr, shift))
985 }
986
987 fn rebuild_from_or_terms(left: &Self, right: &Self) -> Option<Self> {
988 let mut terms = Vec::new();
989 left.push_or_terms(&mut terms);
990 right.push_or_terms(&mut terms);
991 Self::rebuild_from_extracted_byte_terms(&terms)
992 .or_else(|| Self::rebuild_from_shifted_word_fragments(&terms))
993 }
994
995 pub(in crate::runtime) fn push_or_terms<'a>(&'a self, terms: &mut Vec<&'a Self>) {
996 match self.kind() {
997 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
998 left.push_or_terms(terms);
999 right.push_or_terms(terms);
1000 }
1001 _ => terms.push(self),
1002 }
1003 }
1004
1005 fn rebuild_from_extracted_byte_terms(terms: &[&Self]) -> Option<Self> {
1006 if terms.len() <= 1 {
1007 return None;
1008 }
1009
1010 let mut source = None;
1011 let mut seen = [false; 32];
1012 for term in terms {
1013 if term.as_const().is_some_and(|value| value.is_zero()) {
1014 continue;
1015 }
1016 let (term_source, index) = term.extracted_shifted_byte_term()?;
1017 match &source {
1018 Some(source) if source != &term_source => return None,
1019 Some(_) => {}
1020 None => source = Some(term_source),
1021 }
1022 seen[index] = true;
1023 }
1024
1025 let source = source?;
1026 for (index, seen) in seen.into_iter().enumerate() {
1027 if !seen && source.known_byte(index) != Some(0) {
1028 return None;
1029 }
1030 }
1031 Some(source)
1032 }
1033
1034 fn extracted_shifted_byte_term(&self) -> Option<(Self, usize)> {
1035 match self.kind() {
1036 SymExprKind::BinOp(SymBinOp::Shl, byte, shift) => {
1037 let shift = shift.as_const()?;
1038 let Ok(shift) = usize::try_from(shift) else { return None };
1039 if shift % 8 != 0 || shift > 248 {
1040 return None;
1041 }
1042 let index = 31 - shift / 8;
1043 let source = byte.extracted_unshifted_byte_source(index)?;
1044 Some((source, index))
1045 }
1046 _ => self.extracted_unshifted_byte_source(31).map(|source| (source, 31)),
1047 }
1048 }
1049
1050 fn extracted_unshifted_byte_source(&self, index: usize) -> Option<Self> {
1051 let expr = self.strip_low_byte_mask();
1052 if index == 31 {
1053 return Some(expr.clone());
1054 }
1055 let SymExprKind::BinOp(SymBinOp::Shr, source, shift) = expr.kind() else { return None };
1056 let shift = shift.as_const()?;
1057 (shift == U256::from((31 - index) * 8)).then(|| source.clone())
1058 }
1059
1060 fn rebuild_from_shifted_word_fragments(terms: &[&Self]) -> Option<Self> {
1061 if terms.len() != 2 {
1062 return None;
1063 }
1064
1065 let left_low = terms[0].low_word_fragment();
1066 let right_low = terms[1].low_word_fragment();
1067 let left_high = terms[0].shifted_high_word_fragment();
1068 let right_high = terms[1].shifted_high_word_fragment();
1069 match (left_low, right_low, left_high, right_high) {
1070 (Some((low_source, low_bits)), None, None, Some((high_source, high_bits)))
1071 | (None, Some((low_source, low_bits)), Some((high_source, high_bits)), None)
1072 if low_source == high_source && low_bits == high_bits =>
1073 {
1074 Some(low_source)
1075 }
1076 _ => None,
1077 }
1078 }
1079
1080 fn low_word_fragment(&self) -> Option<(Self, usize)> {
1081 let SymExprKind::BinOp(SymBinOp::And, left, right) = self.kind() else { return None };
1082 let mask = right.as_const()?;
1083 mask_low_bits(mask).map(|bits| (left.clone(), bits))
1084 }
1085
1086 fn shifted_high_word_fragment(&self) -> Option<(Self, usize)> {
1087 let SymExprKind::BinOp(SymBinOp::Shl, value, shift) = self.kind() else { return None };
1088 let bits = shift.as_const().and_then(|shift| usize::try_from(shift).ok())?;
1089 if bits == 0 || bits >= 256 {
1090 return None;
1091 }
1092
1093 let (source, source_shift, width) = value.shifted_low_fragment_source()?;
1094 (source_shift == bits && width == 256 - bits).then_some((source, bits))
1095 }
1096
1097 fn shifted_low_fragment_source(&self) -> Option<(Self, usize, usize)> {
1098 let SymExprKind::BinOp(SymBinOp::And, left, right) = self.kind() else { return None };
1099 let mask = right.as_const()?;
1100 Self::shifted_low_fragment_source_with_mask(left, mask)
1101 }
1102
1103 fn shifted_low_fragment_source_with_mask(
1104 value: &Self,
1105 mask: U256,
1106 ) -> Option<(Self, usize, usize)> {
1107 let width = mask_low_bits(mask)?;
1108 match value.kind() {
1109 SymExprKind::BinOp(SymBinOp::Shr, source, shift) => {
1110 let shift = shift.as_const().and_then(|shift| usize::try_from(shift).ok())?;
1111 Some((source.clone(), shift, width))
1112 }
1113 _ => Some((value.clone(), 0, width)),
1114 }
1115 }
1116
1117 pub(crate) fn low_byte(self, cx: &mut SymCx) -> Self {
1118 if let Some(word) = self.as_const() {
1119 return Self::constant(cx, U256::from(word.to::<u8>()));
1120 }
1121 let mask = Self::constant(cx, U256::from(0xff));
1122 Self::binop(cx, SymBinOp::And, self, mask)
1123 }
1124
1125 pub(crate) fn into_byte_exprs(self, cx: &mut SymCx) -> Vec<Self> {
1126 SymBytes::word(cx, self).materialize(cx)
1127 }
1128
1129 pub(crate) fn into_bytes(self, cx: &mut SymCx) -> SymBytes {
1130 SymBytes::word(cx, self)
1131 }
1132
1133 pub(crate) fn from_bytes(cx: &mut SymCx, bytes: impl IntoIterator<Item = Self>) -> Self {
1134 let bytes = bytes.into_iter().collect::<Vec<_>>();
1135 if let Ok(concrete) = concrete_expr_bytes(&bytes, "symbolic word bytes") {
1136 let mut word = [0u8; 32];
1137 for (idx, byte) in concrete.into_iter().take(32).enumerate() {
1138 word[idx] = byte;
1139 }
1140 return Self::constant(cx, U256::from_be_bytes(word));
1141 }
1142
1143 if let Some(expr) = word_from_extracted_bytes(&bytes) {
1144 return expr;
1145 }
1146
1147 let mut expr = Self::zero(cx);
1148 for (idx, byte) in bytes.into_iter().take(32).enumerate() {
1149 let shift = (31 - idx) * 8;
1150 let byte = byte.low_byte(cx);
1151 let byte = if shift == 0 {
1152 byte
1153 } else {
1154 let shift = Self::constant(cx, U256::from(shift));
1155 Self::binop(cx, SymBinOp::Shl, byte, shift)
1156 };
1157 expr = Self::binop(cx, SymBinOp::Or, expr, byte);
1158 }
1159 expr
1160 }
1161
1162 pub(crate) fn as_const(&self) -> Option<U256> {
1163 match self.kind() {
1164 SymExprKind::Const(value) => Some(*value),
1165 _ => None,
1166 }
1167 }
1168
1169 pub(crate) fn eval(&self) -> Option<U256> {
1170 self.eval_model_if_complete(&NoopModel).ok().flatten()
1171 }
1172
1173 pub(crate) fn eval_model<M: SymbolicModelLookup + ?Sized>(
1174 &self,
1175 model: &M,
1176 ) -> Result<U256, SymbolicError> {
1177 let kind = self.kind();
1178 if let Some(var) = kind.get_eval_var() {
1179 return Ok(model.value(var).unwrap_or_default());
1180 }
1181 Ok(match kind {
1182 SymExprKind::Const(value) => *value,
1183 SymExprKind::Var(_) | SymExprKind::GasLeft(_) | SymExprKind::Hash { .. } => {
1184 unreachable!("symbolic eval leaf handled above")
1185 }
1186 SymExprKind::Keccak { len, bytes, .. } => {
1187 let len = len.eval_model(model)?;
1188 let Ok(len) = usize::try_from(len) else {
1189 return Err(SymbolicError::Solver(
1190 "solver model uses an invalid keccak length".to_string(),
1191 ));
1192 };
1193 if len > bytes.len() {
1194 return Err(SymbolicError::Solver(
1195 "solver model uses an invalid keccak length".to_string(),
1196 ));
1197 }
1198
1199 let mut input = Vec::with_capacity(len);
1200 for byte in bytes.iter().take(len) {
1201 input.push((byte.eval_model(model)? & U256::from(0xff)).to::<u8>());
1202 }
1203
1204 U256::from_be_bytes(keccak256(input).0)
1205 }
1206 SymExprKind::Not(value) => !value.eval_model(model)?,
1207 SymExprKind::BinOp(op, left, right) => {
1208 op.eval(left.eval_model(model)?, right.eval_model(model)?)
1209 }
1210 SymExprKind::TernOp(op, left, right, modulus) => op.eval(
1211 left.eval_model(model)?,
1212 right.eval_model(model)?,
1213 modulus.eval_model(model)?,
1214 ),
1215 SymExprKind::Ite(cond, then_expr, else_expr) => {
1216 if cond.eval_model(model)? {
1217 then_expr.eval_model(model)?
1218 } else {
1219 else_expr.eval_model(model)?
1220 }
1221 }
1222 })
1223 }
1224
1225 pub(crate) fn eval_model_if_complete<M: SymbolicModelLookup + ?Sized>(
1226 &self,
1227 model: &M,
1228 ) -> Result<Option<U256>, SymbolicError> {
1229 let mut vars = SymbolicVars::default();
1230 self.collect_eval_vars(&mut vars);
1231 if vars.iter().copied().all(|var| model.contains_name(var)) {
1232 self.eval_model(model).map(Some)
1233 } else {
1234 Ok(None)
1235 }
1236 }
1237
1238 pub(crate) fn assign_model_value(&self, model: &mut SymbolicModel, value: U256) -> bool {
1239 match self.kind() {
1240 SymExprKind::Const(existing) => *existing == value,
1241 SymExprKind::Var(var) => {
1242 if let Some(existing) = model.get(var) {
1243 *existing == value
1244 } else {
1245 model.insert(*var, value);
1246 true
1247 }
1248 }
1249 SymExprKind::GasLeft(symbol) => {
1250 if let Some(existing) = model.get(symbol) {
1251 *existing == value
1252 } else {
1253 model.insert(*symbol, value);
1254 true
1255 }
1256 }
1257 _ => false,
1258 }
1259 }
1260
1261 pub(crate) fn bool_word_condition(&self) -> Option<SymBoolExpr> {
1262 let SymExprKind::Ite(condition, then_expr, else_expr) = self.kind() else {
1263 return None;
1264 };
1265 Self::bool_word_condition_from_parts(condition, then_expr, else_expr)
1266 }
1267
1268 fn bool_word_condition_from_parts(
1269 condition: &SymBoolExpr,
1270 then_expr: &Self,
1271 else_expr: &Self,
1272 ) -> Option<SymBoolExpr> {
1273 match (then_expr.as_const(), else_expr.as_const()) {
1274 (Some(then_value), Some(else_value))
1275 if then_value == U256::ONE && else_value.is_zero() =>
1276 {
1277 Some(condition.clone())
1278 }
1279 (Some(then_value), Some(else_value))
1280 if then_value.is_zero() && else_value == U256::ONE =>
1281 {
1282 None
1283 }
1284 _ => None,
1285 }
1286 }
1287
1288 pub(crate) fn truth(&self) -> Option<bool> {
1289 self.as_const().map(|value| !value.is_zero())
1290 }
1291
1292 pub(crate) fn into_zero_bool(self, cx: &mut SymCx) -> SymBoolExpr {
1293 match self.kind() {
1294 SymExprKind::Const(value) => SymBoolExpr::constant(cx, value.is_zero()),
1295 SymExprKind::Ite(condition, then_expr, else_expr) => {
1296 match Self::bool_word_condition_from_parts(condition, then_expr, else_expr) {
1297 Some(condition) => SymBoolExpr::not_bool(cx, condition),
1298 None => {
1299 let zero = Self::zero(cx);
1300 SymBoolExpr::eq(cx, self, zero)
1301 }
1302 }
1303 }
1304 _ => {
1305 let zero = Self::zero(cx);
1306 SymBoolExpr::eq(cx, self, zero)
1307 }
1308 }
1309 }
1310
1311 pub(crate) fn nonzero_bool(self, cx: &mut SymCx) -> SymBoolExpr {
1312 let zero = self.into_zero_bool(cx);
1313 SymBoolExpr::not_bool(cx, zero)
1314 }
1315
1316 pub(crate) fn as_const_or(&self, reason: &'static str) -> Result<U256, SymbolicError> {
1317 self.as_const().ok_or(SymbolicError::Unsupported(reason))
1318 }
1319
1320 pub(crate) fn as_usize_or(&self, reason: &'static str) -> Result<usize, SymbolicError> {
1321 let value = self.as_const_or(reason)?;
1322 usize::try_from(value).map_err(|_| SymbolicError::Unsupported(reason))
1323 }
1324
1325 pub(crate) fn contains_keccak(&self) -> bool {
1326 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::Keccak { .. }))
1327 }
1328
1329 pub(crate) fn contains_gasleft(&self) -> bool {
1330 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::GasLeft(_)))
1331 }
1332
1333 pub(crate) fn contains_udiv(&self) -> bool {
1334 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::BinOp(SymBinOp::UDiv, _, _)))
1335 }
1336
1337 pub(crate) fn contains_ite(&self) -> bool {
1338 self.visit_bool(|expr| matches!(expr.kind(), SymExprKind::Ite(_, _, _)))
1339 }
1340
1341 pub(in crate::runtime) fn udiv_operands(&self) -> Option<(&Self, &Self)> {
1342 match self.kind() {
1343 SymExprKind::BinOp(SymBinOp::UDiv, numerator, denominator) => {
1344 Some((numerator, denominator))
1345 }
1346 _ => None,
1347 }
1348 }
1349
1350 pub(crate) fn collect_eval_vars(&self, vars: &mut SymbolicVars) {
1351 let _ = self.visit(&mut |expr| {
1352 if let Some(var) = expr.kind().get_eval_var() {
1353 vars.insert(var);
1354 }
1355 ControlFlow::<()>::Continue(())
1356 });
1357 }
1358
1359 pub(crate) fn known_byte(&self, index: usize) -> Option<u8> {
1360 debug_assert!(index < 32);
1361 match self.kind() {
1362 SymExprKind::Const(value) => Some(value.to_be_bytes::<32>()[index]),
1363 SymExprKind::Var(_)
1364 | SymExprKind::GasLeft(_)
1365 | SymExprKind::Keccak { .. }
1366 | SymExprKind::Hash { .. } => None,
1367 SymExprKind::Not(value) => value.known_byte(index).map(|byte| !byte),
1368 SymExprKind::Ite(_, then_expr, else_expr) => {
1369 let then_byte = then_expr.known_byte(index)?;
1370 let else_byte = else_expr.known_byte(index)?;
1371 (then_byte == else_byte).then_some(then_byte)
1372 }
1373 SymExprKind::BinOp(op, left, right) => match op {
1374 SymBinOp::And => match (left.known_byte(index), right.known_byte(index)) {
1375 (Some(left), Some(right)) => Some(left & right),
1376 (Some(0), _) | (_, Some(0)) => Some(0),
1377 _ => None,
1378 },
1379 SymBinOp::Or => Some(left.known_byte(index)? | right.known_byte(index)?),
1380 SymBinOp::Xor => Some(left.known_byte(index)? ^ right.known_byte(index)?),
1381 SymBinOp::Shl => {
1382 let shift = right.as_const()?;
1383 if shift >= U256::from(256) {
1384 return Some(0);
1385 }
1386 let shift = usize::try_from(shift).expect("checked byte shift");
1387 if shift % 8 != 0 {
1388 return None;
1389 }
1390 let source_index = index + shift / 8;
1391 if source_index >= 32 { Some(0) } else { left.known_byte(source_index) }
1392 }
1393 SymBinOp::Shr => {
1394 let shift = right.as_const()?;
1395 if shift >= U256::from(256) {
1396 return Some(0);
1397 }
1398 let shift = usize::try_from(shift).expect("checked byte shift");
1399 if shift % 8 != 0 {
1400 return None;
1401 }
1402 let byte_shift = shift / 8;
1403 if index < byte_shift { Some(0) } else { left.known_byte(index - byte_shift) }
1404 }
1405 SymBinOp::Add
1406 | SymBinOp::Sub
1407 | SymBinOp::Mul
1408 | SymBinOp::UDiv
1409 | SymBinOp::URem
1410 | SymBinOp::SDiv
1411 | SymBinOp::SRem
1412 | SymBinOp::Sar => None,
1413 },
1414 SymExprKind::TernOp(_, _, _, _) => None,
1415 }
1416 }
1417
1418 pub(crate) fn known_word(&self) -> Option<U256> {
1419 let mut word = [0u8; 32];
1420 for (idx, byte) in word.iter_mut().enumerate() {
1421 *byte = self.known_byte(idx)?;
1422 }
1423 Some(U256::from_be_bytes(word))
1424 }
1425
1426 pub(crate) fn unsigned_bits(&self) -> usize {
1427 match self.kind() {
1428 SymExprKind::Const(value) => value.bit_len().max(1),
1429 SymExprKind::BinOp(SymBinOp::And, left, right) => {
1430 if let Some(mask) = right.as_const() {
1431 left.unsigned_bits().min(mask.bit_len())
1432 } else {
1433 256
1434 }
1435 }
1436 SymExprKind::BinOp(SymBinOp::Add, left, right) => {
1437 left.unsigned_bits().max(right.unsigned_bits()).saturating_add(1).min(256)
1438 }
1439 SymExprKind::BinOp(SymBinOp::Mul, left, right) => {
1440 left.unsigned_bits().saturating_add(right.unsigned_bits()).min(256)
1441 }
1442 SymExprKind::BinOp(SymBinOp::Shl, left, right) => {
1443 if let Some(shift) = right.as_const().and_then(|shift| usize::try_from(shift).ok())
1444 {
1445 left.unsigned_bits().saturating_add(shift).min(256)
1446 } else {
1447 256
1448 }
1449 }
1450 SymExprKind::BinOp(SymBinOp::Shr, left, right) => {
1451 if let Some(shift) = right.as_const().and_then(|shift| usize::try_from(shift).ok())
1452 {
1453 left.unsigned_bits().saturating_sub(shift).max(1)
1454 } else {
1455 256
1456 }
1457 }
1458 SymExprKind::BinOp(SymBinOp::UDiv, left, _) => left.unsigned_bits(),
1459 SymExprKind::TernOp(_, _, _, modulus) => modulus.unsigned_bits(),
1460 SymExprKind::Ite(_, left, right) => left.unsigned_bits().max(right.unsigned_bits()),
1461 _ => 256,
1462 }
1463 }
1464
1465 pub(crate) fn extracted_byte(&self, cx: &mut SymCx, index: usize) -> Self {
1466 debug_assert!(index < 32);
1467 let shift = Self::constant(cx, U256::from((31 - index) * 8));
1468 let shifted = Self::binop(cx, SymBinOp::Shr, self.clone(), shift);
1469 let mask = Self::constant(cx, U256::from(0xff));
1470 Self::binop(cx, SymBinOp::And, shifted, mask)
1471 }
1472
1473 pub(crate) fn extracted_byte_source(&self, index: usize) -> Option<Self> {
1474 let expr = self.strip_low_byte_mask();
1475 if index == 31 {
1476 return Some(expr.clone());
1477 }
1478 let SymExprKind::BinOp(SymBinOp::Shr, source, shift) = expr.kind() else { return None };
1479 let shift = shift.as_const()?;
1480 (shift == U256::from((31 - index) * 8)).then(|| source.clone())
1481 }
1482
1483 pub(crate) fn strip_low_byte_mask(&self) -> &Self {
1484 match self.kind() {
1485 SymExprKind::BinOp(SymBinOp::And, left, right)
1486 if right.as_const() == Some(U256::from(0xff)) =>
1487 {
1488 left.strip_low_byte_mask()
1489 }
1490 _ => self,
1491 }
1492 }
1493
1494 pub(crate) fn byte_term(&self, cx: &mut SymCx, index: usize) -> Option<Self> {
1495 debug_assert!(index < 32);
1496
1497 match self.kind() {
1498 SymExprKind::Const(value) => {
1499 Some(Self::constant(cx, U256::from(value.to_be_bytes::<32>()[index])))
1500 }
1501 SymExprKind::Var(_)
1502 | SymExprKind::GasLeft(_)
1503 | SymExprKind::Keccak { .. }
1504 | SymExprKind::Hash { .. } => Some(self.extracted_byte(cx, index)),
1505 SymExprKind::Not(value) => {
1506 let value = value.byte_term(cx, index)?;
1507 Some(Self::not(cx, value))
1508 }
1509 SymExprKind::Ite(cond, then_expr, else_expr) => {
1510 let then_expr = then_expr.byte_term(cx, index)?;
1511 let else_expr = else_expr.byte_term(cx, index)?;
1512 Some(Self::ite(cx, cond.clone(), then_expr, else_expr))
1513 }
1514 SymExprKind::BinOp(op, left, right) => match op {
1515 SymBinOp::And => Self::binary_byte_term(
1516 cx,
1517 left,
1518 right,
1519 index,
1520 SymBinOp::And,
1521 |byte| byte == 0xff,
1522 |byte| byte == 0,
1523 ),
1524 SymBinOp::Or => Self::binary_byte_term(
1525 cx,
1526 left,
1527 right,
1528 index,
1529 SymBinOp::Or,
1530 |byte| byte == 0,
1531 |_| false,
1532 ),
1533 SymBinOp::Xor => Self::binary_byte_term(
1534 cx,
1535 left,
1536 right,
1537 index,
1538 SymBinOp::Xor,
1539 |byte| byte == 0,
1540 |_| false,
1541 ),
1542 SymBinOp::Shl => {
1543 let shift = right.eval()?;
1544 if shift >= U256::from(256) {
1545 return Some(Self::zero(cx));
1546 }
1547 let shift = usize::try_from(shift).expect("checked byte shift");
1548 if shift % 8 != 0 {
1549 return None;
1550 }
1551 let source_index = index + shift / 8;
1552 if source_index >= 32 {
1553 Some(Self::zero(cx))
1554 } else {
1555 left.byte_term(cx, source_index)
1556 }
1557 }
1558 SymBinOp::Shr => {
1559 let shift = right.eval()?;
1560 if shift >= U256::from(256) {
1561 return Some(Self::zero(cx));
1562 }
1563 let shift = usize::try_from(shift).expect("checked byte shift");
1564 if shift % 8 != 0 {
1565 return None;
1566 }
1567 let byte_shift = shift / 8;
1568 if index < byte_shift {
1569 Some(Self::zero(cx))
1570 } else {
1571 left.byte_term(cx, index - byte_shift)
1572 }
1573 }
1574 SymBinOp::Add
1575 | SymBinOp::Sub
1576 | SymBinOp::Mul
1577 | SymBinOp::UDiv
1578 | SymBinOp::URem
1579 | SymBinOp::SDiv
1580 | SymBinOp::SRem
1581 | SymBinOp::Sar => None,
1582 },
1583 SymExprKind::TernOp(_, _, _, _) => None,
1584 }
1585 }
1586
1587 fn binary_byte_term(
1588 cx: &mut SymCx,
1589 left: &Self,
1590 right: &Self,
1591 index: usize,
1592 op: SymBinOp,
1593 identity: impl Fn(u8) -> bool,
1594 absorbing: impl Fn(u8) -> bool,
1595 ) -> Option<Self> {
1596 let left = left.byte_term(cx, index)?;
1597 let right = right.byte_term(cx, index)?;
1598 match (left.byte_const(), right.byte_const()) {
1599 (Some(left), _) if absorbing(left) => Some(Self::constant(cx, U256::from(left))),
1600 (_, Some(right)) if absorbing(right) => Some(Self::constant(cx, U256::from(right))),
1601 (Some(left), _) if identity(left) => Some(right),
1602 (_, Some(right)) if identity(right) => Some(left),
1603 _ => Some(Self::binop(cx, op, left, right)),
1604 }
1605 }
1606
1607 pub(crate) fn byte_const(&self) -> Option<u8> {
1608 self.as_const().map(|value| value.to::<u8>())
1609 }
1610
1611 pub(crate) fn equality_forces_const(
1612 &self,
1613 value: U256,
1614 expr: &Self,
1615 context: &[SymBoolExpr],
1616 ) -> Option<U256> {
1617 if self == expr {
1618 return Some(value);
1619 }
1620 self.equality_forces_const_inner(value, expr, context)
1621 }
1622
1623 fn equality_forces_const_inner(
1624 &self,
1625 value: U256,
1626 expr: &Self,
1627 context: &[SymBoolExpr],
1628 ) -> Option<U256> {
1629 let mask = masked_expr_matches(self.kind(), expr)?;
1630 if value & !mask != U256::ZERO || !context_forces_masked_expr(context, expr, mask) {
1631 return None;
1632 }
1633 Some(value)
1634 }
1635
1636 pub(crate) fn nonzero_forces_const(
1637 &self,
1638 target: &Self,
1639 context: &[SymBoolExpr],
1640 ) -> Option<U256> {
1641 match self.kind() {
1642 SymExprKind::Const(_)
1643 | SymExprKind::Var(_)
1644 | SymExprKind::GasLeft(_)
1645 | SymExprKind::Keccak { .. }
1646 | SymExprKind::Hash { .. }
1647 | SymExprKind::Not(_) => None,
1648 SymExprKind::Ite(cond, then_expr, else_expr) => {
1649 if then_expr.eval().is_some_and(|value| !value.is_zero())
1650 && else_expr.eval().is_some_and(|value| value.is_zero())
1651 {
1652 cond.forces_expr_const_with_context(target, context)
1653 } else {
1654 None
1655 }
1656 }
1657 SymExprKind::BinOp(SymBinOp::Or, left, right) => {
1658 if left.eval().is_some_and(|value| value.is_zero()) {
1659 return right.nonzero_forces_const(target, context);
1660 }
1661 if right.eval().is_some_and(|value| value.is_zero()) {
1662 return left.nonzero_forces_const(target, context);
1663 }
1664 None
1665 }
1666 SymExprKind::BinOp(SymBinOp::And, left, right) => {
1667 if left.eval().is_some_and(|value| !value.is_zero()) {
1668 return right.nonzero_forces_const(target, context);
1669 }
1670 if right.eval().is_some_and(|value| !value.is_zero()) {
1671 return left.nonzero_forces_const(target, context);
1672 }
1673 None
1674 }
1675 SymExprKind::BinOp(SymBinOp::Shl | SymBinOp::Shr, value, shift)
1676 if shift.eval().is_some_and(|shift| shift.is_zero()) =>
1677 {
1678 value.nonzero_forces_const(target, context)
1679 }
1680 SymExprKind::TernOp(_, _, _, _) => None,
1681 SymExprKind::BinOp(_, _, _) => None,
1682 }
1683 }
1684
1685 pub(crate) fn is_raw_gasleft(&self) -> bool {
1686 matches!(self.kind(), SymExprKind::GasLeft(_))
1687 }
1688
1689 pub(crate) fn add_const(cx: &mut SymCx, expr: Self, value: U256) -> Self {
1690 if value.is_zero() {
1691 return expr;
1692 }
1693 match expr.kind() {
1694 SymExprKind::Const(expr) => Self::constant(cx, expr.wrapping_add(value)),
1695 _ => {
1696 let value = Self::constant(cx, value);
1697 Self::binop(cx, SymBinOp::Add, expr, value)
1698 }
1699 }
1700 }
1701
1702 pub(crate) fn visit<B>(
1704 &self,
1705 visitor: &mut impl FnMut(&Self) -> ControlFlow<B>,
1706 ) -> ControlFlow<B> {
1707 visitor(self)?;
1708 match self.kind() {
1709 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => {}
1710 SymExprKind::Keccak { len, bytes, .. } => {
1711 len.visit(visitor)?;
1712 for byte in bytes.iter() {
1713 byte.visit(visitor)?;
1714 }
1715 }
1716 SymExprKind::Hash { bytes, .. } => {
1717 for byte in bytes.iter() {
1718 byte.visit(visitor)?;
1719 }
1720 }
1721 SymExprKind::Not(value) => value.visit(visitor)?,
1722 SymExprKind::BinOp(_, left, right) => {
1723 left.visit(visitor)?;
1724 right.visit(visitor)?;
1725 }
1726 SymExprKind::TernOp(_, left, right, modulus) => {
1727 left.visit(visitor)?;
1728 right.visit(visitor)?;
1729 modulus.visit(visitor)?;
1730 }
1731 SymExprKind::Ite(cond, left, right) => {
1732 cond.visit_exprs(visitor)?;
1733 left.visit(visitor)?;
1734 right.visit(visitor)?;
1735 }
1736 }
1737 ControlFlow::Continue(())
1738 }
1739
1740 pub(crate) fn visit_bool(&self, mut visitor: impl FnMut(&Self) -> bool) -> bool {
1741 self.visit(&mut |expr| {
1742 if visitor(expr) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
1743 })
1744 .is_break()
1745 }
1746
1747 pub(crate) fn fold(
1748 self,
1749 cx: &mut SymCx,
1750 folder: &mut impl FnMut(&mut SymCx, Self) -> Self,
1751 ) -> Self {
1752 if matches!(
1753 self.kind(),
1754 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_)
1755 ) {
1756 return folder(cx, self);
1757 }
1758
1759 let expr = match self.into_kind() {
1760 SymExprKind::Keccak { name, len, bytes } => {
1761 let len = len.fold(cx, folder);
1762 let bytes = bytes.iter().cloned().map(|byte| byte.fold(cx, folder)).collect();
1763 Self::keccak_symbol(cx, name, len, bytes)
1764 }
1765 SymExprKind::Hash { name, algorithm, bytes } => {
1766 let bytes = bytes.iter().cloned().map(|byte| byte.fold(cx, folder)).collect();
1767 Self::hash_symbol(cx, name, algorithm, bytes)
1768 }
1769 SymExprKind::Not(value) => {
1770 let value = value.fold(cx, folder);
1771 Self::not(cx, value)
1772 }
1773 SymExprKind::BinOp(op, left, right) => {
1774 let left = left.fold(cx, folder);
1775 let right = right.fold(cx, folder);
1776 Self::binop(cx, op, left, right)
1777 }
1778 SymExprKind::TernOp(op, left, right, modulus) => {
1779 let left = left.fold(cx, folder);
1780 let right = right.fold(cx, folder);
1781 let modulus = modulus.fold(cx, folder);
1782 Self::ternop(cx, op, left, right, modulus)
1783 }
1784 SymExprKind::Ite(condition, then_expr, else_expr) => {
1785 let condition = condition.fold_exprs(cx, folder);
1786 let then_expr = then_expr.fold(cx, folder);
1787 let else_expr = else_expr.fold(cx, folder);
1788 Self::ite(cx, condition, then_expr, else_expr)
1789 }
1790 SymExprKind::Const(_) | SymExprKind::Var(_) | SymExprKind::GasLeft(_) => {
1791 unreachable!("leaf expression returned before folding children")
1792 }
1793 };
1794 folder(cx, expr)
1795 }
1796
1797 #[cfg(test)]
1798 pub(crate) fn smt(&self, cx: &SymCx) -> String {
1799 let mut smt = String::new();
1800 self.write_smt(cx, &mut smt);
1801 smt
1802 }
1803
1804 pub(in crate::runtime::expr) fn write_smt(&self, cx: &SymCx, out: &mut String) {
1805 match self.kind() {
1806 SymExprKind::Const(value) => {
1807 let _ = write!(out, "(_ bv{value} 256)");
1808 }
1809 SymExprKind::Var(symbol)
1810 | SymExprKind::GasLeft(symbol)
1811 | SymExprKind::Keccak { name: symbol, .. }
1812 | SymExprKind::Hash { name: symbol, .. } => out.push_str(cx.symbol_name(*symbol)),
1813 SymExprKind::Not(value) => {
1814 out.push_str("(bvnot ");
1815 value.write_smt(cx, out);
1816 out.push(')');
1817 }
1818 SymExprKind::BinOp(op, left, right) => {
1819 let _ = write!(out, "({} ", op.smt());
1820 left.write_smt(cx, out);
1821 out.push(' ');
1822 right.write_smt(cx, out);
1823 out.push(')');
1824 }
1825 SymExprKind::TernOp(op, left, right, modulus) => {
1826 write_smt_wide_modular_arithmetic(cx, out, op.smt(), left, right, modulus);
1827 }
1828 SymExprKind::Ite(cond, left, right) => {
1829 out.push_str("(ite ");
1830 cond.write_smt(cx, out);
1831 out.push(' ');
1832 left.write_smt(cx, out);
1833 out.push(' ');
1834 right.write_smt(cx, out);
1835 out.push(')');
1836 }
1837 }
1838 }
1839}
1840
1841fn write_smt_wide_modular_arithmetic(
1842 cx: &SymCx,
1843 out: &mut String,
1844 op: &'static str,
1845 left: &SymExpr,
1846 right: &SymExpr,
1847 modulus: &SymExpr,
1848) {
1849 out.push_str("(ite (= ");
1854 modulus.write_smt(cx, out);
1855 out.push_str(" (_ bv0 256)) (_ bv0 256) ((_ extract 255 0) (bvurem (");
1856 out.push_str(op);
1857 out.push_str(" ((_ zero_extend 256) ");
1858 left.write_smt(cx, out);
1859 out.push_str(") ((_ zero_extend 256) ");
1860 right.write_smt(cx, out);
1861 out.push_str(")) ((_ zero_extend 256) ");
1862 modulus.write_smt(cx, out);
1863 out.push_str("))))");
1864}
1865
1866#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1867pub(crate) enum SymTernOp {
1868 AddMod,
1869 MulMod,
1870}
1871
1872impl SymTernOp {
1873 pub(crate) const fn smt(self) -> &'static str {
1874 match self {
1875 Self::AddMod => "bvadd",
1876 Self::MulMod => "bvmul",
1877 }
1878 }
1879
1880 pub(crate) fn eval(self, left: U256, right: U256, modulus: U256) -> U256 {
1881 if modulus.is_zero() {
1882 return U256::ZERO;
1883 }
1884 match self {
1885 Self::AddMod => left.add_mod(right, modulus),
1886 Self::MulMod => left.mul_mod(right, modulus),
1887 }
1888 }
1889}
1890
1891#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1892pub(crate) enum SymBinOp {
1893 Add,
1894 Sub,
1895 Mul,
1896 UDiv,
1897 URem,
1898 SDiv,
1899 SRem,
1900 And,
1901 Or,
1902 Xor,
1903 Shl,
1904 Shr,
1905 Sar,
1906}
1907
1908impl SymBinOp {
1909 pub(crate) const fn smt(self) -> &'static str {
1910 match self {
1911 Self::Add => "bvadd",
1912 Self::Sub => "bvsub",
1913 Self::Mul => "bvmul",
1914 Self::UDiv => "bvudiv",
1915 Self::URem => "bvurem",
1916 Self::SDiv => "bvsdiv",
1917 Self::SRem => "bvsrem",
1918 Self::And => "bvand",
1919 Self::Or => "bvor",
1920 Self::Xor => "bvxor",
1921 Self::Shl => "bvshl",
1922 Self::Shr => "bvlshr",
1923 Self::Sar => "bvashr",
1924 }
1925 }
1926
1927 pub(crate) fn eval(self, left: U256, right: U256) -> U256 {
1928 match self {
1929 Self::Add => left.wrapping_add(right),
1930 Self::Sub => left.wrapping_sub(right),
1931 Self::Mul => left.wrapping_mul(right),
1932 Self::UDiv => {
1933 if right.is_zero() {
1934 U256::ZERO
1935 } else {
1936 left / right
1937 }
1938 }
1939 Self::URem => {
1940 if right.is_zero() {
1941 U256::ZERO
1942 } else {
1943 left % right
1944 }
1945 }
1946 Self::SDiv => sdiv(left, right),
1947 Self::SRem => smod(left, right),
1948 Self::And => left & right,
1949 Self::Or => left | right,
1950 Self::Xor => left ^ right,
1951 Self::Shl => {
1952 if right >= U256::from(256) {
1953 U256::ZERO
1954 } else {
1955 left << usize::try_from(right).expect("checked word shift")
1956 }
1957 }
1958 Self::Shr => {
1959 if right >= U256::from(256) {
1960 U256::ZERO
1961 } else {
1962 left >> usize::try_from(right).expect("checked word shift")
1963 }
1964 }
1965 Self::Sar => {
1966 if right >= U256::from(256) {
1967 sar(left, 256)
1968 } else {
1969 sar(left, usize::try_from(right).expect("checked word shift"))
1970 }
1971 }
1972 }
1973 }
1974}