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