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;
39pub use 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
64#[derive(Eq, PartialEq, Debug, Clone, Copy)]
66enum EvmOpcodeCosts {
67 Div,
68 SDiv,
69 Mod,
70 SMod,
71 AddMod,
72 MulMod,
73}
74
75impl<T: Config> Token<T> for EvmOpcodeCosts {
76 fn weight(&self) -> Weight {
77 let per_opcode = |weight_fn: fn(u32) -> Weight| weight_fn(1).saturating_sub(weight_fn(0));
78 let pop = per_opcode(T::WeightInfo::evm_pop_opcode);
80 match self {
81 Self::Div => per_opcode(T::WeightInfo::evm_div_opcode)
82 .max(per_opcode(T::WeightInfo::evm_div_opcode_one_limb_variant))
83 .max(per_opcode(T::WeightInfo::evm_div_opcode_mixed_variant))
84 .max(per_opcode(T::WeightInfo::evm_div_opcode_fixed_knuth_variant))
85 .saturating_sub(pop),
86 Self::SDiv => per_opcode(T::WeightInfo::evm_sdiv_opcode)
87 .max(per_opcode(T::WeightInfo::evm_sdiv_opcode_one_limb_variant))
88 .max(per_opcode(T::WeightInfo::evm_sdiv_opcode_mixed_variant))
89 .max(per_opcode(T::WeightInfo::evm_sdiv_opcode_fixed_knuth_variant))
90 .saturating_sub(pop),
91 Self::Mod => per_opcode(T::WeightInfo::evm_mod_opcode)
92 .max(per_opcode(T::WeightInfo::evm_mod_opcode_one_limb_variant))
93 .max(per_opcode(T::WeightInfo::evm_mod_opcode_mixed_variant))
94 .max(per_opcode(T::WeightInfo::evm_mod_opcode_fixed_knuth_variant))
95 .saturating_sub(pop),
96 Self::SMod => per_opcode(T::WeightInfo::evm_smod_opcode)
97 .max(per_opcode(T::WeightInfo::evm_smod_opcode_one_limb_variant))
98 .max(per_opcode(T::WeightInfo::evm_smod_opcode_mixed_variant))
99 .max(per_opcode(T::WeightInfo::evm_smod_opcode_fixed_knuth_variant))
100 .saturating_sub(pop),
101 Self::AddMod => per_opcode(T::WeightInfo::evm_addmod_opcode)
102 .max(per_opcode(T::WeightInfo::evm_addmod_opcode_one_limb_variant))
103 .max(per_opcode(T::WeightInfo::evm_addmod_opcode_mixed_variant))
104 .max(per_opcode(T::WeightInfo::evm_addmod_opcode_fixed_knuth_variant))
105 .saturating_sub(pop),
106 Self::MulMod => per_opcode(T::WeightInfo::evm_mulmod_opcode).saturating_sub(pop),
107 }
108 }
109}
110
111impl<T: Config> ContractBlob<T> {
112 pub fn from_evm_init_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
114 if code.len() > revm::primitives::eip3860::MAX_INITCODE_SIZE &&
115 !DebugSettings::is_unlimited_contract_size_allowed::<T>()
116 {
117 return Err(<Error<T>>::BlobTooLarge.into());
118 }
119
120 let code_len = code.len() as u32;
121 let code_info = CodeInfo {
122 owner,
123 deposit: Default::default(),
124 refcount: 0,
125 code_len,
126 code_type: BytecodeType::Evm,
127 behaviour_version: Default::default(),
128 };
129
130 Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
131 log::debug!(target: LOG_TARGET, "failed to create evm bytecode from init code: {err:?}" );
132 <Error<T>>::CodeRejected
133 })?;
134
135 let code_hash = H256::default();
137 Ok(ContractBlob { code, code_info, code_hash })
138 }
139
140 pub fn from_evm_runtime_code(
142 code: Vec<u8>,
143 owner: AccountIdOf<T>,
144 ) -> Result<Self, DispatchError> {
145 let code_len = code.len() as u32;
146 let deposit = super::calculate_code_deposit::<T>(code_len);
147 Self::from_evm_runtime_code_with_deposit(code, owner, deposit)
148 }
149
150 pub fn from_evm_runtime_code_with_deposit(
159 code: Vec<u8>,
160 owner: AccountIdOf<T>,
161 deposit: BalanceOf<T>,
162 ) -> Result<Self, DispatchError> {
163 if code.len() > revm::primitives::eip170::MAX_CODE_SIZE &&
164 !DebugSettings::is_unlimited_contract_size_allowed::<T>()
165 {
166 return Err(<Error<T>>::BlobTooLarge.into());
167 }
168
169 if code.first() == Some(&0xEF) {
174 return Err(<Error<T>>::CodeRejected.into());
175 }
176
177 let code_len = code.len() as u32;
178
179 let code_info = CodeInfo {
180 owner,
181 deposit,
182 refcount: 0,
183 code_len,
184 code_type: BytecodeType::Evm,
185 behaviour_version: Default::default(),
186 };
187
188 Bytecode::new_raw_checked(Bytes::from(code.to_vec())).map_err(|err| {
189 log::debug!(target: LOG_TARGET, "failed to create evm bytecode from code: {err:?}" );
190 <Error<T>>::CodeRejected
191 })?;
192
193 let code_hash = H256(sp_io::hashing::keccak_256(&code));
194 Ok(ContractBlob { code, code_info, code_hash })
195 }
196}
197
198pub fn call<E: Ext>(bytecode: Bytecode, ext: &mut E, input: Vec<u8>) -> ExecResult {
200 let mut interpreter = Interpreter::new(ExtBytecode::new(bytecode), input, ext);
201 let tracing_enabled = tracing::if_tracing(|t| t.is_execution_tracer()).unwrap_or(false);
202
203 let ControlFlow::Break(halt) = if tracing_enabled {
204 run_plain_with_tracing(&mut interpreter)
205 } else {
206 run_plain(&mut interpreter)
207 };
208 halt.into()
209}
210
211pub fn run_plain<E: Ext>(interpreter: &mut Interpreter<E>) -> ControlFlow<Halt, Infallible> {
212 loop {
213 let opcode = interpreter.bytecode.opcode();
214 interpreter.bytecode.relative_jump(1);
215 exec_instruction(interpreter, opcode)?;
216 }
217}
218
219fn run_plain_with_tracing<E: Ext>(
220 interpreter: &mut Interpreter<E>,
221) -> ControlFlow<Halt, Infallible> {
222 loop {
223 let opcode = interpreter.bytecode.opcode();
224 tracing::if_tracing(|tracer| {
225 let pc = interpreter.bytecode.pc() as u64;
226 tracer.enter_opcode(pc, opcode, interpreter)
227 });
228
229 interpreter.bytecode.relative_jump(1);
230 let res = exec_instruction(interpreter, opcode);
231
232 tracing::if_tracing(|tracer| tracer.exit_step(interpreter, None));
233
234 res?;
235 }
236}