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