foundry_evm_symbolic/runtime/
evm.rs1use super::*;
2use alloy_sol_types::{
3 Panic, PanicKind, Revert, SolError,
4 abi::{AbiDecoderConfig, decode_with_config, token::PackedSeqToken},
5};
6use foundry_evm::core::decode::ASSERTION_FAILED_PREFIX;
7
8pub(crate) fn exp_expr_for_concrete_exponent(
9 cx: &mut SymCx,
10 base: SymExpr,
11 exponent: usize,
12) -> SymExpr {
13 if exponent == 0 {
14 return SymExpr::one(cx);
15 }
16 if let Some(base) = base.as_const() {
17 return SymExpr::constant(cx, base.wrapping_pow(U256::from(exponent)));
18 }
19
20 let mut expr = base.clone();
21 for _ in 1..exponent {
22 expr = SymExpr::binop(cx, SymBinOp::Mul, expr, base.clone());
23 }
24 expr
25}
26
27pub(crate) fn signextend(byte_index: U256, value: U256) -> U256 {
28 if byte_index >= U256::from(32) {
29 return value;
30 }
31 let bit_index = usize::try_from(byte_index).expect("checked byte index") * 8 + 7;
32 let sign_bit = U256::ONE << bit_index;
33 let mask = sign_bit - U256::ONE;
34 if (value & sign_bit).is_zero() { value & mask } else { value | !mask }
35}
36
37pub(crate) fn signextend_word(cx: &mut SymCx, byte_index: U256, value: SymExpr) -> SymExpr {
38 if byte_index >= U256::from(32) {
39 return value;
40 }
41 if let Some(value) = value.as_const() {
42 return SymExpr::constant(cx, signextend(byte_index, value));
43 }
44 let bit_index = usize::try_from(byte_index).expect("checked byte index") * 8 + 7;
45 let sign_bit = U256::ONE << bit_index;
46 let mask_value = sign_bit - U256::ONE;
47 let sign_bit = SymExpr::constant(cx, sign_bit);
48 let masked_sign = SymExpr::binop(cx, SymBinOp::And, value.clone(), sign_bit);
49 let zero = SymExpr::zero(cx);
50 let condition = SymBoolExpr::eq(cx, masked_sign, zero);
51 let inverse_mask = SymExpr::constant(cx, !mask_value);
52 let mask = SymExpr::constant(cx, mask_value);
53 let masked = SymExpr::binop(cx, SymBinOp::And, value.clone(), mask);
54 let extended = SymExpr::binop(cx, SymBinOp::Or, value, inverse_mask);
55 SymExpr::ite(cx, condition, masked, extended)
56}
57
58pub(crate) fn signextend_word_dynamic(
59 cx: &mut SymCx,
60 byte_index: SymExpr,
61 value: SymExpr,
62) -> SymExpr {
63 if let Some(byte_index) = byte_index.as_const() {
64 return signextend_word(cx, byte_index, value);
65 }
66
67 let mut result = value.clone();
68 for idx in (0..31).rev() {
69 let idx_expr = SymExpr::constant(cx, U256::from(idx));
70 let condition = SymBoolExpr::eq(cx, byte_index.clone(), idx_expr);
71 let value = signextend_word(cx, U256::from(idx), value.clone());
72 result = SymExpr::ite(cx, condition, value, result);
73 }
74 result
75}
76
77pub(crate) fn byte_word(cx: &mut SymCx, index: U256, word: SymExpr) -> SymExpr {
78 if index >= U256::from(32) {
79 return SymExpr::zero(cx);
80 }
81 let index = usize::try_from(index).expect("checked byte index");
82 if let Some(word) = word.as_const() {
83 SymExpr::constant(cx, U256::from(word.to_be_bytes::<32>()[index]))
84 } else {
85 byte_expr(cx, index, &word)
86 }
87}
88
89pub(crate) fn byte_word_dynamic(cx: &mut SymCx, index: SymExpr, word: SymExpr) -> SymExpr {
90 if let Some(index) = index.as_const() {
91 return byte_word(cx, index, word);
92 }
93
94 let mut result = SymExpr::zero(cx);
95 if let Some(word) = word.as_const() {
96 let bytes = word.to_be_bytes::<32>();
97 for idx in (0..32).rev() {
98 let idx_expr = SymExpr::constant(cx, U256::from(idx));
99 let condition = SymBoolExpr::eq(cx, index.clone(), idx_expr);
100 let byte = SymExpr::constant(cx, U256::from(bytes[idx]));
101 result = SymExpr::ite(cx, condition, byte, result);
102 }
103 } else {
104 for idx in (0..32).rev() {
105 let idx_expr = SymExpr::constant(cx, U256::from(idx));
106 let condition = SymBoolExpr::eq(cx, index.clone(), idx_expr);
107 let byte = byte_expr(cx, idx, &word);
108 result = SymExpr::ite(cx, condition, byte, result);
109 }
110 }
111 result
112}
113
114pub(crate) fn byte_expr(cx: &mut SymCx, index: usize, expr: &SymExpr) -> SymExpr {
116 debug_assert!(index < 32);
117 if let Some(byte) = expr.known_byte(index) {
118 return SymExpr::constant(cx, U256::from(byte));
119 }
120 expr.extracted_byte(cx, index)
121}
122
123pub(crate) fn shift_left(cx: &mut SymCx, value: SymExpr, bits: usize) -> SymExpr {
124 if let Some(value) = value.as_const() {
125 SymExpr::constant(cx, value << bits)
126 } else {
127 let bits = SymExpr::constant(cx, U256::from(bits));
128 SymExpr::binop(cx, SymBinOp::Shl, value, bits)
129 }
130}
131
132pub(crate) fn is_assertion_revert(data: &[u8]) -> bool {
133 Panic::abi_decode(data).is_ok_and(|panic| panic.kind() == Some(PanicKind::Assert))
134 || is_revert_assertion_failure(data)
135}
136
137pub(crate) fn is_revert_assertion_failure(data: &[u8]) -> bool {
138 if data.len() < ERROR_DATA_MIN_LEN {
139 return false;
140 }
141 let Some(data) = data.strip_prefix(&Revert::SELECTOR) else { return false };
142 let config = AbiDecoderConfig::new().memory_limit(data.len());
144 decode_with_config::<PackedSeqToken<'_>>(data, config)
145 .ok()
146 .and_then(|message| std::str::from_utf8(message.0).ok())
147 .is_some_and(|message| message.contains(ASSERTION_FAILED_PREFIX))
148}