revm_interpreter/instructions/
macros.rs

1//! Utility macros to help implementing opcode instruction functions.
2
3/// Fails the instruction if the current call is static.
4#[macro_export]
5#[collapse_debuginfo(yes)]
6macro_rules! require_non_staticcall {
7    ($interpreter:expr) => {
8        if $interpreter.runtime_flag.is_static() {
9            $interpreter.halt($crate::InstructionResult::StateChangeDuringStaticCall);
10            return;
11        }
12    };
13}
14
15/// Check if the `SPEC` is enabled, and fail the instruction if it is not.
16#[macro_export]
17#[collapse_debuginfo(yes)]
18macro_rules! check {
19    ($interpreter:expr, $min:ident) => {
20        if !$interpreter
21            .runtime_flag
22            .spec_id()
23            .is_enabled_in(primitives::hardfork::SpecId::$min)
24        {
25            $interpreter.halt_not_activated();
26            return;
27        }
28    };
29}
30
31/// Records a `gas` cost and fails the instruction if it would exceed the available gas.
32#[macro_export]
33#[collapse_debuginfo(yes)]
34macro_rules! gas {
35    ($interpreter:expr, $gas:expr) => {
36        $crate::gas!($interpreter, $gas, ())
37    };
38    ($interpreter:expr, $gas:expr, $ret:expr) => {
39        if !$interpreter.gas.record_cost($gas) {
40            $interpreter.halt_oog();
41            return $ret;
42        }
43    };
44}
45
46/// Loads account and account berlin gas cost accounting.
47#[macro_export]
48#[collapse_debuginfo(yes)]
49macro_rules! berlin_load_account {
50    ($context:expr, $address:expr, $load_code:expr) => {
51        $crate::berlin_load_account!($context, $address, $load_code, ())
52    };
53    ($context:expr, $address:expr, $load_code:expr, $ret:expr) => {{
54        let cold_load_gas = $context
55            .interpreter
56            .gas_params
57            .cold_account_additional_cost();
58        let skip_cold_load = $context.interpreter.gas.remaining() < cold_load_gas;
59        match $context
60            .host
61            .load_account_info_skip_cold_load($address, $load_code, skip_cold_load)
62        {
63            Ok(account) => {
64                if account.is_cold {
65                    $crate::gas!($context.interpreter, cold_load_gas, $ret);
66                }
67                account
68            }
69            Err(LoadError::ColdLoadSkipped) => {
70                $context.interpreter.halt_oog();
71                return $ret;
72            }
73            Err(LoadError::DBError) => {
74                $context.interpreter.halt_fatal();
75                return $ret;
76            }
77        }
78    }};
79}
80
81/// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit
82/// is exceeded.
83#[macro_export]
84#[collapse_debuginfo(yes)]
85macro_rules! resize_memory {
86    ($interpreter:expr, $offset:expr, $len:expr) => {
87        $crate::resize_memory!($interpreter, $offset, $len, ())
88    };
89    ($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => {
90        if let Err(result) = $crate::interpreter::resize_memory(
91            &mut $interpreter.gas,
92            &mut $interpreter.memory,
93            &$interpreter.gas_params,
94            $offset,
95            $len,
96        ) {
97            $interpreter.halt(result);
98            return $ret;
99        }
100    };
101}
102
103/// Pops n values from the stack. Fails the instruction if n values can't be popped.
104#[macro_export]
105#[collapse_debuginfo(yes)]
106macro_rules! popn {
107    ([ $($x:ident),* ],$interpreter:expr $(,$ret:expr)? ) => {
108        let Some([$( $x ),*]) = $interpreter.stack.popn() else {
109            $interpreter.halt_underflow();
110            return $($ret)?;
111        };
112    };
113}
114
115#[doc(hidden)]
116#[macro_export]
117#[collapse_debuginfo(yes)]
118macro_rules! _count {
119    (@count) => { 0 };
120    (@count $head:tt $($tail:tt)*) => { 1 + _count!(@count $($tail)*) };
121    ($($arg:tt)*) => { _count!(@count $($arg)*) };
122}
123
124/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't be popped.
125#[macro_export]
126#[collapse_debuginfo(yes)]
127macro_rules! popn_top {
128    ([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => {
129        /*
130        let Some(([$( $x ),*], $top)) = $interpreter.stack.popn_top() else {
131            $interpreter.halt($crate::InstructionResult::StackUnderflow);
132            return $($ret)?;
133        };
134        */
135
136        // Workaround for https://github.com/rust-lang/rust/issues/144329.
137        if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) {
138            $interpreter.halt_underflow();
139            return $($ret)?;
140        }
141        let ([$( $x ),*], $top) = unsafe { $interpreter.stack.popn_top().unwrap_unchecked() };
142    };
143}
144
145/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
146#[macro_export]
147#[collapse_debuginfo(yes)]
148macro_rules! push {
149    ($interpreter:expr, $x:expr $(,$ret:item)?) => (
150        if !($interpreter.stack.push($x)) {
151            $interpreter.halt_overflow();
152            return $($ret)?;
153        }
154    )
155}
156
157/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
158#[macro_export]
159#[collapse_debuginfo(yes)]
160macro_rules! as_u64_saturated {
161    ($v:expr) => {
162        match $v.as_limbs() {
163            x => {
164                if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) {
165                    x[0]
166                } else {
167                    u64::MAX
168                }
169            }
170        }
171    };
172}
173
174/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
175#[macro_export]
176#[collapse_debuginfo(yes)]
177macro_rules! as_usize_saturated {
178    ($v:expr) => {
179        usize::try_from($crate::as_u64_saturated!($v)).unwrap_or(usize::MAX)
180    };
181}
182
183/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
184#[macro_export]
185#[collapse_debuginfo(yes)]
186macro_rules! as_isize_saturated {
187    ($v:expr) => {
188        // `isize_try_from(u64::MAX)`` will fail and return isize::MAX
189        // This is expected behavior as we are saturating the value.
190        isize::try_from($crate::as_u64_saturated!($v)).unwrap_or(isize::MAX)
191    };
192}
193
194/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
195#[macro_export]
196#[collapse_debuginfo(yes)]
197macro_rules! as_usize_or_fail {
198    ($interpreter:expr, $v:expr) => {
199        $crate::as_usize_or_fail_ret!($interpreter, $v, ())
200    };
201    ($interpreter:expr, $v:expr, $reason:expr) => {
202        $crate::as_usize_or_fail_ret!($interpreter, $v, $reason, ())
203    };
204}
205
206/// Converts a `U256` value to a `usize` and returns `ret`,
207/// failing the instruction if the value is too large.
208#[macro_export]
209#[collapse_debuginfo(yes)]
210macro_rules! as_usize_or_fail_ret {
211    ($interpreter:expr, $v:expr, $ret:expr) => {
212        $crate::as_usize_or_fail_ret!(
213            $interpreter,
214            $v,
215            $crate::InstructionResult::InvalidOperandOOG,
216            $ret
217        )
218    };
219
220    ($interpreter:expr, $v:expr, $reason:expr, $ret:expr) => {
221        match $v.as_limbs() {
222            x => {
223                if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
224                    $interpreter.halt($reason);
225                    return $ret;
226                }
227                x[0] as usize
228            }
229        }
230    };
231}