1pub mod env;
21
22use crate::{
23 Code, Config, Error, LOG_TARGET, Pallet, ReentrancyProtection, RuntimeCosts, SENTINEL,
24 access_list::StorageOp,
25 exec::{CallResources, ExecError, ExecResult, Ext, Key},
26 limits,
27 metering::ChargedAmount,
28 precompiles::{All as AllPrecompiles, Precompiles},
29 primitives::ExecReturnValue,
30 tracing::FrameTraceInfo,
31};
32use alloc::{vec, vec::Vec};
33use codec::Encode;
34use core::{fmt, marker::PhantomData, mem};
35#[cfg(doc)]
36pub use env::SyscallDoc;
37use frame_support::{ensure, weights::Weight};
38use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags};
39use sp_core::{H160, H256, U256};
40use sp_runtime::DispatchError;
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(&mut 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(&mut 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>(&mut 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(&mut 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(&mut 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(&mut 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(&mut 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(&mut 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(&mut 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) { Self::CalleeReverted } else { Self::Success }
221 }
222}
223
224#[derive(Debug)]
226pub struct ReturnData {
227 flags: u32,
230 data: Vec<u8>,
232}
233
234#[derive(Debug)]
241pub enum TrapReason {
242 SupervisorError(DispatchError),
245 Return(ReturnData),
247 Termination,
250}
251
252impl<T: Into<DispatchError>> From<T> for TrapReason {
253 fn from(from: T) -> Self {
254 Self::SupervisorError(from.into())
255 }
256}
257
258impl fmt::Display for TrapReason {
259 fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
260 Ok(())
261 }
262}
263
264macro_rules! charge_gas {
269 ($runtime:expr, $costs:expr) => {{ $runtime.ext.frame_meter_mut().charge_weight_token($costs) }};
270}
271
272enum CallType {
274 Call { value_ptr: u32 },
276 DelegateCall,
279}
280
281impl CallType {
282 fn cost(&self) -> RuntimeCosts {
283 match self {
284 CallType::Call { .. } => RuntimeCosts::CallBase,
285 CallType::DelegateCall => RuntimeCosts::DelegateCallBase,
286 }
287 }
288}
289
290fn already_charged(_: u32) -> Option<RuntimeCosts> {
294 None
295}
296
297fn extract_hi_lo(reg: u64) -> (u32, u32) {
299 ((reg >> 32) as u32, reg as u32)
300}
301
302enum StorageValue {
304 Memory { ptr: u32, len: u32 },
308
309 Value(Vec<u8>),
313}
314
315enum StorageReadMode {
317 VariableOutput { output_len_ptr: u32 },
320 FixedOutput32,
323}
324
325pub struct Runtime<'a, E: Ext, M: ?Sized> {
327 ext: &'a mut E,
328 input_data: Option<Vec<u8>>,
329 _phantom_data: PhantomData<M>,
330}
331
332impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
333 pub fn new(ext: &'a mut E, input_data: Vec<u8>) -> Self {
334 Self { ext, input_data: Some(input_data), _phantom_data: Default::default() }
335 }
336
337 pub fn ext(&mut self) -> &mut E {
339 self.ext
340 }
341
342 fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
346 charge_gas!(self, costs)
347 }
348
349 fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) {
354 self.ext.frame_meter_mut().adjust_weight(charged, actual_costs);
355 }
356
357 pub fn write_sandbox_output(
378 &mut self,
379 memory: &mut M,
380 out_ptr: u32,
381 out_len_ptr: u32,
382 buf: &[u8],
383 allow_skip: bool,
384 create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
385 ) -> Result<(), DispatchError> {
386 if allow_skip && out_ptr == SENTINEL {
387 return Ok(());
388 }
389
390 let len = memory.read_u32(out_len_ptr)?;
391 let buf_len = len.min(buf.len() as u32);
392
393 if let Some(costs) = create_token(buf_len) {
394 self.charge_gas(costs)?;
395 }
396
397 memory.write(out_ptr, &buf[..buf_len as usize])?;
398 memory.write(out_len_ptr, &buf_len.encode())
399 }
400
401 pub fn write_fixed_sandbox_output(
403 &mut self,
404 memory: &mut M,
405 out_ptr: u32,
406 buf: &[u8],
407 allow_skip: bool,
408 create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
409 ) -> Result<(), DispatchError> {
410 if buf.is_empty() || (allow_skip && out_ptr == SENTINEL) {
411 return Ok(());
412 }
413
414 let buf_len = buf.len() as u32;
415 if let Some(costs) = create_token(buf_len) {
416 self.charge_gas(costs)?;
417 }
418
419 memory.write(out_ptr, buf)
420 }
421
422 fn compute_hash_on_intermediate_buffer<F, R>(
435 &self,
436 memory: &mut M,
437 hash_fn: F,
438 input_ptr: u32,
439 input_len: u32,
440 output_ptr: u32,
441 ) -> Result<(), DispatchError>
442 where
443 F: FnOnce(&[u8]) -> R,
444 R: AsRef<[u8]>,
445 {
446 let input = memory.read(input_ptr, input_len)?;
448 let hash = hash_fn(&input);
450 memory.write(output_ptr, hash.as_ref())?;
452 Ok(())
453 }
454
455 fn decode_key(&self, memory: &mut M, key_ptr: u32, key_len: u32) -> Result<Key, TrapReason> {
456 let res = match key_len {
457 SENTINEL => {
458 let mut buffer = [0u8; 32];
459 memory.read_into_buf(key_ptr, buffer.as_mut())?;
460 Ok(Key::from_fixed(buffer))
461 },
462 len => {
463 ensure!(len <= limits::STORAGE_KEY_BYTES, Error::<E::T>::DecodingFailed);
464 let key = memory.read(key_ptr, len)?;
465 Key::try_from_var(key)
466 },
467 };
468
469 res.map_err(|_| Error::<E::T>::DecodingFailed.into())
470 }
471
472 fn is_transient(flags: u32) -> Result<bool, TrapReason> {
473 StorageFlags::from_bits(flags)
474 .ok_or_else(|| <Error<E::T>>::InvalidStorageFlags.into())
475 .map(|flags| flags.contains(StorageFlags::TRANSIENT))
476 }
477
478 fn set_storage(
479 &mut self,
480 memory: &mut M,
481 flags: u32,
482 key_ptr: u32,
483 key_len: u32,
484 value: StorageValue,
485 ) -> Result<u32, TrapReason> {
486 let transient = Self::is_transient(flags)?;
487
488 let value_len = match &value {
489 StorageValue::Memory { ptr: _, len } => *len,
490 StorageValue::Value(data) => data.len() as u32,
491 };
492
493 let max_size = limits::STORAGE_BYTES;
494 let key = self.decode_key(memory, key_ptr, key_len)?;
495
496 if value_len > max_size {
497 let access_kind = self.ext.peek_storage_access(transient, &key);
499 self.charge_gas(RuntimeCosts::SetStorage {
500 new_bytes: value_len,
501 old_bytes: max_size,
502 kind: access_kind,
503 })?;
504 return Err(Error::<E::T>::ValueTooLarge.into());
505 }
506
507 let access_kind = self.ext.touch_storage_access(transient, &key, StorageOp::Write);
508 let charged = self.charge_gas(RuntimeCosts::SetStorage {
509 new_bytes: value_len,
510 old_bytes: max_size,
511 kind: access_kind,
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(
525 charged,
526 RuntimeCosts::SetStorage {
527 new_bytes: value_len,
528 old_bytes: write_outcome.old_len(),
529 kind: access_kind,
530 },
531 );
532 Ok(write_outcome.old_len_with_sentinel())
533 }
534
535 fn clear_storage(
536 &mut self,
537 memory: &mut M,
538 flags: u32,
539 key_ptr: u32,
540 key_len: u32,
541 ) -> Result<u32, TrapReason> {
542 let transient = Self::is_transient(flags)?;
543 let key = self.decode_key(memory, key_ptr, key_len)?;
544 let access_kind = self.ext.touch_storage_access(transient, &key, StorageOp::Write);
545 let charged = self.charge_gas(RuntimeCosts::ClearStorage {
546 len: limits::STORAGE_BYTES,
547 kind: access_kind,
548 })?;
549 let outcome = if transient {
550 self.ext.set_transient_storage(&key, None, false)?
551 } else {
552 self.ext.set_storage(&key, None, false)?
553 };
554 self.adjust_gas(
555 charged,
556 RuntimeCosts::ClearStorage { len: outcome.old_len(), kind: access_kind },
557 );
558 Ok(outcome.old_len_with_sentinel())
559 }
560
561 fn get_storage(
562 &mut self,
563 memory: &mut M,
564 flags: u32,
565 key_ptr: u32,
566 key_len: u32,
567 out_ptr: u32,
568 read_mode: StorageReadMode,
569 ) -> Result<ReturnErrorCode, TrapReason> {
570 let transient = Self::is_transient(flags)?;
571 let key = self.decode_key(memory, key_ptr, key_len)?;
572 let access_kind = self.ext.touch_storage_access(transient, &key, StorageOp::Read);
573 let charged = self.charge_gas(RuntimeCosts::GetStorage {
574 len: limits::STORAGE_BYTES,
575 kind: access_kind,
576 })?;
577 let outcome = if transient {
578 self.ext.get_transient_storage(&key)
579 } else {
580 self.ext.get_storage(&key)
581 };
582 let len = outcome.as_ref().map(|v| v.len() as u32).unwrap_or(0);
583 self.adjust_gas(charged, RuntimeCosts::GetStorage { len, kind: access_kind });
584
585 if let Some(value) = outcome {
586 match read_mode {
587 StorageReadMode::FixedOutput32 => {
588 let mut fixed_output = [0u8; 32];
589 let len = value.len().min(fixed_output.len());
590 fixed_output[..len].copy_from_slice(&value[..len]);
591
592 self.write_fixed_sandbox_output(
593 memory,
594 out_ptr,
595 &fixed_output,
596 false,
597 already_charged,
598 )?;
599 Ok(ReturnErrorCode::Success)
600 },
601 StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr } => {
602 self.write_sandbox_output(
603 memory,
604 out_ptr,
605 out_len_ptr,
606 &value,
607 false,
608 already_charged,
609 )?;
610 Ok(ReturnErrorCode::Success)
611 },
612 }
613 } else {
614 match read_mode {
615 StorageReadMode::FixedOutput32 => {
616 self.write_fixed_sandbox_output(
617 memory,
618 out_ptr,
619 &[0u8; 32],
620 false,
621 already_charged,
622 )?;
623 Ok(ReturnErrorCode::Success)
624 },
625 StorageReadMode::VariableOutput { .. } => Ok(ReturnErrorCode::KeyNotFound),
626 }
627 }
628 }
629
630 fn call(
631 &mut self,
632 memory: &mut M,
633 flags: CallFlags,
634 call_type: CallType,
635 callee_ptr: u32,
636 resources: &CallResources<E::T>,
637 input_data_ptr: u32,
638 input_data_len: u32,
639 output_ptr: u32,
640 output_len_ptr: u32,
641 ) -> Result<ReturnErrorCode, TrapReason> {
642 let callee = memory.read_h160(callee_ptr)?;
643 let precompile = <AllPrecompiles<E::T>>::get::<E>(&callee.as_fixed_bytes());
644 match &precompile {
645 Some(precompile) if precompile.has_contract_info() => {
646 self.charge_gas(RuntimeCosts::PrecompileWithInfoBase)?
647 },
648 Some(_) => self.charge_gas(RuntimeCosts::PrecompileBase)?,
649 None => self.charge_gas(call_type.cost())?,
650 };
651
652 if input_data_len > limits::CALLDATA_BYTES {
654 Err(<Error<E::T>>::CallDataTooLarge)?;
655 }
656
657 let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
658 let input = self.input_data.as_ref().ok_or(Error::<E::T>::InputForwarded)?;
659 charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?;
660 input.clone()
661 } else if flags.contains(CallFlags::FORWARD_INPUT) {
662 self.input_data.take().ok_or(Error::<E::T>::InputForwarded)?
663 } else {
664 if precompile.is_some() {
665 self.charge_gas(RuntimeCosts::PrecompileDecode(input_data_len))?;
666 } else {
667 self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?;
668 }
669 memory.read(input_data_ptr, input_data_len)?
670 };
671
672 memory.reset_interpreter_cache();
673
674 let call_outcome = match call_type {
675 CallType::Call { value_ptr } => {
676 let read_only = flags.contains(CallFlags::READ_ONLY);
677 let value = memory.read_u256(value_ptr)?;
678 if value > 0u32.into() {
679 if read_only || self.ext.is_read_only() {
682 return Err(Error::<E::T>::StateChangeDenied.into());
683 }
684
685 self.charge_gas(RuntimeCosts::CallTransferSurcharge {
686 dust_transfer: Pallet::<E::T>::has_dust(value),
687 })?;
688 }
689
690 let reentrancy = if flags.contains(CallFlags::ALLOW_REENTRY) {
691 ReentrancyProtection::AllowReentry
692 } else {
693 ReentrancyProtection::Strict
694 };
695
696 self.ext.call(resources, &callee, value, input_data, reentrancy, read_only)
697 },
698 CallType::DelegateCall => {
699 if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) {
700 return Err(Error::<E::T>::InvalidCallFlags.into());
701 }
702 self.ext.delegate_call(resources, callee, input_data)
703 },
704 };
705
706 match call_outcome {
707 Ok(_) if flags.contains(CallFlags::TAIL_CALL) => {
710 let output = mem::take(self.ext.last_frame_output_mut());
711 return Err(TrapReason::Return(ReturnData {
712 flags: output.flags.bits(),
713 data: output.data,
714 }));
715 },
716 Ok(_) => {
717 let output = mem::take(self.ext.last_frame_output_mut());
718 let write_result = self.write_sandbox_output(
719 memory,
720 output_ptr,
721 output_len_ptr,
722 &output.data,
723 true,
724 |len| Some(RuntimeCosts::CopyToContract(len)),
725 );
726 *self.ext.last_frame_output_mut() = output;
727 write_result?;
728 Ok(self.ext.last_frame_output().into())
729 },
730 Err(err) => {
731 let error_code = super::exec_error_into_return_code::<E>(err)?;
732 memory.write(output_len_ptr, &0u32.to_le_bytes())?;
733 Ok(error_code)
734 },
735 }
736 }
737
738 fn instantiate(
739 &mut self,
740 memory: &mut M,
741 code_hash_ptr: u32,
742 weight: Weight,
743 deposit_ptr: u32,
744 value_ptr: u32,
745 input_data_ptr: u32,
746 input_data_len: u32,
747 address_ptr: u32,
748 output_ptr: u32,
749 output_len_ptr: u32,
750 salt_ptr: u32,
751 ) -> Result<ReturnErrorCode, TrapReason> {
752 let value = match memory.read_u256(value_ptr) {
753 Ok(value) => {
754 self.charge_gas(RuntimeCosts::Instantiate {
755 input_data_len,
756 balance_transfer: Pallet::<E::T>::has_balance(value),
757 dust_transfer: Pallet::<E::T>::has_dust(value),
758 })?;
759 value
760 },
761 Err(err) => {
762 self.charge_gas(RuntimeCosts::Instantiate {
763 input_data_len: 0,
764 balance_transfer: false,
765 dust_transfer: false,
766 })?;
767 return Err(err.into());
768 },
769 };
770 let deposit_limit: U256 = memory.read_u256(deposit_ptr)?;
771 let code_hash = memory.read_h256(code_hash_ptr)?;
772 if input_data_len > limits::CALLDATA_BYTES {
773 Err(<Error<E::T>>::CallDataTooLarge)?;
774 }
775 let input_data = memory.read(input_data_ptr, input_data_len)?;
776 let salt = if salt_ptr == SENTINEL {
777 None
778 } else {
779 let salt: [u8; 32] = memory.read_array(salt_ptr)?;
780 Some(salt)
781 };
782
783 memory.reset_interpreter_cache();
784
785 match self.ext.instantiate(
786 &CallResources::from_weight_and_deposit(weight, deposit_limit),
787 Code::Existing(code_hash),
788 value,
789 input_data,
790 salt.as_ref(),
791 ) {
792 Ok(address) => {
793 if !self.ext.last_frame_output().flags.contains(ReturnFlags::REVERT) {
794 self.write_fixed_sandbox_output(
795 memory,
796 address_ptr,
797 &address.as_bytes(),
798 true,
799 already_charged,
800 )?;
801 }
802 let output = mem::take(self.ext.last_frame_output_mut());
803 let write_result = self.write_sandbox_output(
804 memory,
805 output_ptr,
806 output_len_ptr,
807 &output.data,
808 true,
809 |len| Some(RuntimeCosts::CopyToContract(len)),
810 );
811 *self.ext.last_frame_output_mut() = output;
812 write_result?;
813 Ok(self.ext.last_frame_output().into())
814 },
815 Err(err) => Ok(super::exec_error_into_return_code::<E>(err)?),
816 }
817 }
818}
819
820impl<'a, E: Ext, M: ?Sized + Memory<E::T>> FrameTraceInfo for Runtime<'a, E, M> {
821 fn gas_left(&self) -> u64 {
822 let meter = self.ext.frame_meter();
823 meter.eth_gas_left().unwrap_or_default().try_into().unwrap_or_default()
824 }
825 fn weight_consumed(&self) -> Weight {
826 let meter = self.ext.frame_meter();
827 meter.weight_consumed()
828 }
829
830 fn last_frame_output(&self) -> crate::evm::Bytes {
831 crate::evm::Bytes(self.ext.last_frame_output().data.clone())
832 }
833}
834
835pub struct PreparedCall<'a, E: Ext> {
836 module: polkavm::Module,
837 instance: polkavm::RawInstance,
838 runtime: Runtime<'a, E, polkavm::RawInstance>,
839}
840
841impl<'a, E: Ext> PreparedCall<'a, E> {
842 pub fn call(mut self) -> ExecResult {
843 let exec_result = loop {
844 let interrupt = self.instance.run();
845 if let Some(exec_result) =
846 self.runtime.handle_interrupt(interrupt, &self.module, &mut self.instance)
847 {
848 break exec_result;
849 }
850 };
851 crate::tracing::if_tracing(|tracer| {
852 tracer.enter_ecall(crate::tracing::PVM_FUEL_NAME, &[], &self.runtime)
853 });
854 let sync_result =
855 self.runtime.ext().frame_meter_mut().sync_from_executor(self.instance.gas());
856 crate::tracing::if_tracing(|tracer| tracer.exit_step(&self.runtime, None));
857 sync_result?;
858 exec_result
859 }
860
861 #[cfg(feature = "runtime-benchmarks")]
863 pub fn aux_data_base(&self) -> u32 {
864 self.instance.module().memory_map().aux_data_address()
865 }
866
867 #[cfg(feature = "runtime-benchmarks")]
874 pub fn setup_aux_data(
875 &mut self,
876 data: &[u8],
877 offset: u32,
878 a1: u64,
879 ) -> frame_support::dispatch::DispatchResult {
880 let a0 = self.aux_data_base().saturating_add(offset);
881 self.instance.write_memory(a0, data).map_err(|err| {
882 log::debug!(target: LOG_TARGET, "failed to write aux data: {err:?}");
883 Error::<E::T>::CodeRejected
884 })?;
885 self.instance.set_reg(polkavm::Reg::A0, a0.into());
886 self.instance.set_reg(polkavm::Reg::A1, a1);
887 Ok(())
888 }
889}