// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Environment definition of the wasm smart-contract runtime. use crate::{ exec::{ExecError, ExecResult, Ext, Key, TopicOf}, gas::{ChargedAmount, Token}, primitives::ExecReturnValue, BalanceOf, CodeHash, Config, DebugBufferVec, Error, SENTINEL, }; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use frame_support::{ dispatch::DispatchInfo, ensure, pallet_prelude::{DispatchResult, DispatchResultWithPostInfo}, parameter_types, traits::Get, weights::Weight, }; use pallet_contracts_proc_macro::define_env; use pallet_contracts_uapi::{CallFlags, ReturnFlags}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; use sp_runtime::{ traits::{Bounded, Zero}, DispatchError, RuntimeDebug, }; use sp_std::{fmt, prelude::*}; use wasmi::{core::HostError, errors::LinkerError, Linker, Memory, Store}; use xcm::VersionedXcm; type CallOf = ::RuntimeCall; /// The maximum nesting depth a contract can use when encoding types. const MAX_DECODE_NESTING: u32 = 256; /// Passed to [`Environment`] to determine whether it should expose deprecated interfaces. pub enum AllowDeprecatedInterface { /// No deprecated interfaces are exposed. No, /// Deprecated interfaces are exposed. Yes, } /// Passed to [`Environment`] to determine whether it should expose unstable interfaces. pub enum AllowUnstableInterface { /// No unstable interfaces are exposed. No, /// Unstable interfaces are exposed. Yes, } /// Trait implemented by the [`define_env`](pallet_contracts_proc_macro::define_env) macro for the /// emitted `Env` struct. pub trait Environment { /// Adds all declared functions to the supplied [`Linker`](wasmi::Linker) and /// [`Store`](wasmi::Store). fn define( store: &mut Store, linker: &mut Linker, allow_unstable: AllowUnstableInterface, allow_deprecated: AllowDeprecatedInterface, ) -> Result<(), LinkerError>; } /// Type of a storage key. enum KeyType { /// Legacy fix sized key `[u8;32]`. Fix, /// Variable sized key used in transparent hashing, /// cannot be larger than MaxStorageKeyLen. Var(u32), } pub use pallet_contracts_uapi::ReturnErrorCode; parameter_types! { /// Getter types used by [`crate::api_doc::Current::call_runtime`] const CallRuntimeFailed: ReturnErrorCode = ReturnErrorCode::CallRuntimeFailed; /// Getter types used by [`crate::api_doc::Current::xcm_execute`] const XcmExecutionFailed: ReturnErrorCode = ReturnErrorCode::XcmExecutionFailed; } impl From for ReturnErrorCode { fn from(from: ExecReturnValue) -> Self { if from.flags.contains(ReturnFlags::REVERT) { Self::CalleeReverted } else { Self::Success } } } /// The data passed through when a contract uses `seal_return`. #[derive(RuntimeDebug)] pub struct ReturnData { /// The flags as passed through by the contract. They are still unchecked and /// will later be parsed into a `ReturnFlags` bitflags struct. flags: u32, /// The output buffer passed by the contract as return data. data: Vec, } /// Enumerates all possible reasons why a trap was generated. /// /// This is either used to supply the caller with more information about why an error /// occurred (the SupervisorError variant). /// The other case is where the trap does not constitute an error but rather was invoked /// as a quick way to terminate the application (all other variants). #[derive(RuntimeDebug)] pub enum TrapReason { /// The supervisor trapped the contract because of an error condition occurred during /// execution in privileged code. SupervisorError(DispatchError), /// Signals that trap was generated in response to call `seal_return` host function. Return(ReturnData), /// Signals that a trap was generated in response to a successful call to the /// `seal_terminate` host function. Termination, } impl> From for TrapReason { fn from(from: T) -> Self { Self::SupervisorError(from.into()) } } impl fmt::Display for TrapReason { fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { Ok(()) } } impl HostError for TrapReason {} #[cfg_attr(test, derive(Debug, PartialEq, Eq))] #[derive(Copy, Clone)] pub enum RuntimeCosts { /// Weight charged for copying data from the sandbox. CopyFromContract(u32), /// Weight charged for copying data to the sandbox. CopyToContract(u32), /// Weight of calling `seal_caller`. Caller, /// Weight of calling `seal_is_contract`. IsContract, /// Weight of calling `seal_code_hash`. CodeHash, /// Weight of calling `seal_own_code_hash`. OwnCodeHash, /// Weight of calling `seal_caller_is_origin`. CallerIsOrigin, /// Weight of calling `caller_is_root`. CallerIsRoot, /// Weight of calling `seal_address`. Address, /// Weight of calling `seal_gas_left`. GasLeft, /// Weight of calling `seal_balance`. Balance, /// Weight of calling `seal_value_transferred`. ValueTransferred, /// Weight of calling `seal_minimum_balance`. MinimumBalance, /// Weight of calling `seal_block_number`. BlockNumber, /// Weight of calling `seal_now`. Now, /// Weight of calling `seal_weight_to_fee`. WeightToFee, /// Weight of calling `seal_input` without the weight of copying the input. InputBase, /// Weight of calling `seal_return` for the given output size. Return(u32), /// Weight of calling `seal_terminate`. Terminate, /// Weight of calling `seal_random`. It includes the weight for copying the subject. Random, /// Weight of calling `seal_deposit_event` with the given number of topics and event size. DepositEvent { num_topic: u32, len: u32 }, /// Weight of calling `seal_debug_message` per byte of passed message. DebugMessage(u32), /// Weight of calling `seal_set_storage` for the given storage item sizes. SetStorage { old_bytes: u32, new_bytes: u32 }, /// Weight of calling `seal_clear_storage` per cleared byte. ClearStorage(u32), /// Weight of calling `seal_contains_storage` per byte of the checked item. ContainsStorage(u32), /// Weight of calling `seal_get_storage` with the specified size in storage. GetStorage(u32), /// Weight of calling `seal_take_storage` for the given size. TakeStorage(u32), /// Weight of calling `seal_transfer`. Transfer, /// Base weight of calling `seal_call`. CallBase, /// Weight of calling `seal_delegate_call` for the given input size. DelegateCallBase, /// Weight of the transfer performed during a call. CallSurchargeTransfer, /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. CallInputCloned(u32), /// Weight of calling `seal_instantiate` for the given input length and salt. InstantiateBase { input_data_len: u32, salt_len: u32 }, /// Weight of the transfer performed during an instantiate. InstantiateSurchargeTransfer, /// Weight of calling `seal_hash_sha_256` for the given input size. HashSha256(u32), /// Weight of calling `seal_hash_keccak_256` for the given input size. HashKeccak256(u32), /// Weight of calling `seal_hash_blake2_256` for the given input size. HashBlake256(u32), /// Weight of calling `seal_hash_blake2_128` for the given input size. HashBlake128(u32), /// Weight of calling `seal_ecdsa_recover`. EcdsaRecovery, /// Weight of calling `seal_sr25519_verify` for the given input size. Sr25519Verify(u32), /// Weight charged by a chain extension through `seal_call_chain_extension`. ChainExtension(Weight), /// Weight charged for calling into the runtime. CallRuntime(Weight), /// Weight charged for calling xcm_execute. CallXcmExecute(Weight), /// Weight of calling `seal_set_code_hash` SetCodeHash, /// Weight of calling `ecdsa_to_eth_address` EcdsaToEthAddress, /// Weight of calling `reentrance_count` ReentrantCount, /// Weight of calling `account_reentrance_count` AccountEntranceCount, /// Weight of calling `instantiation_nonce` InstantiationNonce, /// Weight of calling `lock_delegate_dependency` LockDelegateDependency, /// Weight of calling `unlock_delegate_dependency` UnlockDelegateDependency, } impl Token for RuntimeCosts { fn influence_lowest_gas_limit(&self) -> bool { match self { &Self::CallXcmExecute(_) => false, _ => true, } } fn weight(&self) -> Weight { let s = T::Schedule::get().host_fn_weights; use self::RuntimeCosts::*; match *self { CopyFromContract(len) => s.return_per_byte.saturating_mul(len.into()), CopyToContract(len) => s.input_per_byte.saturating_mul(len.into()), Caller => s.caller, IsContract => s.is_contract, CodeHash => s.code_hash, OwnCodeHash => s.own_code_hash, CallerIsOrigin => s.caller_is_origin, CallerIsRoot => s.caller_is_root, Address => s.address, GasLeft => s.gas_left, Balance => s.balance, ValueTransferred => s.value_transferred, MinimumBalance => s.minimum_balance, BlockNumber => s.block_number, Now => s.now, WeightToFee => s.weight_to_fee, InputBase => s.input, Return(len) => s.r#return.saturating_add(s.return_per_byte.saturating_mul(len.into())), Terminate => s.terminate, Random => s.random, DepositEvent { num_topic, len } => s .deposit_event .saturating_add(s.deposit_event_per_topic.saturating_mul(num_topic.into())) .saturating_add(s.deposit_event_per_byte.saturating_mul(len.into())), DebugMessage(len) => s .debug_message .saturating_add(s.deposit_event_per_byte.saturating_mul(len.into())), SetStorage { new_bytes, old_bytes } => s .set_storage .saturating_add(s.set_storage_per_new_byte.saturating_mul(new_bytes.into())) .saturating_add(s.set_storage_per_old_byte.saturating_mul(old_bytes.into())), ClearStorage(len) => s .clear_storage .saturating_add(s.clear_storage_per_byte.saturating_mul(len.into())), ContainsStorage(len) => s .contains_storage .saturating_add(s.contains_storage_per_byte.saturating_mul(len.into())), GetStorage(len) => s.get_storage.saturating_add(s.get_storage_per_byte.saturating_mul(len.into())), TakeStorage(len) => s .take_storage .saturating_add(s.take_storage_per_byte.saturating_mul(len.into())), Transfer => s.transfer, CallBase => s.call, DelegateCallBase => s.delegate_call, CallSurchargeTransfer => s.call_transfer_surcharge, CallInputCloned(len) => s.call_per_cloned_byte.saturating_mul(len.into()), InstantiateBase { input_data_len, salt_len } => s .instantiate .saturating_add(s.instantiate_per_input_byte.saturating_mul(input_data_len.into())) .saturating_add(s.instantiate_per_salt_byte.saturating_mul(salt_len.into())), InstantiateSurchargeTransfer => s.instantiate_transfer_surcharge, HashSha256(len) => s .hash_sha2_256 .saturating_add(s.hash_sha2_256_per_byte.saturating_mul(len.into())), HashKeccak256(len) => s .hash_keccak_256 .saturating_add(s.hash_keccak_256_per_byte.saturating_mul(len.into())), HashBlake256(len) => s .hash_blake2_256 .saturating_add(s.hash_blake2_256_per_byte.saturating_mul(len.into())), HashBlake128(len) => s .hash_blake2_128 .saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())), EcdsaRecovery => s.ecdsa_recover, Sr25519Verify(len) => s .sr25519_verify .saturating_add(s.sr25519_verify_per_byte.saturating_mul(len.into())), ChainExtension(weight) | CallRuntime(weight) | CallXcmExecute(weight) => weight, SetCodeHash => s.set_code_hash, EcdsaToEthAddress => s.ecdsa_to_eth_address, ReentrantCount => s.reentrance_count, AccountEntranceCount => s.account_reentrance_count, InstantiationNonce => s.instantiation_nonce, LockDelegateDependency => s.lock_delegate_dependency, UnlockDelegateDependency => s.unlock_delegate_dependency, } } } /// Same as [`Runtime::charge_gas`]. /// /// We need this access as a macro because sometimes hiding the lifetimes behind /// a function won't work out. macro_rules! charge_gas { ($runtime:expr, $costs:expr) => {{ $runtime.ext.gas_meter_mut().charge($costs) }}; } /// The kind of call that should be performed. enum CallType { /// Execute another instantiated contract Call { callee_ptr: u32, value_ptr: u32, deposit_ptr: u32, weight: Weight }, /// Execute deployed code in the context (storage, account ID, value) of the caller contract DelegateCall { code_hash_ptr: u32 }, } impl CallType { fn cost(&self) -> RuntimeCosts { match self { CallType::Call { .. } => RuntimeCosts::CallBase, CallType::DelegateCall { .. } => RuntimeCosts::DelegateCallBase, } } } /// This is only appropriate when writing out data of constant size that does not depend on user /// input. In this case the costs for this copy was already charged as part of the token at /// the beginning of the API entry point. fn already_charged(_: u32) -> Option { None } /// Ensure that the XCM program is executable, by checking that it does not contain any [`Transact`] /// instruction with a call that is not allowed by the CallFilter. fn ensure_executable(message: &VersionedXcm>) -> DispatchResult { use frame_support::traits::Contains; use xcm::prelude::{Transact, Xcm}; let mut message: Xcm> = message.clone().try_into().map_err(|_| Error::::XCMDecodeFailed)?; message.iter_mut().try_for_each(|inst| -> DispatchResult { let Transact { ref mut call, .. } = inst else { return Ok(()) }; let call = call.ensure_decoded().map_err(|_| Error::::XCMDecodeFailed)?; if !::CallFilter::contains(call) { return Err(frame_system::Error::::CallFiltered.into()) } Ok(()) })?; Ok(()) } /// Can only be used for one call. pub struct Runtime<'a, E: Ext + 'a> { ext: &'a mut E, input_data: Option>, memory: Option, chain_extension: Option::ChainExtension>>, } impl<'a, E: Ext + 'a> Runtime<'a, E> { pub fn new(ext: &'a mut E, input_data: Vec) -> Self { Runtime { ext, input_data: Some(input_data), memory: None, chain_extension: Some(Box::new(Default::default())), } } pub fn memory(&self) -> Option { self.memory } pub fn set_memory(&mut self, memory: Memory) { self.memory = Some(memory); } /// Converts the sandbox result and the runtime state into the execution outcome. pub fn to_execution_result(self, sandbox_result: Result<(), wasmi::Error>) -> ExecResult { use wasmi::core::TrapCode::OutOfFuel; use TrapReason::*; match sandbox_result { // Contract returned from main function -> no data was returned. Ok(_) => Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new() }), // `OutOfGas` when host asks engine to consume more than left in the _store_. // We should never get this case, as gas meter is being charged (and hence raises error) // first. Err(wasmi::Error::Store(_)) => Err(Error::::OutOfGas.into()), // Contract either trapped or some host function aborted the execution. Err(wasmi::Error::Trap(trap)) => { if let Some(OutOfFuel) = trap.trap_code() { // `OutOfGas` during engine execution. return Err(Error::::OutOfGas.into()) } // If we encoded a reason then it is some abort generated by a host function. if let Some(reason) = &trap.downcast_ref::() { match &reason { Return(ReturnData { flags, data }) => { let flags = ReturnFlags::from_bits(*flags) .ok_or(Error::::InvalidCallFlags)?; return Ok(ExecReturnValue { flags, data: data.to_vec() }) }, Termination => return Ok(ExecReturnValue { flags: ReturnFlags::empty(), data: Vec::new(), }), SupervisorError(error) => return Err((*error).into()), } } // Otherwise the trap came from the contract itself. Err(Error::::ContractTrapped.into()) }, // Any other error is returned only if instantiation or linking failed (i.e. // wasm binary tried to import a function that is not provided by the host). // This shouldn't happen because validation process ought to reject such binaries. // // Because panics are really undesirable in the runtime code, we treat this as // a trap for now. Eventually, we might want to revisit this. Err(_) => Err(Error::::CodeRejected.into()), } } /// Get a mutable reference to the inner `Ext`. /// /// This is mainly for the chain extension to have access to the environment the /// contract is executing in. pub fn ext(&mut self) -> &mut E { self.ext } /// Charge the gas meter with the specified token. /// /// Returns `Err(HostError)` if there is not enough gas. pub fn charge_gas(&mut self, costs: RuntimeCosts) -> Result { charge_gas!(self, costs) } /// Adjust a previously charged amount down to its actual amount. /// /// This is when a maximum a priori amount was charged and then should be partially /// refunded to match the actual amount. pub fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) { self.ext.gas_meter_mut().adjust_gas(charged, actual_costs); } /// Charge, Run and adjust gas, for executing the given dispatchable. fn call_dispatchable>( &mut self, dispatch_info: DispatchInfo, runtime_cost: impl Fn(Weight) -> RuntimeCosts, run: impl FnOnce(&mut Self) -> DispatchResultWithPostInfo, ) -> Result { use frame_support::dispatch::extract_actual_weight; let charged = self.charge_gas(runtime_cost(dispatch_info.weight))?; let result = run(self); let actual_weight = extract_actual_weight(&result, &dispatch_info); self.adjust_gas(charged, runtime_cost(actual_weight)); match result { Ok(_) => Ok(ReturnErrorCode::Success), Err(e) => { if self.ext.debug_buffer_enabled() { self.ext.append_debug_buffer("call failed with: "); self.ext.append_debug_buffer(e.into()); }; Ok(ErrorReturnCode::get()) }, } } /// Read designated chunk from the sandbox memory. /// /// Returns `Err` if one of the following conditions occurs: /// /// - requested buffer is not within the bounds of the sandbox memory. pub fn read_sandbox_memory( &self, memory: &[u8], ptr: u32, len: u32, ) -> Result, DispatchError> { ensure!(len <= self.ext.schedule().limits.max_memory_size(), Error::::OutOfBounds); let mut buf = vec![0u8; len as usize]; self.read_sandbox_memory_into_buf(memory, ptr, buf.as_mut_slice())?; Ok(buf) } /// Read designated chunk from the sandbox memory into the supplied buffer. /// /// Returns `Err` if one of the following conditions occurs: /// /// - requested buffer is not within the bounds of the sandbox memory. pub fn read_sandbox_memory_into_buf( &self, memory: &[u8], ptr: u32, buf: &mut [u8], ) -> Result<(), DispatchError> { let ptr = ptr as usize; let bound_checked = memory.get(ptr..ptr + buf.len()).ok_or_else(|| Error::::OutOfBounds)?; buf.copy_from_slice(bound_checked); Ok(()) } /// Reads and decodes a type with a size fixed at compile time from contract memory. /// /// # Note /// /// The weight of reading a fixed value is included in the overall weight of any /// contract callable function. pub fn read_sandbox_memory_as( &self, memory: &[u8], ptr: u32, ) -> Result { let ptr = ptr as usize; let mut bound_checked = memory.get(ptr..).ok_or_else(|| Error::::OutOfBounds)?; let decoded = D::decode_with_depth_limit(MAX_DECODE_NESTING, &mut bound_checked) .map_err(|_| DispatchError::from(Error::::DecodingFailed))?; Ok(decoded) } /// Read designated chunk from the sandbox memory and attempt to decode into the specified type. /// /// Returns `Err` if one of the following conditions occurs: /// /// - requested buffer is not within the bounds of the sandbox memory. /// - the buffer contents cannot be decoded as the required type. /// /// # Note /// /// There must be an extra benchmark for determining the influence of `len` with /// regard to the overall weight. pub fn read_sandbox_memory_as_unbounded( &self, memory: &[u8], ptr: u32, len: u32, ) -> Result { let ptr = ptr as usize; let mut bound_checked = memory.get(ptr..ptr + len as usize).ok_or_else(|| Error::::OutOfBounds)?; let decoded = D::decode_all_with_depth_limit(MAX_DECODE_NESTING, &mut bound_checked) .map_err(|_| DispatchError::from(Error::::DecodingFailed))?; Ok(decoded) } /// Write the given buffer and its length to the designated locations in sandbox memory and /// charge gas according to the token returned by `create_token`. // /// `out_ptr` is the location in sandbox memory where `buf` should be written to. /// `out_len_ptr` is an in-out location in sandbox memory. It is read to determine the /// length of the buffer located at `out_ptr`. If that buffer is large enough the actual /// `buf.len()` is written to this location. /// /// If `out_ptr` is set to the sentinel value of `SENTINEL` and `allow_skip` is true the /// operation is skipped and `Ok` is returned. This is supposed to help callers to make copying /// output optional. For example to skip copying back the output buffer of an `seal_call` /// when the caller is not interested in the result. /// /// `create_token` can optionally instruct this function to charge the gas meter with the token /// it returns. `create_token` receives the variable amount of bytes that are about to be copied /// by this function. /// /// In addition to the error conditions of `write_sandbox_memory` this functions returns /// `Err` if the size of the buffer located at `out_ptr` is too small to fit `buf`. pub fn write_sandbox_output( &mut self, memory: &mut [u8], out_ptr: u32, out_len_ptr: u32, buf: &[u8], allow_skip: bool, create_token: impl FnOnce(u32) -> Option, ) -> Result<(), DispatchError> { if allow_skip && out_ptr == SENTINEL { return Ok(()) } let buf_len = buf.len() as u32; let len: u32 = self.read_sandbox_memory_as(memory, out_len_ptr)?; if len < buf_len { return Err(Error::::OutputBufferTooSmall.into()) } if let Some(costs) = create_token(buf_len) { self.charge_gas(costs)?; } self.write_sandbox_memory(memory, out_ptr, buf)?; self.write_sandbox_memory(memory, out_len_ptr, &buf_len.encode()) } /// Write the given buffer to the designated location in the sandbox memory. /// /// Returns `Err` if one of the following conditions occurs: /// /// - designated area is not within the bounds of the sandbox memory. fn write_sandbox_memory( &self, memory: &mut [u8], ptr: u32, buf: &[u8], ) -> Result<(), DispatchError> { let ptr = ptr as usize; let bound_checked = memory.get_mut(ptr..ptr + buf.len()).ok_or_else(|| Error::::OutOfBounds)?; bound_checked.copy_from_slice(buf); Ok(()) } /// Computes the given hash function on the supplied input. /// /// Reads from the sandboxed input buffer into an intermediate buffer. /// Returns the result directly to the output buffer of the sandboxed memory. /// /// It is the callers responsibility to provide an output buffer that /// is large enough to hold the expected amount of bytes returned by the /// chosen hash function. /// /// # Note /// /// The `input` and `output` buffers may overlap. fn compute_hash_on_intermediate_buffer( &self, memory: &mut [u8], hash_fn: F, input_ptr: u32, input_len: u32, output_ptr: u32, ) -> Result<(), DispatchError> where F: FnOnce(&[u8]) -> R, R: AsRef<[u8]>, { // Copy input into supervisor memory. let input = self.read_sandbox_memory(memory, input_ptr, input_len)?; // Compute the hash on the input buffer using the given hash function. let hash = hash_fn(&input); // Write the resulting hash back into the sandboxed output buffer. self.write_sandbox_memory(memory, output_ptr, hash.as_ref())?; Ok(()) } /// Fallible conversion of `DispatchError` to `ReturnErrorCode`. fn err_into_return_code(from: DispatchError) -> Result { use ReturnErrorCode::*; let transfer_failed = Error::::TransferFailed.into(); let no_code = Error::::CodeNotFound.into(); let not_found = Error::::ContractNotFound.into(); match from { x if x == transfer_failed => Ok(TransferFailed), x if x == no_code => Ok(CodeNotFound), x if x == not_found => Ok(NotCallable), err => Err(err), } } /// Fallible conversion of a `ExecResult` to `ReturnErrorCode`. fn exec_into_return_code(from: ExecResult) -> Result { use crate::exec::ErrorOrigin::Callee; let ExecError { error, origin } = match from { Ok(retval) => return Ok(retval.into()), Err(err) => err, }; match (error, origin) { (_, Callee) => Ok(ReturnErrorCode::CalleeTrapped), (err, _) => Self::err_into_return_code(err), } } fn decode_key( &self, memory: &[u8], key_type: KeyType, key_ptr: u32, ) -> Result, TrapReason> { let res = match key_type { KeyType::Fix => { let key = self.read_sandbox_memory(memory, key_ptr, 32u32)?; Key::try_from_fix(key) }, KeyType::Var(len) => { ensure!( len <= <::T as Config>::MaxStorageKeyLen::get(), Error::::DecodingFailed ); let key = self.read_sandbox_memory(memory, key_ptr, len)?; Key::try_from_var(key) }, }; res.map_err(|_| Error::::DecodingFailed.into()) } fn set_storage( &mut self, memory: &[u8], key_type: KeyType, key_ptr: u32, value_ptr: u32, value_len: u32, ) -> Result { let max_size = self.ext.max_value_size(); let charged = self .charge_gas(RuntimeCosts::SetStorage { new_bytes: value_len, old_bytes: max_size })?; if value_len > max_size { return Err(Error::::ValueTooLarge.into()) } let key = self.decode_key(memory, key_type, key_ptr)?; let value = Some(self.read_sandbox_memory(memory, value_ptr, value_len)?); let write_outcome = self.ext.set_storage(&key, value, false)?; self.adjust_gas( charged, RuntimeCosts::SetStorage { new_bytes: value_len, old_bytes: write_outcome.old_len() }, ); Ok(write_outcome.old_len_with_sentinel()) } fn clear_storage( &mut self, memory: &[u8], key_type: KeyType, key_ptr: u32, ) -> Result { let charged = self.charge_gas(RuntimeCosts::ClearStorage(self.ext.max_value_size()))?; let key = self.decode_key(memory, key_type, key_ptr)?; let outcome = self.ext.set_storage(&key, None, false)?; self.adjust_gas(charged, RuntimeCosts::ClearStorage(outcome.old_len())); Ok(outcome.old_len_with_sentinel()) } fn get_storage( &mut self, memory: &mut [u8], key_type: KeyType, key_ptr: u32, out_ptr: u32, out_len_ptr: u32, ) -> Result { let charged = self.charge_gas(RuntimeCosts::GetStorage(self.ext.max_value_size()))?; let key = self.decode_key(memory, key_type, key_ptr)?; let outcome = self.ext.get_storage(&key); if let Some(value) = outcome { self.adjust_gas(charged, RuntimeCosts::GetStorage(value.len() as u32)); self.write_sandbox_output( memory, out_ptr, out_len_ptr, &value, false, already_charged, )?; Ok(ReturnErrorCode::Success) } else { self.adjust_gas(charged, RuntimeCosts::GetStorage(0)); Ok(ReturnErrorCode::KeyNotFound) } } fn contains_storage( &mut self, memory: &[u8], key_type: KeyType, key_ptr: u32, ) -> Result { let charged = self.charge_gas(RuntimeCosts::ContainsStorage(self.ext.max_value_size()))?; let key = self.decode_key(memory, key_type, key_ptr)?; let outcome = self.ext.get_storage_size(&key); self.adjust_gas(charged, RuntimeCosts::ClearStorage(outcome.unwrap_or(0))); Ok(outcome.unwrap_or(SENTINEL)) } fn call( &mut self, memory: &mut [u8], flags: CallFlags, call_type: CallType, input_data_ptr: u32, input_data_len: u32, output_ptr: u32, output_len_ptr: u32, ) -> Result { self.charge_gas(call_type.cost())?; let input_data = if flags.contains(CallFlags::CLONE_INPUT) { let input = self.input_data.as_ref().ok_or(Error::::InputForwarded)?; charge_gas!(self, RuntimeCosts::CallInputCloned(input.len() as u32))?; input.clone() } else if flags.contains(CallFlags::FORWARD_INPUT) { self.input_data.take().ok_or(Error::::InputForwarded)? } else { self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?; self.read_sandbox_memory(memory, input_data_ptr, input_data_len)? }; let call_outcome = match call_type { CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight } => { let callee: <::T as frame_system::Config>::AccountId = self.read_sandbox_memory_as(memory, callee_ptr)?; let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { BalanceOf::<::T>::zero() } else { self.read_sandbox_memory_as(memory, deposit_ptr)? }; let value: BalanceOf<::T> = self.read_sandbox_memory_as(memory, value_ptr)?; if value > 0u32.into() { self.charge_gas(RuntimeCosts::CallSurchargeTransfer)?; } self.ext.call( weight, deposit_limit, callee, value, input_data, flags.contains(CallFlags::ALLOW_REENTRY), ) }, CallType::DelegateCall { code_hash_ptr } => { if flags.contains(CallFlags::ALLOW_REENTRY) { return Err(Error::::InvalidCallFlags.into()) } let code_hash = self.read_sandbox_memory_as(memory, code_hash_ptr)?; self.ext.delegate_call(code_hash, input_data) }, }; // `TAIL_CALL` only matters on an `OK` result. Otherwise the call stack comes to // a halt anyways without anymore code being executed. if flags.contains(CallFlags::TAIL_CALL) { if let Ok(return_value) = call_outcome { return Err(TrapReason::Return(ReturnData { flags: return_value.flags.bits(), data: return_value.data, })) } } if let Ok(output) = &call_outcome { self.write_sandbox_output( memory, output_ptr, output_len_ptr, &output.data, true, |len| Some(RuntimeCosts::CopyToContract(len)), )?; } Ok(Runtime::::exec_into_return_code(call_outcome)?) } fn instantiate( &mut self, memory: &mut [u8], code_hash_ptr: u32, weight: Weight, deposit_ptr: u32, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, address_ptr: u32, address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, salt_len: u32, ) -> Result { self.charge_gas(RuntimeCosts::InstantiateBase { input_data_len, salt_len })?; let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { BalanceOf::<::T>::zero() } else { self.read_sandbox_memory_as(memory, deposit_ptr)? }; let value: BalanceOf<::T> = self.read_sandbox_memory_as(memory, value_ptr)?; if value > 0u32.into() { self.charge_gas(RuntimeCosts::InstantiateSurchargeTransfer)?; } let code_hash: CodeHash<::T> = self.read_sandbox_memory_as(memory, code_hash_ptr)?; let input_data = self.read_sandbox_memory(memory, input_data_ptr, input_data_len)?; let salt = self.read_sandbox_memory(memory, salt_ptr, salt_len)?; let instantiate_outcome = self.ext.instantiate(weight, deposit_limit, code_hash, value, input_data, &salt); if let Ok((address, output)) = &instantiate_outcome { if !output.flags.contains(ReturnFlags::REVERT) { self.write_sandbox_output( memory, address_ptr, address_len_ptr, &address.encode(), true, already_charged, )?; } self.write_sandbox_output( memory, output_ptr, output_len_ptr, &output.data, true, |len| Some(RuntimeCosts::CopyToContract(len)), )?; } Ok(Runtime::::exec_into_return_code(instantiate_outcome.map(|(_, retval)| retval))?) } fn terminate(&mut self, memory: &[u8], beneficiary_ptr: u32) -> Result<(), TrapReason> { self.charge_gas(RuntimeCosts::Terminate)?; let beneficiary: <::T as frame_system::Config>::AccountId = self.read_sandbox_memory_as(memory, beneficiary_ptr)?; self.ext.terminate(&beneficiary)?; Err(TrapReason::Termination) } } // This is the API exposed to contracts. // // # Note // // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. #[define_env(doc)] pub mod env { /// Set the value at the given key in the contract storage. /// See [`pallet_contracts_uapi::HostFn::set_storage`] #[prefixed_alias] fn set_storage( ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32, ) -> Result<(), TrapReason> { ctx.set_storage(memory, KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) } /// Set the value at the given key in the contract storage. /// See [`pallet_contracts_uapi::HostFn::set_storage_v1`] #[version(1)] #[prefixed_alias] fn set_storage( ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32, ) -> Result { ctx.set_storage(memory, KeyType::Fix, key_ptr, value_ptr, value_len) } /// Set the value at the given key in the contract storage. /// See [`pallet_contracts_uapi::HostFn::set_storage_v2`] #[version(2)] #[prefixed_alias] fn set_storage( ctx: _, memory: _, key_ptr: u32, key_len: u32, value_ptr: u32, value_len: u32, ) -> Result { ctx.set_storage(memory, KeyType::Var(key_len), key_ptr, value_ptr, value_len) } /// Clear the value at the given key in the contract storage. /// See [`pallet_contracts_uapi::HostFn::clear_storage`] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32) -> Result<(), TrapReason> { ctx.clear_storage(memory, KeyType::Fix, key_ptr).map(|_| ()) } /// Clear the value at the given key in the contract storage. /// See [`pallet_contracts_uapi::HostFn::clear_storage_v1`] #[version(1)] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { ctx.clear_storage(memory, KeyType::Var(key_len), key_ptr) } /// Retrieve the value under the given key from storage. /// See [`pallet_contracts_uapi::HostFn::get_storage`] #[prefixed_alias] fn get_storage( ctx: _, memory: _, key_ptr: u32, out_ptr: u32, out_len_ptr: u32, ) -> Result { ctx.get_storage(memory, KeyType::Fix, key_ptr, out_ptr, out_len_ptr) } /// Retrieve the value under the given key from storage. /// See [`pallet_contracts_uapi::HostFn::get_storage_v1`] #[version(1)] #[prefixed_alias] fn get_storage( ctx: _, memory: _, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32, ) -> Result { ctx.get_storage(memory, KeyType::Var(key_len), key_ptr, out_ptr, out_len_ptr) } /// Checks whether there is a value stored under the given key. /// See [`pallet_contracts_uapi::HostFn::contains_storage`] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32) -> Result { ctx.contains_storage(memory, KeyType::Fix, key_ptr) } /// Checks whether there is a value stored under the given key. /// See [`pallet_contracts_uapi::HostFn::contains_storage_v1`] #[version(1)] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { ctx.contains_storage(memory, KeyType::Var(key_len), key_ptr) } /// Retrieve and remove the value under the given key from storage. /// See [`pallet_contracts_uapi::HostFn::take_storage`] #[prefixed_alias] fn take_storage( ctx: _, memory: _, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32, ) -> Result { let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; ensure!( key_len <= <::T as Config>::MaxStorageKeyLen::get(), Error::::DecodingFailed ); let key = ctx.read_sandbox_memory(memory, key_ptr, key_len)?; if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage( &Key::::try_from_var(key).map_err(|_| Error::::DecodingFailed)?, None, true, )? { ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(value.len() as u32)); ctx.write_sandbox_output(memory, out_ptr, out_len_ptr, &value, false, already_charged)?; Ok(ReturnErrorCode::Success) } else { ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(0)); Ok(ReturnErrorCode::KeyNotFound) } } /// Transfer some value to another account. /// See [`pallet_contracts_uapi::HostFn::transfer`]. #[prefixed_alias] fn transfer( ctx: _, memory: _, account_ptr: u32, _account_len: u32, value_ptr: u32, _value_len: u32, ) -> Result { ctx.charge_gas(RuntimeCosts::Transfer)?; let callee: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(memory, account_ptr)?; let value: BalanceOf<::T> = ctx.read_sandbox_memory_as(memory, value_ptr)?; let result = ctx.ext.transfer(&callee, value); match result { Ok(()) => Ok(ReturnErrorCode::Success), Err(err) => { let code = Runtime::::err_into_return_code(err)?; Ok(code) }, } } /// Make a call to another contract. /// /// # Note /// /// The values `_callee_len` and `_value_len` are ignored because the encoded sizes of those /// types are fixed through [`codec::MaxEncodedLen`]. The fields exist for backwards /// compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn call( ctx: _, memory: _, callee_ptr: u32, _callee_len: u32, gas: u64, value_ptr: u32, _value_len: u32, input_data_ptr: u32, input_data_len: u32, output_ptr: u32, output_len_ptr: u32, ) -> Result { ctx.call( memory, CallFlags::ALLOW_REENTRY, CallType::Call { callee_ptr, value_ptr, deposit_ptr: SENTINEL, weight: Weight::from_parts(gas, 0), }, input_data_ptr, input_data_len, output_ptr, output_len_ptr, ) } /// Make a call to another contract. /// See [`pallet_contracts_uapi::HostFn::call_v1`]. #[version(1)] #[prefixed_alias] fn call( ctx: _, memory: _, flags: u32, callee_ptr: u32, gas: u64, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, output_ptr: u32, output_len_ptr: u32, ) -> Result { ctx.call( memory, CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, CallType::Call { callee_ptr, value_ptr, deposit_ptr: SENTINEL, weight: Weight::from_parts(gas, 0), }, input_data_ptr, input_data_len, output_ptr, output_len_ptr, ) } /// Make a call to another contract. /// See [`pallet_contracts_uapi::HostFn::call_v2`]. #[version(2)] fn call( ctx: _, memory: _, flags: u32, callee_ptr: u32, ref_time_limit: u64, proof_size_limit: u64, deposit_ptr: u32, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, output_ptr: u32, output_len_ptr: u32, ) -> Result { ctx.call( memory, CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, CallType::Call { callee_ptr, value_ptr, deposit_ptr, weight: Weight::from_parts(ref_time_limit, proof_size_limit), }, input_data_ptr, input_data_len, output_ptr, output_len_ptr, ) } /// Execute code in the context (storage, caller, value) of the current contract. /// See [`pallet_contracts_uapi::HostFn::delegate_call`]. #[prefixed_alias] fn delegate_call( ctx: _, memory: _, flags: u32, code_hash_ptr: u32, input_data_ptr: u32, input_data_len: u32, output_ptr: u32, output_len_ptr: u32, ) -> Result { ctx.call( memory, CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, CallType::DelegateCall { code_hash_ptr }, input_data_ptr, input_data_len, output_ptr, output_len_ptr, ) } /// Instantiate a contract with the specified code hash. /// See [`pallet_contracts_uapi::HostFn::instantiate`]. /// /// # Note /// /// The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes /// of those types are fixed through [`codec::MaxEncodedLen`]. The fields exist /// for backwards compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn instantiate( ctx: _, memory: _, code_hash_ptr: u32, _code_hash_len: u32, gas: u64, value_ptr: u32, _value_len: u32, input_data_ptr: u32, input_data_len: u32, address_ptr: u32, address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, salt_len: u32, ) -> Result { ctx.instantiate( memory, code_hash_ptr, Weight::from_parts(gas, 0), SENTINEL, value_ptr, input_data_ptr, input_data_len, address_ptr, address_len_ptr, output_ptr, output_len_ptr, salt_ptr, salt_len, ) } /// Instantiate a contract with the specified code hash. /// See [`pallet_contracts_uapi::HostFn::instantiate_v1`]. #[version(1)] #[prefixed_alias] fn instantiate( ctx: _, memory: _, code_hash_ptr: u32, gas: u64, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, address_ptr: u32, address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, salt_len: u32, ) -> Result { ctx.instantiate( memory, code_hash_ptr, Weight::from_parts(gas, 0), SENTINEL, value_ptr, input_data_ptr, input_data_len, address_ptr, address_len_ptr, output_ptr, output_len_ptr, salt_ptr, salt_len, ) } /// Instantiate a contract with the specified code hash. /// See [`pallet_contracts_uapi::HostFn::instantiate_v2`]. #[version(2)] fn instantiate( ctx: _, memory: _, code_hash_ptr: u32, ref_time_limit: u64, proof_size_limit: u64, deposit_ptr: u32, value_ptr: u32, input_data_ptr: u32, input_data_len: u32, address_ptr: u32, address_len_ptr: u32, output_ptr: u32, output_len_ptr: u32, salt_ptr: u32, salt_len: u32, ) -> Result { ctx.instantiate( memory, code_hash_ptr, Weight::from_parts(ref_time_limit, proof_size_limit), deposit_ptr, value_ptr, input_data_ptr, input_data_len, address_ptr, address_len_ptr, output_ptr, output_len_ptr, salt_ptr, salt_len, ) } /// Remove the calling account and transfer remaining balance. /// See [`pallet_contracts_uapi::HostFn::terminate`]. /// /// # Note /// /// The value `_beneficiary_len` is ignored because the encoded sizes /// this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards /// compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn terminate( ctx: _, memory: _, beneficiary_ptr: u32, _beneficiary_len: u32, ) -> Result<(), TrapReason> { ctx.terminate(memory, beneficiary_ptr) } /// Remove the calling account and transfer remaining **free** balance. /// See [`pallet_contracts_uapi::HostFn::terminate_v1`]. #[version(1)] #[prefixed_alias] fn terminate(ctx: _, memory: _, beneficiary_ptr: u32) -> Result<(), TrapReason> { ctx.terminate(memory, beneficiary_ptr) } /// Stores the input passed by the caller into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::input`]. #[prefixed_alias] fn input(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::InputBase)?; if let Some(input) = ctx.input_data.take() { ctx.write_sandbox_output(memory, out_ptr, out_len_ptr, &input, false, |len| { Some(RuntimeCosts::CopyToContract(len)) })?; ctx.input_data = Some(input); Ok(()) } else { Err(Error::::InputForwarded.into()) } } /// Cease contract execution and save a data buffer as a result of the execution. /// See [`pallet_contracts_uapi::HostFn::return_value`]. fn seal_return( ctx: _, memory: _, flags: u32, data_ptr: u32, data_len: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Return(data_len))?; Err(TrapReason::Return(ReturnData { flags, data: ctx.read_sandbox_memory(memory, data_ptr, data_len)?, })) } /// Stores the address of the caller into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::caller`]. #[prefixed_alias] fn caller(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Caller)?; let caller = ctx.ext.caller().account_id()?.clone(); Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &caller.encode(), false, already_charged, )?) } /// Checks whether a specified address belongs to a contract. /// See [`pallet_contracts_uapi::HostFn::is_contract`]. #[prefixed_alias] fn is_contract(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::IsContract)?; let address: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(memory, account_ptr)?; Ok(ctx.ext.is_contract(&address) as u32) } /// Retrieve the code hash for a specified contract address. /// See [`pallet_contracts_uapi::HostFn::code_hash`]. #[prefixed_alias] fn code_hash( ctx: _, memory: _, account_ptr: u32, out_ptr: u32, out_len_ptr: u32, ) -> Result { ctx.charge_gas(RuntimeCosts::CodeHash)?; let address: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(memory, account_ptr)?; if let Some(value) = ctx.ext.code_hash(&address) { ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &value.encode(), false, already_charged, )?; Ok(ReturnErrorCode::Success) } else { Ok(ReturnErrorCode::KeyNotFound) } } /// Retrieve the code hash of the currently executing contract. /// See [`pallet_contracts_uapi::HostFn::own_code_hash`]. #[prefixed_alias] fn own_code_hash(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; let code_hash_encoded = &ctx.ext.own_code_hash().encode(); Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, code_hash_encoded, false, already_charged, )?) } /// Checks whether the caller of the current contract is the origin of the whole call stack. /// See [`pallet_contracts_uapi::HostFn::caller_is_origin`]. #[prefixed_alias] fn caller_is_origin(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; Ok(ctx.ext.caller_is_origin() as u32) } /// Checks whether the caller of the current contract is root. /// See [`pallet_contracts_uapi::HostFn::caller_is_root`]. fn caller_is_root(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsRoot)?; Ok(ctx.ext.caller_is_root() as u32) } /// Stores the address of the current contract into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::address`]. #[prefixed_alias] fn address(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Address)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged, )?) } /// Stores the price for the specified amount of gas into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::weight_to_fee`]. #[prefixed_alias] fn weight_to_fee( ctx: _, memory: _, gas: u64, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { let gas = Weight::from_parts(gas, 0); ctx.charge_gas(RuntimeCosts::WeightToFee)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged, )?) } /// Stores the price for the specified amount of weight into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::weight_to_fee_v1`]. #[version(1)] #[unstable] fn weight_to_fee( ctx: _, memory: _, ref_time_limit: u64, proof_size_limit: u64, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { let weight = Weight::from_parts(ref_time_limit, proof_size_limit); ctx.charge_gas(RuntimeCosts::WeightToFee)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.get_weight_price(weight).encode(), false, already_charged, )?) } /// Stores the weight left into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::gas_left`]. #[prefixed_alias] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::GasLeft)?; let gas_left = &ctx.ext.gas_meter().gas_left().ref_time().encode(); Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, gas_left, false, already_charged, )?) } /// Stores the amount of weight left into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::gas_left_v1`]. #[version(1)] #[unstable] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::GasLeft)?; let gas_left = &ctx.ext.gas_meter().gas_left().encode(); Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, gas_left, false, already_charged, )?) } /// Stores the *free* balance of the current account into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::balance`]. #[prefixed_alias] fn balance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged, )?) } /// Stores the value transferred along with this call/instantiate into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::value_transferred`]. #[prefixed_alias] fn value_transferred( ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::ValueTransferred)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged, )?) } /// Stores a random number for the current block and the given subject into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as `T::Hash`. #[prefixed_alias] #[deprecated] fn random( ctx: _, memory: _, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Random)?; if subject_len > ctx.ext.schedule().limits.subject_len { return Err(Error::::RandomSubjectTooLong.into()) } let subject_buf = ctx.read_sandbox_memory(memory, subject_ptr, subject_len)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).0.encode(), false, already_charged, )?) } /// Stores a random number for the current block and the given subject into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as (T::Hash, frame_system::pallet_prelude::BlockNumberFor::). /// /// # Changes from v0 /// /// In addition to the seed it returns the block number since which it was determinable /// by chain observers. /// /// # Note /// /// The returned seed should only be used to distinguish commitments made before /// the returned block number. If the block number is too early (i.e. commitments were /// made afterwards), then ensure no further commitments may be made and repeatedly /// call this on later blocks until the block number returned is later than the latest /// commitment. #[version(1)] #[prefixed_alias] #[deprecated] fn random( ctx: _, memory: _, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Random)?; if subject_len > ctx.ext.schedule().limits.subject_len { return Err(Error::::RandomSubjectTooLong.into()) } let subject_buf = ctx.read_sandbox_memory(memory, subject_ptr, subject_len)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).encode(), false, already_charged, )?) } /// Load the latest block timestamp into the supplied buffer /// See [`pallet_contracts_uapi::HostFn::now`]. #[prefixed_alias] fn now(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Now)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged, )?) } /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::minimum_balance`]. #[prefixed_alias] fn minimum_balance( ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::MinimumBalance)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged, )?) } /// Stores the tombstone deposit into the supplied buffer. /// /// The value is stored to linear memory at the address pointed to by `out_ptr`. /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// # Note /// /// There is no longer a tombstone deposit. This function always returns `0`. #[prefixed_alias] #[deprecated] fn tombstone_deposit( ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; let deposit = >::zero().encode(); Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &deposit, false, already_charged, )?) } /// Was used to restore the given destination contract sacrificing the caller. /// /// # Note /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatibility #[prefixed_alias] #[deprecated] fn restore_to( ctx: _, memory: _, _dest_ptr: u32, _dest_len: u32, _code_hash_ptr: u32, _code_hash_len: u32, _rent_allowance_ptr: u32, _rent_allowance_len: u32, _delta_ptr: u32, _delta_count: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } /// Was used to restore the given destination contract sacrificing the caller. /// /// # Note /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatibility #[version(1)] #[prefixed_alias] #[deprecated] fn restore_to( ctx: _, memory: _, _dest_ptr: u32, _code_hash_ptr: u32, _rent_allowance_ptr: u32, _delta_ptr: u32, _delta_count: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } /// Was used to set rent allowance of the contract. /// /// # Note /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatibility. #[prefixed_alias] #[deprecated] fn set_rent_allowance( ctx: _, memory: _, _value_ptr: u32, _value_len: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } /// Was used to set rent allowance of the contract. /// /// # Note /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatibility. #[version(1)] #[prefixed_alias] #[deprecated] fn set_rent_allowance(ctx: _, _memory: _, _value_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage(0))?; Ok(()) } /// Was used to store the rent allowance into the supplied buffer. /// /// # Note /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatibility. #[prefixed_alias] #[deprecated] fn rent_allowance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; let rent_allowance = >::max_value().encode(); Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &rent_allowance, false, already_charged, )?) } /// Deposit a contract event with the data buffer and optional list of topics. /// See [pallet_contracts_uapi::HostFn::deposit_event] #[prefixed_alias] fn deposit_event( ctx: _, memory: _, topics_ptr: u32, topics_len: u32, data_ptr: u32, data_len: u32, ) -> Result<(), TrapReason> { let num_topic = topics_len .checked_div(sp_std::mem::size_of::>() as u32) .ok_or("Zero sized topics are not allowed")?; ctx.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?; if data_len > ctx.ext.max_value_size() { return Err(Error::::ValueTooLarge.into()) } let topics: Vec::T>> = match topics_len { 0 => Vec::new(), _ => ctx.read_sandbox_memory_as_unbounded(memory, topics_ptr, topics_len)?, }; // If there are more than `event_topics`, then trap. if topics.len() > ctx.ext.schedule().limits.event_topics as usize { return Err(Error::::TooManyTopics.into()) } let event_data = ctx.read_sandbox_memory(memory, data_ptr, data_len)?; ctx.ext.deposit_event(topics, event_data); Ok(()) } /// Stores the current block number of the current contract into the supplied buffer. /// See [`pallet_contracts_uapi::HostFn::block_number`]. #[prefixed_alias] fn block_number(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::BlockNumber)?; Ok(ctx.write_sandbox_output( memory, out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged, )?) } /// Computes the SHA2 256-bit hash on the given input buffer. /// See [`pallet_contracts_uapi::HostFn::hash_sha2_256`]. #[prefixed_alias] fn hash_sha2_256( ctx: _, memory: _, input_ptr: u32, input_len: u32, output_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer( memory, sha2_256, input_ptr, input_len, output_ptr, )?) } /// Computes the KECCAK 256-bit hash on the given input buffer. /// See [`pallet_contracts_uapi::HostFn::hash_keccak_256`]. #[prefixed_alias] fn hash_keccak_256( ctx: _, memory: _, input_ptr: u32, input_len: u32, output_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer( memory, keccak_256, input_ptr, input_len, output_ptr, )?) } /// Computes the BLAKE2 256-bit hash on the given input buffer. /// See [`pallet_contracts_uapi::HostFn::hash_blake2_256`]. #[prefixed_alias] fn hash_blake2_256( ctx: _, memory: _, input_ptr: u32, input_len: u32, output_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer( memory, blake2_256, input_ptr, input_len, output_ptr, )?) } /// Computes the BLAKE2 128-bit hash on the given input buffer. /// See [`pallet_contracts_uapi::HostFn::hash_blake2_128`]. #[prefixed_alias] fn hash_blake2_128( ctx: _, memory: _, input_ptr: u32, input_len: u32, output_ptr: u32, ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer( memory, blake2_128, input_ptr, input_len, output_ptr, )?) } /// Call into the chain extension provided by the chain if any. /// See [`pallet_contracts_uapi::HostFn::call_chain_extension`]. #[prefixed_alias] fn call_chain_extension( ctx: _, memory: _, id: u32, input_ptr: u32, input_len: u32, output_ptr: u32, output_len_ptr: u32, ) -> Result { use crate::chain_extension::{ChainExtension, Environment, RetVal}; if !::ChainExtension::enabled() { return Err(Error::::NoChainExtension.into()) } let mut chain_extension = ctx.chain_extension.take().expect( "Constructor initializes with `Some`. This is the only place where it is set to `None`.\ It is always reset to `Some` afterwards. qed" ); let env = Environment::new(ctx, memory, id, input_ptr, input_len, output_ptr, output_len_ptr); let ret = match chain_extension.call(env)? { RetVal::Converging(val) => Ok(val), RetVal::Diverging { flags, data } => Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })), }; ctx.chain_extension = Some(chain_extension); ret } /// Emit a custom debug message. /// /// No newlines are added to the supplied message. /// Specifying invalid UTF-8 just drops the message with no trap. /// /// This is a no-op if debug message recording is disabled which is always the case /// when the code is executing on-chain. The message is interpreted as UTF-8 and /// appended to the debug buffer which is then supplied to the calling RPC client. /// /// # Note /// /// Even though no action is taken when debug message recording is disabled there is still /// a non trivial overhead (and weight cost) associated with calling this function. Contract /// languages should remove calls to this function (either at runtime or compile time) when /// not being executed as an RPC. For example, they could allow users to disable logging /// through compile time flags (cargo features) for on-chain deployment. Additionally, the /// return value of this function can be cached in order to prevent further calls at runtime. #[prefixed_alias] fn debug_message( ctx: _, memory: _, str_ptr: u32, str_len: u32, ) -> Result { let str_len = str_len.min(DebugBufferVec::::bound() as u32); ctx.charge_gas(RuntimeCosts::DebugMessage(str_len))?; if ctx.ext.append_debug_buffer("") { let data = ctx.read_sandbox_memory(memory, str_ptr, str_len)?; if let Some(msg) = core::str::from_utf8(&data).ok() { ctx.ext.append_debug_buffer(msg); } } Ok(ReturnErrorCode::Success) } /// Call some dispatchable of the runtime. /// See [`frame_support::traits::call_runtime`]. fn call_runtime( ctx: _, memory: _, call_ptr: u32, call_len: u32, ) -> Result { use frame_support::dispatch::GetDispatchInfo; ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; let call: ::RuntimeCall = ctx.read_sandbox_memory_as_unbounded(memory, call_ptr, call_len)?; ctx.call_dispatchable::( call.get_dispatch_info(), RuntimeCosts::CallRuntime, |ctx| ctx.ext.call_runtime(call), ) } /// Execute an XCM program locally, using the contract's address as the origin. /// See [`pallet_contracts_uapi::HostFn::execute_xcm`]. fn xcm_execute( ctx: _, memory: _, msg_ptr: u32, msg_len: u32, ) -> Result { use frame_support::dispatch::DispatchInfo; use xcm::VersionedXcm; use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo}; ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; let message: VersionedXcm> = ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; ensure_executable::(&message)?; let execute_weight = <::Xcm as ExecuteController<_, _>>::WeightInfo::execute(); let weight = ctx.ext.gas_meter().gas_left().max(execute_weight); let dispatch_info = DispatchInfo { weight, ..Default::default() }; ctx.call_dispatchable::( dispatch_info, RuntimeCosts::CallXcmExecute, |ctx| { let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); let weight_used = <::Xcm>::execute( origin, Box::new(message), weight.saturating_sub(execute_weight), )?; Ok(Some(weight_used.saturating_add(execute_weight)).into()) }, ) } /// Send an XCM program from the contract to the specified destination. /// See [`pallet_contracts_uapi::HostFn::send_xcm`]. fn xcm_send( ctx: _, memory: _, dest_ptr: u32, msg_ptr: u32, msg_len: u32, output_ptr: u32, ) -> Result { use xcm::{VersionedLocation, VersionedXcm}; use xcm_builder::{SendController, SendControllerWeightInfo}; ctx.charge_gas(RuntimeCosts::CopyFromContract(msg_len))?; let dest: VersionedLocation = ctx.read_sandbox_memory_as(memory, dest_ptr)?; let message: VersionedXcm<()> = ctx.read_sandbox_memory_as_unbounded(memory, msg_ptr, msg_len)?; let weight = <::Xcm as SendController<_>>::WeightInfo::send(); ctx.charge_gas(RuntimeCosts::CallRuntime(weight))?; let origin = crate::RawOrigin::Signed(ctx.ext.address().clone()).into(); match <::Xcm>::send(origin, dest.into(), message.into()) { Ok(message_id) => { ctx.write_sandbox_memory(memory, output_ptr, &message_id.encode())?; Ok(ReturnErrorCode::Success) }, Err(e) => { if ctx.ext.append_debug_buffer("") { ctx.ext.append_debug_buffer("seal0::xcm_send failed with: "); ctx.ext.append_debug_buffer(e.into()); }; Ok(ReturnErrorCode::XcmSendFailed) }, } } /// Recovers the ECDSA public key from the given message hash and signature. /// See [`pallet_contracts_uapi::HostFn::ecdsa_recover`]. #[prefixed_alias] fn ecdsa_recover( ctx: _, memory: _, signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32, ) -> Result { ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?; let mut signature: [u8; 65] = [0; 65]; ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?; let mut message_hash: [u8; 32] = [0; 32]; ctx.read_sandbox_memory_into_buf(memory, message_hash_ptr, &mut message_hash)?; let result = ctx.ext.ecdsa_recover(&signature, &message_hash); match result { Ok(pub_key) => { // Write the recovered compressed ecdsa public key back into the sandboxed output // buffer. ctx.write_sandbox_memory(memory, output_ptr, pub_key.as_ref())?; Ok(ReturnErrorCode::Success) }, Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), } } /// Verify a sr25519 signature /// See [`pallet_contracts_uapi::HostFn::sr25519_verify`]. fn sr25519_verify( ctx: _, memory: _, signature_ptr: u32, pub_key_ptr: u32, message_len: u32, message_ptr: u32, ) -> Result { ctx.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?; let mut signature: [u8; 64] = [0; 64]; ctx.read_sandbox_memory_into_buf(memory, signature_ptr, &mut signature)?; let mut pub_key: [u8; 32] = [0; 32]; ctx.read_sandbox_memory_into_buf(memory, pub_key_ptr, &mut pub_key)?; let message: Vec = ctx.read_sandbox_memory(memory, message_ptr, message_len)?; if ctx.ext.sr25519_verify(&signature, &message, &pub_key) { Ok(ReturnErrorCode::Success) } else { Ok(ReturnErrorCode::Sr25519VerifyFailed) } } /// Replace the contract code at the specified address with new code. /// See [`pallet_contracts_uapi::HostFn::set_code_hash`]. #[prefixed_alias] fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::SetCodeHash)?; let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; match ctx.ext.set_code_hash(code_hash) { Err(err) => { let code = Runtime::::err_into_return_code(err)?; Ok(code) }, Ok(()) => Ok(ReturnErrorCode::Success), } } /// Calculates Ethereum address from the ECDSA compressed public key and stores /// See [`pallet_contracts_uapi::HostFn::ecdsa_to_eth_address`]. #[prefixed_alias] fn ecdsa_to_eth_address( ctx: _, memory: _, key_ptr: u32, out_ptr: u32, ) -> Result { ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; let mut compressed_key: [u8; 33] = [0; 33]; ctx.read_sandbox_memory_into_buf(memory, key_ptr, &mut compressed_key)?; let result = ctx.ext.ecdsa_to_eth_address(&compressed_key); match result { Ok(eth_address) => { ctx.write_sandbox_memory(memory, out_ptr, eth_address.as_ref())?; Ok(ReturnErrorCode::Success) }, Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), } } /// Returns the number of times the currently executing contract exists on the call stack in /// addition to the calling instance. /// See [`pallet_contracts_uapi::HostFn::reentrance_count`]. #[unstable] fn reentrance_count(ctx: _, memory: _) -> Result { ctx.charge_gas(RuntimeCosts::ReentrantCount)?; Ok(ctx.ext.reentrance_count()) } /// Returns the number of times specified contract exists on the call stack. Delegated calls are /// not counted as separate calls. /// See [`pallet_contracts_uapi::HostFn::account_reentrance_count`]. #[unstable] fn account_reentrance_count(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::AccountEntranceCount)?; let account_id: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(memory, account_ptr)?; Ok(ctx.ext.account_reentrance_count(&account_id)) } /// Returns a nonce that is unique per contract instantiation. /// See [`pallet_contracts_uapi::HostFn::instantiation_nonce`]. fn instantiation_nonce(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::InstantiationNonce)?; Ok(ctx.ext.nonce()) } /// Adds a new delegate dependency to the contract. /// See [`pallet_contracts_uapi::HostFn::lock_delegate_dependency`]. fn lock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::LockDelegateDependency)?; let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; ctx.ext.lock_delegate_dependency(code_hash)?; Ok(()) } /// Removes the delegate dependency from the contract. /// see [`pallet_contracts_uapi::HostFn::unlock_delegate_dependency`]. fn unlock_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::UnlockDelegateDependency)?; let code_hash = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; ctx.ext.unlock_delegate_dependency(&code_hash)?; Ok(()) } }