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	StorageAccessKind,
25	access_list::StorageOp,
26	exec::{CallResources, ExecError, ExecResult, Ext, Key},
27	limits,
28	metering::ChargedAmount,
29	precompiles::{All as AllPrecompiles, Precompiles},
30	primitives::ExecReturnValue,
31	tracing::FrameTraceInfo,
32};
33use alloc::{vec, vec::Vec};
34use codec::Encode;
35use core::{fmt, marker::PhantomData, mem};
36#[cfg(doc)]
37pub use env::SyscallDoc;
38use frame_support::{ensure, weights::Weight};
39use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags};
40use sp_core::{H160, H256, U256};
41use sp_runtime::DispatchError;
42
43/// Extracts the code and data from a given program blob.
44pub fn extract_code_and_data(data: &[u8]) -> Option<(Vec<u8>, Vec<u8>)> {
45	let blob_len = polkavm::ProgramBlob::blob_length(data)?;
46	let blob_len = blob_len.try_into().ok()?;
47	let (code, data) = data.split_at_checked(blob_len)?;
48	Some((code.to_vec(), data.to_vec()))
49}
50
51/// Abstraction over the memory access within syscalls.
52///
53/// The reason for this abstraction is that we run syscalls on the host machine when
54/// benchmarking them. In that case we have direct access to the contract's memory. However, when
55/// running within PolkaVM we need to resort to copying as we can't map the contracts memory into
56/// the host (as of now).
57pub trait Memory<T: Config> {
58	/// Read designated chunk from the sandbox memory into the supplied buffer.
59	///
60	/// Returns `Err` if one of the following conditions occurs:
61	///
62	/// - requested buffer is not within the bounds of the sandbox memory.
63	fn read_into_buf(&mut self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError>;
64
65	/// Write the given buffer to the designated location in the sandbox memory.
66	///
67	/// Returns `Err` if one of the following conditions occurs:
68	///
69	/// - designated area is not within the bounds of the sandbox memory.
70	fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError>;
71
72	/// Zero the designated location in the sandbox memory.
73	///
74	/// Returns `Err` if one of the following conditions occurs:
75	///
76	/// - designated area is not within the bounds of the sandbox memory.
77	fn zero(&mut self, ptr: u32, len: u32) -> Result<(), DispatchError>;
78
79	/// This will reset all compilation artifacts of the currently executing instance.
80	///
81	/// This is used before we call into a new contract to free up some memory. Doing
82	/// so we make sure that we only ever have to hold one compilation cache at a time
83	/// independtently of of our call stack depth.
84	fn reset_interpreter_cache(&mut self);
85
86	/// Read designated chunk from the sandbox memory.
87	///
88	/// Returns `Err` if one of the following conditions occurs:
89	///
90	/// - requested buffer is not within the bounds of the sandbox memory.
91	fn read(&mut self, ptr: u32, len: u32) -> Result<Vec<u8>, DispatchError> {
92		let mut buf = vec![0u8; len as usize];
93		self.read_into_buf(ptr, buf.as_mut_slice())?;
94		Ok(buf)
95	}
96
97	/// Same as `read` but reads into a fixed size buffer.
98	fn read_array<const N: usize>(&mut self, ptr: u32) -> Result<[u8; N], DispatchError> {
99		let mut buf = [0u8; N];
100		self.read_into_buf(ptr, &mut buf)?;
101		Ok(buf)
102	}
103
104	/// Read a `u32` from the sandbox memory.
105	fn read_u32(&mut self, ptr: u32) -> Result<u32, DispatchError> {
106		let buf: [u8; 4] = self.read_array(ptr)?;
107		Ok(u32::from_le_bytes(buf))
108	}
109
110	/// Read a `U256` from the sandbox memory.
111	fn read_u256(&mut self, ptr: u32) -> Result<U256, DispatchError> {
112		let buf: [u8; 32] = self.read_array(ptr)?;
113		Ok(U256::from_little_endian(&buf))
114	}
115
116	/// Read a `H160` from the sandbox memory.
117	fn read_h160(&mut self, ptr: u32) -> Result<H160, DispatchError> {
118		let mut buf = H160::default();
119		self.read_into_buf(ptr, buf.as_bytes_mut())?;
120		Ok(buf)
121	}
122
123	/// Read a `H256` from the sandbox memory.
124	fn read_h256(&mut self, ptr: u32) -> Result<H256, DispatchError> {
125		let mut code_hash = H256::default();
126		self.read_into_buf(ptr, code_hash.as_bytes_mut())?;
127		Ok(code_hash)
128	}
129}
130
131/// Allows syscalls access to the PolkaVM instance they are executing in.
132///
133/// In case a contract is executing within PolkaVM its `memory` argument will also implement
134/// this trait. The benchmarking implementation of syscalls will only require `Memory`
135/// to be implemented.
136pub trait PolkaVmInstance<T: Config>: Memory<T> {
137	fn gas(&self) -> polkavm::Gas;
138	fn set_gas(&mut self, gas: polkavm::Gas);
139	fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64);
140	fn write_output(&mut self, output: u64);
141}
142
143// Memory implementation used in benchmarking where guest memory is mapped into the host.
144//
145// Please note that we could optimize the `read_as_*` functions by decoding directly from
146// memory without a copy. However, we don't do that because as it would change the behaviour
147// of those functions: A `read_as` with a `len` larger than the actual type can succeed
148// in the streaming implementation while it could fail with a segfault in the copy implementation.
149#[cfg(feature = "runtime-benchmarks")]
150impl<T: Config> Memory<T> for [u8] {
151	fn read_into_buf(&mut self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> {
152		let ptr = ptr as usize;
153		let bound_checked =
154			self.get(ptr..ptr + buf.len()).ok_or_else(|| Error::<T>::OutOfBounds)?;
155		buf.copy_from_slice(bound_checked);
156		Ok(())
157	}
158
159	fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> {
160		let ptr = ptr as usize;
161		let bound_checked =
162			self.get_mut(ptr..ptr + buf.len()).ok_or_else(|| Error::<T>::OutOfBounds)?;
163		bound_checked.copy_from_slice(buf);
164		Ok(())
165	}
166
167	fn zero(&mut self, ptr: u32, len: u32) -> Result<(), DispatchError> {
168		<[u8] as Memory<T>>::write(self, ptr, &vec![0; len as usize])
169	}
170
171	fn reset_interpreter_cache(&mut self) {}
172}
173
174impl<T: Config> Memory<T> for polkavm::RawInstance {
175	fn read_into_buf(&mut self, ptr: u32, buf: &mut [u8]) -> Result<(), DispatchError> {
176		self.read_memory_into(ptr, buf)
177			.map(|_| ())
178			.map_err(|_| Error::<T>::OutOfBounds.into())
179	}
180
181	fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), DispatchError> {
182		self.write_memory(ptr, buf).map_err(|_| Error::<T>::OutOfBounds.into())
183	}
184
185	fn zero(&mut self, ptr: u32, len: u32) -> Result<(), DispatchError> {
186		self.zero_memory(ptr, len).map_err(|_| Error::<T>::OutOfBounds.into())
187	}
188
189	fn reset_interpreter_cache(&mut self) {
190		self.reset_interpreter_cache();
191	}
192}
193
194impl<T: Config> PolkaVmInstance<T> for polkavm::RawInstance {
195	fn gas(&self) -> polkavm::Gas {
196		self.gas()
197	}
198
199	fn set_gas(&mut self, gas: polkavm::Gas) {
200		self.set_gas(gas)
201	}
202
203	fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64) {
204		(
205			self.reg(polkavm::Reg::A0),
206			self.reg(polkavm::Reg::A1),
207			self.reg(polkavm::Reg::A2),
208			self.reg(polkavm::Reg::A3),
209			self.reg(polkavm::Reg::A4),
210			self.reg(polkavm::Reg::A5),
211		)
212	}
213
214	fn write_output(&mut self, output: u64) {
215		self.set_reg(polkavm::Reg::A0, output);
216	}
217}
218
219impl From<&ExecReturnValue> for ReturnErrorCode {
220	fn from(from: &ExecReturnValue) -> Self {
221		if from.flags.contains(ReturnFlags::REVERT) { Self::CalleeReverted } else { Self::Success }
222	}
223}
224
225/// The data passed through when a contract uses `seal_return`.
226#[derive(Debug)]
227pub struct ReturnData {
228	/// The flags as passed through by the contract. They are still unchecked and
229	/// will later be parsed into a `ReturnFlags` bitflags struct.
230	flags: u32,
231	/// The output buffer passed by the contract as return data.
232	data: Vec<u8>,
233}
234
235/// Enumerates all possible reasons why a trap was generated.
236///
237/// This is either used to supply the caller with more information about why an error
238/// occurred (the SupervisorError variant).
239/// The other case is where the trap does not constitute an error but rather was invoked
240/// as a quick way to terminate the application (all other variants).
241#[derive(Debug)]
242pub enum TrapReason {
243	/// The supervisor trapped the contract because of an error condition occurred during
244	/// execution in privileged code.
245	SupervisorError(DispatchError),
246	/// Signals that trap was generated in response to call `seal_return` host function.
247	Return(ReturnData),
248	/// Signals that a trap was generated in response to a successful call to the
249	/// `seal_terminate` host function.
250	Termination,
251}
252
253impl<T: Into<DispatchError>> From<T> for TrapReason {
254	fn from(from: T) -> Self {
255		Self::SupervisorError(from.into())
256	}
257}
258
259impl fmt::Display for TrapReason {
260	fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
261		Ok(())
262	}
263}
264
265/// Same as [`Runtime::charge_gas`].
266///
267/// We need this access as a macro because sometimes hiding the lifetimes behind
268/// a function won't work out.
269macro_rules! charge_gas {
270	($runtime:expr, $costs:expr) => {{ $runtime.ext.frame_meter_mut().charge_weight_token($costs) }};
271}
272
273/// The kind of call that should be performed.
274enum CallType {
275	/// Execute another instantiated contract
276	Call { value_ptr: u32 },
277	/// Execute another contract code in the context (storage, account ID, value) of the caller
278	/// contract
279	DelegateCall,
280}
281
282impl CallType {
283	fn cost(&self) -> RuntimeCosts {
284		match self {
285			CallType::Call { .. } => RuntimeCosts::CallBase,
286			CallType::DelegateCall => RuntimeCosts::DelegateCallBase,
287		}
288	}
289}
290
291/// This is only appropriate when writing out data of constant size that does not depend on user
292/// input. In this case the costs for this copy was already charged as part of the token at
293/// the beginning of the API entry point.
294fn already_charged(_: u32) -> Option<RuntimeCosts> {
295	None
296}
297
298/// Helper to extract two `u32` values from a given `u64` register.
299fn extract_hi_lo(reg: u64) -> (u32, u32) {
300	((reg >> 32) as u32, reg as u32)
301}
302
303/// Provides storage variants to support standard and Etheruem compatible semantics.
304enum StorageValue {
305	/// Indicates that the storage value should be read from a memory buffer.
306	/// - `ptr`: A pointer to the start of the data in sandbox memory.
307	/// - `len`: The length (in bytes) of the data.
308	Memory { ptr: u32, len: u32 },
309
310	/// Indicates that the storage value is provided inline as a fixed-size (256-bit) value.
311	/// This is used by set_storage_or_clear() to avoid double reads.
312	/// This variant is used to implement Ethereum SSTORE-like semantics.
313	Value(Vec<u8>),
314}
315
316/// Controls the output behavior for storage reads, both when a key is found and when it is not.
317enum StorageReadMode {
318	/// VariableOutput mode: if the key exists, the full stored value is returned
319	/// using the caller‑provided output length.
320	VariableOutput { output_len_ptr: u32 },
321	/// Ethereum compatible(FixedOutput32) mode: always write a 32-byte value into the output
322	/// buffer. If the key is missing, write 32 bytes of zeros.
323	FixedOutput32,
324}
325
326/// Can only be used for one call.
327pub struct Runtime<'a, E: Ext, M: ?Sized> {
328	ext: &'a mut E,
329	input_data: Option<Vec<u8>>,
330	_phantom_data: PhantomData<M>,
331}
332
333impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
334	pub fn new(ext: &'a mut E, input_data: Vec<u8>) -> Self {
335		Self { ext, input_data: Some(input_data), _phantom_data: Default::default() }
336	}
337
338	/// Get a mutable reference to the inner `Ext`.
339	pub fn ext(&mut self) -> &mut E {
340		self.ext
341	}
342
343	/// Charge the gas meter with the specified token.
344	///
345	/// Returns `Err(HostError)` if there is not enough gas.
346	fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
347		charge_gas!(self, costs)
348	}
349
350	/// Adjust a previously charged amount down to its actual amount.
351	///
352	/// This is when a maximum a priori amount was charged and then should be partially
353	/// refunded to match the actual amount.
354	fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) {
355		self.ext.frame_meter_mut().adjust_weight(charged, actual_costs);
356	}
357
358	/// Write the given buffer and its length to the designated locations in sandbox memory and
359	/// charge gas according to the token returned by `create_token`.
360	///
361	/// `out_ptr` is the location in sandbox memory where `buf` should be written to.
362	/// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the
363	/// length of the buffer located at `out_ptr`. If that buffer is smaller than the actual
364	/// `buf.len()`, only what fits into that buffer is written to `out_ptr`.
365	/// The actual amount of bytes copied to `out_ptr` is written to `out_len_ptr`.
366	///
367	/// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the
368	/// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying
369	/// output optional. For example to skip copying back the output buffer of an `seal_call`
370	/// when the caller is not interested in the result.
371	///
372	/// `create_token` can optionally instruct this function to charge the gas meter with the token
373	/// it returns. `create_token` receives the variable amount of bytes that are about to be copied
374	/// by this function.
375	///
376	/// In addition to the error conditions of `Memory::write` this functions returns
377	/// `Err` if the size of the buffer located at `out_ptr` is too small to fit `buf`.
378	pub fn write_sandbox_output(
379		&mut self,
380		memory: &mut M,
381		out_ptr: u32,
382		out_len_ptr: u32,
383		buf: &[u8],
384		allow_skip: bool,
385		create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
386	) -> Result<(), DispatchError> {
387		if allow_skip && out_ptr == SENTINEL {
388			return Ok(());
389		}
390
391		let len = memory.read_u32(out_len_ptr)?;
392		let buf_len = len.min(buf.len() as u32);
393
394		if let Some(costs) = create_token(buf_len) {
395			self.charge_gas(costs)?;
396		}
397
398		memory.write(out_ptr, &buf[..buf_len as usize])?;
399		memory.write(out_len_ptr, &buf_len.encode())
400	}
401
402	/// Same as `write_sandbox_output` but for static size output.
403	pub fn write_fixed_sandbox_output(
404		&mut self,
405		memory: &mut M,
406		out_ptr: u32,
407		buf: &[u8],
408		allow_skip: bool,
409		create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
410	) -> Result<(), DispatchError> {
411		if buf.is_empty() || (allow_skip && out_ptr == SENTINEL) {
412			return Ok(());
413		}
414
415		let buf_len = buf.len() as u32;
416		if let Some(costs) = create_token(buf_len) {
417			self.charge_gas(costs)?;
418		}
419
420		memory.write(out_ptr, buf)
421	}
422
423	/// Computes the given hash function on the supplied input.
424	///
425	/// Reads from the sandboxed input buffer into an intermediate buffer.
426	/// Returns the result directly to the output buffer of the sandboxed memory.
427	///
428	/// It is the callers responsibility to provide an output buffer that
429	/// is large enough to hold the expected amount of bytes returned by the
430	/// chosen hash function.
431	///
432	/// # Note
433	///
434	/// The `input` and `output` buffers may overlap.
435	fn compute_hash_on_intermediate_buffer<F, R>(
436		&self,
437		memory: &mut M,
438		hash_fn: F,
439		input_ptr: u32,
440		input_len: u32,
441		output_ptr: u32,
442	) -> Result<(), DispatchError>
443	where
444		F: FnOnce(&[u8]) -> R,
445		R: AsRef<[u8]>,
446	{
447		// Copy input into supervisor memory.
448		let input = memory.read(input_ptr, input_len)?;
449		// Compute the hash on the input buffer using the given hash function.
450		let hash = hash_fn(&input);
451		// Write the resulting hash back into the sandboxed output buffer.
452		memory.write(output_ptr, hash.as_ref())?;
453		Ok(())
454	}
455
456	fn decode_key(&self, memory: &mut M, key_ptr: u32, key_len: u32) -> Result<Key, TrapReason> {
457		let res = match key_len {
458			SENTINEL => {
459				let mut buffer = [0u8; 32];
460				memory.read_into_buf(key_ptr, buffer.as_mut())?;
461				Ok(Key::from_fixed(buffer))
462			},
463			len => {
464				ensure!(len <= limits::STORAGE_KEY_BYTES, Error::<E::T>::DecodingFailed);
465				let key = memory.read(key_ptr, len)?;
466				Key::try_from_var(key)
467			},
468		};
469
470		res.map_err(|_| Error::<E::T>::DecodingFailed.into())
471	}
472
473	fn is_transient(flags: u32) -> Result<bool, TrapReason> {
474		StorageFlags::from_bits(flags)
475			.ok_or_else(|| <Error<E::T>>::InvalidStorageFlags.into())
476			.map(|flags| flags.contains(StorageFlags::TRANSIENT))
477	}
478
479	fn set_storage(
480		&mut self,
481		memory: &mut M,
482		flags: u32,
483		key_ptr: u32,
484		key_len: u32,
485		value: StorageValue,
486	) -> Result<u32, TrapReason> {
487		let transient = Self::is_transient(flags)?;
488
489		let value_len = match &value {
490			StorageValue::Memory { ptr: _, len } => *len,
491			StorageValue::Value(data) => data.len() as u32,
492		};
493
494		let max_size = limits::STORAGE_BYTES;
495		let key = self.decode_key(memory, key_ptr, key_len)?;
496
497		if value_len > max_size {
498			// Don't warm the slot on a failed validation as the storage was not accessed.
499			let access_kind =
500				StorageAccessKind::new(transient, || self.ext.peek_storage_access(&key));
501			self.charge_gas(RuntimeCosts::SetStorage {
502				new_bytes: value_len,
503				old_bytes: max_size,
504				kind: access_kind,
505			})?;
506			return Err(Error::<E::T>::ValueTooLarge.into());
507		}
508
509		let access_kind = StorageAccessKind::new(transient, || {
510			self.ext.touch_storage_access(&key, StorageOp::Write)
511		});
512		let charged = self.charge_gas(RuntimeCosts::SetStorage {
513			new_bytes: value_len,
514			old_bytes: max_size,
515			kind: access_kind,
516		})?;
517		let value = match value {
518			StorageValue::Memory { ptr, len } => Some(memory.read(ptr, len)?),
519			StorageValue::Value(data) => Some(data),
520		};
521
522		let write_outcome = if transient {
523			self.ext.set_transient_storage(&key, value, false)?
524		} else {
525			self.ext.set_storage(&key, value, false)?
526		};
527
528		self.adjust_gas(
529			charged,
530			RuntimeCosts::SetStorage {
531				new_bytes: value_len,
532				old_bytes: write_outcome.old_len(),
533				kind: access_kind,
534			},
535		);
536		Ok(write_outcome.old_len_with_sentinel())
537	}
538
539	fn clear_storage(
540		&mut self,
541		memory: &mut M,
542		flags: u32,
543		key_ptr: u32,
544		key_len: u32,
545	) -> Result<u32, TrapReason> {
546		let transient = Self::is_transient(flags)?;
547		let key = self.decode_key(memory, key_ptr, key_len)?;
548		let access_kind = StorageAccessKind::new(transient, || {
549			self.ext.touch_storage_access(&key, StorageOp::Write)
550		});
551		let charged = self.charge_gas(RuntimeCosts::ClearStorage {
552			len: limits::STORAGE_BYTES,
553			kind: access_kind,
554		})?;
555		let outcome = if transient {
556			self.ext.set_transient_storage(&key, None, false)?
557		} else {
558			self.ext.set_storage(&key, None, false)?
559		};
560		self.adjust_gas(
561			charged,
562			RuntimeCosts::ClearStorage { len: outcome.old_len(), kind: access_kind },
563		);
564		Ok(outcome.old_len_with_sentinel())
565	}
566
567	fn get_storage(
568		&mut self,
569		memory: &mut M,
570		flags: u32,
571		key_ptr: u32,
572		key_len: u32,
573		out_ptr: u32,
574		read_mode: StorageReadMode,
575	) -> Result<ReturnErrorCode, TrapReason> {
576		let transient = Self::is_transient(flags)?;
577		let key = self.decode_key(memory, key_ptr, key_len)?;
578		let access_kind = StorageAccessKind::new(transient, || {
579			self.ext.touch_storage_access(&key, StorageOp::Read)
580		});
581		let charged = self.charge_gas(RuntimeCosts::GetStorage {
582			len: limits::STORAGE_BYTES,
583			kind: access_kind,
584		})?;
585		let outcome = if transient {
586			self.ext.get_transient_storage(&key)
587		} else {
588			self.ext.get_storage(&key)
589		};
590		let len = outcome.as_ref().map(|v| v.len() as u32).unwrap_or(0);
591		self.adjust_gas(charged, RuntimeCosts::GetStorage { len, kind: access_kind });
592
593		if let Some(value) = outcome {
594			match read_mode {
595				StorageReadMode::FixedOutput32 => {
596					let mut fixed_output = [0u8; 32];
597					let len = value.len().min(fixed_output.len());
598					fixed_output[..len].copy_from_slice(&value[..len]);
599
600					self.write_fixed_sandbox_output(
601						memory,
602						out_ptr,
603						&fixed_output,
604						false,
605						already_charged,
606					)?;
607					Ok(ReturnErrorCode::Success)
608				},
609				StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr } => {
610					self.write_sandbox_output(
611						memory,
612						out_ptr,
613						out_len_ptr,
614						&value,
615						false,
616						already_charged,
617					)?;
618					Ok(ReturnErrorCode::Success)
619				},
620			}
621		} else {
622			match read_mode {
623				StorageReadMode::FixedOutput32 => {
624					self.write_fixed_sandbox_output(
625						memory,
626						out_ptr,
627						&[0u8; 32],
628						false,
629						already_charged,
630					)?;
631					Ok(ReturnErrorCode::Success)
632				},
633				StorageReadMode::VariableOutput { .. } => Ok(ReturnErrorCode::KeyNotFound),
634			}
635		}
636	}
637
638	fn call(
639		&mut self,
640		memory: &mut M,
641		flags: CallFlags,
642		call_type: CallType,
643		callee_ptr: u32,
644		resources: &CallResources<E::T>,
645		input_data_ptr: u32,
646		input_data_len: u32,
647		output_ptr: u32,
648		output_len_ptr: u32,
649	) -> Result<ReturnErrorCode, TrapReason> {
650		let callee = memory.read_h160(callee_ptr)?;
651		let precompile = <AllPrecompiles<E::T>>::get::<E>(&callee.as_fixed_bytes());
652		match &precompile {
653			Some(precompile) if precompile.has_contract_info() => {
654				self.charge_gas(RuntimeCosts::PrecompileWithInfoBase)?
655			},
656			Some(_) => self.charge_gas(RuntimeCosts::PrecompileBase)?,
657			None => self.charge_gas(call_type.cost())?,
658		};
659
660		// we do check this in exec.rs but we want to error out early
661		if input_data_len > limits::CALLDATA_BYTES {
662			Err(<Error<E::T>>::CallDataTooLarge)?;
663		}
664
665		let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
666			let input = self.input_data.as_ref().ok_or(Error::<E::T>::InputForwarded)?;
667			charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?;
668			input.clone()
669		} else if flags.contains(CallFlags::FORWARD_INPUT) {
670			self.input_data.take().ok_or(Error::<E::T>::InputForwarded)?
671		} else {
672			if precompile.is_some() {
673				self.charge_gas(RuntimeCosts::PrecompileDecode(input_data_len))?;
674			} else {
675				self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?;
676			}
677			memory.read(input_data_ptr, input_data_len)?
678		};
679
680		memory.reset_interpreter_cache();
681
682		let call_outcome = match call_type {
683			CallType::Call { value_ptr } => {
684				let read_only = flags.contains(CallFlags::READ_ONLY);
685				let value = memory.read_u256(value_ptr)?;
686				if value > 0u32.into() {
687					// If the call value is non-zero and state change is not allowed, issue an
688					// error.
689					if read_only || self.ext.is_read_only() {
690						return Err(Error::<E::T>::StateChangeDenied.into());
691					}
692
693					self.charge_gas(RuntimeCosts::CallTransferSurcharge {
694						dust_transfer: Pallet::<E::T>::has_dust(value),
695					})?;
696				}
697
698				let reentrancy = if flags.contains(CallFlags::ALLOW_REENTRY) {
699					ReentrancyProtection::AllowReentry
700				} else {
701					ReentrancyProtection::Strict
702				};
703
704				self.ext.call(resources, &callee, value, input_data, reentrancy, read_only)
705			},
706			CallType::DelegateCall => {
707				if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) {
708					return Err(Error::<E::T>::InvalidCallFlags.into());
709				}
710				self.ext.delegate_call(resources, callee, input_data)
711			},
712		};
713
714		match call_outcome {
715			// `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to
716			// a halt anyways without anymore code being executed.
717			Ok(_) if flags.contains(CallFlags::TAIL_CALL) => {
718				let output = mem::take(self.ext.last_frame_output_mut());
719				return Err(TrapReason::Return(ReturnData {
720					flags: output.flags.bits(),
721					data: output.data,
722				}));
723			},
724			Ok(_) => {
725				let output = mem::take(self.ext.last_frame_output_mut());
726				let write_result = self.write_sandbox_output(
727					memory,
728					output_ptr,
729					output_len_ptr,
730					&output.data,
731					true,
732					|len| Some(RuntimeCosts::CopyToContract(len)),
733				);
734				*self.ext.last_frame_output_mut() = output;
735				write_result?;
736				Ok(self.ext.last_frame_output().into())
737			},
738			Err(err) => {
739				let error_code = super::exec_error_into_return_code::<E>(err)?;
740				memory.write(output_len_ptr, &0u32.to_le_bytes())?;
741				Ok(error_code)
742			},
743		}
744	}
745
746	fn instantiate(
747		&mut self,
748		memory: &mut M,
749		code_hash_ptr: u32,
750		weight: Weight,
751		deposit_ptr: u32,
752		value_ptr: u32,
753		input_data_ptr: u32,
754		input_data_len: u32,
755		address_ptr: u32,
756		output_ptr: u32,
757		output_len_ptr: u32,
758		salt_ptr: u32,
759	) -> Result<ReturnErrorCode, TrapReason> {
760		let value = match memory.read_u256(value_ptr) {
761			Ok(value) => {
762				self.charge_gas(RuntimeCosts::Instantiate {
763					input_data_len,
764					balance_transfer: Pallet::<E::T>::has_balance(value),
765					dust_transfer: Pallet::<E::T>::has_dust(value),
766				})?;
767				value
768			},
769			Err(err) => {
770				self.charge_gas(RuntimeCosts::Instantiate {
771					input_data_len: 0,
772					balance_transfer: false,
773					dust_transfer: false,
774				})?;
775				return Err(err.into());
776			},
777		};
778		let deposit_limit: U256 = memory.read_u256(deposit_ptr)?;
779		let code_hash = memory.read_h256(code_hash_ptr)?;
780		if input_data_len > limits::CALLDATA_BYTES {
781			Err(<Error<E::T>>::CallDataTooLarge)?;
782		}
783		let input_data = memory.read(input_data_ptr, input_data_len)?;
784		let salt = if salt_ptr == SENTINEL {
785			None
786		} else {
787			let salt: [u8; 32] = memory.read_array(salt_ptr)?;
788			Some(salt)
789		};
790
791		memory.reset_interpreter_cache();
792
793		match self.ext.instantiate(
794			&CallResources::from_weight_and_deposit(weight, deposit_limit),
795			Code::Existing(code_hash),
796			value,
797			input_data,
798			salt.as_ref(),
799		) {
800			Ok(address) => {
801				if !self.ext.last_frame_output().flags.contains(ReturnFlags::REVERT) {
802					self.write_fixed_sandbox_output(
803						memory,
804						address_ptr,
805						&address.as_bytes(),
806						true,
807						already_charged,
808					)?;
809				}
810				let output = mem::take(self.ext.last_frame_output_mut());
811				let write_result = self.write_sandbox_output(
812					memory,
813					output_ptr,
814					output_len_ptr,
815					&output.data,
816					true,
817					|len| Some(RuntimeCosts::CopyToContract(len)),
818				);
819				*self.ext.last_frame_output_mut() = output;
820				write_result?;
821				Ok(self.ext.last_frame_output().into())
822			},
823			Err(err) => Ok(super::exec_error_into_return_code::<E>(err)?),
824		}
825	}
826}
827
828impl<'a, E: Ext, M: ?Sized + Memory<E::T>> FrameTraceInfo for Runtime<'a, E, M> {
829	fn gas_left(&self) -> u64 {
830		let meter = self.ext.frame_meter();
831		meter.eth_gas_left().unwrap_or_default().try_into().unwrap_or_default()
832	}
833	fn weight_consumed(&self) -> Weight {
834		let meter = self.ext.frame_meter();
835		meter.weight_consumed()
836	}
837
838	fn last_frame_output(&self) -> crate::evm::Bytes {
839		crate::evm::Bytes(self.ext.last_frame_output().data.clone())
840	}
841}
842
843pub struct PreparedCall<'a, E: Ext> {
844	module: polkavm::Module,
845	instance: polkavm::RawInstance,
846	runtime: Runtime<'a, E, polkavm::RawInstance>,
847}
848
849impl<'a, E: Ext> PreparedCall<'a, E> {
850	pub fn call(mut self) -> ExecResult {
851		let exec_result = loop {
852			let interrupt = self.instance.run();
853			if let Some(exec_result) =
854				self.runtime.handle_interrupt(interrupt, &self.module, &mut self.instance)
855			{
856				break exec_result;
857			}
858		};
859		crate::tracing::if_tracing(|tracer| {
860			tracer.enter_ecall(crate::tracing::PVM_FUEL_NAME, &[], &self.runtime)
861		});
862		let sync_result =
863			self.runtime.ext().frame_meter_mut().sync_from_executor(self.instance.gas());
864		crate::tracing::if_tracing(|tracer| tracer.exit_step(&self.runtime, None));
865		sync_result?;
866		exec_result
867	}
868
869	/// The guest memory address at which the aux data is located.
870	#[cfg(feature = "runtime-benchmarks")]
871	pub fn aux_data_base(&self) -> u32 {
872		self.instance.module().memory_map().aux_data_address()
873	}
874
875	/// Copies `data` to the aux data at address `offset`.
876	///
877	/// It sets `a0` to the beginning of data inside the aux data.
878	/// It sets `a1` to the value passed.
879	///
880	/// Only used in benchmarking so far.
881	#[cfg(feature = "runtime-benchmarks")]
882	pub fn setup_aux_data(
883		&mut self,
884		data: &[u8],
885		offset: u32,
886		a1: u64,
887	) -> frame_support::dispatch::DispatchResult {
888		let a0 = self.aux_data_base().saturating_add(offset);
889		self.instance.write_memory(a0, data).map_err(|err| {
890			log::debug!(target: LOG_TARGET, "failed to write aux data: {err:?}");
891			Error::<E::T>::CodeRejected
892		})?;
893		self.instance.set_reg(polkavm::Reg::A0, a0.into());
894		self.instance.set_reg(polkavm::Reg::A1, a1);
895		Ok(())
896	}
897}