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		// quering the length is free as it is stored with the contract metadata
619		let len = self.ext.immutable_data_len();
620		self.charge_gas(RuntimeCosts::GetImmutableData(len))?;
621		let data = self.ext.get_immutable_data()?;
622		self.write_sandbox_output(memory, out_ptr, out_len_ptr, &data, false, already_charged)?;
623		Ok(())
624	}
625
626	/// Attaches the supplied immutable data to the currently executing contract.
627	/// See [`pallet_revive_uapi::HostFn::set_immutable_data`].
628	fn set_immutable_data(&mut self, memory: &mut M, ptr: u32, len: u32) -> Result<(), TrapReason> {
629		if len > limits::IMMUTABLE_BYTES {
630			return Err(Error::<E::T>::OutOfBounds.into());
631		}
632		self.charge_gas(RuntimeCosts::SetImmutableData(len))?;
633		let buf = memory.read(ptr, len)?;
634		let data = buf.try_into().expect("bailed out earlier; qed");
635		self.ext.set_immutable_data(data)?;
636		Ok(())
637	}
638
639	/// Stores the *free* balance of the current account into the supplied buffer.
640	/// See [`pallet_revive_uapi::HostFn::balance`].
641	fn balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
642		self.charge_gas(RuntimeCosts::Balance)?;
643		Ok(self.write_fixed_sandbox_output(
644			memory,
645			out_ptr,
646			&self.ext.balance().to_little_endian(),
647			false,
648			already_charged,
649		)?)
650	}
651
652	/// Stores the *free* balance of the supplied address into the supplied buffer.
653	/// See [`pallet_revive_uapi::HostFn::balance`].
654	fn balance_of(
655		&mut self,
656		memory: &mut M,
657		addr_ptr: u32,
658		out_ptr: u32,
659	) -> Result<(), TrapReason> {
660		self.charge_gas(RuntimeCosts::BalanceOf)?;
661		let address = memory.read_h160(addr_ptr)?;
662		Ok(self.write_fixed_sandbox_output(
663			memory,
664			out_ptr,
665			&self.ext.balance_of(&address).to_little_endian(),
666			false,
667			already_charged,
668		)?)
669	}
670
671	/// Returns the chain ID.
672	/// See [`pallet_revive_uapi::HostFn::chain_id`].
673	fn chain_id(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
674		Ok(self.write_fixed_sandbox_output(
675			memory,
676			out_ptr,
677			&U256::from(<E::T as Config>::ChainId::get()).to_little_endian(),
678			false,
679			|_| Some(RuntimeCosts::CopyToContract(32)),
680		)?)
681	}
682
683	/// Returns the block ref_time limit.
684	/// See [`pallet_revive_uapi::HostFn::gas_limit`].
685	fn gas_limit(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
686		self.charge_gas(RuntimeCosts::GasLimit)?;
687		Ok(self.ext.gas_limit())
688	}
689
690	/// Stores the value transferred along with this call/instantiate into the supplied buffer.
691	/// See [`pallet_revive_uapi::HostFn::value_transferred`].
692	fn value_transferred(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
693		self.charge_gas(RuntimeCosts::ValueTransferred)?;
694		Ok(self.write_fixed_sandbox_output(
695			memory,
696			out_ptr,
697			&self.ext.value_transferred().to_little_endian(),
698			false,
699			already_charged,
700		)?)
701	}
702
703	/// Returns the simulated ethereum `GASPRICE` value.
704	/// See [`pallet_revive_uapi::HostFn::gas_price`].
705	fn gas_price(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
706		self.charge_gas(RuntimeCosts::GasPrice)?;
707		Ok(self.ext.effective_gas_price().saturated_into())
708	}
709
710	/// Returns the simulated ethereum `BASEFEE` value.
711	/// See [`pallet_revive_uapi::HostFn::base_fee`].
712	fn base_fee(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
713		self.charge_gas(RuntimeCosts::BaseFee)?;
714		Ok(self.write_fixed_sandbox_output(
715			memory,
716			out_ptr,
717			&Pallet::<E::T>::evm_base_fee().to_little_endian(),
718			false,
719			already_charged,
720		)?)
721	}
722
723	/// Load the latest block timestamp into the supplied buffer
724	/// See [`pallet_revive_uapi::HostFn::now`].
725	fn now(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
726		self.charge_gas(RuntimeCosts::Now)?;
727		Ok(self.write_fixed_sandbox_output(
728			memory,
729			out_ptr,
730			&self.ext.now().to_little_endian(),
731			false,
732			already_charged,
733		)?)
734	}
735
736	/// Deposit a contract event with the data buffer and optional list of topics.
737	/// See [pallet_revive_uapi::HostFn::deposit_event]
738	#[mutating]
739	fn deposit_event(
740		&mut self,
741		memory: &mut M,
742		topics_ptr: u32,
743		num_topic: u32,
744		data_ptr: u32,
745		data_len: u32,
746	) -> Result<(), TrapReason> {
747		self.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?;
748
749		if num_topic > limits::NUM_EVENT_TOPICS {
750			return Err(Error::<E::T>::TooManyTopics.into());
751		}
752
753		if data_len > limits::EVENT_BYTES {
754			return Err(Error::<E::T>::ValueTooLarge.into());
755		}
756
757		let topics: Vec<H256> = match num_topic {
758			0 => Vec::new(),
759			_ => {
760				let mut v = Vec::with_capacity(num_topic as usize);
761				let topics_len = num_topic * H256::len_bytes() as u32;
762				let buf = memory.read(topics_ptr, topics_len)?;
763				for chunk in buf.chunks_exact(H256::len_bytes()) {
764					v.push(H256::from_slice(chunk));
765				}
766				v
767			},
768		};
769
770		let event_data = memory.read(data_ptr, data_len)?;
771		self.ext.deposit_event(topics, event_data);
772		Ok(())
773	}
774
775	/// Stores the current block number of the current contract into the supplied buffer.
776	/// See [`pallet_revive_uapi::HostFn::block_number`].
777	fn block_number(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
778		self.charge_gas(RuntimeCosts::BlockNumber)?;
779		Ok(self.write_fixed_sandbox_output(
780			memory,
781			out_ptr,
782			&self.ext.block_number().to_little_endian(),
783			false,
784			already_charged,
785		)?)
786	}
787
788	/// Stores the block hash at given block height into the supplied buffer.
789	/// See [`pallet_revive_uapi::HostFn::block_hash`].
790	fn block_hash(
791		&mut self,
792		memory: &mut M,
793		block_number_ptr: u32,
794		out_ptr: u32,
795	) -> Result<(), TrapReason> {
796		self.charge_gas(RuntimeCosts::BlockHash)?;
797		let block_number = memory.read_u256(block_number_ptr)?;
798		let block_hash = self.ext.block_hash(block_number).unwrap_or(H256::zero());
799		Ok(self.write_fixed_sandbox_output(
800			memory,
801			out_ptr,
802			&block_hash.as_bytes(),
803			false,
804			already_charged,
805		)?)
806	}
807
808	/// Stores the current block author into the supplied buffer.
809	/// See [`pallet_revive_uapi::HostFn::block_author`].
810	fn block_author(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> {
811		self.charge_gas(RuntimeCosts::BlockAuthor)?;
812		let block_author = self.ext.block_author();
813
814		Ok(self.write_fixed_sandbox_output(
815			memory,
816			out_ptr,
817			&block_author.as_bytes(),
818			false,
819			already_charged,
820		)?)
821	}
822
823	/// Computes the KECCAK 256-bit hash on the given input buffer.
824	/// See [`pallet_revive_uapi::HostFn::hash_keccak_256`].
825	fn hash_keccak_256(
826		&mut self,
827		memory: &mut M,
828		input_ptr: u32,
829		input_len: u32,
830		output_ptr: u32,
831	) -> Result<(), TrapReason> {
832		self.charge_gas(RuntimeCosts::HashKeccak256(input_len))?;
833		Ok(self.compute_hash_on_intermediate_buffer(
834			memory, keccak_256, input_ptr, input_len, output_ptr,
835		)?)
836	}
837
838	/// Stores the length of the data returned by the last call into the supplied buffer.
839	/// See [`pallet_revive_uapi::HostFn::return_data_size`].
840	fn return_data_size(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
841		self.charge_gas(RuntimeCosts::ReturnDataSize)?;
842		Ok(self
843			.ext
844			.last_frame_output()
845			.data
846			.len()
847			.try_into()
848			.expect("usize fits into u64; qed"))
849	}
850
851	/// Stores data returned by the last call, starting from `offset`, into the supplied buffer.
852	/// See [`pallet_revive_uapi::HostFn::return_data_copy`].
853	fn return_data_copy(
854		&mut self,
855		memory: &mut M,
856		out_ptr: u32,
857		out_len_ptr: u32,
858		offset: u32,
859	) -> Result<(), TrapReason> {
860		let output = mem::take(self.ext.last_frame_output_mut());
861		let result = if offset as usize > output.data.len() {
862			Err(Error::<E::T>::OutOfBounds.into())
863		} else {
864			self.write_sandbox_output(
865				memory,
866				out_ptr,
867				out_len_ptr,
868				&output.data[offset as usize..],
869				false,
870				|len| Some(RuntimeCosts::CopyToContract(len)),
871			)
872		};
873		*self.ext.last_frame_output_mut() = output;
874		Ok(result?)
875	}
876
877	/// Returns the amount of evm gas left.
878	///
879	/// The name is only for historical reasons as renaming functions
880	/// would be a breaking change.
881	///
882	/// See [`pallet_revive_uapi::HostFn::gas_left`].
883	fn ref_time_left(&mut self, memory: &mut M) -> Result<u64, TrapReason> {
884		self.charge_gas(RuntimeCosts::RefTimeLeft)?;
885		Ok(self.ext.gas_left())
886	}
887
888	/// Reverts the execution without data and cedes all remaining gas.
889	///
890	/// See [`pallet_revive_uapi::HostFn::consume_all_gas`].
891	fn consume_all_gas(&mut self, memory: &mut M) -> Result<(), TrapReason> {
892		self.ext.frame_meter_mut().consume_all_weight();
893		Err(TrapReason::Return(ReturnData {
894			flags: ReturnFlags::REVERT.bits(),
895			data: Default::default(),
896		}))
897	}
898
899	/// Calculates Ethereum address from the ECDSA compressed public key and stores
900	/// See `uapi/sol/ISystem.sol`.
901	fn ecdsa_to_eth_address(
902		&mut self,
903		memory: &mut M,
904		key_ptr: u32,
905		out_ptr: u32,
906	) -> Result<ReturnErrorCode, TrapReason> {
907		self.charge_gas(RuntimeCosts::EcdsaToEthAddress)?;
908		let mut compressed_key: [u8; 33] = [0; 33];
909		memory.read_into_buf(key_ptr, &mut compressed_key)?;
910		let result = self.ext.ecdsa_to_eth_address(&compressed_key);
911		match result {
912			Ok(eth_address) => {
913				memory.write(out_ptr, eth_address.as_ref())?;
914				Ok(ReturnErrorCode::Success)
915			},
916			Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed),
917		}
918	}
919
920	/// Verify a sr25519 signature
921	/// See `uapi/sol/ISystem.sol`.
922	fn sr25519_verify(
923		&mut self,
924		memory: &mut M,
925		signature_ptr: u32,
926		pub_key_ptr: u32,
927		message_len: u32,
928		message_ptr: u32,
929	) -> Result<ReturnErrorCode, TrapReason> {
930		self.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?;
931
932		let mut signature: [u8; 64] = [0; 64];
933		memory.read_into_buf(signature_ptr, &mut signature)?;
934
935		let mut pub_key: [u8; 32] = [0; 32];
936		memory.read_into_buf(pub_key_ptr, &mut pub_key)?;
937
938		let message: Vec<u8> = memory.read(message_ptr, message_len)?;
939
940		if self.ext.sr25519_verify(&signature, &message, &pub_key) {
941			Ok(ReturnErrorCode::Success)
942		} else {
943			Ok(ReturnErrorCode::Sr25519VerifyFailed)
944		}
945	}
946
947	/// Remove the calling account and transfer remaining balance:
948	/// **total** balance if code is deleted from storage, else **free** balance only.
949	/// See [`pallet_revive_uapi::HostFn::terminate`].
950	#[mutating]
951	fn terminate(&mut self, memory: &mut M, beneficiary_ptr: u32) -> Result<(), TrapReason> {
952		let charged = self.charge_gas(RuntimeCosts::Terminate { code_removed: true })?;
953		let beneficiary = memory.read_h160(beneficiary_ptr)?;
954		if matches!(self.ext.terminate_if_same_tx(&beneficiary)?, crate::CodeRemoved::No) {
955			self.adjust_gas(charged, RuntimeCosts::Terminate { code_removed: false });
956		}
957		Err(TrapReason::Termination)
958	}
959}