Skip to main content

foundry_evm_symbolic/runtime/
evm.rs

1use 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
115/// Returns the byte extraction expression for a symbolic word.
116pub(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}