1use crate::{
18 AccountIdOf, BalanceOf, CodeInfo, Config, ContractBlob, DispatchError, Error, H256, LOG_TARGET,
19 Weight,
20 debug::DebugSettings,
21 precompiles::Token,
22 tracing,
23 vm::{BytecodeType, ExecResult, Ext, evm::instructions::exec_instruction},
24 weights::WeightInfo,
25};
26use alloc::vec::Vec;
27use core::{convert::Infallible, ops::ControlFlow};
28use revm::{bytecode::Bytecode, primitives::Bytes};
29
30#[cfg(feature = "runtime-benchmarks")]
31pub mod instructions;
32#[cfg(not(feature = "runtime-benchmarks"))]
33mod instructions;
34
35mod interpreter;
36pub use interpreter::{Halt, Interpreter};
37
38mod ext_bytecode;
39use ext_bytecode::ExtBytecode;
40
41mod memory;
42mod stack;
43mod util;
44
45pub(crate) const DIFFICULTY: u64 = 2500000000000000_u64;
52
53#[derive(Eq, PartialEq, Debug, Clone, Copy)]
55pub struct EVMGas(pub u64);
56
57impl<T: Config> Token<T> for EVMGas {
58 fn weight(&self) -> Weight {
59 let base_cost = T::WeightInfo::evm_opcode(1).saturating_sub(T::WeightInfo::evm_opcode(0));
60 base_cost.saturating_mul(self.0)
61 }
62}
63
64impl<T: Config> ContractBlob<T> {
65 pub fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
67 if code.len() > revm::primitives::eip3860::MAX_INITCODE_SIZE &&
68 !DebugSettings::is_unlimited_contract_size_allowed::<T>()
69 {
70 return Err(<Error<T>>::BlobTooLarge.into());
71 }
72
73 if code.first() == Some(&0xEF) {
75 return Err(<Error<T>>::CodeRejected.into());
76 }
77
78 let code_len = code.len() as u32;
79 let code_info = CodeInfo {
80 owner,
81 deposit: Default::default(),
82 refcount: 0,
83 code_len,
84 code_type: BytecodeType::Evm,
85 behaviour_version: Default::default(),
86 };
87
88 Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
89 log::debug!(target: LOG_TARGET, "failed to create evm bytecode from init code: {err:?}" );
90 <Error<T>>::CodeRejected
91 })?;
92
93 let code_hash = H256::default();
95 Ok(ContractBlob { code, code_info, code_hash })
96 }
97
98 pub fn from_evm_runtime_code(
100 code: Vec<u8>,
101 owner: AccountIdOf<T>,
102 ) -> Result<Self, DispatchError> {
103 let code_len = code.len() as u32;
104 let deposit = super::calculate_code_deposit::<T>(code_len);
105 Self::from_evm_runtime_code_with_deposit(code, owner, deposit)
106 }
107
108 pub fn from_evm_runtime_code_with_deposit(
117 code: Vec<u8>,
118 owner: AccountIdOf<T>,
119 deposit: BalanceOf<T>,
120 ) -> Result<Self, DispatchError> {
121 if code.len() > revm::primitives::eip170::MAX_CODE_SIZE &&
122 !DebugSettings::is_unlimited_contract_size_allowed::<T>()
123 {
124 return Err(<Error<T>>::BlobTooLarge.into());
125 }
126
127 let code_len = code.len() as u32;
128
129 let code_info = CodeInfo {
130 owner,
131 deposit,
132 refcount: 0,
133 code_len,
134 code_type: BytecodeType::Evm,
135 behaviour_version: Default::default(),
136 };
137
138 Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
139 log::debug!(target: LOG_TARGET, "failed to create evm bytecode from code: {err:?}" );
140 <Error<T>>::CodeRejected
141 })?;
142
143 let code_hash = H256(sp_io::hashing::keccak_256(&code));
144 Ok(ContractBlob { code, code_info, code_hash })
145 }
146}
147
148pub fn call<E: Ext>(bytecode: Bytecode, ext: &mut E, input: Vec<u8>) -> ExecResult {
150 let mut interpreter = Interpreter::new(ExtBytecode::new(bytecode), input, ext);
151 let tracing_enabled = tracing::if_tracing(|t| t.is_execution_tracer()).unwrap_or(false);
152
153 let ControlFlow::Break(halt) = if tracing_enabled {
154 run_plain_with_tracing(&mut interpreter)
155 } else {
156 run_plain(&mut interpreter)
157 };
158 halt.into()
159}
160
161fn run_plain<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt, Infallible> {
162 loop {
163 let opcode = interpreter.bytecode.opcode();
164 interpreter.bytecode.relative_jump(1);
165 exec_instruction(interpreter, opcode)?;
166 }
167}
168
169fn run_plain_with_tracing<E: Ext>(
170 interpreter: &mut Interpreter<E>,
171) -> ControlFlow<Halt, Infallible> {
172 loop {
173 let opcode = interpreter.bytecode.opcode();
174 tracing::if_tracing(|tracer| {
175 let pc = interpreter.bytecode.pc() as u64;
176 tracer.enter_opcode(pc, opcode, interpreter)
177 });
178
179 interpreter.bytecode.relative_jump(1);
180 let res = exec_instruction(interpreter, opcode);
181
182 tracing::if_tracing(|tracer| tracer.exit_step(interpreter, None));
183
184 res?;
185 }
186}