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