referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/
evm.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 crate::{
19	exec::ExecError,
20	vec,
21	vm::{BytecodeType, ExecResult, Ext},
22	AccountIdOf, Code, CodeInfo, Config, ContractBlob, DispatchError, Error, ExecReturnValue, H256,
23	LOG_TARGET, U256,
24};
25use alloc::{boxed::Box, vec::Vec};
26use core::cmp::min;
27use instructions::instruction_table;
28use pallet_revive_uapi::ReturnFlags;
29use revm::{
30	bytecode::Bytecode,
31	context::CreateScheme,
32	interpreter::{
33		host::DummyHost,
34		interpreter::{ExtBytecode, ReturnDataImpl, RuntimeFlags},
35		interpreter_action::InterpreterAction,
36		interpreter_types::{InputsTr, MemoryTr, ReturnData},
37		CallInput, CallInputs, CallScheme, CreateInputs, FrameInput, Gas, InstructionResult,
38		Interpreter, InterpreterResult, InterpreterTypes, SharedMemory, Stack,
39	},
40	primitives::{self, hardfork::SpecId, Address, Bytes},
41};
42use sp_core::H160;
43use sp_runtime::Weight;
44
45mod instructions;
46
47/// Hard-coded value returned by the EVM `DIFFICULTY` opcode.
48///
49/// After Ethereum's Merge (Sept 2022), the `DIFFICULTY` opcode was redefined to return
50/// `prevrandao`, a randomness value from the beacon chain. In Substrate pallet-revive
51/// a fixed constant is returned instead for compatibility with contracts that still read this
52/// opcode. The value is aligned with the difficulty hardcoded for PVM contracts.
53pub(crate) const DIFFICULTY: u64 = 2500000000000000_u64;
54
55/// The base fee per gas used in the network as defined by EIP-1559.
56///
57/// For `pallet-revive`, this is hardcoded to 0
58pub(crate) const BASE_FEE: U256 = U256::zero();
59
60impl<T: Config> ContractBlob<T> {
61	/// Create a new contract from EVM init code.
62	pub fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
63		if code.len() > revm::primitives::eip3860::MAX_INITCODE_SIZE {
64			return Err(<Error<T>>::BlobTooLarge.into());
65		}
66
67		// EIP-3541: Reject new contract code starting with the 0xEF byte
68		if code.first() == Some(&0xEF) {
69			return Err(<Error<T>>::CodeRejected.into());
70		}
71
72		let code_len = code.len() as u32;
73		let code_info = CodeInfo {
74			owner,
75			deposit: Default::default(),
76			refcount: 0,
77			code_len,
78			code_type: BytecodeType::Evm,
79			behaviour_version: Default::default(),
80		};
81
82		Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
83			log::debug!(target: LOG_TARGET, "failed to create evm bytecode from init code: {err:?}" );
84			<Error<T>>::CodeRejected
85		})?;
86
87		// Code hash is not relevant for init code, since it is not stored on-chain.
88		let code_hash = H256::default();
89		Ok(ContractBlob { code, code_info, code_hash })
90	}
91
92	/// Create a new contract from EVM runtime code.
93	pub fn from_evm_runtime_code(
94		code: Vec<u8>,
95		owner: AccountIdOf<T>,
96	) -> Result<Self, DispatchError> {
97		if code.len() > revm::primitives::eip170::MAX_CODE_SIZE {
98			return Err(<Error<T>>::BlobTooLarge.into());
99		}
100
101		let code_len = code.len() as u32;
102		let deposit = super::calculate_code_deposit::<T>(code_len);
103
104		let code_info = CodeInfo {
105			owner,
106			deposit,
107			refcount: 0,
108			code_len,
109			code_type: BytecodeType::Evm,
110			behaviour_version: Default::default(),
111		};
112
113		Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
114			log::debug!(target: LOG_TARGET, "failed to create evm bytecode from code: {err:?}" );
115			<Error<T>>::CodeRejected
116		})?;
117
118		let code_hash = H256(sp_io::hashing::keccak_256(&code));
119		Ok(ContractBlob { code, code_info, code_hash })
120	}
121}
122
123/// Calls the EVM interpreter with the provided bytecode and inputs.
124pub fn call<'a, E: Ext>(bytecode: Bytecode, ext: &'a mut E, inputs: EVMInputs) -> ExecResult {
125	let mut interpreter: Interpreter<EVMInterpreter<'a, E>> = Interpreter {
126		gas: Gas::default(),
127		bytecode: ExtBytecode::new(bytecode),
128		stack: Stack::new(),
129		return_data: Default::default(),
130		memory: SharedMemory::new(),
131		input: inputs,
132		runtime_flag: RuntimeFlags { is_static: false, spec_id: SpecId::default() },
133		extend: ext,
134	};
135
136	let table = instruction_table::<'a, E>();
137	let result = run(&mut interpreter, &table);
138
139	instruction_result_into_exec_error::<E>(result.result)
140		.map(Err)
141		.unwrap_or_else(|| {
142			Ok(ExecReturnValue {
143				flags: if result.is_revert() { ReturnFlags::REVERT } else { ReturnFlags::empty() },
144				data: result.output.to_vec(),
145			})
146		})
147}
148
149/// Runs the EVM interpreter
150fn run<'a, E: Ext>(
151	interpreter: &mut Interpreter<EVMInterpreter<'a, E>>,
152	table: &revm::interpreter::InstructionTable<EVMInterpreter<'a, E>, DummyHost>,
153) -> InterpreterResult {
154	let host = &mut DummyHost {};
155	loop {
156		let action = interpreter.run_plain(table, host);
157		match action {
158			InterpreterAction::Return(result) => {
159				log::trace!(target: LOG_TARGET, "Evm return {:?}", result);
160				debug_assert!(
161					result.gas.limit() == 0 &&
162						result.gas.remaining() == 0 &&
163						result.gas.refunded() == 0,
164					"Interpreter gas state should remain unchanged; found: {:?}",
165					result.gas,
166				);
167				return result;
168			},
169			InterpreterAction::NewFrame(frame_input) => match frame_input {
170				FrameInput::Call(call_input) => run_call(interpreter, call_input),
171				FrameInput::Create(create_input) => run_create(interpreter, create_input),
172				FrameInput::Empty => unreachable!(),
173			},
174		}
175	}
176}
177
178fn run_call<'a, E: Ext>(
179	interpreter: &mut Interpreter<EVMInterpreter<'a, E>>,
180	call_input: Box<CallInputs>,
181) {
182	let callee: H160 = if call_input.scheme.is_delegate_call() {
183		call_input.bytecode_address.0 .0.into()
184	} else {
185		call_input.target_address.0 .0.into()
186	};
187
188	let input = match &call_input.input {
189		CallInput::Bytes(bytes) => bytes.to_vec(),
190		// Consider the usage fo SharedMemory as REVM is doing
191		CallInput::SharedBuffer(range) => interpreter.memory.global_slice(range.clone()).to_vec(),
192	};
193	let call_result = match call_input.scheme {
194		CallScheme::Call | CallScheme::StaticCall => interpreter.extend.call(
195			Weight::from_parts(call_input.gas_limit, u64::MAX),
196			U256::MAX,
197			&callee,
198			U256::from_revm_u256(&call_input.call_value()),
199			input,
200			true,
201			call_input.is_static,
202		),
203		CallScheme::CallCode => {
204			unreachable!()
205		},
206		CallScheme::DelegateCall => interpreter.extend.delegate_call(
207			Weight::from_parts(call_input.gas_limit, u64::MAX),
208			U256::MAX,
209			callee,
210			input,
211		),
212	};
213
214	let return_value = interpreter.extend.last_frame_output();
215	let return_data: Bytes = return_value.data.clone().into();
216
217	let mem_length = call_input.return_memory_offset.len();
218	let mem_start = call_input.return_memory_offset.start;
219	let returned_len = return_data.len();
220	let target_len = min(mem_length, returned_len);
221	// Set the interpreter with the nested frame result
222	interpreter.return_data.set_buffer(return_data);
223
224	match call_result {
225		Ok(()) => {
226			// success or revert
227			// TODO: Charge CopyToContract
228			interpreter
229				.memory
230				.set(mem_start, &interpreter.return_data.buffer()[..target_len]);
231			let _ =
232				interpreter.stack.push(primitives::U256::from(!return_value.did_revert() as u8));
233		},
234		Err(err) => {
235			let _ = interpreter.stack.push(primitives::U256::ZERO);
236			if let Some(reason) = exec_error_into_halt_reason::<E>(err) {
237				interpreter.halt(reason);
238			}
239		},
240	}
241}
242
243fn run_create<'a, E: Ext>(
244	interpreter: &mut Interpreter<EVMInterpreter<'a, E>>,
245	create_input: Box<CreateInputs>,
246) {
247	let value = U256::from_revm_u256(&create_input.value);
248
249	let salt = match create_input.scheme {
250		CreateScheme::Create => None,
251		CreateScheme::Create2 { salt } => Some(salt.to_le_bytes()),
252		CreateScheme::Custom { .. } => unreachable!("custom create schemes are not supported"),
253	};
254
255	let call_result = interpreter.extend.instantiate(
256		Weight::from_parts(create_input.gas_limit, u64::MAX),
257		U256::MAX,
258		Code::Upload(create_input.init_code.to_vec()),
259		value,
260		vec![],
261		salt.as_ref(),
262	);
263
264	let return_value = interpreter.extend.last_frame_output();
265	let return_data: Bytes = return_value.data.clone().into();
266
267	match call_result {
268		Ok(address) => {
269			if return_value.did_revert() {
270				// Contract creation reverted — return data must be propagated
271				// TODO: Charge CopyToContract
272				interpreter.return_data.set_buffer(return_data);
273				let _ = interpreter.stack.push(primitives::U256::ZERO);
274			} else {
275				// Otherwise clear it. Note that RETURN opcode should abort.
276				interpreter.return_data.clear();
277				let stack_item: Address = address.0.into();
278				let _ = interpreter.stack.push(stack_item.into_word().into());
279			}
280		},
281		Err(err) => {
282			let _ = interpreter.stack.push(primitives::U256::ZERO);
283			if let Some(reason) = exec_error_into_halt_reason::<E>(err) {
284				interpreter.halt(reason);
285			}
286		},
287	}
288}
289
290/// EVMInterpreter implements the `InterpreterTypes`.
291///
292/// Note:
293///
294/// Our implementation set the `InterpreterTypes::Extend` associated type, to the `Ext` trait, to
295/// reuse all the host functions that are defined by this trait
296pub struct EVMInterpreter<'a, E: Ext> {
297	_phantom: core::marker::PhantomData<&'a E>,
298}
299
300impl<'a, E: Ext> InterpreterTypes for EVMInterpreter<'a, E> {
301	type Stack = Stack;
302	type Memory = SharedMemory;
303	type Bytecode = ExtBytecode;
304	type ReturnData = ReturnDataImpl;
305	type Input = EVMInputs;
306	type RuntimeFlag = RuntimeFlags;
307	type Extend = &'a mut E;
308	type Output = InterpreterAction;
309}
310
311/// EVMInputs implements the `InputsTr` trait for EVM inputs, allowing the EVM interpreter to access
312/// the call input data.
313///
314/// Note:
315///
316/// In our implementation of the instruction table, Everything except the call input data will be
317/// accessed through the `InterpreterTypes::Extend` associated type, our implementation will panic
318/// if any of those methods are called.
319#[derive(Debug, Clone, Default)]
320pub struct EVMInputs(CallInput);
321
322impl EVMInputs {
323	pub fn new(input: Vec<u8>) -> Self {
324		Self(CallInput::Bytes(input.into()))
325	}
326}
327
328impl InputsTr for EVMInputs {
329	fn target_address(&self) -> Address {
330		panic!()
331	}
332
333	fn caller_address(&self) -> Address {
334		panic!()
335	}
336
337	fn bytecode_address(&self) -> Option<&Address> {
338		panic!()
339	}
340
341	fn input(&self) -> &CallInput {
342		&self.0
343	}
344
345	fn call_value(&self) -> primitives::U256 {
346		panic!()
347	}
348}
349
350/// Conversion of a `ExecError` to `ReturnErrorCode`.
351///
352/// Used when converting the error returned from a subcall in order to map it to the
353/// equivalent EVM interpreter [InstructionResult].
354///
355/// - Returns `None` when the caller can recover the error.
356/// - Otherwise, some [InstructionResult] error code (the halt reason) is returned. Most [ExecError]
357///   variants don't map to a [InstructionResult]. The conversion is lossy and defaults to
358///   [InstructionResult::Revert] for most cases.
359///
360/// Uses the overarching [super::exec_error_into_return_code] method to determine if
361/// the error is recoverable or not. This guarantees consistent behavior accross both
362/// VM backends.
363fn exec_error_into_halt_reason<E: Ext>(from: ExecError) -> Option<InstructionResult> {
364	log::trace!("call frame execution error in EVM caller: {:?}", &from);
365
366	if super::exec_error_into_return_code::<E>(from).is_ok() {
367		return None;
368	}
369
370	let static_memory_too_large = Error::<E::T>::StaticMemoryTooLarge.into();
371	let code_rejected = Error::<E::T>::CodeRejected.into();
372	let transfer_failed = Error::<E::T>::TransferFailed.into();
373	let duplicate_contract = Error::<E::T>::DuplicateContract.into();
374	let balance_conversion_failed = Error::<E::T>::BalanceConversionFailed.into();
375	let value_too_large = Error::<E::T>::ValueTooLarge.into();
376	let out_of_gas = Error::<E::T>::OutOfGas.into();
377	let out_of_deposit = Error::<E::T>::StorageDepositLimitExhausted.into();
378
379	Some(match from.error {
380		err if err == static_memory_too_large => InstructionResult::MemoryLimitOOG,
381		err if err == code_rejected => InstructionResult::OpcodeNotFound,
382		err if err == transfer_failed => InstructionResult::OutOfFunds,
383		err if err == duplicate_contract => InstructionResult::CreateCollision,
384		err if err == balance_conversion_failed => InstructionResult::OverflowPayment,
385		err if err == value_too_large => InstructionResult::OverflowPayment,
386		err if err == out_of_deposit => InstructionResult::OutOfFunds,
387		err if err == out_of_gas => InstructionResult::OutOfGas,
388		_ => InstructionResult::Revert,
389	})
390}
391
392/// Map [InstructionResult] into an [ExecError] for passing it up the stack.
393///
394/// Returns `None` if the instruction result is not an error case.
395fn instruction_result_into_exec_error<E: Ext>(from: InstructionResult) -> Option<ExecError> {
396	match from {
397		InstructionResult::OutOfGas |
398		InstructionResult::InvalidOperandOOG |
399		InstructionResult::ReentrancySentryOOG |
400		InstructionResult::PrecompileOOG |
401		InstructionResult::MemoryOOG => Some(Error::<E::T>::OutOfGas),
402		InstructionResult::MemoryLimitOOG => Some(Error::<E::T>::StaticMemoryTooLarge),
403		InstructionResult::OpcodeNotFound |
404		InstructionResult::InvalidJump |
405		InstructionResult::NotActivated |
406		InstructionResult::InvalidFEOpcode |
407		InstructionResult::CreateContractStartingWithEF => Some(Error::<E::T>::InvalidInstruction),
408		InstructionResult::CallNotAllowedInsideStatic |
409		InstructionResult::StateChangeDuringStaticCall => Some(Error::<E::T>::StateChangeDenied),
410		InstructionResult::StackUnderflow |
411		InstructionResult::StackOverflow |
412		InstructionResult::NonceOverflow |
413		InstructionResult::PrecompileError |
414		InstructionResult::FatalExternalError => Some(Error::<E::T>::ContractTrapped),
415		InstructionResult::OutOfOffset => Some(Error::<E::T>::OutOfBounds),
416		InstructionResult::CreateCollision => Some(Error::<E::T>::DuplicateContract),
417		InstructionResult::OverflowPayment => Some(Error::<E::T>::BalanceConversionFailed),
418		InstructionResult::CreateContractSizeLimit | InstructionResult::CreateInitCodeSizeLimit =>
419			Some(Error::<E::T>::StaticMemoryTooLarge),
420		InstructionResult::CallTooDeep => Some(Error::<E::T>::MaxCallDepthReached),
421		InstructionResult::OutOfFunds => Some(Error::<E::T>::TransferFailed),
422		InstructionResult::CreateInitCodeStartingEF00 |
423		InstructionResult::InvalidEOFInitCode |
424		InstructionResult::InvalidExtDelegateCallTarget => Some(Error::<E::T>::ContractTrapped),
425		InstructionResult::Stop |
426		InstructionResult::Return |
427		InstructionResult::Revert |
428		InstructionResult::SelfDestruct => None,
429	}
430	.map(Into::into)
431}
432
433/// Blanket conversion trait between `sp_core::U256` and `revm::primitives::U256`
434pub trait U256Converter {
435	/// Convert `self` into `revm::primitives::U256`
436	fn into_revm_u256(&self) -> revm::primitives::U256;
437
438	/// Convert from `revm::primitives::U256` into `Self`
439	fn from_revm_u256(value: &revm::primitives::U256) -> Self;
440}
441
442impl U256Converter for sp_core::U256 {
443	fn into_revm_u256(&self) -> revm::primitives::U256 {
444		let bytes = self.to_big_endian();
445		revm::primitives::U256::from_be_bytes(bytes)
446	}
447
448	fn from_revm_u256(value: &revm::primitives::U256) -> Self {
449		let bytes = value.to_be_bytes::<32>();
450		sp_core::U256::from_big_endian(&bytes)
451	}
452}