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 let costs = |new_bytes: u32, old_bytes: u32| {
487 if transient {
488 RuntimeCosts::SetTransientStorage { new_bytes, old_bytes }
489 } else {
490 RuntimeCosts::SetStorage { new_bytes, old_bytes }
491 }
492 };
493
494 let value_len = match &value {
495 StorageValue::Memory { ptr: _, len } => *len,
496 StorageValue::Value(data) => data.len() as u32,
497 };
498
499 let max_size = limits::STORAGE_BYTES;
500 let charged = self.charge_gas(costs(value_len, max_size))?;
501 if value_len > max_size {
502 return Err(Error::<E::T>::ValueTooLarge.into());
503 }
504
505 let key = self.decode_key(memory, key_ptr, key_len)?;
506
507 let value = match value {
508 StorageValue::Memory { ptr, len } => Some(memory.read(ptr, len)?),
509 StorageValue::Value(data) => Some(data),
510 };
511
512 let write_outcome = if transient {
513 self.ext.set_transient_storage(&key, value, false)?
514 } else {
515 self.ext.set_storage(&key, value, false)?
516 };
517
518 self.adjust_gas(charged, costs(value_len, write_outcome.old_len()));
519 Ok(write_outcome.old_len_with_sentinel())
520 }
521
522 fn clear_storage(
523 &mut self,
524 memory: &mut M,
525 flags: u32,
526 key_ptr: u32,
527 key_len: u32,
528 ) -> Result<u32, TrapReason> {
529 let transient = Self::is_transient(flags)?;
530 let costs = |len| {
531 if transient {
532 RuntimeCosts::ClearTransientStorage(len)
533 } else {
534 RuntimeCosts::ClearStorage(len)
535 }
536 };
537 let charged = self.charge_gas(costs(limits::STORAGE_BYTES))?;
538 let key = self.decode_key(memory, key_ptr, key_len)?;
539 let outcome = if transient {
540 self.ext.set_transient_storage(&key, None, false)?
541 } else {
542 self.ext.set_storage(&key, None, false)?
543 };
544 self.adjust_gas(charged, costs(outcome.old_len()));
545 Ok(outcome.old_len_with_sentinel())
546 }
547
548 fn get_storage(
549 &mut self,
550 memory: &mut M,
551 flags: u32,
552 key_ptr: u32,
553 key_len: u32,
554 out_ptr: u32,
555 read_mode: StorageReadMode,
556 ) -> Result<ReturnErrorCode, TrapReason> {
557 let transient = Self::is_transient(flags)?;
558 let costs = |len| {
559 if transient {
560 RuntimeCosts::GetTransientStorage(len)
561 } else {
562 RuntimeCosts::GetStorage(len)
563 }
564 };
565 let charged = self.charge_gas(costs(limits::STORAGE_BYTES))?;
566 let key = self.decode_key(memory, key_ptr, key_len)?;
567 let outcome = if transient {
568 self.ext.get_transient_storage(&key)
569 } else {
570 self.ext.get_storage(&key)
571 };
572
573 if let Some(value) = outcome {
574 self.adjust_gas(charged, costs(value.len() as u32));
575
576 match read_mode {
577 StorageReadMode::FixedOutput32 => {
578 let mut fixed_output = [0u8; 32];
579 let len = value.len().min(fixed_output.len());
580 fixed_output[..len].copy_from_slice(&value[..len]);
581
582 self.write_fixed_sandbox_output(
583 memory,
584 out_ptr,
585 &fixed_output,
586 false,
587 already_charged,
588 )?;
589 Ok(ReturnErrorCode::Success)
590 },
591 StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr } => {
592 self.write_sandbox_output(
593 memory,
594 out_ptr,
595 out_len_ptr,
596 &value,
597 false,
598 already_charged,
599 )?;
600 Ok(ReturnErrorCode::Success)
601 },
602 }
603 } else {
604 self.adjust_gas(charged, costs(0));
605
606 match read_mode {
607 StorageReadMode::FixedOutput32 => {
608 self.write_fixed_sandbox_output(
609 memory,
610 out_ptr,
611 &[0u8; 32],
612 false,
613 already_charged,
614 )?;
615 Ok(ReturnErrorCode::Success)
616 },
617 StorageReadMode::VariableOutput { .. } => Ok(ReturnErrorCode::KeyNotFound),
618 }
619 }
620 }
621
622 fn call(
623 &mut self,
624 memory: &mut M,
625 flags: CallFlags,
626 call_type: CallType,
627 callee_ptr: u32,
628 resources: &CallResources<E::T>,
629 input_data_ptr: u32,
630 input_data_len: u32,
631 output_ptr: u32,
632 output_len_ptr: u32,
633 ) -> Result<ReturnErrorCode, TrapReason> {
634 let callee = memory.read_h160(callee_ptr)?;
635 let precompile = <AllPrecompiles<E::T>>::get::<E>(&callee.as_fixed_bytes());
636 match &precompile {
637 Some(precompile) if precompile.has_contract_info() => {
638 self.charge_gas(RuntimeCosts::PrecompileWithInfoBase)?
639 },
640 Some(_) => self.charge_gas(RuntimeCosts::PrecompileBase)?,
641 None => self.charge_gas(call_type.cost())?,
642 };
643
644 if input_data_len > limits::CALLDATA_BYTES {
646 Err(<Error<E::T>>::CallDataTooLarge)?;
647 }
648
649 let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
650 let input = self.input_data.as_ref().ok_or(Error::<E::T>::InputForwarded)?;
651 charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?;
652 input.clone()
653 } else if flags.contains(CallFlags::FORWARD_INPUT) {
654 self.input_data.take().ok_or(Error::<E::T>::InputForwarded)?
655 } else {
656 if precompile.is_some() {
657 self.charge_gas(RuntimeCosts::PrecompileDecode(input_data_len))?;
658 } else {
659 self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?;
660 }
661 memory.read(input_data_ptr, input_data_len)?
662 };
663
664 memory.reset_interpreter_cache();
665
666 let call_outcome = match call_type {
667 CallType::Call { value_ptr } => {
668 let read_only = flags.contains(CallFlags::READ_ONLY);
669 let value = memory.read_u256(value_ptr)?;
670 if value > 0u32.into() {
671 if read_only || self.ext.is_read_only() {
674 return Err(Error::<E::T>::StateChangeDenied.into());
675 }
676
677 self.charge_gas(RuntimeCosts::CallTransferSurcharge {
678 dust_transfer: Pallet::<E::T>::has_dust(value),
679 })?;
680 }
681
682 let reentrancy = if flags.contains(CallFlags::ALLOW_REENTRY) {
683 ReentrancyProtection::AllowReentry
684 } else {
685 ReentrancyProtection::Strict
686 };
687
688 self.ext.call(resources, &callee, value, input_data, reentrancy, read_only)
689 },
690 CallType::DelegateCall => {
691 if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) {
692 return Err(Error::<E::T>::InvalidCallFlags.into());
693 }
694 self.ext.delegate_call(resources, callee, input_data)
695 },
696 };
697
698 match call_outcome {
699 Ok(_) if flags.contains(CallFlags::TAIL_CALL) => {
702 let output = mem::take(self.ext.last_frame_output_mut());
703 return Err(TrapReason::Return(ReturnData {
704 flags: output.flags.bits(),
705 data: output.data,
706 }));
707 },
708 Ok(_) => {
709 let output = mem::take(self.ext.last_frame_output_mut());
710 let write_result = self.write_sandbox_output(
711 memory,
712 output_ptr,
713 output_len_ptr,
714 &output.data,
715 true,
716 |len| Some(RuntimeCosts::CopyToContract(len)),
717 );
718 *self.ext.last_frame_output_mut() = output;
719 write_result?;
720 Ok(self.ext.last_frame_output().into())
721 },
722 Err(err) => {
723 let error_code = super::exec_error_into_return_code::<E>(err)?;
724 memory.write(output_len_ptr, &0u32.to_le_bytes())?;
725 Ok(error_code)
726 },
727 }
728 }
729
730 fn instantiate(
731 &mut self,
732 memory: &mut M,
733 code_hash_ptr: u32,
734 weight: Weight,
735 deposit_ptr: u32,
736 value_ptr: u32,
737 input_data_ptr: u32,
738 input_data_len: u32,
739 address_ptr: u32,
740 output_ptr: u32,
741 output_len_ptr: u32,
742 salt_ptr: u32,
743 ) -> Result<ReturnErrorCode, TrapReason> {
744 let value = match memory.read_u256(value_ptr) {
745 Ok(value) => {
746 self.charge_gas(RuntimeCosts::Instantiate {
747 input_data_len,
748 balance_transfer: Pallet::<E::T>::has_balance(value),
749 dust_transfer: Pallet::<E::T>::has_dust(value),
750 })?;
751 value
752 },
753 Err(err) => {
754 self.charge_gas(RuntimeCosts::Instantiate {
755 input_data_len: 0,
756 balance_transfer: false,
757 dust_transfer: false,
758 })?;
759 return Err(err.into());
760 },
761 };
762 let deposit_limit: U256 = memory.read_u256(deposit_ptr)?;
763 let code_hash = memory.read_h256(code_hash_ptr)?;
764 if input_data_len > limits::CALLDATA_BYTES {
765 Err(<Error<E::T>>::CallDataTooLarge)?;
766 }
767 let input_data = memory.read(input_data_ptr, input_data_len)?;
768 let salt = if salt_ptr == SENTINEL {
769 None
770 } else {
771 let salt: [u8; 32] = memory.read_array(salt_ptr)?;
772 Some(salt)
773 };
774
775 memory.reset_interpreter_cache();
776
777 match self.ext.instantiate(
778 &CallResources::from_weight_and_deposit(weight, deposit_limit),
779 Code::Existing(code_hash),
780 value,
781 input_data,
782 salt.as_ref(),
783 ) {
784 Ok(address) => {
785 if !self.ext.last_frame_output().flags.contains(ReturnFlags::REVERT) {
786 self.write_fixed_sandbox_output(
787 memory,
788 address_ptr,
789 &address.as_bytes(),
790 true,
791 already_charged,
792 )?;
793 }
794 let output = mem::take(self.ext.last_frame_output_mut());
795 let write_result = self.write_sandbox_output(
796 memory,
797 output_ptr,
798 output_len_ptr,
799 &output.data,
800 true,
801 |len| Some(RuntimeCosts::CopyToContract(len)),
802 );
803 *self.ext.last_frame_output_mut() = output;
804 write_result?;
805 Ok(self.ext.last_frame_output().into())
806 },
807 Err(err) => Ok(super::exec_error_into_return_code::<E>(err)?),
808 }
809 }
810}
811
812impl<'a, E: Ext, M: ?Sized + Memory<E::T>> FrameTraceInfo for Runtime<'a, E, M> {
813 fn gas_left(&self) -> u64 {
814 let meter = self.ext.frame_meter();
815 meter.eth_gas_left().unwrap_or_default().try_into().unwrap_or_default()
816 }
817 fn weight_consumed(&self) -> Weight {
818 let meter = self.ext.frame_meter();
819 meter.weight_consumed()
820 }
821
822 fn last_frame_output(&self) -> crate::evm::Bytes {
823 crate::evm::Bytes(self.ext.last_frame_output().data.clone())
824 }
825}
826
827pub struct PreparedCall<'a, E: Ext> {
828 module: polkavm::Module,
829 instance: polkavm::RawInstance,
830 runtime: Runtime<'a, E, polkavm::RawInstance>,
831}
832
833impl<'a, E: Ext> PreparedCall<'a, E> {
834 pub fn call(mut self) -> ExecResult {
835 let exec_result = loop {
836 let interrupt = self.instance.run();
837 if let Some(exec_result) =
838 self.runtime.handle_interrupt(interrupt, &self.module, &mut self.instance)
839 {
840 break exec_result;
841 }
842 };
843 crate::tracing::if_tracing(|tracer| {
844 tracer.enter_ecall(crate::tracing::PVM_FUEL_NAME, &[], &self.runtime)
845 });
846 let sync_result =
847 self.runtime.ext().frame_meter_mut().sync_from_executor(self.instance.gas());
848 crate::tracing::if_tracing(|tracer| tracer.exit_step(&self.runtime, None));
849 sync_result?;
850 exec_result
851 }
852
853 #[cfg(feature = "runtime-benchmarks")]
855 pub fn aux_data_base(&self) -> u32 {
856 self.instance.module().memory_map().aux_data_address()
857 }
858
859 #[cfg(feature = "runtime-benchmarks")]
866 pub fn setup_aux_data(
867 &mut self,
868 data: &[u8],
869 offset: u32,
870 a1: u64,
871 ) -> frame_support::dispatch::DispatchResult {
872 let a0 = self.aux_data_base().saturating_add(offset);
873 self.instance.write_memory(a0, data).map_err(|err| {
874 log::debug!(target: LOG_TARGET, "failed to write aux data: {err:?}");
875 Error::<E::T>::CodeRejected
876 })?;
877 self.instance.set_reg(polkavm::Reg::A0, a0.into());
878 self.instance.set_reg(polkavm::Reg::A1, a1);
879 Ok(())
880 }
881}