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