pallet_evm_polkavm/vm/
mod.rs

1// This file is part of Frontier.
2
3// Copyright (C) Frontier developers.
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
18mod runtime;
19
20use crate::{Config, ConvertPolkaVmGas, WeightInfo};
21use fp_evm::PrecompileHandle;
22use sp_runtime::Weight;
23
24pub use self::runtime::{ExecResult, Runtime, RuntimeCosts, SupervisorError};
25
26pub const PREFIX: [u8; 8] = [0xef, 0x70, 0x6F, 0x6C, 0x6B, 0x61, 0x76, 0x6D];
27pub const CALL_IDENTIFIER: &str = "call";
28pub const PAGE_SIZE: u32 = 4 * 1024;
29pub const SENTINEL: u32 = u32::MAX;
30pub const LOG_TARGET: &str = "runtime::evm::polkavm";
31
32fn code_load_weight<T: Config>(size: u32) -> Weight {
33	<T as Config>::WeightInfo::call_with_code_per_byte(size)
34}
35
36pub struct PreparedCall<'a, T, H> {
37	module: polkavm::Module,
38	instance: polkavm::RawInstance,
39	runtime: Runtime<'a, T, H, polkavm::RawInstance>,
40}
41
42impl<'a, T: Config, H: PrecompileHandle> PreparedCall<'a, T, H> {
43	pub fn load(handle: &'a mut H) -> Result<Self, SupervisorError> {
44		let code = pallet_evm::AccountCodes::<T>::get(handle.code_address());
45		if code[0..8] != PREFIX {
46			return Err(SupervisorError::NotPolkaVm);
47		}
48		let code_load_weight = code_load_weight::<T>(code.len() as u32);
49		handle
50			.record_external_cost(
51				Some(code_load_weight.ref_time()),
52				Some(code_load_weight.proof_size()),
53				None,
54			)
55			.map_err(|_| SupervisorError::OutOfGas)?;
56
57		let polkavm_code = &code[8..];
58
59		let mut config = polkavm::Config::default();
60		config.set_backend(Some(polkavm::BackendKind::Interpreter));
61		config.set_cache_enabled(false);
62
63		let engine = polkavm::Engine::new(&config).expect(
64			"on-chain (no_std) use of interpreter is hard coded.
65				interpreter is available on all platforms; qed",
66		);
67
68		let mut module_config = polkavm::ModuleConfig::new();
69		module_config.set_page_size(PAGE_SIZE);
70		module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync));
71		module_config.set_allow_sbrk(false);
72		let module =
73			polkavm::Module::new(&engine, &module_config, polkavm_code.into()).map_err(|err| {
74				log::debug!(target: LOG_TARGET, "failed to create polkavm module: {err:?}");
75				SupervisorError::CodeRejected
76			})?;
77
78		let entry_program_counter = module
79			.exports()
80			.find(|export| export.symbol().as_bytes() == CALL_IDENTIFIER.as_bytes())
81			.ok_or(SupervisorError::CodeRejected)?
82			.program_counter();
83		let input_data = handle.input().to_vec();
84		let gas_limit_polkavm = T::ConvertPolkaVmGas::evm_gas_to_polkavm_gas(
85			handle.gas_limit().ok_or(SupervisorError::OutOfGas)?,
86		);
87		let runtime: Runtime<'_, T, _, polkavm::RawInstance> =
88			Runtime::new(handle, input_data, gas_limit_polkavm);
89
90		let mut instance = module.instantiate().map_err(|err| {
91			log::debug!(target: LOG_TARGET, "failed to instantiate polkavm module: {err:?}");
92			SupervisorError::CodeRejected
93		})?;
94
95		instance.set_gas(gas_limit_polkavm);
96		instance.prepare_call_untyped(entry_program_counter, &[]);
97
98		Ok(Self {
99			module,
100			instance,
101			runtime,
102		})
103	}
104
105	pub fn call(mut self) -> ExecResult {
106		let exec_result = loop {
107			let interrupt = self.instance.run();
108			if let Some(exec_result) =
109				self.runtime
110					.handle_interrupt(interrupt, &self.module, &mut self.instance)
111			{
112				break exec_result;
113			}
114		};
115		self.runtime.charge_polkavm_gas(&mut self.instance)?;
116		exec_result
117	}
118}