// Copyright (C) Parity Technologies (UK) Ltd. // // 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. use crate::{CallFlags, Result, ReturnFlags, StorageFlags}; use pezpallet_revive_proc_macro::unstable_hostfn; #[cfg(target_arch = "riscv64")] mod riscv64; /// Implements [`HostFn`] when compiled on supported architectures (RISC-V). pub enum HostFnImpl {} /// Defines all the host apis available to contracts. pub trait HostFn: private::Sealed { /// Stores the address of the current contract into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the address. fn address(output: &mut [u8; 20]); /// Get the contract immutable data. /// /// Traps if: /// - Called from within the deploy export. /// - Called by contracts that didn't set immutable data by calling `set_immutable_data` during /// their constructor execution. /// /// # Parameters /// - `output`: A reference to the output buffer to write the immutable bytes. fn get_immutable_data(output: &mut &mut [u8]); /// Set the contract immutable data. /// /// It is only valid to set non-empty immutable data in the constructor once. /// /// Traps if: /// - Called from within the call export. /// - Called more than once. /// - The provided data was empty. /// /// # Parameters /// - `data`: A reference to the data to be stored as immutable bytes. fn set_immutable_data(data: &[u8]); /// Stores the **reducible** balance of the current account into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the balance. fn balance(output: &mut [u8; 32]); /// Stores the **reducible** balance of the supplied address into the supplied buffer. /// /// # Parameters /// /// - `addr`: The target address of which to retreive the free balance. /// - `output`: A reference to the output data buffer to write the balance. fn balance_of(addr: &[u8; 20], output: &mut [u8; 32]); /// Returns the [EIP-155](https://eips.ethereum.org/EIPS/eip-155) chain ID. fn chain_id(output: &mut [u8; 32]); /// Returns the price per ref_time, akin to the EVM /// [GASPRICE](https://www.evm.codes/?fork=cancun#3a) opcode. fn gas_price() -> u64; /// Returns the base fee, akin to the EVM /// [BASEFEE](https://www.evm.codes/?fork=cancun#48) opcode. fn base_fee(output: &mut [u8; 32]); /// Returns the call data size. fn call_data_size() -> u64; /// Call (possibly transferring some amount of funds) into the specified account. /// /// # Parameters /// /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps /// otherwise. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. /// - `deposit`: The storage deposit limit for instantiation. Passing `None` means setting no /// specific limit for the call, which implies storage usage up to the limit of the parent /// call. /// - `value`: The value to transfer into the contract. /// - `input`: The input data buffer used to call the contract. /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` /// is provided then the output buffer is not copied. /// /// # Errors /// /// An error means that the call wasn't successful output buffer is returned unless /// stated otherwise. /// /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [OutOfResources][`crate::ReturnErrorCode::OutOfResources] fn call( flags: CallFlags, callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, deposit: &[u8; 32], value: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, ) -> Result; /// Stores the address of the caller into the supplied buffer. /// /// If this is a top-level call (i.e. initiated by an extrinsic) the origin address of the /// extrinsic will be returned. Otherwise, if this call is initiated by another contract then /// the address of the contract will be returned. /// /// If there is no address associated with the caller (e.g. because the caller is root) then /// it traps with `BadOrigin`. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the caller address. fn caller(output: &mut [u8; 20]); /// Stores the origin address (initator of the call stack) into the supplied buffer. /// /// If there is no address associated with the origin (e.g. because the origin is root) then /// it traps with `BadOrigin`. This can only happen through on-chain governance actions or /// customized runtimes. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the origin's address. fn origin(output: &mut [u8; 20]); /// Retrieve the code hash for a specified contract address. /// /// # Parameters /// /// - `addr`: The address of the contract. /// - `output`: A reference to the output data buffer to write the code hash. /// /// # Note /// /// If `addr` is not a contract but the account exists then the hash of empty data /// `0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470` is written, /// otherwise `zero`. fn code_hash(addr: &[u8; 20], output: &mut [u8; 32]); /// Returns the code size for a specified contract address. /// /// # Parameters /// /// - `addr`: The address of the contract. /// /// # Note /// /// If `addr` is not a contract the `output` will be zero. fn code_size(addr: &[u8; 20]) -> u64; /// Execute code in the context (storage, caller, value) of the current contract. /// /// Reentrancy protection is always disabled since the callee is allowed /// to modify the callers storage. This makes going through a reentrancy attack /// unnecessary for the callee when it wants to exploit the caller. /// /// # Parameters /// /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. /// - `address`: The address of the code to be executed. Should be decodable as an /// `T::AccountId`. Traps otherwise. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. /// - `deposit_limit`: The storage deposit limit for delegate call. Passing `None` means setting /// no specific limit for the call, which implies storage usage up to the limit of the parent /// call. /// - `input`: The input data buffer used to call the contract. /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` /// is provided then the output buffer is not copied. /// /// # Errors /// /// An error means that the call wasn't successful and no output buffer is returned unless /// stated otherwise. /// /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [OutOfResources][`crate::ReturnErrorCode::OutOfResources] fn delegate_call( flags: CallFlags, address: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, deposit_limit: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, ) -> Result; /// Deposit a contract event with the data buffer and optional list of topics. There is a limit /// on the maximum number of topics specified by `event_topics`. /// /// There should not be any duplicates in `topics`. /// /// # Parameters /// /// - `topics`: The topics list. It can't contain duplicates. fn deposit_event(topics: &[[u8; 32]], data: &[u8]); /// Retrieve the value under the given key from storage. /// /// The key length must not exceed the maximum defined by the `pezpallet-revive` parameter. /// /// # Parameters /// - `key`: The storage key. /// - `output`: A reference to the output data buffer to write the storage entry. /// /// # Errors /// /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result; /// Computes the keccak_256 32-bit hash on the given input buffer. /// /// - The `input` and `output` buffer may overlap. /// - The output buffer is expected to hold at least 32 bits. /// - It is the callers responsibility to provide an output buffer that is large enough to hold /// the expected amount of bytes returned by the hash function. /// /// # Parameters /// /// - `input`: The input data buffer. /// - `output`: The output buffer to write the hash result to. fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]); /// Stores the input data passed by the caller into the supplied `output` buffer, /// starting from the given input data `offset`. /// /// The `output` buffer is guaranteed to always be fully populated: /// - If the call data (starting from the given `offset`) is larger than the `output` buffer, /// only what fits into the `output` buffer is written. /// - If the `output` buffer size exceeds the call data size (starting from `offset`), remaining /// bytes in the `output` buffer are zeroed out. /// - If the provided call data `offset` is out-of-bounds, the whole `output` buffer is zeroed /// out. /// /// # Note /// /// This function traps if: /// - the input was previously forwarded by a [`call()`][`Self::call()`]. /// - the `output` buffer is located in an PolkaVM invalid memory range. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the call data. /// - `offset`: The offset index into the call data from where to start copying. fn call_data_copy(output: &mut [u8], offset: u32); /// Stores the U256 value at given `offset` from the input passed by the caller /// into the supplied buffer. /// /// # Note /// - If `offset` is out of bounds, a value of zero will be returned. /// - If `offset` is in bounds but there is not enough call data, the available data /// is right-padded in order to fill a whole U256 value. /// - The data written to `output` is a little endian U256 integer value. /// /// # Parameters /// /// - `output`: A reference to the fixed output data buffer to write the value. /// - `offset`: The offset (index) into the call data. fn call_data_load(output: &mut [u8; 32], offset: u32); /// Instantiate a contract with the specified code hash. /// /// This function creates an account and executes the constructor defined in the code specified /// by the code hash. /// /// # Parameters /// /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. /// - `deposit`: The storage deposit limit for instantiation. Passing `None` means setting no /// specific limit for the call, which implies storage usage up to the limit of the parent /// call. /// - `value`: The value to transfer into the contract. /// - `input`: The code hash and constructor input data buffer. The first 32 bytes are the code /// hash of the code to be instantiated. The remaining bytes are the constructor call data. /// - `address`: A reference to the address buffer to write the address of the contract. If /// `None` is provided then the output buffer is not copied. /// - `output`: A reference to the return value buffer to write the constructor output buffer. /// If `None` is provided then the output buffer is not copied. /// - `salt`: The salt bytes to use for this instantiation. /// /// # Errors /// /// Please consult the [ReturnErrorCode][`crate::ReturnErrorCode`] enum declaration for more /// information on those errors. Here we only note things specific to this function. /// /// An error means that the account wasn't created and no address or output buffer /// is returned unless stated otherwise. /// /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [OutOfResources][`crate::ReturnErrorCode::OutOfResources] fn instantiate( ref_time_limit: u64, proof_size_limit: u64, deposit: &[u8; 32], value: &[u8; 32], input: &[u8], address: Option<&mut [u8; 20]>, output: Option<&mut &mut [u8]>, salt: Option<&[u8; 32]>, ) -> Result; /// Load the latest block timestamp in seconds into the supplied buffer /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the timestamp. fn now(output: &mut [u8; 32]); /// Returns the block ref_time limit. fn gas_limit() -> u64; /// Cease contract execution and save a data buffer as a result of the execution. /// /// This function never returns as it stops execution of the caller. /// This is the only way to return a data buffer to the caller. Returning from /// execution without calling this function is equivalent to calling: /// ```nocompile /// return_value(ReturnFlags::empty(), &[]) /// ``` /// /// Using an unnamed non empty `ReturnFlags` triggers a trap. /// /// # Parameters /// /// - `flags`: Flag used to signal special return conditions to the supervisor. See /// [`ReturnFlags`] for a documentation of the supported flags. /// - `return_value`: The return value buffer. fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; /// Set the value at the given key in the contract storage. /// /// The key and value lengths must not exceed the maximums defined by the `pezpallet-revive` /// parameters. /// /// # Parameters /// /// - `key`: The storage key. /// - `encoded_value`: The storage value. /// /// # Return /// /// Returns the size of the pre-existing value at the specified key if any. fn set_storage(flags: StorageFlags, key: &[u8], value: &[u8]) -> Option; /// Sets the storage entry for a fixed 256‑bit key with a fixed 256‑bit value. /// /// If the provided 32‑byte value is all zeros then the key is cleared (i.e. deleted), /// mimicking Ethereum’s SSTORE behavior. /// /// # Parameters /// - `key`: The fixed 256‑bit storage key (32 bytes). /// - `value`: The fixed 256‑bit storage value (32 bytes). /// /// # Return /// Returns the size (in bytes) of the pre‑existing value at the specified key, if any. fn set_storage_or_clear(flags: StorageFlags, key: &[u8; 32], value: &[u8; 32]) -> Option; /// Retrieves the storage entry for a fixed 256‑bit key. /// /// If the key does not exist, the output buffer is filled with 32 zero bytes. /// /// # Parameters /// - `key`: The fixed 256‑bit storage key (32 bytes). /// - `output`: A mutable output buffer (32 bytes) where the storage entry is written. fn get_storage_or_zero(flags: StorageFlags, key: &[u8; 32], output: &mut [u8; 32]); /// Stores the value transferred along with this call/instantiate into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the transferred value. fn value_transferred(output: &mut [u8; 32]); /// Returns the size of the returned data of the last contract call or instantiation. fn return_data_size() -> u64; /// Stores the returned data of the last contract call or contract instantiation. /// /// # Parameters /// - `output`: A reference to the output buffer to write the data. /// - `offset`: Byte offset into the returned data fn return_data_copy(output: &mut &mut [u8], offset: u32); /// Returns the amount of ethereum gas left. fn gas_left() -> u64; /// Stores the current block author of into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the block author. fn block_author(output: &mut [u8; 20]); /// Stores the current block number of the current contract into the supplied buffer. /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the block number. fn block_number(output: &mut [u8; 32]); /// Stores the block hash of the given block number into the supplied buffer. /// /// # Parameters /// /// - `block_number`: A reference to the block number buffer. /// - `output`: A reference to the output data buffer to write the block number. fn block_hash(block_number: &[u8; 32], output: &mut [u8; 32]); /// Reverts the execution and cedes all supplied gas, /// akin to the `INVALID` EVM opcode. fn consume_all_gas() -> !; /// Calculates Ethereum address from the ECDSA compressed public key and stores /// it into the supplied buffer. /// /// # Parameters /// /// - `pubkey`: The public key bytes. /// - `output`: A reference to the output data buffer to write the address. /// /// # Errors /// /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] #[unstable_hostfn] fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; /// Replace the contract code at the specified address with new code. /// /// # Note /// /// There are a couple of important considerations which must be taken into account when /// using this API: /// /// 1. The storage at the code address will remain untouched. This means that contract /// developers must ensure that the storage layout of the new code is compatible with that of /// the old code. /// /// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another /// way, when using this API you lose the guarantee that an address always identifies a specific /// code hash. /// /// 3. If a contract calls into itself after changing its code the new call would use /// the new code. However, if the original caller panics after returning from the sub call it /// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next /// caller would use the old code. /// /// # Parameters /// /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps /// otherwise. /// /// # Panics /// /// Panics if there is no code on-chain with the specified hash. #[unstable_hostfn] fn set_code_hash(code_hash: &[u8; 32]); /// Verify a sr25519 signature /// /// # Parameters /// /// - `signature`: The signature bytes. /// - `message`: The message bytes. /// /// # Errors /// /// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed] #[unstable_hostfn] fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; /// Remove the calling account and transfer remaining **free** balance. /// /// This function never returns. Either the termination was successful and the /// execution of the destroyed contract is halted. Or it failed during the termination /// which is considered fatal and results in a trap + rollback. /// /// # Parameters /// /// - `beneficiary`: The address of the beneficiary account /// /// # Traps /// /// - The contract is live i.e is already on the call stack. /// - Failed to send the balance to the beneficiary. /// - The deletion queue is full. #[unstable_hostfn] fn terminate(beneficiary: &[u8; 20]) -> !; } mod private { pub trait Sealed {} impl Sealed for super::HostFnImpl {} }