referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/
pvm.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Environment definition of the vm smart-contract runtime.
19
20pub 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
42/// Extracts the code and data from a given program blob.
43pub 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
50/// Abstraction over the memory access within syscalls.
51///
52/// The reason for this abstraction is that we run syscalls on the host machine when
53/// benchmarking them. In that case we have direct access to the contract's memory. However, when
54/// running within PolkaVM we need to resort to copying as we can't map the contracts memory into
55/// the host (as of now).
56pub trait Memory<T: Config> {
57	/// Read designated chunk from the sandbox memory into the supplied buffer.
58	///
59	/// Returns `Err` if one of the following conditions occurs:
60	///
61	/// - requested buffer is not within the bounds of the sandbox memory.
62	fn read_into_buf(&mut self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError>;
63
64	/// Write the given buffer to the designated location in the sandbox memory.
65	///
66	/// Returns `Err` if one of the following conditions occurs:
67	///
68	/// - designated area is not within the bounds of the sandbox memory.
69	fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError>;
70
71	/// Zero the designated location in the sandbox memory.
72	///
73	/// Returns `Err` if one of the following conditions occurs:
74	///
75	/// - designated area is not within the bounds of the sandbox memory.
76	fn zero(&mut self, ptr: u32, len: u32) -> Result<(), DispatchError>;
77
78	/// This will reset all compilation artifacts of the currently executing instance.
79	///
80	/// This is used before we call into a new contract to free up some memory. Doing
81	/// so we make sure that we only ever have to hold one compilation cache at a time
82	/// independtently of of our call stack depth.
83	fn reset_interpreter_cache(&mut self);
84
85	/// Read designated chunk from the sandbox memory.
86	///
87	/// Returns `Err` if one of the following conditions occurs:
88	///
89	/// - requested buffer is not within the bounds of the sandbox memory.
90	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	/// Same as `read` but reads into a fixed size buffer.
97	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	/// Read a `u32` from the sandbox memory.
104	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	/// Read a `U256` from the sandbox memory.
110	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	/// Read a `H160` from the sandbox memory.
116	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	/// Read a `H256` from the sandbox memory.
123	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
130/// Allows syscalls access to the PolkaVM instance they are executing in.
131///
132/// In case a contract is executing within PolkaVM its `memory` argument will also implement
133/// this trait. The benchmarking implementation of syscalls will only require `Memory`
134/// to be implemented.
135pub 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// Memory implementation used in benchmarking where guest memory is mapped into the host.
143//
144// Please note that we could optimize the `read_as_*` functions by decoding directly from
145// memory without a copy. However, we don't do that because as it would change the behaviour
146// of those functions: A `read_as` with a `len` larger than the actual type can succeed
147// in the streaming implementation while it could fail with a segfault in the copy implementation.
148#[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/// The data passed through when a contract uses `seal_return`.
225#[derive(Debug)]
226pub struct ReturnData {
227	/// The flags as passed through by the contract. They are still unchecked and
228	/// will later be parsed into a `ReturnFlags` bitflags struct.
229	flags: u32,
230	/// The output buffer passed by the contract as return data.
231	data: Vec<u8>,
232}
233
234/// Enumerates all possible reasons why a trap was generated.
235///
236/// This is either used to supply the caller with more information about why an error
237/// occurred (the SupervisorError variant).
238/// The other case is where the trap does not constitute an error but rather was invoked
239/// as a quick way to terminate the application (all other variants).
240#[derive(Debug)]
241pub enum TrapReason {
242	/// The supervisor trapped the contract because of an error condition occurred during
243	/// execution in privileged code.
244	SupervisorError(DispatchError),
245	/// Signals that trap was generated in response to call `seal_return` host function.
246	Return(ReturnData),
247	/// Signals that a trap was generated in response to a successful call to the
248	/// `seal_terminate` host function.
249	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
264/// Same as [`Runtime::charge_gas`].
265///
266/// We need this access as a macro because sometimes hiding the lifetimes behind
267/// a function won't work out.
268macro_rules! charge_gas {
269	($runtime:expr, $costs:expr) => {{ $runtime.ext.frame_meter_mut().charge_weight_token($costs) }};
270}
271
272/// The kind of call that should be performed.
273enum CallType {
274	/// Execute another instantiated contract
275	Call { value_ptr: u32 },
276	/// Execute another contract code in the context (storage, account ID, value) of the caller
277	/// contract
278	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
290/// This is only appropriate when writing out data of constant size that does not depend on user
291/// input. In this case the costs for this copy was already charged as part of the token at
292/// the beginning of the API entry point.
293fn already_charged(_: u32) -> Option<RuntimeCosts> {
294	None
295}
296
297/// Helper to extract two `u32` values from a given `u64` register.
298fn extract_hi_lo(reg: u64) -> (u32, u32) {
299	((reg >> 32) as u32, reg as u32)
300}
301
302/// Provides storage variants to support standard and Etheruem compatible semantics.
303enum StorageValue {
304	/// Indicates that the storage value should be read from a memory buffer.
305	/// - `ptr`: A pointer to the start of the data in sandbox memory.
306	/// - `len`: The length (in bytes) of the data.
307	Memory { ptr: u32, len: u32 },
308
309	/// Indicates that the storage value is provided inline as a fixed-size (256-bit) value.
310	/// This is used by set_storage_or_clear() to avoid double reads.
311	/// This variant is used to implement Ethereum SSTORE-like semantics.
312	Value(Vec<u8>),
313}
314
315/// Controls the output behavior for storage reads, both when a key is found and when it is not.
316enum StorageReadMode {
317	/// VariableOutput mode: if the key exists, the full stored value is returned
318	/// using the caller‑provided output length.
319	VariableOutput { output_len_ptr: u32 },
320	/// Ethereum compatible(FixedOutput32) mode: always write a 32-byte value into the output
321	/// buffer. If the key is missing, write 32 bytes of zeros.
322	FixedOutput32,
323}
324
325/// Can only be used for one call.
326pub 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	/// Get a mutable reference to the inner `Ext`.
338	pub fn ext(&mut self) -> &mut E {
339		self.ext
340	}
341
342	/// Charge the gas meter with the specified token.
343	///
344	/// Returns `Err(HostError)` if there is not enough gas.
345	fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
346		charge_gas!(self, costs)
347	}
348
349	/// Adjust a previously charged amount down to its actual amount.
350	///
351	/// This is when a maximum a priori amount was charged and then should be partially
352	/// refunded to match the actual amount.
353	fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) {
354		self.ext.frame_meter_mut().adjust_weight(charged, actual_costs);
355	}
356
357	/// Write the given buffer and its length to the designated locations in sandbox memory and
358	/// charge gas according to the token returned by `create_token`.
359	///
360	/// `out_ptr` is the location in sandbox memory where `buf` should be written to.
361	/// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the
362	/// length of the buffer located at `out_ptr`. If that buffer is smaller than the actual
363	/// `buf.len()`, only what fits into that buffer is written to `out_ptr`.
364	/// The actual amount of bytes copied to `out_ptr` is written to `out_len_ptr`.
365	///
366	/// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the
367	/// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying
368	/// output optional. For example to skip copying back the output buffer of an `seal_call`
369	/// when the caller is not interested in the result.
370	///
371	/// `create_token` can optionally instruct this function to charge the gas meter with the token
372	/// it returns. `create_token` receives the variable amount of bytes that are about to be copied
373	/// by this function.
374	///
375	/// In addition to the error conditions of `Memory::write` this functions returns
376	/// `Err` if the size of the buffer located at `out_ptr` is too small to fit `buf`.
377	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	/// Same as `write_sandbox_output` but for static size output.
402	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	/// Computes the given hash function on the supplied input.
423	///
424	/// Reads from the sandboxed input buffer into an intermediate buffer.
425	/// Returns the result directly to the output buffer of the sandboxed memory.
426	///
427	/// It is the callers responsibility to provide an output buffer that
428	/// is large enough to hold the expected amount of bytes returned by the
429	/// chosen hash function.
430	///
431	/// # Note
432	///
433	/// The `input` and `output` buffers may overlap.
434	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		// Copy input into supervisor memory.
447		let input = memory.read(input_ptr, input_len)?;
448		// Compute the hash on the input buffer using the given hash function.
449		let hash = hash_fn(&input);
450		// Write the resulting hash back into the sandboxed output buffer.
451		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			// Don't warm the slot on a failed validation as the storage was not accessed.
498			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		// we do check this in exec.rs but we want to error out early
653		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 the call value is non-zero and state change is not allowed, issue an
680					// error.
681					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			// `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to
708			// a halt anyways without anymore code being executed.
709			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	/// The guest memory address at which the aux data is located.
862	#[cfg(feature = "runtime-benchmarks")]
863	pub fn aux_data_base(&self) -> u32 {
864		self.instance.module().memory_map().aux_data_address()
865	}
866
867	/// Copies `data` to the aux data at address `offset`.
868	///
869	/// It sets `a0` to the beginning of data inside the aux data.
870	/// It sets `a1` to the value passed.
871	///
872	/// Only used in benchmarking so far.
873	#[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}