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
22#[cfg(doc)]
23pub use env::SyscallDoc;
24
25use crate::{
26	evm::runtime::GAS_PRICE,
27	exec::{ExecError, ExecResult, Ext, Key},
28	gas::ChargedAmount,
29	limits,
30	precompiles::{All as AllPrecompiles, Precompiles},
31	primitives::ExecReturnValue,
32	BalanceOf, Code, Config, Error, Pallet, RuntimeCosts, LOG_TARGET, SENTINEL,
33};
34use alloc::{vec, vec::Vec};
35use codec::Encode;
36use core::{fmt, marker::PhantomData, mem};
37use frame_support::{ensure, weights::Weight};
38use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags, StorageFlags};
39use sp_core::{H160, H256, U256};
40use sp_runtime::{DispatchError, RuntimeDebug};
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(&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(&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>(&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(&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(&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(&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(&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(&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(&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) {
221			Self::CalleeReverted
222		} else {
223			Self::Success
224		}
225	}
226}
227
228/// The data passed through when a contract uses `seal_return`.
229#[derive(RuntimeDebug)]
230pub struct ReturnData {
231	/// The flags as passed through by the contract. They are still unchecked and
232	/// will later be parsed into a `ReturnFlags` bitflags struct.
233	flags: u32,
234	/// The output buffer passed by the contract as return data.
235	data: Vec<u8>,
236}
237
238/// Enumerates all possible reasons why a trap was generated.
239///
240/// This is either used to supply the caller with more information about why an error
241/// occurred (the SupervisorError variant).
242/// The other case is where the trap does not constitute an error but rather was invoked
243/// as a quick way to terminate the application (all other variants).
244#[derive(RuntimeDebug)]
245pub enum TrapReason {
246	/// The supervisor trapped the contract because of an error condition occurred during
247	/// execution in privileged code.
248	SupervisorError(DispatchError),
249	/// Signals that trap was generated in response to call `seal_return` host function.
250	Return(ReturnData),
251	/// Signals that a trap was generated in response to a successful call to the
252	/// `seal_terminate` host function.
253	Termination,
254}
255
256impl<T: Into<DispatchError>> From<T> for TrapReason {
257	fn from(from: T) -> Self {
258		Self::SupervisorError(from.into())
259	}
260}
261
262impl fmt::Display for TrapReason {
263	fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
264		Ok(())
265	}
266}
267
268/// Same as [`Runtime::charge_gas`].
269///
270/// We need this access as a macro because sometimes hiding the lifetimes behind
271/// a function won't work out.
272macro_rules! charge_gas {
273	($runtime:expr, $costs:expr) => {{
274		$runtime.ext.gas_meter_mut().charge($costs)
275	}};
276}
277
278/// The kind of call that should be performed.
279enum CallType {
280	/// Execute another instantiated contract
281	Call { value_ptr: u32 },
282	/// Execute another contract code in the context (storage, account ID, value) of the caller
283	/// contract
284	DelegateCall,
285}
286
287impl CallType {
288	fn cost(&self) -> RuntimeCosts {
289		match self {
290			CallType::Call { .. } => RuntimeCosts::CallBase,
291			CallType::DelegateCall => RuntimeCosts::DelegateCallBase,
292		}
293	}
294}
295
296/// This is only appropriate when writing out data of constant size that does not depend on user
297/// input. In this case the costs for this copy was already charged as part of the token at
298/// the beginning of the API entry point.
299fn already_charged(_: u32) -> Option<RuntimeCosts> {
300	None
301}
302
303/// Helper to extract two `u32` values from a given `u64` register.
304fn extract_hi_lo(reg: u64) -> (u32, u32) {
305	((reg >> 32) as u32, reg as u32)
306}
307
308/// Provides storage variants to support standard and Etheruem compatible semantics.
309enum StorageValue {
310	/// Indicates that the storage value should be read from a memory buffer.
311	/// - `ptr`: A pointer to the start of the data in sandbox memory.
312	/// - `len`: The length (in bytes) of the data.
313	Memory { ptr: u32, len: u32 },
314
315	/// Indicates that the storage value is provided inline as a fixed-size (256-bit) value.
316	/// This is used by set_storage_or_clear() to avoid double reads.
317	/// This variant is used to implement Ethereum SSTORE-like semantics.
318	Value(Vec<u8>),
319}
320
321/// Controls the output behavior for storage reads, both when a key is found and when it is not.
322enum StorageReadMode {
323	/// VariableOutput mode: if the key exists, the full stored value is returned
324	/// using the caller‑provided output length.
325	VariableOutput { output_len_ptr: u32 },
326	/// Ethereum compatible(FixedOutput32) mode: always write a 32-byte value into the output
327	/// buffer. If the key is missing, write 32 bytes of zeros.
328	FixedOutput32,
329}
330
331/// Can only be used for one call.
332pub struct Runtime<'a, E: Ext, M: ?Sized> {
333	ext: &'a mut E,
334	input_data: Option<Vec<u8>>,
335	_phantom_data: PhantomData<M>,
336}
337
338impl<'a, E: Ext, M: ?Sized + Memory<E::T>> Runtime<'a, E, M> {
339	pub fn new(ext: &'a mut E, input_data: Vec<u8>) -> Self {
340		Self { ext, input_data: Some(input_data), _phantom_data: Default::default() }
341	}
342
343	/// Get a mutable reference to the inner `Ext`.
344	pub fn ext(&mut self) -> &mut E {
345		self.ext
346	}
347
348	/// Charge the gas meter with the specified token.
349	///
350	/// Returns `Err(HostError)` if there is not enough gas.
351	fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<ChargedAmount, DispatchError> {
352		charge_gas!(self, costs)
353	}
354
355	/// Adjust a previously charged amount down to its actual amount.
356	///
357	/// This is when a maximum a priori amount was charged and then should be partially
358	/// refunded to match the actual amount.
359	fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) {
360		self.ext.gas_meter_mut().adjust_gas(charged, actual_costs);
361	}
362
363	/// Write the given buffer and its length to the designated locations in sandbox memory and
364	/// charge gas according to the token returned by `create_token`.
365	///
366	/// `out_ptr` is the location in sandbox memory where `buf` should be written to.
367	/// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the
368	/// length of the buffer located at `out_ptr`. If that buffer is smaller than the actual
369	/// `buf.len()`, only what fits into that buffer is written to `out_ptr`.
370	/// The actual amount of bytes copied to `out_ptr` is written to `out_len_ptr`.
371	///
372	/// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the
373	/// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying
374	/// output optional. For example to skip copying back the output buffer of an `seal_call`
375	/// when the caller is not interested in the result.
376	///
377	/// `create_token` can optionally instruct this function to charge the gas meter with the token
378	/// it returns. `create_token` receives the variable amount of bytes that are about to be copied
379	/// by this function.
380	///
381	/// In addition to the error conditions of `Memory::write` this functions returns
382	/// `Err` if the size of the buffer located at `out_ptr` is too small to fit `buf`.
383	pub fn write_sandbox_output(
384		&mut self,
385		memory: &mut M,
386		out_ptr: u32,
387		out_len_ptr: u32,
388		buf: &[u8],
389		allow_skip: bool,
390		create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
391	) -> Result<(), DispatchError> {
392		if allow_skip && out_ptr == SENTINEL {
393			return Ok(());
394		}
395
396		let len = memory.read_u32(out_len_ptr)?;
397		let buf_len = len.min(buf.len() as u32);
398
399		if let Some(costs) = create_token(buf_len) {
400			self.charge_gas(costs)?;
401		}
402
403		memory.write(out_ptr, &buf[..buf_len as usize])?;
404		memory.write(out_len_ptr, &buf_len.encode())
405	}
406
407	/// Same as `write_sandbox_output` but for static size output.
408	pub fn write_fixed_sandbox_output(
409		&mut self,
410		memory: &mut M,
411		out_ptr: u32,
412		buf: &[u8],
413		allow_skip: bool,
414		create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
415	) -> Result<(), DispatchError> {
416		if buf.is_empty() || (allow_skip && out_ptr == SENTINEL) {
417			return Ok(());
418		}
419
420		let buf_len = buf.len() as u32;
421		if let Some(costs) = create_token(buf_len) {
422			self.charge_gas(costs)?;
423		}
424
425		memory.write(out_ptr, buf)
426	}
427
428	/// Computes the given hash function on the supplied input.
429	///
430	/// Reads from the sandboxed input buffer into an intermediate buffer.
431	/// Returns the result directly to the output buffer of the sandboxed memory.
432	///
433	/// It is the callers responsibility to provide an output buffer that
434	/// is large enough to hold the expected amount of bytes returned by the
435	/// chosen hash function.
436	///
437	/// # Note
438	///
439	/// The `input` and `output` buffers may overlap.
440	fn compute_hash_on_intermediate_buffer<F, R>(
441		&self,
442		memory: &mut M,
443		hash_fn: F,
444		input_ptr: u32,
445		input_len: u32,
446		output_ptr: u32,
447	) -> Result<(), DispatchError>
448	where
449		F: FnOnce(&[u8]) -> R,
450		R: AsRef<[u8]>,
451	{
452		// Copy input into supervisor memory.
453		let input = memory.read(input_ptr, input_len)?;
454		// Compute the hash on the input buffer using the given hash function.
455		let hash = hash_fn(&input);
456		// Write the resulting hash back into the sandboxed output buffer.
457		memory.write(output_ptr, hash.as_ref())?;
458		Ok(())
459	}
460
461	fn decode_key(&self, memory: &M, key_ptr: u32, key_len: u32) -> Result<Key, TrapReason> {
462		let res = match key_len {
463			SENTINEL => {
464				let mut buffer = [0u8; 32];
465				memory.read_into_buf(key_ptr, buffer.as_mut())?;
466				Ok(Key::from_fixed(buffer))
467			},
468			len => {
469				ensure!(len <= limits::STORAGE_KEY_BYTES, Error::<E::T>::DecodingFailed);
470				let key = memory.read(key_ptr, len)?;
471				Key::try_from_var(key)
472			},
473		};
474
475		res.map_err(|_| Error::<E::T>::DecodingFailed.into())
476	}
477
478	fn is_transient(flags: u32) -> Result<bool, TrapReason> {
479		StorageFlags::from_bits(flags)
480			.ok_or_else(|| <Error<E::T>>::InvalidStorageFlags.into())
481			.map(|flags| flags.contains(StorageFlags::TRANSIENT))
482	}
483
484	fn set_storage(
485		&mut self,
486		memory: &M,
487		flags: u32,
488		key_ptr: u32,
489		key_len: u32,
490		value: StorageValue,
491	) -> Result<u32, TrapReason> {
492		let transient = Self::is_transient(flags)?;
493		let costs = |new_bytes: u32, old_bytes: u32| {
494			if transient {
495				RuntimeCosts::SetTransientStorage { new_bytes, old_bytes }
496			} else {
497				RuntimeCosts::SetStorage { new_bytes, old_bytes }
498			}
499		};
500
501		let value_len = match &value {
502			StorageValue::Memory { ptr: _, len } => *len,
503			StorageValue::Value(data) => data.len() as u32,
504		};
505
506		let max_size = self.ext.max_value_size();
507		let charged = self.charge_gas(costs(value_len, self.ext.max_value_size()))?;
508		if value_len > max_size {
509			return Err(Error::<E::T>::ValueTooLarge.into());
510		}
511
512		let key = self.decode_key(memory, key_ptr, key_len)?;
513
514		let value = match value {
515			StorageValue::Memory { ptr, len } => Some(memory.read(ptr, len)?),
516			StorageValue::Value(data) => Some(data),
517		};
518
519		let write_outcome = if transient {
520			self.ext.set_transient_storage(&key, value, false)?
521		} else {
522			self.ext.set_storage(&key, value, false)?
523		};
524
525		self.adjust_gas(charged, costs(value_len, write_outcome.old_len()));
526		Ok(write_outcome.old_len_with_sentinel())
527	}
528
529	fn clear_storage(
530		&mut self,
531		memory: &M,
532		flags: u32,
533		key_ptr: u32,
534		key_len: u32,
535	) -> Result<u32, TrapReason> {
536		let transient = Self::is_transient(flags)?;
537		let costs = |len| {
538			if transient {
539				RuntimeCosts::ClearTransientStorage(len)
540			} else {
541				RuntimeCosts::ClearStorage(len)
542			}
543		};
544		let charged = self.charge_gas(costs(self.ext.max_value_size()))?;
545		let key = self.decode_key(memory, key_ptr, key_len)?;
546		let outcome = if transient {
547			self.ext.set_transient_storage(&key, None, false)?
548		} else {
549			self.ext.set_storage(&key, None, false)?
550		};
551		self.adjust_gas(charged, costs(outcome.old_len()));
552		Ok(outcome.old_len_with_sentinel())
553	}
554
555	fn get_storage(
556		&mut self,
557		memory: &mut M,
558		flags: u32,
559		key_ptr: u32,
560		key_len: u32,
561		out_ptr: u32,
562		read_mode: StorageReadMode,
563	) -> Result<ReturnErrorCode, TrapReason> {
564		let transient = Self::is_transient(flags)?;
565		let costs = |len| {
566			if transient {
567				RuntimeCosts::GetTransientStorage(len)
568			} else {
569				RuntimeCosts::GetStorage(len)
570			}
571		};
572		let charged = self.charge_gas(costs(self.ext.max_value_size()))?;
573		let key = self.decode_key(memory, key_ptr, key_len)?;
574		let outcome = if transient {
575			self.ext.get_transient_storage(&key)
576		} else {
577			self.ext.get_storage(&key)
578		};
579
580		if let Some(value) = outcome {
581			self.adjust_gas(charged, costs(value.len() as u32));
582
583			match read_mode {
584				StorageReadMode::FixedOutput32 => {
585					let mut fixed_output = [0u8; 32];
586					let len = value.len().min(fixed_output.len());
587					fixed_output[..len].copy_from_slice(&value[..len]);
588
589					self.write_fixed_sandbox_output(
590						memory,
591						out_ptr,
592						&fixed_output,
593						false,
594						already_charged,
595					)?;
596					Ok(ReturnErrorCode::Success)
597				},
598				StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr } => {
599					self.write_sandbox_output(
600						memory,
601						out_ptr,
602						out_len_ptr,
603						&value,
604						false,
605						already_charged,
606					)?;
607					Ok(ReturnErrorCode::Success)
608				},
609			}
610		} else {
611			self.adjust_gas(charged, costs(0));
612
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 contains_storage(
630		&mut self,
631		memory: &M,
632		flags: u32,
633		key_ptr: u32,
634		key_len: u32,
635	) -> Result<u32, TrapReason> {
636		let transient = Self::is_transient(flags)?;
637		let costs = |len| {
638			if transient {
639				RuntimeCosts::ContainsTransientStorage(len)
640			} else {
641				RuntimeCosts::ContainsStorage(len)
642			}
643		};
644		let charged = self.charge_gas(costs(self.ext.max_value_size()))?;
645		let key = self.decode_key(memory, key_ptr, key_len)?;
646		let outcome = if transient {
647			self.ext.get_transient_storage_size(&key)
648		} else {
649			self.ext.get_storage_size(&key)
650		};
651		self.adjust_gas(charged, costs(outcome.unwrap_or(0)));
652		Ok(outcome.unwrap_or(SENTINEL))
653	}
654
655	fn take_storage(
656		&mut self,
657		memory: &mut M,
658		flags: u32,
659		key_ptr: u32,
660		key_len: u32,
661		out_ptr: u32,
662		out_len_ptr: u32,
663	) -> Result<ReturnErrorCode, TrapReason> {
664		let transient = Self::is_transient(flags)?;
665		let costs = |len| {
666			if transient {
667				RuntimeCosts::TakeTransientStorage(len)
668			} else {
669				RuntimeCosts::TakeStorage(len)
670			}
671		};
672		let charged = self.charge_gas(costs(self.ext.max_value_size()))?;
673		let key = self.decode_key(memory, key_ptr, key_len)?;
674		let outcome = if transient {
675			self.ext.set_transient_storage(&key, None, true)?
676		} else {
677			self.ext.set_storage(&key, None, true)?
678		};
679
680		if let crate::storage::WriteOutcome::Taken(value) = outcome {
681			self.adjust_gas(charged, costs(value.len() as u32));
682			self.write_sandbox_output(
683				memory,
684				out_ptr,
685				out_len_ptr,
686				&value,
687				false,
688				already_charged,
689			)?;
690			Ok(ReturnErrorCode::Success)
691		} else {
692			self.adjust_gas(charged, costs(0));
693			Ok(ReturnErrorCode::KeyNotFound)
694		}
695	}
696
697	fn call(
698		&mut self,
699		memory: &mut M,
700		flags: CallFlags,
701		call_type: CallType,
702		callee_ptr: u32,
703		deposit_ptr: u32,
704		weight: Weight,
705		input_data_ptr: u32,
706		input_data_len: u32,
707		output_ptr: u32,
708		output_len_ptr: u32,
709	) -> Result<ReturnErrorCode, TrapReason> {
710		let callee = memory.read_h160(callee_ptr)?;
711		let precompile = <AllPrecompiles<E::T>>::get::<E>(&callee.as_fixed_bytes());
712		match &precompile {
713			Some(precompile) if precompile.has_contract_info() =>
714				self.charge_gas(RuntimeCosts::PrecompileWithInfoBase)?,
715			Some(_) => self.charge_gas(RuntimeCosts::PrecompileBase)?,
716			None => self.charge_gas(call_type.cost())?,
717		};
718
719		let deposit_limit = memory.read_u256(deposit_ptr)?;
720
721		// we do check this in exec.rs but we want to error out early
722		if input_data_len > limits::CALLDATA_BYTES {
723			Err(<Error<E::T>>::CallDataTooLarge)?;
724		}
725
726		let input_data = if flags.contains(CallFlags::CLONE_INPUT) {
727			let input = self.input_data.as_ref().ok_or(Error::<E::T>::InputForwarded)?;
728			charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?;
729			input.clone()
730		} else if flags.contains(CallFlags::FORWARD_INPUT) {
731			self.input_data.take().ok_or(Error::<E::T>::InputForwarded)?
732		} else {
733			if precompile.is_some() {
734				self.charge_gas(RuntimeCosts::PrecompileDecode(input_data_len))?;
735			} else {
736				self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?;
737			}
738			memory.read(input_data_ptr, input_data_len)?
739		};
740
741		memory.reset_interpreter_cache();
742
743		let call_outcome = match call_type {
744			CallType::Call { value_ptr } => {
745				let read_only = flags.contains(CallFlags::READ_ONLY);
746				let value = memory.read_u256(value_ptr)?;
747				if value > 0u32.into() {
748					// If the call value is non-zero and state change is not allowed, issue an
749					// error.
750					if read_only || self.ext.is_read_only() {
751						return Err(Error::<E::T>::StateChangeDenied.into());
752					}
753
754					self.charge_gas(RuntimeCosts::CallTransferSurcharge {
755						dust_transfer: Pallet::<E::T>::has_dust(value),
756					})?;
757				}
758				self.ext.call(
759					weight,
760					deposit_limit,
761					&callee,
762					value,
763					input_data,
764					flags.contains(CallFlags::ALLOW_REENTRY),
765					read_only,
766				)
767			},
768			CallType::DelegateCall => {
769				if flags.intersects(CallFlags::ALLOW_REENTRY | CallFlags::READ_ONLY) {
770					return Err(Error::<E::T>::InvalidCallFlags.into());
771				}
772				self.ext.delegate_call(weight, deposit_limit, callee, input_data)
773			},
774		};
775
776		match call_outcome {
777			// `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to
778			// a halt anyways without anymore code being executed.
779			Ok(_) if flags.contains(CallFlags::TAIL_CALL) => {
780				let output = mem::take(self.ext.last_frame_output_mut());
781				return Err(TrapReason::Return(ReturnData {
782					flags: output.flags.bits(),
783					data: output.data,
784				}));
785			},
786			Ok(_) => {
787				let output = mem::take(self.ext.last_frame_output_mut());
788				let write_result = self.write_sandbox_output(
789					memory,
790					output_ptr,
791					output_len_ptr,
792					&output.data,
793					true,
794					|len| Some(RuntimeCosts::CopyToContract(len)),
795				);
796				*self.ext.last_frame_output_mut() = output;
797				write_result?;
798				Ok(self.ext.last_frame_output().into())
799			},
800			Err(err) => {
801				let error_code = super::exec_error_into_return_code::<E>(err)?;
802				memory.write(output_len_ptr, &0u32.to_le_bytes())?;
803				Ok(error_code)
804			},
805		}
806	}
807
808	fn instantiate(
809		&mut self,
810		memory: &mut M,
811		code_hash_ptr: u32,
812		weight: Weight,
813		deposit_ptr: u32,
814		value_ptr: u32,
815		input_data_ptr: u32,
816		input_data_len: u32,
817		address_ptr: u32,
818		output_ptr: u32,
819		output_len_ptr: u32,
820		salt_ptr: u32,
821	) -> Result<ReturnErrorCode, TrapReason> {
822		let value = match memory.read_u256(value_ptr) {
823			Ok(value) => {
824				self.charge_gas(RuntimeCosts::Instantiate {
825					input_data_len,
826					balance_transfer: Pallet::<E::T>::has_balance(value),
827					dust_transfer: Pallet::<E::T>::has_dust(value),
828				})?;
829				value
830			},
831			Err(err) => {
832				self.charge_gas(RuntimeCosts::Instantiate {
833					input_data_len: 0,
834					balance_transfer: false,
835					dust_transfer: false,
836				})?;
837				return Err(err.into());
838			},
839		};
840		let deposit_limit: U256 = memory.read_u256(deposit_ptr)?;
841		let code_hash = memory.read_h256(code_hash_ptr)?;
842		if input_data_len > limits::CALLDATA_BYTES {
843			Err(<Error<E::T>>::CallDataTooLarge)?;
844		}
845		let input_data = memory.read(input_data_ptr, input_data_len)?;
846		let salt = if salt_ptr == SENTINEL {
847			None
848		} else {
849			let salt: [u8; 32] = memory.read_array(salt_ptr)?;
850			Some(salt)
851		};
852
853		memory.reset_interpreter_cache();
854
855		match self.ext.instantiate(
856			weight,
857			deposit_limit,
858			Code::Existing(code_hash),
859			value,
860			input_data,
861			salt.as_ref(),
862		) {
863			Ok(address) => {
864				if !self.ext.last_frame_output().flags.contains(ReturnFlags::REVERT) {
865					self.write_fixed_sandbox_output(
866						memory,
867						address_ptr,
868						&address.as_bytes(),
869						true,
870						already_charged,
871					)?;
872				}
873				let output = mem::take(self.ext.last_frame_output_mut());
874				let write_result = self.write_sandbox_output(
875					memory,
876					output_ptr,
877					output_len_ptr,
878					&output.data,
879					true,
880					|len| Some(RuntimeCosts::CopyToContract(len)),
881				);
882				*self.ext.last_frame_output_mut() = output;
883				write_result?;
884				Ok(self.ext.last_frame_output().into())
885			},
886			Err(err) => Ok(super::exec_error_into_return_code::<E>(err)?),
887		}
888	}
889}
890
891pub struct PreparedCall<'a, E: Ext> {
892	module: polkavm::Module,
893	instance: polkavm::RawInstance,
894	runtime: Runtime<'a, E, polkavm::RawInstance>,
895}
896
897impl<'a, E: Ext> PreparedCall<'a, E>
898where
899	BalanceOf<E::T>: Into<U256>,
900	BalanceOf<E::T>: TryFrom<U256>,
901{
902	pub fn call(mut self) -> ExecResult {
903		let exec_result = loop {
904			let interrupt = self.instance.run();
905			if let Some(exec_result) =
906				self.runtime.handle_interrupt(interrupt, &self.module, &mut self.instance)
907			{
908				break exec_result
909			}
910		};
911		let _ = self.runtime.ext().gas_meter_mut().sync_from_executor(self.instance.gas())?;
912		exec_result
913	}
914
915	/// The guest memory address at which the aux data is located.
916	#[cfg(feature = "runtime-benchmarks")]
917	pub fn aux_data_base(&self) -> u32 {
918		self.instance.module().memory_map().aux_data_address()
919	}
920
921	/// Copies `data` to the aux data at address `offset`.
922	///
923	/// It sets `a0` to the beginning of data inside the aux data.
924	/// It sets `a1` to the value passed.
925	///
926	/// Only used in benchmarking so far.
927	#[cfg(feature = "runtime-benchmarks")]
928	pub fn setup_aux_data(
929		&mut self,
930		data: &[u8],
931		offset: u32,
932		a1: u64,
933	) -> frame_support::dispatch::DispatchResult {
934		let a0 = self.aux_data_base().saturating_add(offset);
935		self.instance.write_memory(a0, data).map_err(|err| {
936			log::debug!(target: LOG_TARGET, "failed to write aux data: {err:?}");
937			Error::<E::T>::CodeRejected
938		})?;
939		self.instance.set_reg(polkavm::Reg::A0, a0.into());
940		self.instance.set_reg(polkavm::Reg::A1, a1);
941		Ok(())
942	}
943}