1pub mod env;
21
22#[cfg(doc)]
23pub use env::SyscallDoc;
24
25use crate::{
26 exec::{CallResources, ExecError, ExecResult, Ext, Key},
27 limits,
28 metering::ChargedAmount,
29 precompiles::{All as AllPrecompiles, Precompiles},
30 primitives::ExecReturnValue,
31 Code, Config, Error, Pallet, ReentrancyProtection, RuntimeCosts, LOG_TARGET, SENTINEL,
32};
33use alloc::{vec, vec::Vec};
34use codec::Encode;
35use core::{fmt, marker::PhantomData, mem};
36use frame_support::{ensure, weights::Weight};
37use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags};
38use sp_core::{H160, H256, U256};
39use sp_runtime::{DispatchError, RuntimeDebug};
40
41pub fn extract_code_and_data(data: &[u8]) -> Option<(Vec<u8>, Vec<u8>)> {
43 let blob_len = polkavm::ProgramBlob::blob_length(data)?;
44 let blob_len = blob_len.try_into().ok()?;
45 let (code, data) = data.split_at_checked(blob_len)?;
46 Some((code.to_vec(), data.to_vec()))
47}
48
49pub trait Memory<T: Config> {
56 fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError>;
62
63 fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError>;
69
70 fn zero(&mut self, ptr: u32, len: u32) -> Result<(), DispatchError>;
76
77 fn reset_interpreter_cache(&mut self);
83
84 fn read(&self, ptr: u32, len: u32) -> Result<Vec<u8>, DispatchError> {
90 let mut buf = vec![0u8; len as usize];
91 self.read_into_buf(ptr, buf.as_mut_slice())?;
92 Ok(buf)
93 }
94
95 fn read_array<const N: usize>(&self, ptr: u32) -> Result<[u8; N], DispatchError> {
97 let mut buf = [0u8; N];
98 self.read_into_buf(ptr, &mut buf)?;
99 Ok(buf)
100 }
101
102 fn read_u32(&self, ptr: u32) -> Result<u32, DispatchError> {
104 let buf: [u8; 4] = self.read_array(ptr)?;
105 Ok(u32::from_le_bytes(buf))
106 }
107
108 fn read_u256(&self, ptr: u32) -> Result<U256, DispatchError> {
110 let buf: [u8; 32] = self.read_array(ptr)?;
111 Ok(U256::from_little_endian(&buf))
112 }
113
114 fn read_h160(&self, ptr: u32) -> Result<H160, DispatchError> {
116 let mut buf = H160::default();
117 self.read_into_buf(ptr, buf.as_bytes_mut())?;
118 Ok(buf)
119 }
120
121 fn read_h256(&self, ptr: u32) -> Result<H256, DispatchError> {
123 let mut code_hash = H256::default();
124 self.read_into_buf(ptr, code_hash.as_bytes_mut())?;
125 Ok(code_hash)
126 }
127}
128
129pub trait PolkaVmInstance<T: Config>: Memory<T> {
135 fn gas(&self) -> polkavm::Gas;
136 fn set_gas(&mut self, gas: polkavm::Gas);
137 fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64);
138 fn write_output(&mut self, output: u64);
139}
140
141#[cfg(feature = "runtime-benchmarks")]
148impl<T: Config> Memory<T> for [u8] {
149 fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> {
150 let ptr = ptr as usize;
151 let bound_checked =
152 self.get(ptr..ptr + buf.len()).ok_or_else(|| Error::<T>::OutOfBounds)?;
153 buf.copy_from_slice(bound_checked);
154 Ok(())
155 }
156
157 fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> {
158 let ptr = ptr as usize;
159 let bound_checked =
160 self.get_mut(ptr..ptr + buf.len()).ok_or_else(|| Error::<T>::OutOfBounds)?;
161 bound_checked.copy_from_slice(buf);
162 Ok(())
163 }
164
165 fn zero(&mut self, ptr: u32, len: u32) -> Result<(), DispatchError> {
166 <[u8] as Memory<T>>::write(self, ptr, &vec![0; len as usize])
167 }
168
169 fn reset_interpreter_cache(&mut self) {}
170}
171
172impl<T: Config> Memory<T> for polkavm::RawInstance {
173 fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> {
174 self.read_memory_into(ptr, buf)
175 .map(|_| ())
176 .map_err(|_| Error::<T>::OutOfBounds.into())
177 }
178
179 fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> {
180 self.write_memory(ptr, buf).map_err(|_| Error::<T>::OutOfBounds.into())
181 }
182
183 fn zero(&mut self, ptr: u32, len: u32) -> Result<(), DispatchError> {
184 self.zero_memory(ptr, len).map_err(|_| Error::<T>::OutOfBounds.into())
185 }
186
187 fn reset_interpreter_cache(&mut self) {
188 self.reset_interpreter_cache();
189 }
190}
191
192impl<T: Config> PolkaVmInstance<T> for polkavm::RawInstance {
193 fn gas(&self) -> polkavm::Gas {
194 self.gas()
195 }
196
197 fn set_gas(&mut self, gas: polkavm::Gas) {
198 self.set_gas(gas)
199 }
200
201 fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64) {
202 (
203 self.reg(polkavm::Reg::A0),
204 self.reg(polkavm::Reg::A1),
205 self.reg(polkavm::Reg::A2),
206 self.reg(polkavm::Reg::A3),
207 self.reg(polkavm::Reg::A4),
208 self.reg(polkavm::Reg::A5),
209 )
210 }
211
212 fn write_output(&mut self, output: u64) {
213 self.set_reg(polkavm::Reg::A0, output);
214 }
215}
216
217impl From<&ExecReturnValue> for ReturnErrorCode {
218 fn from(from: &ExecReturnValue) -> Self {
219 if from.flags.contains(ReturnFlags::REVERT) {
220 Self::CalleeReverted
221 } else {
222 Self::Success
223 }
224 }
225}
226
227#[derive(RuntimeDebug)]
229pub struct ReturnData {
230 flags: u32,
233 data: Vec<u8>,
235}
236
237#[derive(RuntimeDebug)]
244pub enum TrapReason {
245 SupervisorError(DispatchError),
248 Return(ReturnData),
250 Termination,
253}
254
255impl<T: Into<DispatchError>> From<T> for TrapReason {
256 fn from(from: T) -> Self {
257 Self::SupervisorError(from.into())
258 }
259}
260
261impl fmt::Display for TrapReason {
262 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
263 Ok(())
264 }
265}
266
267macro_rules! charge_gas {
272 ($runtime:expr, $costs:expr) => {{
273 $runtime.ext.frame_meter_mut().charge_weight_token($costs)
274 }};
275}
276
277enum CallType {
279 Call { value_ptr: u32 },
281 DelegateCall,
284}
285
286impl CallType {
287 fn cost(&self) -> RuntimeCosts {
288 match self {
289 CallType::Call { .. } => RuntimeCosts::CallBase,
290 CallType::DelegateCall => RuntimeCosts::DelegateCallBase,
291 }
292 }
293}
294
295fn already_charged(_: u32) -> Option<RuntimeCosts> {
299 None
300}
301
302fn extract_hi_lo(reg: u64) -> (u32, u32) {
304 ((reg >> 32) as u32, reg as u32)
305}
306
307enum StorageValue {
309 Memory { ptr: u32, len: u32 },
313
314 Value(Vec<u8>),
318}
319
320enum StorageReadMode {
322 VariableOutput { output_len_ptr: u32 },
325 FixedOutput32,
328}
329
330pub struct Runtime<'a, E: Ext, M: ?Sized> {
332 ext: &'a mut E,
333 input_data: Option<Vec<u8>>,
334 _phantom_data: PhantomData<M>,
335}
336
337impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
338 pub fn new(ext: &'a mut E, input_data: Vec<u8>) -> Self {
339 Self { ext, input_data: Some(input_data), _phantom_data: Default::default() }
340 }
341
342 pub fn ext(&mut self) -> &mut E {
344 self.ext
345 }
346
347 fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
351 charge_gas!(self, costs)
352 }
353
354 fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) {
359 self.ext.frame_meter_mut().adjust_weight(charged, actual_costs);
360 }
361
362 pub fn write_sandbox_output(
383 &mut self,
384 memory: &mut M,
385 out_ptr: u32,
386 out_len_ptr: u32,
387 buf: &[u8],
388 allow_skip: bool,
389 create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
390 ) -> Result<(), DispatchError> {
391 if allow_skip && out_ptr == SENTINEL {
392 return Ok(());
393 }
394
395 let len = memory.read_u32(out_len_ptr)?;
396 let buf_len = len.min(buf.len() as u32);
397
398 if let Some(costs) = create_token(buf_len) {
399 self.charge_gas(costs)?;
400 }
401
402 memory.write(out_ptr, &buf[..buf_len as usize])?;
403 memory.write(out_len_ptr, &buf_len.encode())
404 }
405
406 pub fn write_fixed_sandbox_output(
408 &mut self,
409 memory: &mut M,
410 out_ptr: u32,
411 buf: &[u8],
412 allow_skip: bool,
413 create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
414 ) -> Result<(), DispatchError> {
415 if buf.is_empty() || (allow_skip && out_ptr == SENTINEL) {
416 return Ok(());
417 }
418
419 let buf_len = buf.len() as u32;
420 if let Some(costs) = create_token(buf_len) {
421 self.charge_gas(costs)?;
422 }
423
424 memory.write(out_ptr, buf)
425 }
426
427 fn compute_hash_on_intermediate_buffer<F, R>(
440 &self,
441 memory: &mut M,
442 hash_fn: F,
443 input_ptr: u32,
444 input_len: u32,
445 output_ptr: u32,
446 ) -> Result<(), DispatchError>
447 where
448 F: FnOnce(&[u8]) -> R,
449 R: AsRef<[u8]>,
450 {
451 let input = memory.read(input_ptr, input_len)?;
453 let hash = hash_fn(&input);
455 memory.write(output_ptr, hash.as_ref())?;
457 Ok(())
458 }
459
460 fn decode_key(&self, memory: &M, key_ptr: u32, key_len: u32) -> Result<Key, TrapReason> {
461 let res = match key_len {
462 SENTINEL => {
463 let mut buffer = [0u8; 32];
464 memory.read_into_buf(key_ptr, buffer.as_mut())?;
465 Ok(Key::from_fixed(buffer))
466 },
467 len => {
468 ensure!(len <= limits::STORAGE_KEY_BYTES, Error::<E::T>::DecodingFailed);
469 let key = memory.read(key_ptr, len)?;
470 Key::try_from_var(key)
471 },
472 };
473
474 res.map_err(|_| Error::<E::T>::DecodingFailed.into())
475 }
476
477 fn is_transient(flags: u32) -> Result<bool, TrapReason> {
478 StorageFlags::from_bits(flags)
479 .ok_or_else(|| <Error<E::T>>::InvalidStorageFlags.into())
480 .map(|flags| flags.contains(StorageFlags::TRANSIENT))
481 }
482
483 fn set_storage(
484 &mut self,
485 memory: &M,
486 flags: u32,
487 key_ptr: u32,
488 key_len: u32,
489 value: StorageValue,
490 ) -> Result<u32, TrapReason> {
491 let transient = Self::is_transient(flags)?;
492 let costs = |new_bytes: u32, old_bytes: u32| {
493 if transient {
494 RuntimeCosts::SetTransientStorage { new_bytes, old_bytes }
495 } else {
496 RuntimeCosts::SetStorage { new_bytes, old_bytes }
497 }
498 };
499
500 let value_len = match &value {
501 StorageValue::Memory { ptr: _, len } => *len,
502 StorageValue::Value(data) => data.len() as u32,
503 };
504
505 let max_size = limits::STORAGE_BYTES;
506 let charged = self.charge_gas(costs(value_len, max_size))?;
507 if value_len > max_size {
508 return Err(Error::<E::T>::ValueTooLarge.into());
509 }
510
511 let key = self.decode_key(memory, key_ptr, key_len)?;
512
513 let value = match value {
514 StorageValue::Memory { ptr, len } => Some(memory.read(ptr, len)?),
515 StorageValue::Value(data) => Some(data),
516 };
517
518 let write_outcome = if transient {
519 self.ext.set_transient_storage(&key, value, false)?
520 } else {
521 self.ext.set_storage(&key, value, false)?
522 };
523
524 self.adjust_gas(charged, costs(value_len, write_outcome.old_len()));
525 Ok(write_outcome.old_len_with_sentinel())
526 }
527
528 fn clear_storage(
529 &mut self,
530 memory: &M,
531 flags: u32,
532 key_ptr: u32,
533 key_len: u32,
534 ) -> Result<u32, TrapReason> {
535 let transient = Self::is_transient(flags)?;
536 let costs = |len| {
537 if transient {
538 RuntimeCosts::ClearTransientStorage(len)
539 } else {
540 RuntimeCosts::ClearStorage(len)
541 }
542 };
543 let charged = self.charge_gas(costs(limits::STORAGE_BYTES))?;
544 let key = self.decode_key(memory, key_ptr, key_len)?;
545 let outcome = if transient {
546 self.ext.set_transient_storage(&key, None, false)?
547 } else {
548 self.ext.set_storage(&key, None, false)?
549 };
550 self.adjust_gas(charged, costs(outcome.old_len()));
551 Ok(outcome.old_len_with_sentinel())
552 }
553
554 fn get_storage(
555 &mut self,
556 memory: &mut M,
557 flags: u32,
558 key_ptr: u32,
559 key_len: u32,
560 out_ptr: u32,
561 read_mode: StorageReadMode,
562 ) -> Result<ReturnErrorCode, TrapReason> {
563 let transient = Self::is_transient(flags)?;
564 let costs = |len| {
565 if transient {
566 RuntimeCosts::GetTransientStorage(len)
567 } else {
568 RuntimeCosts::GetStorage(len)
569 }
570 };
571 let charged = self.charge_gas(costs(limits::STORAGE_BYTES))?;
572 let key = self.decode_key(memory, key_ptr, key_len)?;
573 let outcome = if transient {
574 self.ext.get_transient_storage(&key)
575 } else {
576 self.ext.get_storage(&key)
577 };
578
579 if let Some(value) = outcome {
580 self.adjust_gas(charged, costs(value.len() as u32));
581
582 match read_mode {
583 StorageReadMode::FixedOutput32 => {
584 let mut fixed_output = [0u8; 32];
585 let len = value.len().min(fixed_output.len());
586 fixed_output[..len].copy_from_slice(&value[..len]);
587
588 self.write_fixed_sandbox_output(
589 memory,
590 out_ptr,
591 &fixed_output,
592 false,
593 already_charged,
594 )?;
595 Ok(ReturnErrorCode::Success)
596 },
597 StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr } => {
598 self.write_sandbox_output(
599 memory,
600 out_ptr,
601 out_len_ptr,
602 &value,
603 false,
604 already_charged,
605 )?;
606 Ok(ReturnErrorCode::Success)
607 },
608 }
609 } else {
610 self.adjust_gas(charged, costs(0));
611
612 match read_mode {
613 StorageReadMode::FixedOutput32 => {
614 self.write_fixed_sandbox_output(
615 memory,
616 out_ptr,
617 &[0u8; 32],
618 false,
619 already_charged,
620 )?;
621 Ok(ReturnErrorCode::Success)
622 },
623 StorageReadMode::VariableOutput { .. } => Ok(ReturnErrorCode::KeyNotFound),
624 }
625 }
626 }
627
628 fn call(
629 &mut self,
630 memory: &mut M,
631 flags: CallFlags,
632 call_type: CallType,
633 callee_ptr: u32,
634 resources: &CallResources<E::T>,
635 input_data_ptr: u32,
636 input_data_len: u32,
637 output_ptr: u32,
638 output_len_ptr: u32,
639 ) -> Result<ReturnErrorCode, TrapReason> {
640 let callee = memory.read_h160(callee_ptr)?;
641 let precompile = <AllPrecompiles<E::T>>::get::<E>(&callee.as_fixed_bytes());
642 match &precompile {
643 Some(precompile) if precompile.has_contract_info() =>
644 self.charge_gas(RuntimeCosts::PrecompileWithInfoBase)?,
645 Some(_) => self.charge_gas(RuntimeCosts::PrecompileBase)?,
646 None => self.charge_gas(call_type.cost())?,
647 };
648
649 if input_data_len > limits::CALLDATA_BYTES {
651 Err(<Error<E::T>>::CallDataTooLarge)?;
652 }
653
654 let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
655 let input = self.input_data.as_ref().ok_or(Error::<E::T>::InputForwarded)?;
656 charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?;
657 input.clone()
658 } else if flags.contains(CallFlags::FORWARD_INPUT) {
659 self.input_data.take().ok_or(Error::<E::T>::InputForwarded)?
660 } else {
661 if precompile.is_some() {
662 self.charge_gas(RuntimeCosts::PrecompileDecode(input_data_len))?;
663 } else {
664 self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?;
665 }
666 memory.read(input_data_ptr, input_data_len)?
667 };
668
669 memory.reset_interpreter_cache();
670
671 let call_outcome = match call_type {
672 CallType::Call { value_ptr } => {
673 let read_only = flags.contains(CallFlags::READ_ONLY);
674 let value = memory.read_u256(value_ptr)?;
675 if value > 0u32.into() {
676 if read_only || self.ext.is_read_only() {
679 return Err(Error::<E::T>::StateChangeDenied.into());
680 }
681
682 self.charge_gas(RuntimeCosts::CallTransferSurcharge {
683 dust_transfer: Pallet::<E::T>::has_dust(value),
684 })?;
685 }
686
687 let reentrancy = if flags.contains(CallFlags::ALLOW_REENTRY) {
688 ReentrancyProtection::AllowReentry
689 } else {
690 ReentrancyProtection::Strict
691 };
692
693 self.ext.call(resources, &callee, value, input_data, reentrancy, read_only)
694 },
695 CallType::DelegateCall => {
696 if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) {
697 return Err(Error::<E::T>::InvalidCallFlags.into());
698 }
699 self.ext.delegate_call(resources, callee, input_data)
700 },
701 };
702
703 match call_outcome {
704 Ok(_) if flags.contains(CallFlags::TAIL_CALL) => {
707 let output = mem::take(self.ext.last_frame_output_mut());
708 return Err(TrapReason::Return(ReturnData {
709 flags: output.flags.bits(),
710 data: output.data,
711 }));
712 },
713 Ok(_) => {
714 let output = mem::take(self.ext.last_frame_output_mut());
715 let write_result = self.write_sandbox_output(
716 memory,
717 output_ptr,
718 output_len_ptr,
719 &output.data,
720 true,
721 |len| Some(RuntimeCosts::CopyToContract(len)),
722 );
723 *self.ext.last_frame_output_mut() = output;
724 write_result?;
725 Ok(self.ext.last_frame_output().into())
726 },
727 Err(err) => {
728 let error_code = super::exec_error_into_return_code::<E>(err)?;
729 memory.write(output_len_ptr, &0u32.to_le_bytes())?;
730 Ok(error_code)
731 },
732 }
733 }
734
735 fn instantiate(
736 &mut self,
737 memory: &mut M,
738 code_hash_ptr: u32,
739 weight: Weight,
740 deposit_ptr: u32,
741 value_ptr: u32,
742 input_data_ptr: u32,
743 input_data_len: u32,
744 address_ptr: u32,
745 output_ptr: u32,
746 output_len_ptr: u32,
747 salt_ptr: u32,
748 ) -> Result<ReturnErrorCode, TrapReason> {
749 let value = match memory.read_u256(value_ptr) {
750 Ok(value) => {
751 self.charge_gas(RuntimeCosts::Instantiate {
752 input_data_len,
753 balance_transfer: Pallet::<E::T>::has_balance(value),
754 dust_transfer: Pallet::<E::T>::has_dust(value),
755 })?;
756 value
757 },
758 Err(err) => {
759 self.charge_gas(RuntimeCosts::Instantiate {
760 input_data_len: 0,
761 balance_transfer: false,
762 dust_transfer: false,
763 })?;
764 return Err(err.into());
765 },
766 };
767 let deposit_limit: U256 = memory.read_u256(deposit_ptr)?;
768 let code_hash = memory.read_h256(code_hash_ptr)?;
769 if input_data_len > limits::CALLDATA_BYTES {
770 Err(<Error<E::T>>::CallDataTooLarge)?;
771 }
772 let input_data = memory.read(input_data_ptr, input_data_len)?;
773 let salt = if salt_ptr == SENTINEL {
774 None
775 } else {
776 let salt: [u8; 32] = memory.read_array(salt_ptr)?;
777 Some(salt)
778 };
779
780 memory.reset_interpreter_cache();
781
782 match self.ext.instantiate(
783 &CallResources::from_weight_and_deposit(weight, deposit_limit),
784 Code::Existing(code_hash),
785 value,
786 input_data,
787 salt.as_ref(),
788 ) {
789 Ok(address) => {
790 if !self.ext.last_frame_output().flags.contains(ReturnFlags::REVERT) {
791 self.write_fixed_sandbox_output(
792 memory,
793 address_ptr,
794 &address.as_bytes(),
795 true,
796 already_charged,
797 )?;
798 }
799 let output = mem::take(self.ext.last_frame_output_mut());
800 let write_result = self.write_sandbox_output(
801 memory,
802 output_ptr,
803 output_len_ptr,
804 &output.data,
805 true,
806 |len| Some(RuntimeCosts::CopyToContract(len)),
807 );
808 *self.ext.last_frame_output_mut() = output;
809 write_result?;
810 Ok(self.ext.last_frame_output().into())
811 },
812 Err(err) => Ok(super::exec_error_into_return_code::<E>(err)?),
813 }
814 }
815}
816
817pub struct PreparedCall<'a, E: Ext> {
818 module: polkavm::Module,
819 instance: polkavm::RawInstance,
820 runtime: Runtime<'a, E, polkavm::RawInstance>,
821}
822
823impl<'a, E: Ext> PreparedCall<'a, E> {
824 pub fn call(mut self) -> ExecResult {
825 let exec_result = loop {
826 let interrupt = self.instance.run();
827 if let Some(exec_result) =
828 self.runtime.handle_interrupt(interrupt, &self.module, &mut self.instance)
829 {
830 break exec_result
831 }
832 };
833 self.runtime.ext().frame_meter_mut().sync_from_executor(self.instance.gas())?;
834 exec_result
835 }
836
837 #[cfg(feature = "runtime-benchmarks")]
839 pub fn aux_data_base(&self) -> u32 {
840 self.instance.module().memory_map().aux_data_address()
841 }
842
843 #[cfg(feature = "runtime-benchmarks")]
850 pub fn setup_aux_data(
851 &mut self,
852 data: &[u8],
853 offset: u32,
854 a1: u64,
855 ) -> frame_support::dispatch::DispatchResult {
856 let a0 = self.aux_data_base().saturating_add(offset);
857 self.instance.write_memory(a0, data).map_err(|err| {
858 log::debug!(target: LOG_TARGET, "failed to write aux data: {err:?}");
859 Error::<E::T>::CodeRejected
860 })?;
861 self.instance.set_reg(polkavm::Reg::A0, a0.into());
862 self.instance.set_reg(polkavm::Reg::A1, a1);
863 Ok(())
864 }
865}