pallet_evm_polkavm/vm/
mod.rs1mod 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}