foundry_evm_symbolic/runtime/
evm.rs1use super::*;
2
3pub(crate) fn failed_slot() -> U256 {
4 let mut bytes = [0u8; 32];
5 bytes[..6].copy_from_slice(b"failed");
6 U256::from_be_bytes(bytes)
7}
8
9pub(crate) fn exp_expr_for_concrete_exponent(
10 cx: &mut SymCx,
11 base: SymExpr,
12 exponent: usize,
13) -> SymExpr {
14 if exponent == 0 {
15 return SymExpr::one(cx);
16 }
17 if let Some(base) = base.as_const() {
18 return SymExpr::constant(cx, base.wrapping_pow(U256::from(exponent)));
19 }
20
21 let mut expr = base.clone();
22 for _ in 1..exponent {
23 expr = SymExpr::binop(cx, SymBinOp::Mul, expr, base.clone());
24 }
25 expr
26}
27
28pub(crate) fn signextend(byte_index: U256, value: U256) -> U256 {
29 if byte_index >= U256::from(32) {
30 return value;
31 }
32 let bit_index = usize::try_from(byte_index).expect("checked byte index") * 8 + 7;
33 let sign_bit = U256::from(1) << bit_index;
34 let mask = sign_bit - U256::from(1);
35 if value & sign_bit == U256::ZERO { value & mask } else { value | !mask }
36}
37
38pub(crate) fn signextend_word(cx: &mut SymCx, byte_index: U256, value: SymExpr) -> SymExpr {
39 if byte_index >= U256::from(32) {
40 return value;
41 }
42 if let Some(value) = value.as_const() {
43 return SymExpr::constant(cx, signextend(byte_index, value));
44 }
45 let bit_index = usize::try_from(byte_index).expect("checked byte index") * 8 + 7;
46 let sign_bit = U256::from(1) << bit_index;
47 let mask_value = sign_bit - U256::from(1);
48 let sign_bit = SymExpr::constant(cx, sign_bit);
49 let masked_sign = SymExpr::binop(cx, SymBinOp::And, value.clone(), sign_bit);
50 let zero = SymExpr::zero(cx);
51 let condition = SymBoolExpr::eq(cx, masked_sign, zero);
52 let inverse_mask = SymExpr::constant(cx, !mask_value);
53 let mask = SymExpr::constant(cx, mask_value);
54 let masked = SymExpr::binop(cx, SymBinOp::And, value.clone(), mask);
55 let extended = SymExpr::binop(cx, SymBinOp::Or, value, inverse_mask);
56 SymExpr::ite(cx, condition, masked, extended)
57}
58
59pub(crate) fn signextend_word_dynamic(
60 cx: &mut SymCx,
61 byte_index: SymExpr,
62 value: SymExpr,
63) -> SymExpr {
64 if let Some(byte_index) = byte_index.as_const() {
65 return signextend_word(cx, byte_index, value);
66 }
67
68 let mut result = value.clone();
69 for idx in (0..31).rev() {
70 let idx_expr = SymExpr::constant(cx, U256::from(idx));
71 let condition = SymBoolExpr::eq(cx, byte_index.clone(), idx_expr);
72 let value = signextend_word(cx, U256::from(idx), value.clone());
73 result = SymExpr::ite(cx, condition, value, result);
74 }
75 result
76}
77
78pub(crate) fn byte_word(cx: &mut SymCx, index: U256, word: SymExpr) -> SymExpr {
79 if index >= U256::from(32) {
80 return SymExpr::zero(cx);
81 }
82 let index = usize::try_from(index).expect("checked byte index");
83 if let Some(word) = word.as_const() {
84 SymExpr::constant(cx, U256::from(word.to_be_bytes::<32>()[index]))
85 } else {
86 byte_expr(cx, index, &word)
87 }
88}
89
90pub(crate) fn byte_word_dynamic(cx: &mut SymCx, index: SymExpr, word: SymExpr) -> SymExpr {
91 if let Some(index) = index.as_const() {
92 return byte_word(cx, index, word);
93 }
94
95 let mut result = SymExpr::zero(cx);
96 if let Some(word) = word.as_const() {
97 let bytes = word.to_be_bytes::<32>();
98 for idx in (0..32).rev() {
99 let idx_expr = SymExpr::constant(cx, U256::from(idx));
100 let condition = SymBoolExpr::eq(cx, index.clone(), idx_expr);
101 let byte = SymExpr::constant(cx, U256::from(bytes[idx]));
102 result = SymExpr::ite(cx, condition, byte, result);
103 }
104 } else {
105 for idx in (0..32).rev() {
106 let idx_expr = SymExpr::constant(cx, U256::from(idx));
107 let condition = SymBoolExpr::eq(cx, index.clone(), idx_expr);
108 let byte = byte_expr(cx, idx, &word);
109 result = SymExpr::ite(cx, condition, byte, result);
110 }
111 }
112 result
113}
114
115pub(crate) fn byte_expr(cx: &mut SymCx, index: usize, expr: &SymExpr) -> SymExpr {
117 debug_assert!(index < 32);
118 if let Some(byte) = expr.known_byte(index) {
119 return SymExpr::constant(cx, U256::from(byte));
120 }
121 expr.extracted_byte(cx, index)
122}
123
124pub(crate) fn shift_left(cx: &mut SymCx, value: SymExpr, bits: usize) -> SymExpr {
125 if let Some(value) = value.as_const() {
126 SymExpr::constant(cx, value << bits)
127 } else {
128 let bits = SymExpr::constant(cx, U256::from(bits));
129 SymExpr::binop(cx, SymBinOp::Shl, value, bits)
130 }
131}
132
133pub(crate) fn is_assertion_revert(data: &[u8]) -> bool {
134 is_assert_panic(data) || is_revert_assertion_failure(data)
135}
136
137pub(crate) fn is_assert_panic(data: &[u8]) -> bool {
138 data.len() >= ABI_SELECTOR_PLUS_WORD_LEN
139 && data.starts_with(&PANIC_SELECTOR)
140 && abi_word(&data[4..ABI_SELECTOR_PLUS_WORD_LEN])
141 .is_some_and(|code| code == ASSERT_PANIC_CODE)
142}
143
144pub(crate) fn is_revert_assertion_failure(data: &[u8]) -> bool {
145 if data.len() < ERROR_DATA_MIN_LEN || !data.starts_with(&ERROR_SELECTOR) {
146 return false;
147 }
148
149 let Some(offset) = abi_word_usize(&data[4..ABI_SELECTOR_PLUS_WORD_LEN]) else {
150 return false;
151 };
152 let Some(length_offset) = 4usize.checked_add(offset) else {
153 return false;
154 };
155 let Some(length_end) = length_offset.checked_add(32) else {
156 return false;
157 };
158 if length_end > data.len() {
159 return false;
160 }
161
162 let Some(length) = abi_word_usize(&data[length_offset..length_end]) else {
163 return false;
164 };
165 let Some(message_end) = length_end.checked_add(length) else {
166 return false;
167 };
168 if message_end > data.len() {
169 return false;
170 }
171
172 std::str::from_utf8(&data[length_end..message_end])
173 .is_ok_and(|message| message.contains(ASSERTION_FAILED_PREFIX))
174}
175
176pub(crate) fn abi_word_usize(word: &[u8]) -> Option<usize> {
177 usize::try_from(abi_word(word)?).ok()
178}
179
180pub(crate) const fn abi_word(word: &[u8]) -> Option<U256> {
181 if word.len() != 32 {
182 return None;
183 }
184 let mut bytes = [0u8; 32];
185 bytes.copy_from_slice(word);
186 Some(U256::from_be_bytes(bytes))
187}