1use alloc::{vec, vec::Vec};
22use core::{fmt, marker::PhantomData};
23use fp_evm::PrecompileHandle;
24use frame_support::weights::Weight;
25use pallet_evm_polkavm_proc_macro::define_env;
26use pallet_evm_polkavm_uapi::{ReturnErrorCode, ReturnFlags};
27use scale_codec::{Decode, Encode};
28use scale_info::TypeInfo;
29use sp_core::{H160, H256, U256};
30use sp_runtime::RuntimeDebug;
31
32use super::{LOG_TARGET, SENTINEL};
33use crate::{Config, ConvertPolkaVmGas, WeightInfo};
34
35#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
37pub struct ExecReturnValue {
38 pub flags: ReturnFlags,
40 pub data: Vec<u8>,
42}
43
44pub type ExecResult = Result<ExecReturnValue, SupervisorError>;
45
46impl ExecReturnValue {
47 pub fn did_revert(&self) -> bool {
49 self.flags.contains(ReturnFlags::REVERT)
50 }
51}
52
53pub trait Memory {
60 fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), SupervisorError>;
66
67 fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), SupervisorError>;
73
74 fn zero(&mut self, ptr: u32, len: u32) -> Result<(), SupervisorError>;
80
81 fn read(&self, ptr: u32, len: u32) -> Result<Vec<u8>, SupervisorError> {
87 let mut buf = vec![0u8; len as usize];
88 self.read_into_buf(ptr, buf.as_mut_slice())?;
89 Ok(buf)
90 }
91
92 fn read_array<const N: usize>(&self, ptr: u32) -> Result<[u8; N], SupervisorError> {
94 let mut buf = [0u8; N];
95 self.read_into_buf(ptr, &mut buf)?;
96 Ok(buf)
97 }
98
99 fn read_u32(&self, ptr: u32) -> Result<u32, SupervisorError> {
101 let buf: [u8; 4] = self.read_array(ptr)?;
102 Ok(u32::from_le_bytes(buf))
103 }
104
105 fn read_u256(&self, ptr: u32) -> Result<U256, SupervisorError> {
107 let buf: [u8; 32] = self.read_array(ptr)?;
108 Ok(U256::from_little_endian(&buf))
109 }
110
111 fn read_h160(&self, ptr: u32) -> Result<H160, SupervisorError> {
113 let mut buf = H160::default();
114 self.read_into_buf(ptr, buf.as_bytes_mut())?;
115 Ok(buf)
116 }
117
118 fn read_h256(&self, ptr: u32) -> Result<H256, SupervisorError> {
120 let mut code_hash = H256::default();
121 self.read_into_buf(ptr, code_hash.as_bytes_mut())?;
122 Ok(code_hash)
123 }
124}
125
126pub trait PolkaVmInstance: Memory {
132 fn gas(&self) -> polkavm::Gas;
133 fn set_gas(&mut self, gas: polkavm::Gas);
134 fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64);
135 fn write_output(&mut self, output: u64);
136}
137
138#[cfg(feature = "runtime-benchmarks")]
145impl Memory for [u8] {
146 fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), SupervisorError> {
147 let ptr = ptr as usize;
148 let bound_checked = self
149 .get(ptr..ptr + buf.len())
150 .ok_or(SupervisorError::OutOfBounds)?;
151 buf.copy_from_slice(bound_checked);
152 Ok(())
153 }
154
155 fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), SupervisorError> {
156 let ptr = ptr as usize;
157 let bound_checked = self
158 .get_mut(ptr..ptr + buf.len())
159 .ok_or(SupervisorError::OutOfBounds)?;
160 bound_checked.copy_from_slice(buf);
161 Ok(())
162 }
163
164 fn zero(&mut self, ptr: u32, len: u32) -> Result<(), SupervisorError> {
165 <[u8] as Memory>::write(self, ptr, &vec![0; len as usize])
166 }
167}
168
169impl Memory for polkavm::RawInstance {
170 fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), SupervisorError> {
171 self.read_memory_into(ptr, buf)
172 .map(|_| ())
173 .map_err(|_| SupervisorError::OutOfBounds)
174 }
175
176 fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), SupervisorError> {
177 self.write_memory(ptr, buf)
178 .map_err(|_| SupervisorError::OutOfBounds)
179 }
180
181 fn zero(&mut self, ptr: u32, len: u32) -> Result<(), SupervisorError> {
182 self.zero_memory(ptr, len)
183 .map_err(|_| SupervisorError::OutOfBounds)
184 }
185}
186
187impl PolkaVmInstance for polkavm::RawInstance {
188 fn gas(&self) -> polkavm::Gas {
189 self.gas()
190 }
191
192 fn set_gas(&mut self, gas: polkavm::Gas) {
193 self.set_gas(gas)
194 }
195
196 fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64) {
197 (
198 self.reg(polkavm::Reg::A0),
199 self.reg(polkavm::Reg::A1),
200 self.reg(polkavm::Reg::A2),
201 self.reg(polkavm::Reg::A3),
202 self.reg(polkavm::Reg::A4),
203 self.reg(polkavm::Reg::A5),
204 )
205 }
206
207 fn write_output(&mut self, output: u64) {
208 self.set_reg(polkavm::Reg::A0, output);
209 }
210}
211
212impl From<&ExecReturnValue> for ReturnErrorCode {
213 fn from(from: &ExecReturnValue) -> Self {
214 if from.flags.contains(ReturnFlags::REVERT) {
215 Self::CalleeReverted
216 } else {
217 Self::Success
218 }
219 }
220}
221
222#[derive(RuntimeDebug)]
224pub struct ReturnData {
225 flags: u32,
228 data: Vec<u8>,
230}
231
232#[derive(RuntimeDebug)]
233pub enum SupervisorError {
234 OutOfBounds,
235 ExecutionFailed,
236 ContractTrapped,
237 OutOfGas,
238 InvalidSyscall,
239 InvalidCallFlags,
240 StateChangeDenied,
241 InputForwarded,
242 NotPolkaVm,
243 CodeRejected,
244}
245
246#[derive(RuntimeDebug)]
253pub enum TrapReason {
254 SupervisorError(SupervisorError),
257 Return(ReturnData),
259}
260
261impl From<SupervisorError> for TrapReason {
262 fn from(from: SupervisorError) -> Self {
263 TrapReason::SupervisorError(from)
264 }
265}
266
267impl fmt::Display for TrapReason {
268 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
269 Ok(())
270 }
271}
272
273macro_rules! cost_args {
274 ($name:ident, $( $arg: expr ),+) => {
276 (<T as Config>::WeightInfo::$name($( $arg ),+).saturating_sub(cost_args!(@call_zero $name, $( $arg ),+)))
277 };
278 (@call_zero $name:ident, $( $arg:expr ),*) => {
280 <T as Config>::WeightInfo::$name($( cost_args!(@replace_token $arg) ),*)
281 };
282 (@replace_token $_in:tt) => { 0 };
284}
285
286#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
287#[derive(Copy, Clone)]
288pub enum RuntimeCosts {
289 HostFn,
291 CopyFromContract(u32),
293 CallDataLoad,
295 CallDataCopy(u32),
297 Caller,
299 CallDataSize,
301 Origin,
303 Address,
305}
306
307impl RuntimeCosts {
308 fn weight<T: Config>(&self) -> Weight {
309 use self::RuntimeCosts::*;
310 match *self {
311 HostFn => cost_args!(noop_host_fn, 1),
312 CopyFromContract(len) => <T as Config>::WeightInfo::seal_return(len),
313 CallDataSize => <T as Config>::WeightInfo::seal_call_data_size(),
314 CallDataLoad => <T as Config>::WeightInfo::seal_call_data_load(),
315 CallDataCopy(len) => <T as Config>::WeightInfo::seal_call_data_copy(len),
316 Caller => <T as Config>::WeightInfo::seal_caller(),
317 Origin => <T as Config>::WeightInfo::seal_origin(),
318 Address => <T as Config>::WeightInfo::seal_address(),
319 }
320 }
321}
322
323fn already_charged(_: u32) -> Option<RuntimeCosts> {
327 None
328}
329
330pub struct Runtime<'a, T, H, M: ?Sized> {
332 handle: &'a mut H,
333 input_data: Option<Vec<u8>>,
334 last_gas: polkavm::Gas,
335 _phantom_data: PhantomData<(T, M)>,
336}
337
338impl<T: Config, H: PrecompileHandle, M: PolkaVmInstance> Runtime<'_, T, H, M> {
339 pub fn handle_interrupt(
340 &mut self,
341 interrupt: Result<polkavm::InterruptKind, polkavm::Error>,
342 module: &polkavm::Module,
343 instance: &mut M,
344 ) -> Option<ExecResult> {
345 use polkavm::InterruptKind::*;
346
347 match interrupt {
348 Err(error) => {
349 log::error!(target: LOG_TARGET, "polkavm execution error: {error}");
351 Some(Err(SupervisorError::ExecutionFailed))
352 }
353 Ok(Finished) => Some(Ok(ExecReturnValue {
354 flags: ReturnFlags::empty(),
355 data: Vec::new(),
356 })),
357 Ok(Trap) => Some(Err(SupervisorError::ContractTrapped)),
358 Ok(Segfault(_)) => Some(Err(SupervisorError::ExecutionFailed)),
359 Ok(NotEnoughGas) => Some(Err(SupervisorError::OutOfGas)),
360 Ok(Step) => None,
361 Ok(Ecalli(idx)) => {
362 if cfg!(feature = "runtime-benchmarks") && idx == SENTINEL {
367 return Some(Ok(ExecReturnValue {
368 flags: ReturnFlags::empty(),
369 data: Vec::new(),
370 }));
371 }
372 let Some(syscall_symbol) = module.imports().get(idx) else {
373 return Some(Err(SupervisorError::InvalidSyscall));
374 };
375 match self.handle_ecall(instance, syscall_symbol.as_bytes()) {
376 Ok(None) => None,
377 Ok(Some(return_value)) => {
378 instance.write_output(return_value);
379 None
380 }
381 Err(TrapReason::Return(ReturnData { flags, data })) => {
382 match ReturnFlags::from_bits(flags) {
383 None => Some(Err(SupervisorError::InvalidCallFlags)),
384 Some(flags) => Some(Ok(ExecReturnValue { flags, data })),
385 }
386 }
387 Err(TrapReason::SupervisorError(error)) => Some(Err(error)),
388 }
389 }
390 }
391 }
392}
393
394impl<'a, T: Config, H: PrecompileHandle, M: PolkaVmInstance> Runtime<'a, T, H, M> {
395 pub fn new(handle: &'a mut H, input_data: Vec<u8>, gas_limit: polkavm::Gas) -> Self {
396 Self {
397 handle,
398 input_data: Some(input_data),
399 last_gas: gas_limit,
400 _phantom_data: Default::default(),
401 }
402 }
403
404 pub(crate) fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<(), SupervisorError> {
408 let weight = costs.weight::<T>();
409 self.handle
410 .record_external_cost(Some(weight.ref_time()), Some(weight.proof_size()), None)
411 .map_err(|_| SupervisorError::OutOfGas)?;
412
413 Ok(())
414 }
415
416 pub(crate) fn charge_polkavm_gas(&mut self, memory: &mut M) -> Result<(), SupervisorError> {
417 let gas = self.last_gas - memory.gas();
418 if gas < 0 {
419 return Err(SupervisorError::OutOfGas);
420 }
421
422 self.handle
423 .record_cost(T::ConvertPolkaVmGas::polkavm_gas_to_evm_gas(gas))
424 .map_err(|_| SupervisorError::OutOfGas)?;
425
426 self.last_gas = memory.gas();
427 Ok(())
428 }
429
430 pub fn write_sandbox_output(
451 &mut self,
452 memory: &mut M,
453 out_ptr: u32,
454 out_len_ptr: u32,
455 buf: &[u8],
456 allow_skip: bool,
457 create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
458 ) -> Result<(), SupervisorError> {
459 if allow_skip && out_ptr == SENTINEL {
460 return Ok(());
461 }
462
463 let len = memory.read_u32(out_len_ptr)?;
464 let buf_len = len.min(buf.len() as u32);
465
466 if let Some(costs) = create_token(buf_len) {
467 self.charge_gas(costs)?;
468 }
469
470 memory.write(out_ptr, &buf[..buf_len as usize])?;
471 memory.write(out_len_ptr, &buf_len.encode())
472 }
473
474 pub fn write_fixed_sandbox_output(
476 &mut self,
477 memory: &mut M,
478 out_ptr: u32,
479 buf: &[u8],
480 allow_skip: bool,
481 create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
482 ) -> Result<(), SupervisorError> {
483 if buf.is_empty() || (allow_skip && out_ptr == SENTINEL) {
484 return Ok(());
485 }
486
487 let buf_len = buf.len() as u32;
488 if let Some(costs) = create_token(buf_len) {
489 self.charge_gas(costs)?;
490 }
491
492 memory.write(out_ptr, buf)
493 }
494}
495
496#[define_env]
504pub mod env {
505 #[cfg(feature = "runtime-benchmarks")]
510 #[stable]
511 fn noop(&mut self, memory: &mut M) -> Result<(), TrapReason> {
512 Ok(())
513 }
514
515 #[stable]
518 fn call_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
519 self.charge_gas(RuntimeCosts::CallDataSize)?;
520 Ok(self
521 .input_data
522 .as_ref()
523 .map(|input| input.len().try_into().expect("usize fits into u64; qed"))
524 .unwrap_or_default())
525 }
526
527 #[stable]
530 fn call_data_copy(
531 &mut self,
532 memory: &mut M,
533 out_ptr: u32,
534 out_len: u32,
535 offset: u32,
536 ) -> Result<(), TrapReason> {
537 self.charge_gas(RuntimeCosts::CallDataCopy(out_len))?;
538
539 let Some(input) = self.input_data.as_ref() else {
540 return Err(SupervisorError::InputForwarded.into());
541 };
542
543 let start = offset as usize;
544 if start >= input.len() {
545 memory.zero(out_ptr, out_len)?;
546 return Ok(());
547 }
548
549 let end = start.saturating_add(out_len as usize).min(input.len());
550 memory.write(out_ptr, &input[start..end])?;
551
552 let bytes_written = (end - start) as u32;
553 memory.zero(
554 out_ptr.saturating_add(bytes_written),
555 out_len - bytes_written,
556 )?;
557
558 Ok(())
559 }
560
561 #[stable]
564 fn call_data_load(
565 &mut self,
566 memory: &mut M,
567 out_ptr: u32,
568 offset: u32,
569 ) -> Result<(), TrapReason> {
570 self.charge_gas(RuntimeCosts::CallDataLoad)?;
571
572 let Some(input) = self.input_data.as_ref() else {
573 return Err(SupervisorError::InputForwarded.into());
574 };
575
576 let mut data = [0; 32];
577 let start = offset as usize;
578 let data = if start >= input.len() {
579 data } else {
581 let end = start.saturating_add(32).min(input.len());
582 data[..end - start].copy_from_slice(&input[start..end]);
583 data.reverse();
584 data };
586
587 self.write_fixed_sandbox_output(memory, out_ptr, &data, false, already_charged)?;
588
589 Ok(())
590 }
591
592 #[stable]
595 fn seal_return(
596 &mut self,
597 memory: &mut M,
598 flags: u32,
599 data_ptr: u32,
600 data_len: u32,
601 ) -> Result<(), TrapReason> {
602 self.charge_gas(RuntimeCosts::CopyFromContract(data_len))?;
603 Err(TrapReason::Return(ReturnData {
604 flags,
605 data: memory.read(data_ptr, data_len)?,
606 }))
607 }
608
609 #[stable]
612 fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
613 self.charge_gas(RuntimeCosts::Caller)?;
614 let caller = self.handle.context().caller;
615 Ok(self.write_fixed_sandbox_output(
616 memory,
617 out_ptr,
618 caller.as_bytes(),
619 false,
620 already_charged,
621 )?)
622 }
623
624 #[stable]
627 fn origin(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
628 self.charge_gas(RuntimeCosts::Origin)?;
629 let origin = self.handle.origin();
630 Ok(self.write_fixed_sandbox_output(
631 memory,
632 out_ptr,
633 origin.as_bytes(),
634 false,
635 already_charged,
636 )?)
637 }
638
639 #[stable]
642 fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
643 self.charge_gas(RuntimeCosts::Address)?;
644 let address = self.handle.context().address;
645 Ok(self.write_fixed_sandbox_output(
646 memory,
647 out_ptr,
648 address.as_bytes(),
649 false,
650 already_charged,
651 )?)
652 }
653}