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