referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/evm/instructions/
macros.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Utility macros to help implementing opcode instruction functions.
19
20/// `const` Option `?`.
21#[macro_export]
22macro_rules! tri {
23	($e:expr) => {
24		match $e {
25			Some(v) => v,
26			None => return None,
27		}
28	};
29}
30
31/// Macro for optional try - returns early if the expression evaluates to None.
32/// Similar to the `?` operator but for use in instruction implementations.
33#[macro_export]
34macro_rules! otry {
35	($expression: expr) => {{
36		let Some(value) = $expression else {
37			return;
38		};
39		value
40	}};
41}
42
43/// Error if the current call is executing EOF.
44#[macro_export]
45macro_rules! require_eof {
46	($interpreter:expr) => {
47		if !$interpreter.runtime_flag.is_eof() {
48			$interpreter.halt(revm::interpreter::InstructionResult::EOFOpcodeDisabledInLegacy);
49			return;
50		}
51	};
52}
53
54/// Check if the `SPEC` is enabled, and fail the instruction if it is not.
55#[macro_export]
56macro_rules! check {
57	($interpreter:expr, $min:ident) => {
58		if !$interpreter
59			.runtime_flag
60			.spec_id()
61			.is_enabled_in(revm::primitives::hardfork::SpecId::$min)
62		{
63			$interpreter.halt(revm::interpreter::InstructionResult::NotActivated);
64			return;
65		}
66	};
67}
68
69/// Records a `gas` cost and fails the instruction if it would exceed the available gas.
70#[macro_export]
71macro_rules! gas_legacy {
72	($interpreter:expr, $gas:expr) => {
73		gas_legacy!($interpreter, $gas, ())
74	};
75	($interpreter:expr, $gas:expr, $ret:expr) => {
76		if $interpreter.extend.gas_meter_mut().charge_evm_gas($gas).is_err() {
77			$interpreter.halt(revm::interpreter::InstructionResult::OutOfGas);
78			return $ret;
79		}
80	};
81}
82
83#[macro_export]
84macro_rules! gas {
85	($interpreter:expr, $gas:expr) => {
86		gas!($interpreter, $gas, ())
87	};
88	($interpreter:expr, $gas:expr, $ret:expr) => {
89		let meter = $interpreter.extend.gas_meter_mut();
90		if meter.charge_evm_gas(1).is_err() || meter.charge($gas).is_err() {
91			$interpreter.halt(revm::interpreter::InstructionResult::OutOfGas);
92			return $ret;
93		}
94	};
95}
96
97/// Same as [`gas_legacy!`], but with `gas` as an option.
98#[macro_export]
99macro_rules! gas_or_fail_legacy {
100	($interpreter:expr, $gas:expr) => {
101		gas_or_fail_legacy!($interpreter, $gas, ())
102	};
103	($interpreter:expr, $gas:expr, $ret:expr) => {
104		match $gas {
105			Some(gas_used) => gas_legacy!($interpreter, gas_used, $ret),
106			None => {
107				$interpreter.halt(revm::interpreter::InstructionResult::OutOfGas);
108				return $ret;
109			},
110		}
111	};
112}
113
114/// Resizes the interpreterreter memory if necessary. Fails the instruction if the memory or gas
115/// limit is exceeded.
116#[macro_export]
117macro_rules! resize_memory {
118	($interpreter:expr, $offset:expr, $len:expr) => {
119		resize_memory!($interpreter, $offset, $len, ())
120	};
121	($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => {
122		let current_len = $interpreter.memory.len();
123		let target_len = revm::interpreter::num_words($offset.saturating_add($len)) * 32;
124		if target_len as u32 > $crate::limits::code::BASELINE_MEMORY_LIMIT {
125			log::debug!(target: $crate::LOG_TARGET, "check memory bounds failed: offset={} target_len={target_len} current_len={current_len}", $offset);
126			$interpreter.halt(revm::interpreter::InstructionResult::MemoryOOG);
127			return $ret;
128		}
129
130		if target_len > current_len {
131			$interpreter.memory.resize(target_len);
132		};
133	};
134}
135
136/// Pops n values from the stack. Fails the instruction if n values can't be popped.
137#[macro_export]
138macro_rules! popn {
139    ([ $($x:ident),* ],$interpreterreter:expr $(,$ret:expr)? ) => {
140        let Some([$( $x ),*]) = <_ as StackTr>::popn(&mut $interpreterreter.stack) else {
141            $interpreterreter.halt(revm::interpreter::InstructionResult::StackUnderflow);
142            return $($ret)?;
143        };
144    };
145}
146
147/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't
148/// be popped.
149#[macro_export]
150macro_rules! popn_top {
151    ([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => {
152		let Some(([$($x),*], $top)) = <_ as StackTr>::popn_top(&mut $interpreter.stack) else {
153            $interpreter.halt(revm::interpreter::InstructionResult::StackUnderflow);
154            return $($ret)?;
155        };
156    };
157}
158
159/// Pushes a `B256` value onto the stack. Fails the instruction if the stack is full.
160#[macro_export]
161macro_rules! push {
162    ($interpreter:expr, $x:expr $(,$ret:item)?) => (
163        if !($interpreter.stack.push($x)) {
164            $interpreter.halt(revm::interpreter::InstructionResult::StackOverflow);
165            return $($ret)?;
166        }
167    )
168}
169
170/// Converts a `U256` value to a `u64`, saturating to `MAX` if the value is too large.
171#[macro_export]
172macro_rules! as_u64_saturated {
173	($v:expr) => {
174		match $v.as_limbs() {
175			x =>
176				if (x[1] == 0) & (x[2] == 0) & (x[3] == 0) {
177					x[0]
178				} else {
179					u64::MAX
180				},
181		}
182	};
183}
184
185/// Converts a `U256` value to a `usize`, saturating to `MAX` if the value is too large.
186#[macro_export]
187macro_rules! as_usize_saturated {
188	($v:expr) => {
189		usize::try_from(as_u64_saturated!($v)).unwrap_or(usize::MAX)
190	};
191}
192
193/// Converts a `U256` value to a `isize`, saturating to `isize::MAX` if the value is too large.
194#[macro_export]
195macro_rules! as_isize_saturated {
196	($v:expr) => {
197		// `isize_try_from(u64::MAX)`` will fail and return isize::MAX
198		// This is expected behavior as we are saturating the value.
199		isize::try_from(as_u64_saturated!($v)).unwrap_or(isize::MAX)
200	};
201}
202
203/// Converts a `U256` value to a `usize`, failing the instruction if the value is too large.
204#[macro_export]
205macro_rules! as_usize_or_fail {
206	($interpreter:expr, $v:expr) => {
207		as_usize_or_fail_ret!($interpreter, $v, ())
208	};
209	($interpreter:expr, $v:expr, $reason:expr) => {
210		as_usize_or_fail_ret!($interpreter, $v, $reason, ())
211	};
212}
213
214/// Converts a `U256` value to a `usize` and returns `ret`,
215/// failing the instruction if the value is too large.
216#[macro_export]
217macro_rules! as_usize_or_fail_ret {
218	($interpreter:expr, $v:expr, $ret:expr) => {
219		as_usize_or_fail_ret!(
220			$interpreter,
221			$v,
222			revm::interpreter::InstructionResult::InvalidOperandOOG,
223			$ret
224		)
225	};
226
227	($interpreter:expr, $v:expr, $reason:expr, $ret:expr) => {
228		match $v.as_limbs() {
229			x => {
230				if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) {
231					$interpreter.halt($reason);
232					return $ret;
233				}
234				x[0] as usize
235			},
236		}
237	};
238}