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.
17use crate::{
18	debug::DebugSettings,
19	precompiles::Token,
20	vm::{evm::instructions::exec_instruction, BytecodeType, ExecResult, Ext},
21	weights::WeightInfo,
22	AccountIdOf, CodeInfo, Config, ContractBlob, DispatchError, Error, Weight, H256, LOG_TARGET,
23};
24use alloc::vec::Vec;
25use core::{convert::Infallible, ops::ControlFlow};
26use revm::{bytecode::Bytecode, primitives::Bytes};
27
28#[cfg(feature = "runtime-benchmarks")]
29pub mod instructions;
30#[cfg(not(feature = "runtime-benchmarks"))]
31mod instructions;
32
33mod interpreter;
34pub use interpreter::{Halt, Interpreter};
35
36mod ext_bytecode;
37use ext_bytecode::ExtBytecode;
38
39mod memory;
40mod stack;
41mod util;
42
43/// Hard-coded value returned by the EVM `DIFFICULTY` opcode.
44///
45/// After Ethereum's Merge (Sept 2022), the `DIFFICULTY` opcode was redefined to return
46/// `prevrandao`, a randomness value from the beacon chain. In Substrate pallet-revive
47/// a fixed constant is returned instead for compatibility with contracts that still read this
48/// opcode. The value is aligned with the difficulty hardcoded for PVM contracts.
49pub(crate) const DIFFICULTY: u64 = 2500000000000000_u64;
50
51/// Cost  for a single unit of EVM gas.
52#[derive(Eq, PartialEq, Debug, Clone, Copy)]
53pub struct EVMGas(pub u64);
54
55impl<T: Config> Token<T> for EVMGas {
56	fn weight(&self) -> Weight {
57		let base_cost = T::WeightInfo::evm_opcode(1).saturating_sub(T::WeightInfo::evm_opcode(0));
58		base_cost.saturating_mul(self.0)
59	}
60}
61
62impl<T: Config> ContractBlob<T> {
63	/// Create a new contract from EVM init code.
64	pub fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
65		if code.len() > revm::primitives::eip3860::MAX_INITCODE_SIZE &&
66			!DebugSettings::is_unlimited_contract_size_allowed::<T>()
67		{
68			return Err(<Error<T>>::BlobTooLarge.into());
69		}
70
71		// EIP-3541: Reject new contract code starting with the 0xEF byte
72		if code.first() == Some(&0xEF) {
73			return Err(<Error<T>>::CodeRejected.into());
74		}
75
76		let code_len = code.len() as u32;
77		let code_info = CodeInfo {
78			owner,
79			deposit: Default::default(),
80			refcount: 0,
81			code_len,
82			code_type: BytecodeType::Evm,
83			behaviour_version: Default::default(),
84		};
85
86		Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
87			log::debug!(target: LOG_TARGET, "failed to create evm bytecode from init code: {err:?}" );
88			<Error<T>>::CodeRejected
89		})?;
90
91		// Code hash is not relevant for init code, since it is not stored on-chain.
92		let code_hash = H256::default();
93		Ok(ContractBlob { code, code_info, code_hash })
94	}
95
96	/// Create a new contract from EVM runtime code.
97	pub fn from_evm_runtime_code(
98		code: Vec<u8>,
99		owner: AccountIdOf<T>,
100	) -> Result<Self, DispatchError> {
101		if code.len() > revm::primitives::eip170::MAX_CODE_SIZE &&
102			!DebugSettings::is_unlimited_contract_size_allowed::<T>()
103		{
104			return Err(<Error<T>>::BlobTooLarge.into());
105		}
106
107		let code_len = code.len() as u32;
108		let deposit = super::calculate_code_deposit::<T>(code_len);
109
110		let code_info = CodeInfo {
111			owner,
112			deposit,
113			refcount: 0,
114			code_len,
115			code_type: BytecodeType::Evm,
116			behaviour_version: Default::default(),
117		};
118
119		Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
120			log::debug!(target: LOG_TARGET, "failed to create evm bytecode from code: {err:?}" );
121			<Error<T>>::CodeRejected
122		})?;
123
124		let code_hash = H256(sp_io::hashing::keccak_256(&code));
125		Ok(ContractBlob { code, code_info, code_hash })
126	}
127}
128
129/// Calls the EVM interpreter with the provided bytecode and inputs.
130pub fn call<E: Ext>(bytecode: Bytecode, ext: &mut E, input: Vec<u8>) -> ExecResult {
131	let mut interpreter = Interpreter::new(ExtBytecode::new(bytecode), input, ext);
132	let ControlFlow::Break(halt) = run_plain(&mut interpreter);
133	halt.into()
134}
135
136fn run_plain<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt, Infallible> {
137	loop {
138		let opcode = interpreter.bytecode.opcode();
139		interpreter.bytecode.relative_jump(1);
140		exec_instruction(interpreter, opcode)?;
141	}
142}