referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/evm/instructions/
control.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
18use super::Context;
19use crate::vm::Ext;
20use revm::{
21	interpreter::{
22		gas as revm_gas,
23		interpreter_action::InterpreterAction,
24		interpreter_types::{Jumps, LoopControl, RuntimeFlag, StackTr},
25		InstructionResult, Interpreter,
26	},
27	primitives::{Bytes, U256},
28};
29
30/// Implements the JUMP instruction.
31///
32/// Unconditional jump to a valid destination.
33pub fn jump<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
34	gas_legacy!(context.interpreter, revm_gas::MID);
35	let Some([target]) = <_ as StackTr>::popn(&mut context.interpreter.stack) else {
36		context.interpreter.halt(InstructionResult::StackUnderflow);
37		return;
38	};
39	jump_inner(context.interpreter, target);
40}
41
42/// Implements the JUMPI instruction.
43///
44/// Conditional jump to a valid destination if condition is true.
45pub fn jumpi<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
46	gas_legacy!(context.interpreter, revm_gas::HIGH);
47	let Some([target, cond]) = <_ as StackTr>::popn(&mut context.interpreter.stack) else {
48		context.interpreter.halt(InstructionResult::StackUnderflow);
49		return;
50	};
51
52	if !cond.is_zero() {
53		jump_inner(context.interpreter, target);
54	}
55}
56
57#[inline(always)]
58/// Internal helper function for jump operations.
59///
60/// Validates jump target and performs the actual jump.
61fn jump_inner(
62	interpreter: &mut Interpreter<impl revm::interpreter::interpreter_types::InterpreterTypes>,
63	target: U256,
64) {
65	let target = as_usize_or_fail!(interpreter, target, InstructionResult::InvalidJump);
66	if !interpreter.bytecode.is_valid_legacy_jump(target) {
67		interpreter.halt(InstructionResult::InvalidJump);
68		return;
69	}
70	// SAFETY: `is_valid_jump` ensures that `dest` is in bounds.
71	interpreter.bytecode.absolute_jump(target);
72}
73
74/// Implements the JUMPDEST instruction.
75///
76/// Marks a valid destination for jump operations.
77pub fn jumpdest<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
78	gas_legacy!(context.interpreter, revm_gas::JUMPDEST);
79}
80
81/// Implements the PC instruction.
82///
83/// Pushes the current program counter onto the stack.
84pub fn pc<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
85	gas_legacy!(context.interpreter, revm_gas::BASE);
86	// - 1 because we have already advanced the instruction pointer in `Interpreter::step`
87	push!(context.interpreter, U256::from(context.interpreter.bytecode.pc() - 1));
88}
89
90#[inline]
91/// Internal helper function for return operations.
92///
93/// Handles memory data retrieval and sets the return action.
94fn return_inner<'a, E: Ext>(
95	interpreter: &mut Interpreter<crate::vm::evm::EVMInterpreter<'a, E>>,
96	instruction_result: InstructionResult,
97) {
98	// Zero gas cost
99	// gas_legacy!(interpreter, revm_gas::ZERO)
100	let Some([offset, len]) = <_ as StackTr>::popn(&mut interpreter.stack) else {
101		interpreter.halt(InstructionResult::StackUnderflow);
102		return;
103	};
104	let len = as_usize_or_fail!(interpreter, len);
105	// Important: Offset must be ignored if len is zeros
106	let mut output = Bytes::default();
107	if len != 0 {
108		let offset = as_usize_or_fail!(interpreter, offset);
109		resize_memory!(interpreter, offset, len);
110		output = interpreter.memory.slice_len(offset, len).to_vec().into()
111	}
112
113	interpreter.bytecode.set_action(InterpreterAction::new_return(
114		instruction_result,
115		output,
116		interpreter.gas,
117	));
118}
119
120/// Implements the RETURN instruction.
121///
122/// Halts execution and returns data from memory.
123pub fn ret<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
124	return_inner(context.interpreter, InstructionResult::Return);
125}
126
127/// EIP-140: REVERT instruction
128pub fn revert<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
129	check!(context.interpreter, BYZANTIUM);
130	return_inner(context.interpreter, InstructionResult::Revert);
131}
132
133/// Stop opcode. This opcode halts the execution.
134pub fn stop<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
135	context.interpreter.halt(InstructionResult::Stop);
136}
137
138/// Invalid opcode. This opcode halts the execution.
139pub fn invalid<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
140	context.interpreter.halt(InstructionResult::InvalidFEOpcode);
141}
142
143/// Unknown opcode. This opcode halts the execution.
144pub fn unknown<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
145	context.interpreter.halt(InstructionResult::OpcodeNotFound);
146}