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