pallet_evm_polkavm/vm/
runtime.rs

1// This file is part of Frontier.
2
3// Copyright (C) Frontier developers.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0
6
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// 	http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18
19//! Environment definition of the vm smart-contract runtime.
20
21use alloc::{vec, vec::Vec};
22use core::{fmt, marker::PhantomData};
23use fp_evm::PrecompileHandle;
24use frame_support::weights::Weight;
25use pallet_evm_polkavm_proc_macro::define_env;
26use pallet_evm_polkavm_uapi::{ReturnErrorCode, ReturnFlags};
27use scale_codec::{Decode, Encode};
28use scale_info::TypeInfo;
29use sp_core::{H160, H256, U256};
30use sp_runtime::RuntimeDebug;
31
32use super::{LOG_TARGET, SENTINEL};
33use crate::{Config, ConvertPolkaVmGas, WeightInfo};
34
35/// Output of a contract call or instantiation which ran to completion.
36#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo, Default)]
37pub struct ExecReturnValue {
38	/// Flags passed along by `seal_return`. Empty when `seal_return` was never called.
39	pub flags: ReturnFlags,
40	/// Buffer passed along by `seal_return`. Empty when `seal_return` was never called.
41	pub data: Vec<u8>,
42}
43
44pub type ExecResult = Result<ExecReturnValue, SupervisorError>;
45
46impl ExecReturnValue {
47	/// The contract did revert all storage changes.
48	pub fn did_revert(&self) -> bool {
49		self.flags.contains(ReturnFlags::REVERT)
50	}
51}
52
53/// Abstraction over the memory access within syscalls.
54///
55/// The reason for this abstraction is that we run syscalls on the host machine when
56/// benchmarking them. In that case we have direct access to the contract's memory. However, when
57/// running within PolkaVM we need to resort to copying as we can't map the contracts memory into
58/// the host (as of now).
59pub trait Memory {
60	/// Read designated chunk from the sandbox memory into the supplied buffer.
61	///
62	/// Returns `Err` if one of the following conditions occurs:
63	///
64	/// - requested buffer is not within the bounds of the sandbox memory.
65	fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), SupervisorError>;
66
67	/// Write the given buffer to the designated location in the sandbox memory.
68	///
69	/// Returns `Err` if one of the following conditions occurs:
70	///
71	/// - designated area is not within the bounds of the sandbox memory.
72	fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), SupervisorError>;
73
74	/// Zero the designated location in the sandbox memory.
75	///
76	/// Returns `Err` if one of the following conditions occurs:
77	///
78	/// - designated area is not within the bounds of the sandbox memory.
79	fn zero(&mut self, ptr: u32, len: u32) -> Result<(), SupervisorError>;
80
81	/// Read designated chunk from the sandbox memory.
82	///
83	/// Returns `Err` if one of the following conditions occurs:
84	///
85	/// - requested buffer is not within the bounds of the sandbox memory.
86	fn read(&self, ptr: u32, len: u32) -> Result<Vec<u8>, SupervisorError> {
87		let mut buf = vec![0u8; len as usize];
88		self.read_into_buf(ptr, buf.as_mut_slice())?;
89		Ok(buf)
90	}
91
92	/// Same as `read` but reads into a fixed size buffer.
93	fn read_array<const N: usize>(&self, ptr: u32) -> Result<[u8; N], SupervisorError> {
94		let mut buf = [0u8; N];
95		self.read_into_buf(ptr, &mut buf)?;
96		Ok(buf)
97	}
98
99	/// Read a `u32` from the sandbox memory.
100	fn read_u32(&self, ptr: u32) -> Result<u32, SupervisorError> {
101		let buf: [u8; 4] = self.read_array(ptr)?;
102		Ok(u32::from_le_bytes(buf))
103	}
104
105	/// Read a `U256` from the sandbox memory.
106	fn read_u256(&self, ptr: u32) -> Result<U256, SupervisorError> {
107		let buf: [u8; 32] = self.read_array(ptr)?;
108		Ok(U256::from_little_endian(&buf))
109	}
110
111	/// Read a `H160` from the sandbox memory.
112	fn read_h160(&self, ptr: u32) -> Result<H160, SupervisorError> {
113		let mut buf = H160::default();
114		self.read_into_buf(ptr, buf.as_bytes_mut())?;
115		Ok(buf)
116	}
117
118	/// Read a `H256` from the sandbox memory.
119	fn read_h256(&self, ptr: u32) -> Result<H256, SupervisorError> {
120		let mut code_hash = H256::default();
121		self.read_into_buf(ptr, code_hash.as_bytes_mut())?;
122		Ok(code_hash)
123	}
124}
125
126/// Allows syscalls access to the PolkaVM instance they are executing in.
127///
128/// In case a contract is executing within PolkaVM its `memory` argument will also implement
129/// this trait. The benchmarking implementation of syscalls will only require `Memory`
130/// to be implemented.
131pub trait PolkaVmInstance: Memory {
132	fn gas(&self) -> polkavm::Gas;
133	fn set_gas(&mut self, gas: polkavm::Gas);
134	fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64);
135	fn write_output(&mut self, output: u64);
136}
137
138// Memory implementation used in benchmarking where guest memory is mapped into the host.
139//
140// Please note that we could optimize the `read_as_*` functions by decoding directly from
141// memory without a copy. However, we don't do that because as it would change the behaviour
142// of those functions: A `read_as` with a `len` larger than the actual type can succeed
143// in the streaming implementation while it could fail with a segfault in the copy implementation.
144#[cfg(feature = "runtime-benchmarks")]
145impl Memory for [u8] {
146	fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), SupervisorError> {
147		let ptr = ptr as usize;
148		let bound_checked = self
149			.get(ptr..ptr + buf.len())
150			.ok_or(SupervisorError::OutOfBounds)?;
151		buf.copy_from_slice(bound_checked);
152		Ok(())
153	}
154
155	fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), SupervisorError> {
156		let ptr = ptr as usize;
157		let bound_checked = self
158			.get_mut(ptr..ptr + buf.len())
159			.ok_or(SupervisorError::OutOfBounds)?;
160		bound_checked.copy_from_slice(buf);
161		Ok(())
162	}
163
164	fn zero(&mut self, ptr: u32, len: u32) -> Result<(), SupervisorError> {
165		<[u8] as Memory>::write(self, ptr, &vec![0; len as usize])
166	}
167}
168
169impl Memory for polkavm::RawInstance {
170	fn read_into_buf(&self, ptr: u32, buf: &mut [u8]) -> Result<(), SupervisorError> {
171		self.read_memory_into(ptr, buf)
172			.map(|_| ())
173			.map_err(|_| SupervisorError::OutOfBounds)
174	}
175
176	fn write(&mut self, ptr: u32, buf: &[u8]) -> Result<(), SupervisorError> {
177		self.write_memory(ptr, buf)
178			.map_err(|_| SupervisorError::OutOfBounds)
179	}
180
181	fn zero(&mut self, ptr: u32, len: u32) -> Result<(), SupervisorError> {
182		self.zero_memory(ptr, len)
183			.map_err(|_| SupervisorError::OutOfBounds)
184	}
185}
186
187impl PolkaVmInstance for polkavm::RawInstance {
188	fn gas(&self) -> polkavm::Gas {
189		self.gas()
190	}
191
192	fn set_gas(&mut self, gas: polkavm::Gas) {
193		self.set_gas(gas)
194	}
195
196	fn read_input_regs(&self) -> (u64, u64, u64, u64, u64, u64) {
197		(
198			self.reg(polkavm::Reg::A0),
199			self.reg(polkavm::Reg::A1),
200			self.reg(polkavm::Reg::A2),
201			self.reg(polkavm::Reg::A3),
202			self.reg(polkavm::Reg::A4),
203			self.reg(polkavm::Reg::A5),
204		)
205	}
206
207	fn write_output(&mut self, output: u64) {
208		self.set_reg(polkavm::Reg::A0, output);
209	}
210}
211
212impl From<&ExecReturnValue> for ReturnErrorCode {
213	fn from(from: &ExecReturnValue) -> Self {
214		if from.flags.contains(ReturnFlags::REVERT) {
215			Self::CalleeReverted
216		} else {
217			Self::Success
218		}
219	}
220}
221
222/// The data passed through when a contract uses `seal_return`.
223#[derive(RuntimeDebug)]
224pub struct ReturnData {
225	/// The flags as passed through by the contract. They are still unchecked and
226	/// will later be parsed into a `ReturnFlags` bitflags struct.
227	flags: u32,
228	/// The output buffer passed by the contract as return data.
229	data: Vec<u8>,
230}
231
232#[derive(RuntimeDebug)]
233pub enum SupervisorError {
234	OutOfBounds,
235	ExecutionFailed,
236	ContractTrapped,
237	OutOfGas,
238	InvalidSyscall,
239	InvalidCallFlags,
240	StateChangeDenied,
241	InputForwarded,
242	NotPolkaVm,
243	CodeRejected,
244}
245
246/// Enumerates all possible reasons why a trap was generated.
247///
248/// This is either used to supply the caller with more information about why an error
249/// occurred (the SupervisorError variant).
250/// The other case is where the trap does not constitute an error but rather was invoked
251/// as a quick way to terminate the application (all other variants).
252#[derive(RuntimeDebug)]
253pub enum TrapReason {
254	/// The supervisor trapped the contract because of an error condition occurred during
255	/// execution in privileged code.
256	SupervisorError(SupervisorError),
257	/// Signals that trap was generated in response to call `seal_return` host function.
258	Return(ReturnData),
259}
260
261impl From<SupervisorError> for TrapReason {
262	fn from(from: SupervisorError) -> Self {
263		TrapReason::SupervisorError(from)
264	}
265}
266
267impl fmt::Display for TrapReason {
268	fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
269		Ok(())
270	}
271}
272
273macro_rules! cost_args {
274	// cost_args!(name, a, b, c) -> T::WeightInfo::name(a, b, c).saturating_sub(T::WeightInfo::name(0, 0, 0))
275	($name:ident, $( $arg: expr ),+) => {
276		(<T as Config>::WeightInfo::$name($( $arg ),+).saturating_sub(cost_args!(@call_zero $name, $( $arg ),+)))
277	};
278	// Transform T::WeightInfo::name(a, b, c) into T::WeightInfo::name(0, 0, 0)
279	(@call_zero $name:ident, $( $arg:expr ),*) => {
280		<T as Config>::WeightInfo::$name($( cost_args!(@replace_token $arg) ),*)
281	};
282	// Replace the token with 0.
283	(@replace_token $_in:tt) => { 0 };
284}
285
286#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
287#[derive(Copy, Clone)]
288pub enum RuntimeCosts {
289	/// Base Weight of calling a host function.
290	HostFn,
291	/// Weight charged for copying data from the sandbox.
292	CopyFromContract(u32),
293	/// Weight of calling `seal_call_data_load``.
294	CallDataLoad,
295	/// Weight of calling `seal_call_data_copy`.
296	CallDataCopy(u32),
297	/// Weight of calling `seal_caller`.
298	Caller,
299	/// Weight of calling `seal_call_data_size`.
300	CallDataSize,
301	/// Weight of calling `seal_origin`.
302	Origin,
303	/// Weight of calling `seal_address`.
304	Address,
305}
306
307impl RuntimeCosts {
308	fn weight<T: Config>(&self) -> Weight {
309		use self::RuntimeCosts::*;
310		match *self {
311			HostFn => cost_args!(noop_host_fn, 1),
312			CopyFromContract(len) => <T as Config>::WeightInfo::seal_return(len),
313			CallDataSize => <T as Config>::WeightInfo::seal_call_data_size(),
314			CallDataLoad => <T as Config>::WeightInfo::seal_call_data_load(),
315			CallDataCopy(len) => <T as Config>::WeightInfo::seal_call_data_copy(len),
316			Caller => <T as Config>::WeightInfo::seal_caller(),
317			Origin => <T as Config>::WeightInfo::seal_origin(),
318			Address => <T as Config>::WeightInfo::seal_address(),
319		}
320	}
321}
322
323/// This is only appropriate when writing out data of constant size that does not depend on user
324/// input. In this case the costs for this copy was already charged as part of the token at
325/// the beginning of the API entry point.
326fn already_charged(_: u32) -> Option<RuntimeCosts> {
327	None
328}
329
330/// Can only be used for one call.
331pub struct Runtime<'a, T, H, M: ?Sized> {
332	handle: &'a mut H,
333	input_data: Option<Vec<u8>>,
334	last_gas: polkavm::Gas,
335	_phantom_data: PhantomData<(T, M)>,
336}
337
338impl<T: Config, H: PrecompileHandle, M: PolkaVmInstance> Runtime<'_, T, H, M> {
339	pub fn handle_interrupt(
340		&mut self,
341		interrupt: Result<polkavm::InterruptKind, polkavm::Error>,
342		module: &polkavm::Module,
343		instance: &mut M,
344	) -> Option<ExecResult> {
345		use polkavm::InterruptKind::*;
346
347		match interrupt {
348			Err(error) => {
349				// in contrast to the other returns this "should" not happen: log level error
350				log::error!(target: LOG_TARGET, "polkavm execution error: {error}");
351				Some(Err(SupervisorError::ExecutionFailed))
352			}
353			Ok(Finished) => Some(Ok(ExecReturnValue {
354				flags: ReturnFlags::empty(),
355				data: Vec::new(),
356			})),
357			Ok(Trap) => Some(Err(SupervisorError::ContractTrapped)),
358			Ok(Segfault(_)) => Some(Err(SupervisorError::ExecutionFailed)),
359			Ok(NotEnoughGas) => Some(Err(SupervisorError::OutOfGas)),
360			Ok(Step) => None,
361			Ok(Ecalli(idx)) => {
362				// This is a special hard coded syscall index which is used by benchmarks
363				// to abort contract execution. It is used to terminate the execution without
364				// breaking up a basic block. The fixed index is used so that the benchmarks
365				// don't have to deal with import tables.
366				if cfg!(feature = "runtime-benchmarks") && idx == SENTINEL {
367					return Some(Ok(ExecReturnValue {
368						flags: ReturnFlags::empty(),
369						data: Vec::new(),
370					}));
371				}
372				let Some(syscall_symbol) = module.imports().get(idx) else {
373					return Some(Err(SupervisorError::InvalidSyscall));
374				};
375				match self.handle_ecall(instance, syscall_symbol.as_bytes()) {
376					Ok(None) => None,
377					Ok(Some(return_value)) => {
378						instance.write_output(return_value);
379						None
380					}
381					Err(TrapReason::Return(ReturnData { flags, data })) => {
382						match ReturnFlags::from_bits(flags) {
383							None => Some(Err(SupervisorError::InvalidCallFlags)),
384							Some(flags) => Some(Ok(ExecReturnValue { flags, data })),
385						}
386					}
387					Err(TrapReason::SupervisorError(error)) => Some(Err(error)),
388				}
389			}
390		}
391	}
392}
393
394impl<'a, T: Config, H: PrecompileHandle, M: PolkaVmInstance> Runtime<'a, T, H, M> {
395	pub fn new(handle: &'a mut H, input_data: Vec<u8>, gas_limit: polkavm::Gas) -> Self {
396		Self {
397			handle,
398			input_data: Some(input_data),
399			last_gas: gas_limit,
400			_phantom_data: Default::default(),
401		}
402	}
403
404	/// Charge the gas meter with the specified token.
405	///
406	/// Returns `Err(HostError)` if there is not enough gas.
407	pub(crate) fn charge_gas(&mut self, costs: RuntimeCosts) -> Result<(), SupervisorError> {
408		let weight = costs.weight::<T>();
409		self.handle
410			.record_external_cost(Some(weight.ref_time()), Some(weight.proof_size()), None)
411			.map_err(|_| SupervisorError::OutOfGas)?;
412
413		Ok(())
414	}
415
416	pub(crate) fn charge_polkavm_gas(&mut self, memory: &mut M) -> Result<(), SupervisorError> {
417		let gas = self.last_gas - memory.gas();
418		if gas < 0 {
419			return Err(SupervisorError::OutOfGas);
420		}
421
422		self.handle
423			.record_cost(T::ConvertPolkaVmGas::polkavm_gas_to_evm_gas(gas))
424			.map_err(|_| SupervisorError::OutOfGas)?;
425
426		self.last_gas = memory.gas();
427		Ok(())
428	}
429
430	/// Write the given buffer and its length to the designated locations in sandbox memory and
431	/// charge gas according to the token returned by `create_token`.
432	///
433	/// `out_ptr` is the location in sandbox memory where `buf` should be written to.
434	/// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the
435	/// length of the buffer located at `out_ptr`. If that buffer is smaller than the actual
436	/// `buf.len()`, only what fits into that buffer is written to `out_ptr`.
437	/// The actual amount of bytes copied to `out_ptr` is written to `out_len_ptr`.
438	///
439	/// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the
440	/// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying
441	/// output optional. For example to skip copying back the output buffer of an `seal_call`
442	/// when the caller is not interested in the result.
443	///
444	/// `create_token` can optionally instruct this function to charge the gas meter with the token
445	/// it returns. `create_token` receives the variable amount of bytes that are about to be copied
446	/// by this function.
447	///
448	/// In addition to the error conditions of `Memory::write` this functions returns
449	/// `Err` if the size of the buffer located at `out_ptr` is too small to fit `buf`.
450	pub fn write_sandbox_output(
451		&mut self,
452		memory: &mut M,
453		out_ptr: u32,
454		out_len_ptr: u32,
455		buf: &[u8],
456		allow_skip: bool,
457		create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
458	) -> Result<(), SupervisorError> {
459		if allow_skip && out_ptr == SENTINEL {
460			return Ok(());
461		}
462
463		let len = memory.read_u32(out_len_ptr)?;
464		let buf_len = len.min(buf.len() as u32);
465
466		if let Some(costs) = create_token(buf_len) {
467			self.charge_gas(costs)?;
468		}
469
470		memory.write(out_ptr, &buf[..buf_len as usize])?;
471		memory.write(out_len_ptr, &buf_len.encode())
472	}
473
474	/// Same as `write_sandbox_output` but for static size output.
475	pub fn write_fixed_sandbox_output(
476		&mut self,
477		memory: &mut M,
478		out_ptr: u32,
479		buf: &[u8],
480		allow_skip: bool,
481		create_token: impl FnOnce(u32) -> Option<RuntimeCosts>,
482	) -> Result<(), SupervisorError> {
483		if buf.is_empty() || (allow_skip && out_ptr == SENTINEL) {
484			return Ok(());
485		}
486
487		let buf_len = buf.len() as u32;
488		if let Some(costs) = create_token(buf_len) {
489			self.charge_gas(costs)?;
490		}
491
492		memory.write(out_ptr, buf)
493	}
494}
495
496// This is the API exposed to contracts.
497//
498// # Note
499//
500// Any input that leads to a out of bound error (reading or writing) or failing to decode
501// data passed to the supervisor will lead to a trap. This is not documented explicitly
502// for every function.
503#[define_env]
504pub mod env {
505	/// Noop function used to benchmark the time it takes to execute an empty function.
506	///
507	/// Marked as stable because it needs to be called from benchmarks even when the benchmarked
508	/// parachain has unstable functions disabled.
509	#[cfg(feature = "runtime-benchmarks")]
510	#[stable]
511	fn noop(&mut self, memory: &mut M) -> Result<(), TrapReason> {
512		Ok(())
513	}
514
515	/// Returns the total size of the contract call input data.
516	/// See [`pallet_evm_polkavm_uapi::HostFn::call_data_size `].
517	#[stable]
518	fn call_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
519		self.charge_gas(RuntimeCosts::CallDataSize)?;
520		Ok(self
521			.input_data
522			.as_ref()
523			.map(|input| input.len().try_into().expect("usize fits into u64; qed"))
524			.unwrap_or_default())
525	}
526
527	/// Stores the input passed by the caller into the supplied buffer.
528	/// See [`pallet_evm_polkavm_uapi::HostFn::call_data_copy`].
529	#[stable]
530	fn call_data_copy(
531		&mut self,
532		memory: &mut M,
533		out_ptr: u32,
534		out_len: u32,
535		offset: u32,
536	) -> Result<(), TrapReason> {
537		self.charge_gas(RuntimeCosts::CallDataCopy(out_len))?;
538
539		let Some(input) = self.input_data.as_ref() else {
540			return Err(SupervisorError::InputForwarded.into());
541		};
542
543		let start = offset as usize;
544		if start >= input.len() {
545			memory.zero(out_ptr, out_len)?;
546			return Ok(());
547		}
548
549		let end = start.saturating_add(out_len as usize).min(input.len());
550		memory.write(out_ptr, &input[start..end])?;
551
552		let bytes_written = (end - start) as u32;
553		memory.zero(
554			out_ptr.saturating_add(bytes_written),
555			out_len - bytes_written,
556		)?;
557
558		Ok(())
559	}
560
561	/// Stores the U256 value at given call input `offset` into the supplied buffer.
562	/// See [`pallet_evm_polkavm_uapi::HostFn::call_data_load`].
563	#[stable]
564	fn call_data_load(
565		&mut self,
566		memory: &mut M,
567		out_ptr: u32,
568		offset: u32,
569	) -> Result<(), TrapReason> {
570		self.charge_gas(RuntimeCosts::CallDataLoad)?;
571
572		let Some(input) = self.input_data.as_ref() else {
573			return Err(SupervisorError::InputForwarded.into());
574		};
575
576		let mut data = [0; 32];
577		let start = offset as usize;
578		let data = if start >= input.len() {
579			data // Any index is valid to request; OOB offsets return zero.
580		} else {
581			let end = start.saturating_add(32).min(input.len());
582			data[..end - start].copy_from_slice(&input[start..end]);
583			data.reverse();
584			data // Solidity expects right-padded data
585		};
586
587		self.write_fixed_sandbox_output(memory, out_ptr, &data, false, already_charged)?;
588
589		Ok(())
590	}
591
592	/// Cease contract execution and save a data buffer as a result of the execution.
593	/// See [`pallet_evm_polkavm_uapi::HostFn::return_value`].
594	#[stable]
595	fn seal_return(
596		&mut self,
597		memory: &mut M,
598		flags: u32,
599		data_ptr: u32,
600		data_len: u32,
601	) -> Result<(), TrapReason> {
602		self.charge_gas(RuntimeCosts::CopyFromContract(data_len))?;
603		Err(TrapReason::Return(ReturnData {
604			flags,
605			data: memory.read(data_ptr, data_len)?,
606		}))
607	}
608
609	/// Stores the address of the caller into the supplied buffer.
610	/// See [`pallet_evm_polkavm_uapi::HostFn::caller`].
611	#[stable]
612	fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
613		self.charge_gas(RuntimeCosts::Caller)?;
614		let caller = self.handle.context().caller;
615		Ok(self.write_fixed_sandbox_output(
616			memory,
617			out_ptr,
618			caller.as_bytes(),
619			false,
620			already_charged,
621		)?)
622	}
623
624	/// Stores the address of the call stack origin into the supplied buffer.
625	/// See [`pallet_evm_polkavm_uapi::HostFn::origin`].
626	#[stable]
627	fn origin(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
628		self.charge_gas(RuntimeCosts::Origin)?;
629		let origin = self.handle.origin();
630		Ok(self.write_fixed_sandbox_output(
631			memory,
632			out_ptr,
633			origin.as_bytes(),
634			false,
635			already_charged,
636		)?)
637	}
638
639	/// Stores the address of the current contract into the supplied buffer.
640	/// See [`pallet_evm_polkavm_uapi::HostFn::address`].
641	#[stable]
642	fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
643		self.charge_gas(RuntimeCosts::Address)?;
644		let address = self.handle.context().address;
645		Ok(self.write_fixed_sandbox_output(
646			memory,
647			out_ptr,
648			address.as_bytes(),
649			false,
650			already_charged,
651		)?)
652	}
653}