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.host.gas_params().cold_account_additional_cost();
55        let skip_cold_load = $context.interpreter.gas.remaining() < cold_load_gas;
56        match $context
57            .host
58            .load_account_info_skip_cold_load($address, $load_code, skip_cold_load)
59        {
60            Ok(account) => {
61                if account.is_cold {
62                    $crate::gas!($context.interpreter, cold_load_gas, $ret);
63                }
64                account
65            }
66            Err(LoadError::ColdLoadSkipped) => {
67                $context.interpreter.halt_oog();
68                return $ret;
69            }
70            Err(LoadError::DBError) => {
71                $context.interpreter.halt_fatal();
72                return $ret;
73            }
74        }
75    }};
76}
77
78/// Resizes the interpreter memory if necessary. Fails the instruction if the memory or gas limit
79/// is exceeded.
80#[macro_export]
81#[collapse_debuginfo(yes)]
82macro_rules! resize_memory {
83    ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr) => {
84        $crate::resize_memory!($interpreter, $gas_params, $offset, $len, ())
85    };
86    ($interpreter:expr, $gas_params:expr, $offset:expr, $len:expr, $ret:expr) => {
87        if let Err(result) = $crate::interpreter::resize_memory(
88            &mut $interpreter.gas,
89            &mut $interpreter.memory,
90            $gas_params,
91            $offset,
92            $len,
93        ) {
94            $interpreter.halt(result);
95            return $ret;
96        }
97    };
98}
99
100/// Pops n values from the stack. Fails the instruction if n values can't be popped.
101#[macro_export]
102#[collapse_debuginfo(yes)]
103macro_rules! popn {
104    ([ $($x:ident),* ],$interpreter:expr $(,$ret:expr)? ) => {
105        let Some([$( $x ),*]) = $interpreter.stack.popn() else {
106            $interpreter.halt_underflow();
107            return $($ret)?;
108        };
109    };
110}
111
112#[doc(hidden)]
113#[macro_export]
114#[collapse_debuginfo(yes)]
115macro_rules! _count {
116    (@count) => { 0 };
117    (@count $head:tt $($tail:tt)*) => { 1 + _count!(@count $($tail)*) };
118    ($($arg:tt)*) => { _count!(@count $($arg)*) };
119}
120
121/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't be popped.
122#[macro_export]
123#[collapse_debuginfo(yes)]
124macro_rules! popn_top {
125    ([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => {
126        /*
127        let Some(([$( $x ),*], $top)) = $interpreter.stack.popn_top() else {
128            $interpreter.halt($crate::InstructionResult::StackUnderflow);
129            return $($ret)?;
130        };
131        */
132
133        // Workaround for https://github.com/rust-lang/rust/issues/144329.
134        if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) {
135            $interpreter.halt_underflow();
136            return $($ret)?;
137        }
138        let ([$( $x ),*], $top) = unsafe { $interpreter.stack.popn_top().unwrap_unchecked() };
139    };
140}
141
142/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
143#[macro_export]
144#[collapse_debuginfo(yes)]
145macro_rules! push {
146    ($interpreter:expr, $x:expr $(,$ret:item)?) => (
147        if !($interpreter.stack.push($x)) {
148            $interpreter.halt_overflow();
149            return $($ret)?;
150        }
151    )
152}
153
154/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
155#[macro_export]
156#[collapse_debuginfo(yes)]
157macro_rules! as_u64_saturated {
158    ($v:expr) => {
159        match $v.as_limbs() {
160            x => {
161                if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) {
162                    x[0]
163                } else {
164                    u64::MAX
165                }
166            }
167        }
168    };
169}
170
171/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
172#[macro_export]
173#[collapse_debuginfo(yes)]
174macro_rules! as_usize_saturated {
175    ($v:expr) => {
176        usize::try_from($crate::as_u64_saturated!($v)).unwrap_or(usize::MAX)
177    };
178}
179
180/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
181#[macro_export]
182#[collapse_debuginfo(yes)]
183macro_rules! as_isize_saturated {
184    ($v:expr) => {
185        // `isize_try_from(u64::MAX)`` will fail and return isize::MAX
186        // This is expected behavior as we are saturating the value.
187        isize::try_from($crate::as_u64_saturated!($v)).unwrap_or(isize::MAX)
188    };
189}
190
191/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
192#[macro_export]
193#[collapse_debuginfo(yes)]
194macro_rules! as_usize_or_fail {
195    ($interpreter:expr, $v:expr) => {
196        $crate::as_usize_or_fail_ret!($interpreter, $v, ())
197    };
198    ($interpreter:expr, $v:expr, $reason:expr) => {
199        $crate::as_usize_or_fail_ret!($interpreter, $v, $reason, ())
200    };
201}
202
203/// Converts a `U256` value to a `usize` and returns `ret`,
204/// failing the instruction if the value is too large.
205#[macro_export]
206#[collapse_debuginfo(yes)]
207macro_rules! as_usize_or_fail_ret {
208    ($interpreter:expr, $v:expr, $ret:expr) => {
209        $crate::as_usize_or_fail_ret!(
210            $interpreter,
211            $v,
212            $crate::InstructionResult::InvalidOperandOOG,
213            $ret
214        )
215    };
216
217    ($interpreter:expr, $v:expr, $reason:expr, $ret:expr) => {
218        match $v.as_limbs() {
219            x => {
220                if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
221                    $interpreter.halt($reason);
222                    return $ret;
223                }
224                x[0] as usize
225            }
226        }
227    };
228}