referrerpolicy=no-referrer-when-downgrade

pallet_revive/vm/pvm/
env.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
18use super::*;
19
20use crate::{
21	AccountIdOf, CodeInfo, Config, ContractBlob, Error, SENTINEL, Weight,
22	address::AddressMapper,
23	debug::DebugSettings,
24	exec::Ext,
25	limits,
26	primitives::ExecReturnValue,
27	vm::{BytecodeType, ExportedFunction, RuntimeCosts, calculate_code_deposit},
28};
29use alloc::vec::Vec;
30use core::mem;
31use frame_support::traits::Get;
32use pallet_revive_proc_macro::define_env;
33use pallet_revive_uapi::{CallFlags, ReturnErrorCode, ReturnFlags};
34use sp_core::U256;
35use sp_io::hashing::keccak_256;
36use sp_runtime::{DispatchError, SaturatedConversion};
37
38impl<T: Config> ContractBlob<T> {
39	/// Compile and instantiate contract.
40	///
41	/// `aux_data_size` is only used for runtime benchmarks. Real contracts
42	/// don't make use of this buffer. Hence this should not be set to anything
43	/// other than `0` when not used for benchmarking.
44	pub fn prepare_call<E: Ext<T = T>>(
45		self,
46		mut runtime: Runtime<E, polkavm::RawInstance>,
47		entry_point: ExportedFunction,
48		aux_data_size: u32,
49	) -> Result<PreparedCall<E>, ExecError> {
50		let mut config = polkavm::Config::default();
51		// Log filtering by level with log::enabled! returns always true,
52		// passing all logs through impacting performance \
53		// (more details: https://github.com/paritytech/polkadot-sdk/issues/8760#issuecomment-3499548774)
54		// By default, disable polkavm logging unless pvm_logs debug setting is enabled.
55		let pvm_logs_enabled = DebugSettings::is_pvm_logs_enabled::<T>();
56		config.set_imperfect_logger_filtering_workaround(!pvm_logs_enabled);
57		config.set_backend(Some(polkavm::BackendKind::Interpreter));
58		config.set_cache_enabled(false);
59		#[cfg(feature = "std")]
60		if std::env::var_os("REVIVE_USE_COMPILER").is_some() {
61			log::warn!(target: LOG_TARGET, "Using PolkaVM compiler backend because env var REVIVE_USE_COMPILER is set");
62			config.set_backend(Some(polkavm::BackendKind::Compiler));
63		}
64		let engine = polkavm::Engine::new(&config).expect(
65			"on-chain (no_std) use of interpreter is hard coded.
66				interpreter is available on all platforms; qed",
67		);
68
69		let mut module_config = polkavm::ModuleConfig::new();
70		module_config.set_page_size(limits::PAGE_SIZE);
71		module_config.set_gas_metering(Some(polkavm::GasMeteringKind::Sync));
72		module_config.set_aux_data_size(aux_data_size);
73		let module =
74			polkavm::Module::new(&engine, &module_config, self.code.into()).map_err(|err| {
75				log::debug!(target: LOG_TARGET, "failed to create polkavm module: {err:?}");
76				Error::<T>::CodeRejected
77			})?;
78
79		let entry_program_counter = module
80			.exports()
81			.find(|export| export.symbol().as_bytes() == entry_point.identifier().as_bytes())
82			.ok_or_else(|| <Error<T>>::CodeRejected)?
83			.program_counter();
84
85		let gas_limit_polkavm: polkavm::Gas = runtime.ext().frame_meter_mut().sync_to_executor();
86
87		let mut instance = module.instantiate().map_err(|err| {
88			log::debug!(target: LOG_TARGET, "failed to instantiate polkavm module: {err:?}");
89			Error::<T>::CodeRejected
90		})?;
91
92		instance.set_gas(gas_limit_polkavm);
93		instance
94			.set_interpreter_cache_size_limit(Some(polkavm::SetCacheSizeLimitArgs {
95				max_block_size: limits::code::BASIC_BLOCK_SIZE,
96				max_cache_size_bytes: limits::code::INTERPRETER_CACHE_BYTES
97					.try_into()
98					.map_err(|_| Error::<T>::CodeRejected)?,
99			}))
100			.map_err(|_| Error::<T>::CodeRejected)?;
101		instance.prepare_call_untyped(entry_program_counter, &[]);
102
103		Ok(PreparedCall { module, instance, runtime })
104	}
105}
106
107impl<T: Config> ContractBlob<T> {
108	/// We only check for size and nothing else when the code is uploaded.
109	pub fn from_pvm_code(code: Vec<u8>, owner: AccountIdOf<T>) -> Result<Self, DispatchError> {
110		// We do validation only when new code is deployed. This allows us to increase
111		// the limits later without affecting already deployed code.
112		let available_syscalls = list_syscalls();
113		let code = limits::code::enforce::<T>(code, available_syscalls)?;
114
115		let code_len = code.len() as u32;
116		let deposit = calculate_code_deposit::<T>(code_len);
117
118		let code_info = CodeInfo {
119			owner,
120			deposit,
121			refcount: 0,
122			code_len,
123			code_type: BytecodeType::Pvm,
124			behaviour_version: Default::default(),
125		};
126		let code_hash = H256(sp_io::hashing::keccak_256(&code));
127		Ok(ContractBlob { code, code_info, code_hash })
128	}
129}
130
131impl<'a, E: Ext, M: PolkaVmInstance<E::T>> Runtime<'a, E, M> {
132	pub fn handle_interrupt(
133		&mut self,
134		interrupt: Result<polkavm::InterruptKind, polkavm::Error>,
135		module: &polkavm::Module,
136		instance: &mut M,
137	) -> Option<ExecResult> {
138		use polkavm::InterruptKind::*;
139
140		match interrupt {
141			Err(error) => {
142				// in contrast to the other returns this "should" not happen: log level error
143				log::error!(target: LOG_TARGET, "polkavm execution error: {error}");
144				Some(Err(Error::<E::T>::ExecutionFailed.into()))
145			},
146			Ok(Finished) => {
147				Some(Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }))
148			},
149			Ok(Trap) => Some(Err(Error::<E::T>::ContractTrapped.into())),
150			Ok(Segfault(_)) => Some(Err(Error::<E::T>::ExecutionFailed.into())),
151			Ok(NotEnoughGas) => Some(Err(Error::<E::T>::OutOfGas.into())),
152			Ok(Step) => None,
153			Ok(Ecalli(idx)) => {
154				// This is a special hard coded syscall index which is used by benchmarks
155				// to abort contract execution. It is used to terminate the execution without
156				// breaking up a basic block. The fixed index is used so that the benchmarks
157				// don't have to deal with import tables.
158				if cfg!(feature = "runtime-benchmarks") && idx == SENTINEL {
159					return Some(Ok(ExecReturnValue {
160						flags: ReturnFlags::empty(),
161						data: Vec::new(),
162					}));
163				}
164				let Some(syscall_symbol) = module.imports().get(idx) else {
165					return Some(Err(<Error<E::T>>::InvalidSyscall.into()));
166				};
167				match self.handle_ecall(instance, syscall_symbol.as_bytes()) {
168					Ok(None) => None,
169					Ok(Some(return_value)) => {
170						instance.write_output(return_value);
171						None
172					},
173					Err(TrapReason::Return(ReturnData { flags, data })) => {
174						match ReturnFlags::from_bits(flags) {
175							None => Some(Err(Error::<E::T>::InvalidCallFlags.into())),
176							Some(flags) => Some(Ok(ExecReturnValue { flags, data })),
177						}
178					},
179					Err(TrapReason::Termination) => Some(Ok(Default::default())),
180					Err(TrapReason::SupervisorError(error)) => Some(Err(error.into())),
181				}
182			},
183		}
184	}
185}
186
187// This is the API exposed to contracts.
188//
189// # Note
190//
191// Any input that leads to a out of bound error (reading or writing) or failing to decode
192// data passed to the supervisor will lead to a trap. This is not documented explicitly
193// for every function.
194#[define_env]
195pub mod env {
196	/// Noop function used to benchmark the time it takes to execute an empty function.
197	#[cfg(feature = "runtime-benchmarks")]
198	fn noop(&mut self, memory: &mut M) -> Result<(), TrapReason> {
199		Ok(())
200	}
201
202	/// Set the value at the given key in the contract storage.
203	/// See [`pallet_revive_uapi::HostFn::set_storage`]
204	#[mutating]
205	fn set_storage(
206		&mut self,
207		memory: &mut M,
208		flags: u32,
209		key_ptr: u32,
210		key_len: u32,
211		value_ptr: u32,
212		value_len: u32,
213	) -> Result<u32, TrapReason> {
214		self.set_storage(
215			memory,
216			flags,
217			key_ptr,
218			key_len,
219			StorageValue::Memory { ptr: value_ptr, len: value_len },
220		)
221	}
222
223	/// Sets the storage at a fixed 256-bit key with a fixed 256-bit value.
224	/// See [`pallet_revive_uapi::HostFn::set_storage_or_clear`].
225	#[mutating]
226	fn set_storage_or_clear(
227		&mut self,
228		memory: &mut M,
229		flags: u32,
230		key_ptr: u32,
231		value_ptr: u32,
232	) -> Result<u32, TrapReason> {
233		let value = memory.read(value_ptr, 32)?;
234
235		if value.iter().all(|&b| b == 0) {
236			self.clear_storage(memory, flags, key_ptr, SENTINEL)
237		} else {
238			self.set_storage(memory, flags, key_ptr, SENTINEL, StorageValue::Value(value))
239		}
240	}
241
242	/// Retrieve the value under the given key from storage.
243	/// See [`pallet_revive_uapi::HostFn::get_storage`]
244	fn get_storage(
245		&mut self,
246		memory: &mut M,
247		flags: u32,
248		key_ptr: u32,
249		key_len: u32,
250		out_ptr: u32,
251		out_len_ptr: u32,
252	) -> Result<ReturnErrorCode, TrapReason> {
253		self.get_storage(
254			memory,
255			flags,
256			key_ptr,
257			key_len,
258			out_ptr,
259			StorageReadMode::VariableOutput { output_len_ptr: out_len_ptr },
260		)
261	}
262
263	/// Reads the storage at a fixed 256-bit key and writes back a fixed 256-bit value.
264	/// See [`pallet_revive_uapi::HostFn::get_storage_or_zero`].
265	fn get_storage_or_zero(
266		&mut self,
267		memory: &mut M,
268		flags: u32,
269		key_ptr: u32,
270		out_ptr: u32,
271	) -> Result<(), TrapReason> {
272		let _ = self.get_storage(
273			memory,
274			flags,
275			key_ptr,
276			SENTINEL,
277			out_ptr,
278			StorageReadMode::FixedOutput32,
279		)?;
280
281		Ok(())
282	}
283
284	/// Make a call to another contract.
285	/// See [`pallet_revive_uapi::HostFn::call`].
286	fn call(
287		&mut self,
288		memory: &mut M,
289		flags_and_callee: u64,
290		ref_time_limit: u64,
291		proof_size_limit: u64,
292		deposit_and_value: u64,
293		input_data: u64,
294		output_data: u64,
295	) -> Result<ReturnErrorCode, TrapReason> {
296		let (flags, callee_ptr) = extract_hi_lo(flags_and_callee);
297		let (deposit_ptr, value_ptr) = extract_hi_lo(deposit_and_value);
298		let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
299		let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
300		let weight = Weight::from_parts(ref_time_limit, proof_size_limit);
301
302		self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
303		let deposit_limit = memory.read_u256(deposit_ptr)?;
304
305		self.call(
306			memory,
307			CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
308			CallType::Call { value_ptr },
309			callee_ptr,
310			&CallResources::from_weight_and_deposit(weight, deposit_limit),
311			input_data_ptr,
312			input_data_len,
313			output_ptr,
314			output_len_ptr,
315		)
316	}
317
318	/// Make a call to another contract.
319	/// See [`pallet_revive_uapi::HostFn::call_evm`].
320	fn call_evm(
321		&mut self,
322		memory: &mut M,
323		flags: u32,
324		callee: u32,
325		value_ptr: u32,
326		gas: u64,
327		input_data: u64,
328		output_data: u64,
329	) -> Result<ReturnErrorCode, TrapReason> {
330		let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
331		let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
332		let resources = if gas == u64::MAX {
333			CallResources::NoLimits
334		} else {
335			self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
336			let value = memory.read_u256(value_ptr)?;
337			// We also need to detect the 2300: We need to add something scaled.
338			let add_stipend = !value.is_zero() || gas == revm::interpreter::gas::CALL_STIPEND;
339			CallResources::from_ethereum_gas(gas.into(), add_stipend)
340		};
341
342		self.call(
343			memory,
344			CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
345			CallType::Call { value_ptr },
346			callee,
347			&resources,
348			input_data_ptr,
349			input_data_len,
350			output_ptr,
351			output_len_ptr,
352		)
353	}
354
355	/// Execute code in the context (storage, caller, value) of the current contract.
356	/// See [`pallet_revive_uapi::HostFn::delegate_call`].
357	fn delegate_call(
358		&mut self,
359		memory: &mut M,
360		flags_and_callee: u64,
361		ref_time_limit: u64,
362		proof_size_limit: u64,
363		deposit_ptr: u32,
364		input_data: u64,
365		output_data: u64,
366	) -> Result<ReturnErrorCode, TrapReason> {
367		let (flags, address_ptr) = extract_hi_lo(flags_and_callee);
368		let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
369		let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
370		let weight = Weight::from_parts(ref_time_limit, proof_size_limit);
371
372		self.charge_gas(RuntimeCosts::CopyFromContract(32))?;
373		let deposit_limit = memory.read_u256(deposit_ptr)?;
374
375		self.call(
376			memory,
377			CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
378			CallType::DelegateCall,
379			address_ptr,
380			&CallResources::from_weight_and_deposit(weight, deposit_limit),
381			input_data_ptr,
382			input_data_len,
383			output_ptr,
384			output_len_ptr,
385		)
386	}
387
388	/// Same as `delegate_call` but with EVM gas.
389	/// See [`pallet_revive_uapi::HostFn::delegate_call_evm`].
390	fn delegate_call_evm(
391		&mut self,
392		memory: &mut M,
393		flags: u32,
394		callee: u32,
395		gas: u64,
396		input_data: u64,
397		output_data: u64,
398	) -> Result<ReturnErrorCode, TrapReason> {
399		let (input_data_len, input_data_ptr) = extract_hi_lo(input_data);
400		let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
401		let resources = if gas == u64::MAX {
402			CallResources::NoLimits
403		} else {
404			CallResources::from_ethereum_gas(gas.into(), false)
405		};
406
407		self.call(
408			memory,
409			CallFlags::from_bits(flags).ok_or(Error::<E::T>::InvalidCallFlags)?,
410			CallType::DelegateCall,
411			callee,
412			&resources,
413			input_data_ptr,
414			input_data_len,
415			output_ptr,
416			output_len_ptr,
417		)
418	}
419
420	/// Instantiate a contract with the specified code hash.
421	/// See [`pallet_revive_uapi::HostFn::instantiate`].
422	#[mutating]
423	fn instantiate(
424		&mut self,
425		memory: &mut M,
426		ref_time_limit: u64,
427		proof_size_limit: u64,
428		deposit_and_value: u64,
429		input_data: u64,
430		output_data: u64,
431		address_and_salt: u64,
432	) -> Result<ReturnErrorCode, TrapReason> {
433		let (deposit_ptr, value_ptr) = extract_hi_lo(deposit_and_value);
434		let (input_data_len, code_hash_ptr) = extract_hi_lo(input_data);
435		let (output_len_ptr, output_ptr) = extract_hi_lo(output_data);
436		let (address_ptr, salt_ptr) = extract_hi_lo(address_and_salt);
437		let Some(input_data_ptr) = code_hash_ptr.checked_add(32) else {
438			return Err(Error::<E::T>::OutOfBounds.into());
439		};
440		let Some(input_data_len) = input_data_len.checked_sub(32) else {
441			return Err(Error::<E::T>::OutOfBounds.into());
442		};
443
444		self.instantiate(
445			memory,
446			code_hash_ptr,
447			Weight::from_parts(ref_time_limit, proof_size_limit),
448			deposit_ptr,
449			value_ptr,
450			input_data_ptr,
451			input_data_len,
452			address_ptr,
453			output_ptr,
454			output_len_ptr,
455			salt_ptr,
456		)
457	}
458
459	/// Returns the total size of the contract call input data.
460	/// See [`pallet_revive_uapi::HostFn::call_data_size `].
461	fn call_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
462		self.charge_gas(RuntimeCosts::CallDataSize)?;
463		Ok(self
464			.input_data
465			.as_ref()
466			.map(|input| input.len().try_into().expect("usize fits into u64; qed"))
467			.unwrap_or_default())
468	}
469
470	/// Stores the input passed by the caller into the supplied buffer.
471	/// See [`pallet_revive_uapi::HostFn::call_data_copy`].
472	fn call_data_copy(
473		&mut self,
474		memory: &mut M,
475		out_ptr: u32,
476		out_len: u32,
477		offset: u32,
478	) -> Result<(), TrapReason> {
479		self.charge_gas(RuntimeCosts::CallDataCopy(out_len))?;
480
481		let Some(input) = self.input_data.as_ref() else {
482			return Err(Error::<E::T>::InputForwarded.into());
483		};
484
485		let start = offset as usize;
486		if start >= input.len() {
487			memory.zero(out_ptr, out_len)?;
488			return Ok(());
489		}
490
491		let end = start.saturating_add(out_len as usize).min(input.len());
492		memory.write(out_ptr, &input[start..end])?;
493
494		let bytes_written = (end - start) as u32;
495		memory.zero(out_ptr.saturating_add(bytes_written), out_len - bytes_written)?;
496
497		Ok(())
498	}
499
500	/// Stores the U256 value at given call input `offset` into the supplied buffer.
501	/// See [`pallet_revive_uapi::HostFn::call_data_load`].
502	fn call_data_load(
503		&mut self,
504		memory: &mut M,
505		out_ptr: u32,
506		offset: u32,
507	) -> Result<(), TrapReason> {
508		self.charge_gas(RuntimeCosts::CallDataLoad)?;
509
510		let Some(input) = self.input_data.as_ref() else {
511			return Err(Error::<E::T>::InputForwarded.into());
512		};
513
514		let mut data = [0; 32];
515		let start = offset as usize;
516		let data = if start >= input.len() {
517			data // Any index is valid to request; OOB offsets return zero.
518		} else {
519			let end = start.saturating_add(32).min(input.len());
520			data[..end - start].copy_from_slice(&input[start..end]);
521			data.reverse();
522			data // Solidity expects right-padded data
523		};
524
525		self.write_fixed_sandbox_output(memory, out_ptr, &data, false, already_charged)?;
526
527		Ok(())
528	}
529
530	/// Cease contract execution and save a data buffer as a result of the execution.
531	/// See [`pallet_revive_uapi::HostFn::return_value`].
532	fn seal_return(
533		&mut self,
534		memory: &mut M,
535		flags: u32,
536		data_ptr: u32,
537		data_len: u32,
538	) -> Result<(), TrapReason> {
539		self.charge_gas(RuntimeCosts::CopyFromContract(data_len))?;
540		if data_len > limits::CALLDATA_BYTES {
541			Err(<Error<E::T>>::ReturnDataTooLarge)?;
542		}
543		Err(TrapReason::Return(ReturnData { flags, data: memory.read(data_ptr, data_len)? }))
544	}
545
546	/// Stores the address of the caller into the supplied buffer.
547	/// See [`pallet_revive_uapi::HostFn::caller`].
548	fn caller(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
549		self.charge_gas(RuntimeCosts::Caller)?;
550		let caller = <E::T as Config>::AddressMapper::to_address(self.ext.caller().account_id()?);
551		Ok(self.write_fixed_sandbox_output(
552			memory,
553			out_ptr,
554			caller.as_bytes(),
555			false,
556			already_charged,
557		)?)
558	}
559
560	/// Stores the address of the call stack origin into the supplied buffer.
561	/// See [`pallet_revive_uapi::HostFn::origin`].
562	fn origin(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
563		self.charge_gas(RuntimeCosts::Origin)?;
564		let origin = <E::T as Config>::AddressMapper::to_address(self.ext.origin().account_id()?);
565		Ok(self.write_fixed_sandbox_output(
566			memory,
567			out_ptr,
568			origin.as_bytes(),
569			false,
570			already_charged,
571		)?)
572	}
573
574	/// Retrieve the code hash for a specified contract address.
575	/// See [`pallet_revive_uapi::HostFn::code_hash`].
576	fn code_hash(&mut self, memory: &mut M, addr_ptr: u32, out_ptr: u32) -> Result<(), TrapReason> {
577		self.charge_gas(RuntimeCosts::CodeHash)?;
578		let address = memory.read_h160(addr_ptr)?;
579		Ok(self.write_fixed_sandbox_output(
580			memory,
581			out_ptr,
582			&self.ext.code_hash(&address).as_bytes(),
583			false,
584			already_charged,
585		)?)
586	}
587
588	/// Retrieve the code size for a given contract address.
589	/// See [`pallet_revive_uapi::HostFn::code_size`].
590	fn code_size(&mut self, memory: &mut M, addr_ptr: u32) -> Result<u64, TrapReason> {
591		self.charge_gas(RuntimeCosts::CodeSize)?;
592		let address = memory.read_h160(addr_ptr)?;
593		Ok(self.ext.code_size(&address))
594	}
595
596	/// Stores the address of the current contract into the supplied buffer.
597	/// See [`pallet_revive_uapi::HostFn::address`].
598	fn address(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
599		self.charge_gas(RuntimeCosts::Address)?;
600		let address = self.ext.address();
601		Ok(self.write_fixed_sandbox_output(
602			memory,
603			out_ptr,
604			address.as_bytes(),
605			false,
606			already_charged,
607		)?)
608	}
609
610	/// Stores the immutable data into the supplied buffer.
611	/// See [`pallet_revive_uapi::HostFn::get_immutable_data`].
612	fn get_immutable_data(
613		&mut self,
614		memory: &mut M,
615		out_ptr: u32,
616		out_len_ptr: u32,
617	) -> Result<(), TrapReason> {
618		let is_delegate = self.ext.is_delegate_call();
619		let charge_len =
620			if is_delegate { limits::IMMUTABLE_BYTES } else { self.ext.immutable_data_len() };
621		let charged = self.charge_gas(RuntimeCosts::GetImmutableData(charge_len))?;
622		let data = self.ext.get_immutable_data()?;
623		if is_delegate {
624			self.adjust_gas(charged, RuntimeCosts::GetImmutableData(data.len() as u32));
625		}
626		self.write_sandbox_output(memory, out_ptr, out_len_ptr, &data, false, already_charged)?;
627		Ok(())
628	}
629
630	/// Attaches the supplied immutable data to the currently executing contract.
631	/// See [`pallet_revive_uapi::HostFn::set_immutable_data`].
632	fn set_immutable_data(&mut self, memory: &mut M, ptr: u32, len: u32) -> Result<(), TrapReason> {
633		if len > limits::IMMUTABLE_BYTES {
634			return Err(Error::<E::T>::OutOfBounds.into());
635		}
636		self.charge_gas(RuntimeCosts::SetImmutableData(len))?;
637		let buf = memory.read(ptr, len)?;
638		let data = buf.try_into().expect("bailed out earlier; qed");
639		self.ext.set_immutable_data(data)?;
640		Ok(())
641	}
642
643	/// Stores the *free* balance of the current account into the supplied buffer.
644	/// See [`pallet_revive_uapi::HostFn::balance`].
645	fn balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
646		self.charge_gas(RuntimeCosts::Balance)?;
647		Ok(self.write_fixed_sandbox_output(
648			memory,
649			out_ptr,
650			&self.ext.balance().to_little_endian(),
651			false,
652			already_charged,
653		)?)
654	}
655
656	/// Stores the *free* balance of the supplied address into the supplied buffer.
657	/// See [`pallet_revive_uapi::HostFn::balance`].
658	fn balance_of(
659		&mut self,
660		memory: &mut M,
661		addr_ptr: u32,
662		out_ptr: u32,
663	) -> Result<(), TrapReason> {
664		self.charge_gas(RuntimeCosts::BalanceOf)?;
665		let address = memory.read_h160(addr_ptr)?;
666		Ok(self.write_fixed_sandbox_output(
667			memory,
668			out_ptr,
669			&self.ext.balance_of(&address).to_little_endian(),
670			false,
671			already_charged,
672		)?)
673	}
674
675	/// Returns the chain ID.
676	/// See [`pallet_revive_uapi::HostFn::chain_id`].
677	fn chain_id(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
678		Ok(self.write_fixed_sandbox_output(
679			memory,
680			out_ptr,
681			&U256::from(<E::T as Config>::ChainId::get()).to_little_endian(),
682			false,
683			|_| Some(RuntimeCosts::CopyToContract(32)),
684		)?)
685	}
686
687	/// Returns the block ref_time limit.
688	/// See [`pallet_revive_uapi::HostFn::gas_limit`].
689	fn gas_limit(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
690		self.charge_gas(RuntimeCosts::GasLimit)?;
691		Ok(self.ext.gas_limit())
692	}
693
694	/// Stores the value transferred along with this call/instantiate into the supplied buffer.
695	/// See [`pallet_revive_uapi::HostFn::value_transferred`].
696	fn value_transferred(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
697		self.charge_gas(RuntimeCosts::ValueTransferred)?;
698		Ok(self.write_fixed_sandbox_output(
699			memory,
700			out_ptr,
701			&self.ext.value_transferred().to_little_endian(),
702			false,
703			already_charged,
704		)?)
705	}
706
707	/// Returns the simulated ethereum `GASPRICE` value.
708	/// See [`pallet_revive_uapi::HostFn::gas_price`].
709	fn gas_price(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
710		self.charge_gas(RuntimeCosts::GasPrice)?;
711		Ok(self.ext.effective_gas_price().saturated_into())
712	}
713
714	/// Returns the simulated ethereum `BASEFEE` value.
715	/// See [`pallet_revive_uapi::HostFn::base_fee`].
716	fn base_fee(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
717		self.charge_gas(RuntimeCosts::BaseFee)?;
718		Ok(self.write_fixed_sandbox_output(
719			memory,
720			out_ptr,
721			&Pallet::<E::T>::evm_base_fee().to_little_endian(),
722			false,
723			already_charged,
724		)?)
725	}
726
727	/// Load the latest block timestamp into the supplied buffer
728	/// See [`pallet_revive_uapi::HostFn::now`].
729	fn now(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
730		self.charge_gas(RuntimeCosts::Now)?;
731		Ok(self.write_fixed_sandbox_output(
732			memory,
733			out_ptr,
734			&self.ext.now().to_little_endian(),
735			false,
736			already_charged,
737		)?)
738	}
739
740	/// Deposit a contract event with the data buffer and optional list of topics.
741	/// See [pallet_revive_uapi::HostFn::deposit_event]
742	#[mutating]
743	fn deposit_event(
744		&mut self,
745		memory: &mut M,
746		topics_ptr: u32,
747		num_topic: u32,
748		data_ptr: u32,
749		data_len: u32,
750	) -> Result<(), TrapReason> {
751		self.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?;
752
753		if num_topic > limits::NUM_EVENT_TOPICS {
754			return Err(Error::<E::T>::TooManyTopics.into());
755		}
756
757		if data_len > limits::EVENT_BYTES {
758			return Err(Error::<E::T>::ValueTooLarge.into());
759		}
760
761		let topics: Vec<H256> = match num_topic {
762			0 => Vec::new(),
763			_ => {
764				let mut v = Vec::with_capacity(num_topic as usize);
765				let topics_len = num_topic * H256::len_bytes() as u32;
766				let buf = memory.read(topics_ptr, topics_len)?;
767				for chunk in buf.chunks_exact(H256::len_bytes()) {
768					v.push(H256::from_slice(chunk));
769				}
770				v
771			},
772		};
773
774		let event_data = memory.read(data_ptr, data_len)?;
775		self.ext.deposit_event(topics, event_data);
776		Ok(())
777	}
778
779	/// Stores the current block number of the current contract into the supplied buffer.
780	/// See [`pallet_revive_uapi::HostFn::block_number`].
781	fn block_number(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
782		self.charge_gas(RuntimeCosts::BlockNumber)?;
783		Ok(self.write_fixed_sandbox_output(
784			memory,
785			out_ptr,
786			&self.ext.block_number().to_little_endian(),
787			false,
788			already_charged,
789		)?)
790	}
791
792	/// Stores the block hash at given block height into the supplied buffer.
793	/// See [`pallet_revive_uapi::HostFn::block_hash`].
794	fn block_hash(
795		&mut self,
796		memory: &mut M,
797		block_number_ptr: u32,
798		out_ptr: u32,
799	) -> Result<(), TrapReason> {
800		self.charge_gas(RuntimeCosts::BlockHash)?;
801		let block_number = memory.read_u256(block_number_ptr)?;
802		let block_hash = self.ext.block_hash(block_number).unwrap_or(H256::zero());
803		Ok(self.write_fixed_sandbox_output(
804			memory,
805			out_ptr,
806			&block_hash.as_bytes(),
807			false,
808			already_charged,
809		)?)
810	}
811
812	/// Stores the current block author into the supplied buffer.
813	/// See [`pallet_revive_uapi::HostFn::block_author`].
814	fn block_author(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
815		self.charge_gas(RuntimeCosts::BlockAuthor)?;
816		let block_author = self.ext.block_author();
817
818		Ok(self.write_fixed_sandbox_output(
819			memory,
820			out_ptr,
821			&block_author.as_bytes(),
822			false,
823			already_charged,
824		)?)
825	}
826
827	/// Computes the KECCAK 256-bit hash on the given input buffer.
828	/// See [`pallet_revive_uapi::HostFn::hash_keccak_256`].
829	fn hash_keccak_256(
830		&mut self,
831		memory: &mut M,
832		input_ptr: u32,
833		input_len: u32,
834		output_ptr: u32,
835	) -> Result<(), TrapReason> {
836		self.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
837		Ok(self.compute_hash_on_intermediate_buffer(
838			memory, keccak_256, input_ptr, input_len, output_ptr,
839		)?)
840	}
841
842	/// Stores the length of the data returned by the last call into the supplied buffer.
843	/// See [`pallet_revive_uapi::HostFn::return_data_size`].
844	fn return_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
845		self.charge_gas(RuntimeCosts::ReturnDataSize)?;
846		Ok(self
847			.ext
848			.last_frame_output()
849			.data
850			.len()
851			.try_into()
852			.expect("usize fits into u64; qed"))
853	}
854
855	/// Stores data returned by the last call, starting from `offset`, into the supplied buffer.
856	/// See [`pallet_revive_uapi::HostFn::return_data_copy`].
857	fn return_data_copy(
858		&mut self,
859		memory: &mut M,
860		out_ptr: u32,
861		out_len_ptr: u32,
862		offset: u32,
863	) -> Result<(), TrapReason> {
864		let output = mem::take(self.ext.last_frame_output_mut());
865		let result = if offset as usize > output.data.len() {
866			Err(Error::<E::T>::OutOfBounds.into())
867		} else {
868			self.write_sandbox_output(
869				memory,
870				out_ptr,
871				out_len_ptr,
872				&output.data[offset as usize..],
873				false,
874				|len| Some(RuntimeCosts::CopyToContract(len)),
875			)
876		};
877		*self.ext.last_frame_output_mut() = output;
878		Ok(result?)
879	}
880
881	/// Returns the amount of evm gas left.
882	///
883	/// The name is only for historical reasons as renaming functions
884	/// would be a breaking change.
885	///
886	/// See [`pallet_revive_uapi::HostFn::gas_left`].
887	fn ref_time_left(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
888		self.charge_gas(RuntimeCosts::RefTimeLeft)?;
889		Ok(self.ext.gas_left())
890	}
891
892	/// Reverts the execution without data and cedes all remaining gas.
893	///
894	/// See [`pallet_revive_uapi::HostFn::consume_all_gas`].
895	fn consume_all_gas(&mut self, memory: &mut M) -> Result<(), TrapReason> {
896		self.ext.frame_meter_mut().consume_all_weight();
897		Err(TrapReason::Return(ReturnData {
898			flags: ReturnFlags::REVERT.bits(),
899			data: Default::default(),
900		}))
901	}
902
903	/// Calculates Ethereum address from the ECDSA compressed public key and stores
904	/// See `uapi/sol/ISystem.sol`.
905	fn ecdsa_to_eth_address(
906		&mut self,
907		memory: &mut M,
908		key_ptr: u32,
909		out_ptr: u32,
910	) -> Result<ReturnErrorCode, TrapReason> {
911		self.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
912		let mut compressed_key: [u8; 33] = [0; 33];
913		memory.read_into_buf(key_ptr, &mut compressed_key)?;
914		let result = self.ext.ecdsa_to_eth_address(&compressed_key);
915		match result {
916			Ok(eth_address) => {
917				memory.write(out_ptr, eth_address.as_ref())?;
918				Ok(ReturnErrorCode::Success)
919			},
920			Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),
921		}
922	}
923
924	/// Verify a sr25519 signature
925	/// See `uapi/sol/ISystem.sol`.
926	fn sr25519_verify(
927		&mut self,
928		memory: &mut M,
929		signature_ptr: u32,
930		pub_key_ptr: u32,
931		message_len: u32,
932		message_ptr: u32,
933	) -> Result<ReturnErrorCode, TrapReason> {
934		self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;
935
936		let mut signature: [u8; 64] = [0; 64];
937		memory.read_into_buf(signature_ptr, &mut signature)?;
938
939		let mut pub_key: [u8; 32] = [0; 32];
940		memory.read_into_buf(pub_key_ptr, &mut pub_key)?;
941
942		let message: Vec<u8> = memory.read(message_ptr, message_len)?;
943
944		if self.ext.sr25519_verify(&signature, &message, &pub_key) {
945			Ok(ReturnErrorCode::Success)
946		} else {
947			Ok(ReturnErrorCode::Sr25519VerifyFailed)
948		}
949	}
950
951	/// Remove the calling account and transfer remaining balance:
952	/// **total** balance if code is deleted from storage, else **free** balance only.
953	/// See [`pallet_revive_uapi::HostFn::terminate`].
954	#[mutating]
955	fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> {
956		let charged = self.charge_gas(RuntimeCosts::Terminate { code_removed: true })?;
957		let beneficiary = memory.read_h160(beneficiary_ptr)?;
958		if matches!(self.ext.terminate_if_same_tx(&beneficiary)?, crate::CodeRemoved::No) {
959			self.adjust_gas(charged, RuntimeCosts::Terminate { code_removed: false });
960		}
961		Err(TrapReason::Termination)
962	}
963}
964
965#[cfg(test)]
966mod tests {
967	use super::*;
968	use pallet_revive_types::runtime_api::*;
969	use strum::VariantArray;
970
971	#[test]
972	fn wire_has_all_syscalls() {
973		let wire_syscalls = PolkavmSyscallV1::VARIANTS
974			.iter()
975			.copied()
976			.map(<&'static str>::from)
977			.collect::<Vec<_>>();
978		let canonical_syscalls = list_trace_ops()
979			.iter()
980			.map(|bytes| str::from_utf8(bytes).unwrap())
981			.collect::<Vec<_>>();
982
983		assert_eq!(
984			wire_syscalls, canonical_syscalls,
985			"There is a difference between the syscalls defined for the wire and the syscalls \
986			available in the system. Wire = {wire_syscalls:?}. Available = {canonical_syscalls:?}"
987		)
988	}
989}