revm_interpreter/instructions/
macros.rs

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