Skip to main content

ethrex_levm/opcode_handlers/
arithmetic.rs

1//! # Arithmetic operations
2//!
3//! Includes the following opcodes:
4//!   - `ADD`
5//!   - `SUB`
6//!   - `MUL`
7//!   - `DIV`
8//!   - `SDIV`
9//!   - `MOD`
10//!   - `SMOD`
11//!   - `ADDMOD`
12//!   - `MULMOD`
13//!   - `EXP`
14//!   - `SIGNEXTEND`
15//!   - `CLZ`
16
17use crate::{
18    errors::{OpcodeResult, VMError},
19    gas_cost,
20    opcode_handlers::OpcodeHandler,
21    vm::VM,
22};
23use ethrex_common::{U256, U512};
24
25/// Implementation for the `ADD` opcode.
26pub struct OpAddHandler;
27impl OpcodeHandler for OpAddHandler {
28    #[inline(always)]
29    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
30        vm.current_call_frame.increase_consumed_gas(gas_cost::ADD)?;
31
32        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
33        *rhs = lhs.overflowing_add(*rhs).0;
34
35        Ok(OpcodeResult::Continue)
36    }
37}
38
39/// Implementation for the `SUB` opcode.
40pub struct OpSubHandler;
41impl OpcodeHandler for OpSubHandler {
42    #[inline(always)]
43    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
44        vm.current_call_frame.increase_consumed_gas(gas_cost::SUB)?;
45
46        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
47        *rhs = lhs.overflowing_sub(*rhs).0;
48
49        Ok(OpcodeResult::Continue)
50    }
51}
52
53/// Implementation for the `MUL` opcode.
54pub struct OpMulHandler;
55impl OpcodeHandler for OpMulHandler {
56    #[inline(always)]
57    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
58        vm.current_call_frame.increase_consumed_gas(gas_cost::MUL)?;
59
60        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
61        *rhs = lhs.overflowing_mul(*rhs).0;
62
63        Ok(OpcodeResult::Continue)
64    }
65}
66
67/// Implementation for the `DIV` opcode.
68pub struct OpDivHandler;
69impl OpcodeHandler for OpDivHandler {
70    #[inline(always)]
71    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
72        vm.current_call_frame.increase_consumed_gas(gas_cost::DIV)?;
73
74        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
75        *rhs = lhs.checked_div(*rhs).unwrap_or(U256::zero());
76
77        Ok(OpcodeResult::Continue)
78    }
79}
80
81/// Implementation for the `SDIV` opcode.
82pub struct OpSDivHandler;
83impl OpcodeHandler for OpSDivHandler {
84    #[inline(always)]
85    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
86        vm.current_call_frame
87            .increase_consumed_gas(gas_cost::SDIV)?;
88
89        let (top, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
90        let mut lhs = top;
91        let mut rhs = *slot;
92
93        let mut sign = false;
94        if lhs.bit(255) {
95            lhs = U256::zero().overflowing_sub(lhs).0;
96            sign = !sign;
97        }
98        if rhs.bit(255) {
99            rhs = U256::zero().overflowing_sub(rhs).0;
100            sign = !sign;
101        }
102
103        *slot = match lhs.checked_div(rhs) {
104            Some(mut res) => {
105                if sign {
106                    res = U256::zero().overflowing_sub(res).0;
107                }
108                res
109            }
110            None => U256::zero(),
111        };
112
113        Ok(OpcodeResult::Continue)
114    }
115}
116
117/// Implementation for the `MOD` opcode.
118pub struct OpModHandler;
119impl OpcodeHandler for OpModHandler {
120    #[inline(always)]
121    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
122        vm.current_call_frame.increase_consumed_gas(gas_cost::MOD)?;
123
124        let (lhs, rhs) = vm.current_call_frame.stack.pop1_and_top_mut()?;
125        *rhs = lhs.checked_rem(*rhs).unwrap_or(U256::zero());
126
127        Ok(OpcodeResult::Continue)
128    }
129}
130
131/// Implementation for the `SMOD` opcode.
132pub struct OpSModHandler;
133impl OpcodeHandler for OpSModHandler {
134    #[inline(always)]
135    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
136        vm.current_call_frame
137            .increase_consumed_gas(gas_cost::SMOD)?;
138
139        let (top, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
140        let mut lhs = top;
141        let mut rhs = *slot;
142
143        let sign = lhs.bit(255);
144        if sign {
145            (lhs, _) = (!lhs).overflowing_add(U256::one());
146        }
147        if rhs.bit(255) {
148            (rhs, _) = (!rhs).overflowing_add(U256::one());
149        }
150
151        *slot = match lhs.checked_rem(rhs) {
152            Some(mut res) => {
153                if sign {
154                    (res, _) = (!res).overflowing_add(U256::one());
155                }
156                res
157            }
158            None => U256::zero(),
159        };
160
161        Ok(OpcodeResult::Continue)
162    }
163}
164
165/// Implementation for the `ADDMOD` opcode.
166pub struct OpAddModHandler;
167impl OpcodeHandler for OpAddModHandler {
168    #[inline(always)]
169    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
170        vm.current_call_frame
171            .increase_consumed_gas(gas_cost::ADDMOD)?;
172
173        let [lhs, rhs, r#mod] = *vm.current_call_frame.stack.pop()?;
174        if r#mod.is_zero() || r#mod == U256::one() {
175            vm.current_call_frame.stack.push_zero()?;
176        } else {
177            #[expect(
178                clippy::arithmetic_side_effects,
179                reason = "mod is checked non-zero above"
180            )]
181            let res = U512::from(lhs).overflowing_add(rhs.into()).0 % r#mod;
182            vm.current_call_frame
183                .stack
184                .push(U256([res.0[0], res.0[1], res.0[2], res.0[3]]))?;
185        }
186
187        Ok(OpcodeResult::Continue)
188    }
189}
190
191/// Implementation for the `MULMOD` opcode.
192pub struct OpMulModHandler;
193impl OpcodeHandler for OpMulModHandler {
194    #[inline(always)]
195    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
196        vm.current_call_frame
197            .increase_consumed_gas(gas_cost::MULMOD)?;
198
199        let [multiplicand, multiplier, modulus] = *vm.current_call_frame.stack.pop()?;
200        if modulus.is_zero() || multiplicand.is_zero() || multiplier.is_zero() {
201            vm.current_call_frame.stack.push_zero()?;
202        } else {
203            let a_bytes = multiplicand.to_big_endian();
204            let b_bytes = multiplier.to_big_endian();
205            let m_bytes = modulus.to_big_endian();
206            let result_bytes = vm.crypto.mulmod256(&a_bytes, &b_bytes, &m_bytes);
207            let product_mod = U256::from_big_endian(&result_bytes);
208            vm.current_call_frame.stack.push(product_mod)?;
209        }
210
211        Ok(OpcodeResult::Continue)
212    }
213}
214
215/// Implementation for the `EXP` opcode.
216pub struct OpExpHandler;
217impl OpcodeHandler for OpExpHandler {
218    #[inline(always)]
219    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
220        let [base, exp] = *vm.current_call_frame.stack.pop()?;
221        vm.current_call_frame
222            .increase_consumed_gas(gas_cost::exp(exp)?)?;
223
224        let (res, _) = base.overflowing_pow(exp);
225        vm.current_call_frame.stack.push(res)?;
226
227        Ok(OpcodeResult::Continue)
228    }
229}
230
231/// Implementation for the `SIGNEXTEND` opcode.
232pub struct OpSignExtendHandler;
233impl OpcodeHandler for OpSignExtendHandler {
234    #[inline(always)]
235    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
236        vm.current_call_frame
237            .increase_consumed_gas(gas_cost::SIGNEXTEND)?;
238
239        let (index, slot) = vm.current_call_frame.stack.pop1_and_top_mut()?;
240        let mut value = *slot;
241        *slot = match usize::try_from(index) {
242            #[expect(
243                clippy::arithmetic_side_effects,
244                reason = "x < 32 guard prevents overflow"
245            )]
246            Ok(x) if x < 32 => {
247                if value.bit(8 * x + 7) {
248                    value |= U256::MAX << (8 * (x + 1));
249                } else if x != 31 {
250                    value &= (U256::one() << (8 * (x + 1))) - 1;
251                }
252
253                value
254            }
255            _ => value,
256        };
257
258        Ok(OpcodeResult::Continue)
259    }
260}
261
262/// Implementation for the `CLZ` opcode.
263pub struct OpClzHandler;
264impl OpcodeHandler for OpClzHandler {
265    #[inline(always)]
266    fn eval(vm: &mut VM<'_>) -> Result<OpcodeResult, VMError> {
267        vm.current_call_frame.increase_consumed_gas(gas_cost::CLZ)?;
268
269        let slot = vm.current_call_frame.stack.top_mut()?;
270        let lz = slot.leading_zeros();
271        *slot = lz.into();
272
273        Ok(OpcodeResult::Continue)
274    }
275}