referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/evm/instructions/
system.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::{
20	address::AddressMapper,
21	vm::{evm::U256Converter, Ext, RuntimeCosts},
22	Config,
23};
24use core::ptr;
25use revm::{
26	interpreter::{
27		gas as revm_gas,
28		interpreter_types::{InputsTr, LegacyBytecode, MemoryTr, ReturnData, StackTr},
29		CallInput, InstructionResult, Interpreter,
30	},
31	primitives::{Address, B256, KECCAK_EMPTY, U256},
32};
33use sp_io::hashing::keccak_256;
34
35// TODO: Fix the gas handling for the memory operations
36
37/// Implements the KECCAK256 instruction.
38///
39/// Computes Keccak-256 hash of memory data.
40pub fn keccak256<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
41	popn_top!([offset], top, context.interpreter);
42	let len = as_usize_or_fail!(context.interpreter, top);
43	gas!(context.interpreter, RuntimeCosts::HashKeccak256(len as u32));
44	let hash = if len == 0 {
45		KECCAK_EMPTY
46	} else {
47		let from = as_usize_or_fail!(context.interpreter, offset);
48		resize_memory!(context.interpreter, from, len);
49		keccak_256(context.interpreter.memory.slice_len(from, len).as_ref()).into()
50	};
51	*top = hash.into();
52}
53
54/// Implements the ADDRESS instruction.
55///
56/// Pushes the current contract's address onto the stack.
57pub fn address<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
58	gas!(context.interpreter, RuntimeCosts::Address);
59	let address: Address = context.interpreter.extend.address().0.into();
60	push!(context.interpreter, address.into_word().into());
61}
62
63/// Implements the CALLER instruction.
64///
65/// Pushes the caller's address onto the stack.
66pub fn caller<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
67	gas!(context.interpreter, RuntimeCosts::Caller);
68	match context.interpreter.extend.caller().account_id() {
69		Ok(account_id) => {
70			let address: Address = <E::T as Config>::AddressMapper::to_address(account_id).0.into();
71			push!(context.interpreter, address.into_word().into());
72		},
73		Err(_) => {
74			context
75				.interpreter
76				.halt(revm::interpreter::InstructionResult::FatalExternalError);
77		},
78	}
79}
80
81/// Implements the CODESIZE instruction.
82///
83/// Pushes the size of running contract's bytecode onto the stack.
84pub fn codesize<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
85	gas_legacy!(context.interpreter, revm_gas::BASE);
86	push!(context.interpreter, U256::from(context.interpreter.bytecode.bytecode_len()));
87}
88
89/// Implements the CODECOPY instruction.
90///
91/// Copies running contract's bytecode to memory.
92pub fn codecopy<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
93	popn!([memory_offset, code_offset, len], context.interpreter);
94	let len = as_usize_or_fail!(context.interpreter, len);
95	let Some(memory_offset) = memory_resize(context.interpreter, memory_offset, len) else {
96		return;
97	};
98	let code_offset = as_usize_saturated!(code_offset);
99
100	// Note: This can't panic because we resized memory to fit.
101	context.interpreter.memory.set_data(
102		memory_offset,
103		code_offset,
104		len,
105		context.interpreter.bytecode.bytecode_slice(),
106	);
107}
108
109/// Implements the CALLDATALOAD instruction.
110///
111/// Loads 32 bytes of input data from the specified offset.
112pub fn calldataload<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
113	gas_legacy!(context.interpreter, revm_gas::VERYLOW);
114	//pop_top!(interpreter, offset_ptr);
115	popn_top!([], offset_ptr, context.interpreter);
116	let mut word = B256::ZERO;
117	let offset = as_usize_saturated!(offset_ptr);
118	let input = context.interpreter.input.input();
119	let input_len = input.len();
120	if offset < input_len {
121		let count = 32.min(input_len - offset);
122
123		// SAFETY: `count` is bounded by the calldata length.
124		// This is `word[..count].copy_from_slice(input[offset..offset + count])`, written using
125		// raw pointers as apparently the compiler cannot optimize the slice version, and using
126		// `get_unchecked` twice is uglier.
127		match context.interpreter.input.input() {
128			CallInput::Bytes(bytes) => {
129				unsafe {
130					ptr::copy_nonoverlapping(bytes.as_ptr().add(offset), word.as_mut_ptr(), count)
131				};
132			},
133			CallInput::SharedBuffer(range) => {
134				let input_slice = context.interpreter.memory.global_slice(range.clone());
135				unsafe {
136					ptr::copy_nonoverlapping(
137						input_slice.as_ptr().add(offset),
138						word.as_mut_ptr(),
139						count,
140					)
141				};
142			},
143		}
144	}
145	*offset_ptr = word.into();
146}
147
148/// Implements the CALLDATASIZE instruction.
149///
150/// Pushes the size of input data onto the stack.
151pub fn calldatasize<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
152	gas_legacy!(context.interpreter, revm_gas::BASE);
153	push!(context.interpreter, U256::from(context.interpreter.input.input().len()));
154}
155
156/// Implements the CALLVALUE instruction.
157///
158/// Pushes the value sent with the current call onto the stack.
159pub fn callvalue<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
160	gas!(context.interpreter, RuntimeCosts::ValueTransferred);
161	let call_value = context.interpreter.extend.value_transferred();
162	push!(context.interpreter, call_value.into_revm_u256());
163}
164
165/// Implements the CALLDATACOPY instruction.
166///
167/// Copies input data to memory.
168pub fn calldatacopy<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
169	popn!([memory_offset, data_offset, len], context.interpreter);
170	let len = as_usize_or_fail!(context.interpreter, len);
171	let Some(memory_offset) = memory_resize(context.interpreter, memory_offset, len) else {
172		return;
173	};
174
175	let data_offset = as_usize_saturated!(data_offset);
176	match context.interpreter.input.input() {
177		CallInput::Bytes(bytes) => {
178			context
179				.interpreter
180				.memory
181				.set_data(memory_offset, data_offset, len, bytes.as_ref());
182		},
183		CallInput::SharedBuffer(range) => {
184			context.interpreter.memory.set_data_from_global(
185				memory_offset,
186				data_offset,
187				len,
188				range.clone(),
189			);
190		},
191	}
192}
193
194/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
195pub fn returndatasize<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
196	gas_legacy!(context.interpreter, revm_gas::BASE);
197	push!(context.interpreter, U256::from(context.interpreter.return_data.buffer().len()));
198}
199
200/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
201pub fn returndatacopy<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
202	popn!([memory_offset, offset, len], context.interpreter);
203
204	let len = as_usize_or_fail!(context.interpreter, len);
205	let data_offset = as_usize_saturated!(offset);
206
207	// Old legacy behavior is to panic if data_end is out of scope of return buffer.
208	let data_end = data_offset.saturating_add(len);
209	if data_end > context.interpreter.return_data.buffer().len() {
210		context.interpreter.halt(InstructionResult::OutOfOffset);
211		return;
212	}
213
214	let Some(memory_offset) = memory_resize(context.interpreter, memory_offset, len) else {
215		return;
216	};
217
218	// Note: This can't panic because we resized memory to fit.
219	context.interpreter.memory.set_data(
220		memory_offset,
221		data_offset,
222		len,
223		context.interpreter.return_data.buffer(),
224	);
225}
226
227/// Implements the GAS instruction.
228///
229/// Pushes the amount of remaining gas onto the stack.
230pub fn gas<'ext, E: Ext>(context: Context<'_, 'ext, E>) {
231	gas!(context.interpreter, RuntimeCosts::RefTimeLeft);
232	// TODO: This accounts only for 'ref_time' now. It should be fixed to also account for other
233	// costs. See #9577 for more context.
234	let gas = context.interpreter.extend.gas_meter().gas_left().ref_time();
235	push!(context.interpreter, U256::from(gas));
236}
237
238/// Common logic for copying data from a source buffer to the EVM's memory.
239///
240/// Handles memory expansion and gas calculation for data copy operations.
241pub fn memory_resize<'a, E: Ext>(
242	interpreter: &mut Interpreter<crate::vm::evm::EVMInterpreter<'a, E>>,
243	memory_offset: U256,
244	len: usize,
245) -> Option<usize> {
246	gas!(interpreter, RuntimeCosts::CopyToContract(len as u32), None);
247	if len == 0 {
248		return None;
249	}
250	let memory_offset = as_usize_or_fail_ret!(interpreter, memory_offset, None);
251	resize_memory!(interpreter, memory_offset, len, None);
252
253	Some(memory_offset)
254}