feat: Rebrand Polkadot/Substrate references to PezkuwiChain
This commit systematically rebrands various references from Parity Technologies' Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk. Key changes include: - Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks. - Modified internal documentation and code comments to reflect PezkuwiChain naming and structure. - Replaced direct references to with or specific paths within the for XCM, Pezkuwi, and other modules. - Cleaned up deprecated issue and PR references in various and files, particularly in and modules. - Adjusted image and logo URLs in documentation to point to PezkuwiChain assets. - Removed or rephrased comments related to external Polkadot/Substrate PRs and issues. This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "pezpallet-revive-uapi"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license = "Apache-2.0"
|
||||
homepage.workspace = true
|
||||
repository.workspace = true
|
||||
description = "Exposes all the host functions that a contract can import."
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["unstable-hostfn"]
|
||||
targets = ["riscv64imac-unknown-none-elf"]
|
||||
|
||||
[dependencies]
|
||||
alloy-core = { workspace = true, optional = true, features = ["sol-types"] }
|
||||
bitflags = { workspace = true }
|
||||
codec = { features = [
|
||||
"derive",
|
||||
"max-encoded-len",
|
||||
], optional = true, workspace = true }
|
||||
const-crypto = { version = "0.3.0", default-features = false }
|
||||
hex-literal = { version = "0.4.1", default-features = false }
|
||||
pezpallet-revive-proc-macro = { workspace = true }
|
||||
scale-info = { features = ["derive"], optional = true, workspace = true }
|
||||
|
||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||
polkavm-derive = { version = "0.29.0" }
|
||||
|
||||
[features]
|
||||
default = ["scale"]
|
||||
scale = ["dep:codec", "scale-info"]
|
||||
precompiles-sol-interfaces = ["alloy-core"]
|
||||
unstable-hostfn = []
|
||||
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
address constant STORAGE_ADDR = 0x0000000000000000000000000000000000000901;
|
||||
|
||||
interface IStorage {
|
||||
/// Clear the value at the given key in the contract storage.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This function can only be called via a delegate call! For Solidity, the low level
|
||||
/// `delegatecall` function has to be used. For languages that use the FFI
|
||||
/// of `pallet-revive`, the [`crate::HostFn::delegate_call`] function can be used.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `key`: The storage key.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If no entry existed for this key, `containedKey` is `false` and
|
||||
/// `valueLen` is `0`.
|
||||
function clearStorage(uint32 flags, bool isFixedKey, bytes memory key)
|
||||
external returns (bool containedKey, uint valueLen);
|
||||
|
||||
/// Checks whether there is a value stored under the given key.
|
||||
///
|
||||
/// The key length must not exceed the maximum defined by the contracts module parameter.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This function can only be called via a delegate call! For Solidity, the low level
|
||||
/// `delegatecall` function has to be used. For languages that use the FFI
|
||||
/// of `pallet-revive`, the [`crate::HostFn::delegate_call`] function can be used.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `key`: The storage key.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// Returns the size of the pre-existing value at the specified key.
|
||||
/// If no entry exists for this key `containedKey` is `false` and
|
||||
/// `valueLen` is `0`.
|
||||
function containsStorage(uint32 flags, bool isFixedKey, bytes memory key)
|
||||
external view returns (bool containedKey, uint valueLen);
|
||||
|
||||
/// Retrieve and remove the value under the given key from storage.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This function can only be called via a delegate call! For Solidity, the low level
|
||||
/// `delegatecall` function has to be used. For languages that use the FFI
|
||||
/// of `pallet-revive`, the [`crate::HostFn::delegate_call`] function can be used.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `key`: The storage key.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns empty bytes if no value was found under `key`.
|
||||
function takeStorage(uint32 flags, bool isFixedKey, bytes memory key)
|
||||
external returns (bytes memory);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.0;
|
||||
|
||||
address constant SYSTEM_ADDR = 0x0000000000000000000000000000000000000900;
|
||||
|
||||
interface ISystem {
|
||||
/// Computes the BLAKE2 256-bit hash on the given input.
|
||||
function hashBlake256(bytes memory input) external pure returns (bytes32 digest);
|
||||
|
||||
/// Computes the BLAKE2 128-bit hash on the given input.
|
||||
function hashBlake128(bytes memory input) external pure returns (bytes32 digest);
|
||||
|
||||
/// Retrieve the account id for a specified `H160` address.
|
||||
///
|
||||
/// Calling this function on a native `H160` chain (`type AccountId = H160`)
|
||||
/// does not make sense, as it would just return the `address` that it was
|
||||
/// called with.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// If no mapping exists for `addr`, the fallback account id will be returned.
|
||||
function toAccountId(address input) external view returns (bytes memory account_id);
|
||||
|
||||
/// Checks whether the caller of the contract calling this function is the origin
|
||||
/// of the whole call stack.
|
||||
function callerIsOrigin() external view returns (bool);
|
||||
|
||||
/// Checks whether the caller of the contract calling this function is root.
|
||||
///
|
||||
/// Note that only the origin of the call stack can be root. Hence this
|
||||
/// function returning `true` implies that the contract is being called by the origin.
|
||||
///
|
||||
/// A return value of `true` indicates that this contract is being called by a root origin,
|
||||
/// and `false` indicates that the caller is a signed origin.
|
||||
function callerIsRoot() external view returns (bool);
|
||||
|
||||
/// Returns the minimum balance that is required for creating an account
|
||||
/// (the existential deposit).
|
||||
function minimumBalance() external view returns (uint);
|
||||
|
||||
/// Returns the code hash of the caller.
|
||||
function ownCodeHash() external view returns (bytes32);
|
||||
|
||||
/// Returns the amount of `Weight` left.
|
||||
function weightLeft() external view returns (uint64 refTime, uint64 proofSize);
|
||||
|
||||
/// Terminate the calling contract of this function and send balance to `beneficiary`.
|
||||
/// This will revert if:
|
||||
/// - called from constructor
|
||||
/// - called from static context
|
||||
/// - called from delegate context
|
||||
/// - the contract introduced balance locks
|
||||
function terminate(address beneficiary) external;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// 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.
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
/// Flags used by a contract to customize exit behaviour.
|
||||
#[cfg_attr(feature = "scale", derive(codec::Encode, codec::Decode, scale_info::TypeInfo))]
|
||||
#[derive(Default)]
|
||||
pub struct ReturnFlags: u32 {
|
||||
/// If this bit is set all changes made by the contract execution are rolled back.
|
||||
const REVERT = 0b0000_0001;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`.
|
||||
pub struct CallFlags: u32 {
|
||||
/// Forward the input of current function to the callee.
|
||||
///
|
||||
/// Supplied input pointers are ignored when set.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// A forwarding call will consume the current contracts input. Any attempt to
|
||||
/// access the input after this call returns will lead to [`Error::InputForwarded`].
|
||||
/// It does not matter if this is due to calling `call_data_copy` or trying another
|
||||
/// forwarding call. Consider using [`Self::CLONE_INPUT`] in order to preserve
|
||||
/// the input.
|
||||
const FORWARD_INPUT = 0b0000_0001;
|
||||
/// Identical to [`Self::FORWARD_INPUT`] but without consuming the input.
|
||||
///
|
||||
/// This adds some additional weight costs to the call.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This implies [`Self::FORWARD_INPUT`] and takes precedence when both are set.
|
||||
const CLONE_INPUT = 0b0000_0010;
|
||||
/// Do not return from the call but rather return the result of the callee to the
|
||||
/// callers caller.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This makes the current contract completely transparent to its caller by replacing
|
||||
/// this contracts potential output by the callee ones. Any code after `seal_call`
|
||||
/// can be safely considered unreachable.
|
||||
const TAIL_CALL = 0b0000_0100;
|
||||
/// Allow the callee to reenter into the current contract.
|
||||
///
|
||||
/// Without this flag any reentrancy into the current contract that originates from
|
||||
/// the callee (or any of its callees) is denied. This includes the first callee:
|
||||
/// You cannot call into yourself with this flag set.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// For `seal_delegate_call` should be always unset, otherwise
|
||||
/// [`Error::InvalidCallFlags`] is returned.
|
||||
const ALLOW_REENTRY = 0b0000_1000;
|
||||
/// Indicates that the callee is restricted from modifying the state during call execution,
|
||||
/// equivalent to Ethereum's STATICCALL.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// For `seal_delegate_call` should be always unset, otherwise
|
||||
/// [`Error::InvalidCallFlags`] is returned.
|
||||
const READ_ONLY = 0b0001_0000;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags used by a contract to customize storage behaviour.
|
||||
pub struct StorageFlags: u32 {
|
||||
/// Access the transient storage instead of the persistent one.
|
||||
const TRANSIENT = 0x0000_0001;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
// 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<u32>;
|
||||
|
||||
/// 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<u32>;
|
||||
|
||||
/// 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 {}
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
// 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.
|
||||
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use crate::{
|
||||
host::{CallFlags, HostFn, HostFnImpl, Result, StorageFlags},
|
||||
pack_hi_lo, ReturnFlags,
|
||||
};
|
||||
use pezpallet_revive_proc_macro::unstable_hostfn;
|
||||
|
||||
mod sys {
|
||||
use crate::ReturnCode;
|
||||
|
||||
#[polkavm_derive::polkavm_define_abi]
|
||||
mod abi {}
|
||||
|
||||
impl abi::FromHost for ReturnCode {
|
||||
type Regs = (u64,);
|
||||
|
||||
fn from_host((a0,): Self::Regs) -> Self {
|
||||
ReturnCode(a0 as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[polkavm_derive::polkavm_import(abi = self::abi)]
|
||||
extern "C" {
|
||||
pub fn set_storage(
|
||||
flags: u32,
|
||||
key_ptr: *const u8,
|
||||
key_len: u32,
|
||||
value_ptr: *const u8,
|
||||
value_len: u32,
|
||||
) -> ReturnCode;
|
||||
pub fn set_storage_or_clear(
|
||||
flags: u32,
|
||||
key_ptr: *const u8,
|
||||
value_ptr: *const u8,
|
||||
) -> ReturnCode;
|
||||
pub fn get_storage(
|
||||
flags: u32,
|
||||
key_ptr: *const u8,
|
||||
key_len: u32,
|
||||
out_ptr: *mut u8,
|
||||
out_len_ptr: *mut u32,
|
||||
) -> ReturnCode;
|
||||
pub fn get_storage_or_zero(flags: u32, key_ptr: *const u8, out_ptr: *mut u8);
|
||||
pub fn call(
|
||||
flags_and_callee: u64,
|
||||
ref_time_limit: u64,
|
||||
proof_size_limit: u64,
|
||||
deposit_and_value: u64,
|
||||
input_data: u64,
|
||||
output_data: u64,
|
||||
) -> ReturnCode;
|
||||
pub fn delegate_call(
|
||||
flags_and_callee: u64,
|
||||
ref_time_limit: u64,
|
||||
proof_size_limit: u64,
|
||||
deposit_ptr: *const u8,
|
||||
input_data: u64,
|
||||
output_data: u64,
|
||||
) -> ReturnCode;
|
||||
pub fn instantiate(
|
||||
ref_time_limit: u64,
|
||||
proof_size_limit: u64,
|
||||
deposit_and_value: u64,
|
||||
input_data: u64,
|
||||
output_data: u64,
|
||||
address_and_salt: u64,
|
||||
) -> ReturnCode;
|
||||
pub fn terminate(beneficiary_ptr: *const u8);
|
||||
pub fn call_data_copy(out_ptr: *mut u8, out_len: u32, offset: u32);
|
||||
pub fn call_data_load(out_ptr: *mut u8, offset: u32);
|
||||
pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32);
|
||||
pub fn caller(out_ptr: *mut u8);
|
||||
pub fn origin(out_ptr: *mut u8);
|
||||
pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8);
|
||||
pub fn code_size(address_ptr: *const u8) -> u64;
|
||||
pub fn address(out_ptr: *mut u8);
|
||||
pub fn ref_time_left() -> u64;
|
||||
pub fn get_immutable_data(out_ptr: *mut u8, out_len_ptr: *mut u32);
|
||||
pub fn set_immutable_data(ptr: *const u8, len: u32);
|
||||
pub fn balance(out_ptr: *mut u8);
|
||||
pub fn balance_of(addr_ptr: *const u8, out_ptr: *mut u8);
|
||||
pub fn chain_id(out_ptr: *mut u8);
|
||||
pub fn value_transferred(out_ptr: *mut u8);
|
||||
pub fn now(out_ptr: *mut u8);
|
||||
pub fn gas_limit() -> u64;
|
||||
pub fn deposit_event(
|
||||
topics_ptr: *const [u8; 32],
|
||||
num_topic: u32,
|
||||
data_ptr: *const u8,
|
||||
data_len: u32,
|
||||
);
|
||||
pub fn gas_price() -> u64;
|
||||
pub fn base_fee(out_ptr: *mut u8);
|
||||
pub fn call_data_size() -> u64;
|
||||
pub fn block_number(out_ptr: *mut u8);
|
||||
pub fn block_hash(block_number_ptr: *const u8, out_ptr: *mut u8);
|
||||
pub fn block_author(out_ptr: *mut u8);
|
||||
pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8);
|
||||
pub fn sr25519_verify(
|
||||
signature_ptr: *const u8,
|
||||
pub_key_ptr: *const u8,
|
||||
message_len: u32,
|
||||
message_ptr: *const u8,
|
||||
) -> ReturnCode;
|
||||
pub fn set_code_hash(code_hash_ptr: *const u8);
|
||||
pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> ReturnCode;
|
||||
pub fn instantiation_nonce() -> u64;
|
||||
pub fn return_data_size() -> u64;
|
||||
pub fn return_data_copy(out_ptr: *mut u8, out_len_ptr: *mut u32, offset: u32);
|
||||
pub fn consume_all_gas();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn extract_from_slice(output: &mut &mut [u8], new_len: usize) {
|
||||
debug_assert!(new_len <= output.len());
|
||||
let tmp = core::mem::take(output);
|
||||
*output = &mut tmp[..new_len];
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr_len_or_sentinel(data: &mut Option<&mut &mut [u8]>) -> (*mut u8, u32) {
|
||||
match data {
|
||||
Some(ref mut data) => (data.as_mut_ptr(), data.len() as _),
|
||||
None => (crate::SENTINEL as _, 0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ptr_or_sentinel(data: &Option<&[u8; 32]>) -> *const u8 {
|
||||
match data {
|
||||
Some(ref data) => data.as_ptr(),
|
||||
None => crate::SENTINEL as _,
|
||||
}
|
||||
}
|
||||
|
||||
impl HostFn for HostFnImpl {
|
||||
fn instantiate(
|
||||
ref_time_limit: u64,
|
||||
proof_size_limit: u64,
|
||||
deposit_limit: &[u8; 32],
|
||||
value: &[u8; 32],
|
||||
input: &[u8],
|
||||
mut address: Option<&mut [u8; 20]>,
|
||||
mut output: Option<&mut &mut [u8]>,
|
||||
salt: Option<&[u8; 32]>,
|
||||
) -> Result {
|
||||
let address = match address {
|
||||
Some(ref mut data) => data.as_mut_ptr(),
|
||||
None => crate::SENTINEL as _,
|
||||
};
|
||||
let (output_ptr, mut output_len_ptr) = ptr_len_or_sentinel(&mut output);
|
||||
let deposit_limit_ptr = deposit_limit.as_ptr();
|
||||
let salt_ptr = ptr_or_sentinel(&salt);
|
||||
|
||||
let deposit_and_value = pack_hi_lo(deposit_limit_ptr as _, value.as_ptr() as _);
|
||||
let address_and_salt = pack_hi_lo(address as _, salt_ptr as _);
|
||||
let input_data = pack_hi_lo(input.len() as _, input.as_ptr() as _);
|
||||
let output_data = pack_hi_lo(&mut output_len_ptr as *mut _ as _, output_ptr as _);
|
||||
|
||||
let ret_code = unsafe {
|
||||
sys::instantiate(
|
||||
ref_time_limit,
|
||||
proof_size_limit,
|
||||
deposit_and_value,
|
||||
input_data,
|
||||
output_data,
|
||||
address_and_salt,
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(ref mut output) = output {
|
||||
extract_from_slice(output, output_len_ptr as usize);
|
||||
}
|
||||
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
fn call(
|
||||
flags: CallFlags,
|
||||
callee: &[u8; 20],
|
||||
ref_time_limit: u64,
|
||||
proof_size_limit: u64,
|
||||
deposit_limit: &[u8; 32],
|
||||
value: &[u8; 32],
|
||||
input: &[u8],
|
||||
mut output: Option<&mut &mut [u8]>,
|
||||
) -> Result {
|
||||
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
|
||||
let deposit_limit_ptr = deposit_limit.as_ptr();
|
||||
|
||||
let flags_and_callee = pack_hi_lo(flags.bits(), callee.as_ptr() as _);
|
||||
let deposit_and_value = pack_hi_lo(deposit_limit_ptr as _, value.as_ptr() as _);
|
||||
let input_data = pack_hi_lo(input.len() as _, input.as_ptr() as _);
|
||||
let output_data = pack_hi_lo(&mut output_len as *mut _ as _, output_ptr as _);
|
||||
|
||||
let ret_code = unsafe {
|
||||
sys::call(
|
||||
flags_and_callee,
|
||||
ref_time_limit,
|
||||
proof_size_limit,
|
||||
deposit_and_value,
|
||||
input_data,
|
||||
output_data,
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(ref mut output) = output {
|
||||
extract_from_slice(output, output_len as usize);
|
||||
}
|
||||
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
fn delegate_call(
|
||||
flags: CallFlags,
|
||||
address: &[u8; 20],
|
||||
ref_time_limit: u64,
|
||||
proof_size_limit: u64,
|
||||
deposit_limit: &[u8; 32],
|
||||
input: &[u8],
|
||||
mut output: Option<&mut &mut [u8]>,
|
||||
) -> Result {
|
||||
let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output);
|
||||
let deposit_limit_ptr = deposit_limit.as_ptr();
|
||||
|
||||
let flags_and_callee = pack_hi_lo(flags.bits(), address.as_ptr() as u32);
|
||||
let input_data = pack_hi_lo(input.len() as u32, input.as_ptr() as u32);
|
||||
let output_data = pack_hi_lo(&mut output_len as *mut _ as u32, output_ptr as u32);
|
||||
|
||||
let ret_code = unsafe {
|
||||
sys::delegate_call(
|
||||
flags_and_callee,
|
||||
ref_time_limit,
|
||||
proof_size_limit,
|
||||
deposit_limit_ptr as _,
|
||||
input_data,
|
||||
output_data,
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(ref mut output) = output {
|
||||
extract_from_slice(output, output_len as usize);
|
||||
}
|
||||
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
fn deposit_event(topics: &[[u8; 32]], data: &[u8]) {
|
||||
unsafe {
|
||||
sys::deposit_event(
|
||||
topics.as_ptr(),
|
||||
topics.len() as u32,
|
||||
data.as_ptr(),
|
||||
data.len() as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_storage(flags: StorageFlags, key: &[u8], encoded_value: &[u8]) -> Option<u32> {
|
||||
let ret_code = unsafe {
|
||||
sys::set_storage(
|
||||
flags.bits(),
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
encoded_value.as_ptr(),
|
||||
encoded_value.len() as u32,
|
||||
)
|
||||
};
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
fn set_storage_or_clear(
|
||||
flags: StorageFlags,
|
||||
key: &[u8; 32],
|
||||
encoded_value: &[u8; 32],
|
||||
) -> Option<u32> {
|
||||
let ret_code = unsafe {
|
||||
sys::set_storage_or_clear(flags.bits(), key.as_ptr(), encoded_value.as_ptr())
|
||||
};
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
fn get_storage_or_zero(flags: StorageFlags, key: &[u8; 32], output: &mut [u8; 32]) {
|
||||
unsafe { sys::get_storage_or_zero(flags.bits(), key.as_ptr(), output.as_mut_ptr()) };
|
||||
}
|
||||
|
||||
fn get_storage(flags: StorageFlags, key: &[u8], output: &mut &mut [u8]) -> Result {
|
||||
let mut output_len = output.len() as u32;
|
||||
let ret_code = {
|
||||
unsafe {
|
||||
sys::get_storage(
|
||||
flags.bits(),
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
output.as_mut_ptr(),
|
||||
&mut output_len,
|
||||
)
|
||||
}
|
||||
};
|
||||
extract_from_slice(output, output_len as usize);
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
fn call_data_load(out_ptr: &mut [u8; 32], offset: u32) {
|
||||
unsafe { sys::call_data_load(out_ptr.as_mut_ptr(), offset) };
|
||||
}
|
||||
|
||||
fn gas_limit() -> u64 {
|
||||
unsafe { sys::gas_limit() }
|
||||
}
|
||||
|
||||
fn call_data_size() -> u64 {
|
||||
unsafe { sys::call_data_size() }
|
||||
}
|
||||
|
||||
fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! {
|
||||
unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) }
|
||||
panic!("seal_return does not return");
|
||||
}
|
||||
|
||||
fn gas_price() -> u64 {
|
||||
unsafe { sys::gas_price() }
|
||||
}
|
||||
|
||||
fn base_fee(output: &mut [u8; 32]) {
|
||||
unsafe { sys::base_fee(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn balance(output: &mut [u8; 32]) {
|
||||
unsafe { sys::balance(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn value_transferred(output: &mut [u8; 32]) {
|
||||
unsafe { sys::value_transferred(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn now(output: &mut [u8; 32]) {
|
||||
unsafe { sys::now(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn chain_id(output: &mut [u8; 32]) {
|
||||
unsafe { sys::chain_id(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn address(output: &mut [u8; 20]) {
|
||||
unsafe { sys::address(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn caller(output: &mut [u8; 20]) {
|
||||
unsafe { sys::caller(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn origin(output: &mut [u8; 20]) {
|
||||
unsafe { sys::origin(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn block_number(output: &mut [u8; 32]) {
|
||||
unsafe { sys::block_number(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn block_author(output: &mut [u8; 20]) {
|
||||
unsafe { sys::block_author(output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn hash_keccak_256(input: &[u8], output: &mut [u8; 32]) {
|
||||
unsafe { sys::hash_keccak_256(input.as_ptr(), input.len() as u32, output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn get_immutable_data(output: &mut &mut [u8]) {
|
||||
let mut output_len = output.len() as u32;
|
||||
unsafe { sys::get_immutable_data(output.as_mut_ptr(), &mut output_len) };
|
||||
extract_from_slice(output, output_len as usize);
|
||||
}
|
||||
|
||||
fn set_immutable_data(data: &[u8]) {
|
||||
unsafe { sys::set_immutable_data(data.as_ptr(), data.len() as u32) }
|
||||
}
|
||||
|
||||
fn balance_of(address: &[u8; 20], output: &mut [u8; 32]) {
|
||||
unsafe { sys::balance_of(address.as_ptr(), output.as_mut_ptr()) };
|
||||
}
|
||||
|
||||
fn code_hash(address: &[u8; 20], output: &mut [u8; 32]) {
|
||||
unsafe { sys::code_hash(address.as_ptr(), output.as_mut_ptr()) }
|
||||
}
|
||||
|
||||
fn code_size(address: &[u8; 20]) -> u64 {
|
||||
unsafe { sys::code_size(address.as_ptr()) }
|
||||
}
|
||||
|
||||
fn return_data_size() -> u64 {
|
||||
unsafe { sys::return_data_size() }
|
||||
}
|
||||
|
||||
fn return_data_copy(output: &mut &mut [u8], offset: u32) {
|
||||
let mut output_len = output.len() as u32;
|
||||
{
|
||||
unsafe { sys::return_data_copy(output.as_mut_ptr(), &mut output_len, offset) };
|
||||
}
|
||||
extract_from_slice(output, output_len as usize);
|
||||
}
|
||||
|
||||
fn gas_left() -> u64 {
|
||||
// The name is only for historical reasons; it's the correct method.
|
||||
unsafe { sys::ref_time_left() }
|
||||
}
|
||||
|
||||
fn block_hash(block_number_ptr: &[u8; 32], output: &mut [u8; 32]) {
|
||||
unsafe { sys::block_hash(block_number_ptr.as_ptr(), output.as_mut_ptr()) };
|
||||
}
|
||||
|
||||
fn call_data_copy(output: &mut [u8], offset: u32) {
|
||||
let len = output.len() as u32;
|
||||
unsafe { sys::call_data_copy(output.as_mut_ptr(), len, offset) };
|
||||
}
|
||||
|
||||
fn consume_all_gas() -> ! {
|
||||
unsafe { sys::consume_all_gas() }
|
||||
unreachable!("consume_all_gas does not return");
|
||||
}
|
||||
|
||||
#[unstable_hostfn]
|
||||
fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result {
|
||||
let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) };
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
#[unstable_hostfn]
|
||||
fn set_code_hash(code_hash: &[u8; 32]) {
|
||||
unsafe { sys::set_code_hash(code_hash.as_ptr()) }
|
||||
}
|
||||
|
||||
#[unstable_hostfn]
|
||||
fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result {
|
||||
let ret_code = unsafe {
|
||||
sys::sr25519_verify(
|
||||
signature.as_ptr(),
|
||||
pub_key.as_ptr(),
|
||||
message.len() as u32,
|
||||
message.as_ptr(),
|
||||
)
|
||||
};
|
||||
ret_code.into()
|
||||
}
|
||||
|
||||
#[unstable_hostfn]
|
||||
fn terminate(beneficiary: &[u8; 20]) -> ! {
|
||||
unsafe { sys::terminate(beneficiary.as_ptr()) }
|
||||
panic!("terminate does not return");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// 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.
|
||||
|
||||
//! External C API to communicate with Pezkuwi SDK's `pezpallet-revive` module.
|
||||
//!
|
||||
//! Refer to the FRAME `pezpallet-revive` module for more documentation.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
mod flags;
|
||||
pub use flags::*;
|
||||
mod host;
|
||||
mod macros;
|
||||
|
||||
pub mod precompiles;
|
||||
pub use precompiles::{
|
||||
storage::STORAGE_PRECOMPILE_ADDR, system::SYSTEM_PRECOMPILE_ADDR, utils::solidity_selector,
|
||||
};
|
||||
|
||||
pub use host::{HostFn, HostFnImpl};
|
||||
|
||||
/// Convert a u64 into a [u8; 32].
|
||||
pub const fn u256_bytes(value: u64) -> [u8; 32] {
|
||||
let mut buffer = [0u8; 32];
|
||||
let bytes = value.to_le_bytes();
|
||||
|
||||
buffer[0] = bytes[0];
|
||||
buffer[1] = bytes[1];
|
||||
buffer[2] = bytes[2];
|
||||
buffer[3] = bytes[3];
|
||||
buffer[4] = bytes[4];
|
||||
buffer[5] = bytes[5];
|
||||
buffer[6] = bytes[6];
|
||||
buffer[7] = bytes[7];
|
||||
buffer
|
||||
}
|
||||
|
||||
macro_rules! define_error_codes {
|
||||
(
|
||||
$(
|
||||
$( #[$attr:meta] )*
|
||||
$name:ident = $discr:literal,
|
||||
)*
|
||||
) => {
|
||||
/// Every error that can be returned to a contract when it calls any of the host functions.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[repr(u32)]
|
||||
pub enum ReturnErrorCode {
|
||||
/// API call successful.
|
||||
Success = 0,
|
||||
$(
|
||||
$( #[$attr] )*
|
||||
$name = $discr,
|
||||
)*
|
||||
/// Returns if an unknown error was received from the host module.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<ReturnCode> for Result {
|
||||
fn from(return_code: ReturnCode) -> Self {
|
||||
match return_code.0 {
|
||||
0 => Ok(()),
|
||||
$(
|
||||
$discr => Err(ReturnErrorCode::$name),
|
||||
)*
|
||||
_ => Err(ReturnErrorCode::Unknown),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl From<ReturnErrorCode> for u32 {
|
||||
fn from(code: ReturnErrorCode) -> u32 {
|
||||
code as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ReturnErrorCode> for u64 {
|
||||
fn from(error: ReturnErrorCode) -> Self {
|
||||
u32::from(error).into()
|
||||
}
|
||||
}
|
||||
|
||||
define_error_codes! {
|
||||
/// The called function trapped and has its state changes reverted.
|
||||
/// In this case no output buffer is returned.
|
||||
/// Can only be returned from `call` and `instantiate`.
|
||||
CalleeTrapped = 1,
|
||||
/// The called function ran to completion but decided to revert its state.
|
||||
/// An output buffer is returned when one was supplied.
|
||||
/// Can only be returned from `call` and `instantiate`.
|
||||
CalleeReverted = 2,
|
||||
/// The passed key does not exist in storage.
|
||||
KeyNotFound = 3,
|
||||
/// Transfer failed for other not further specified reason. Most probably
|
||||
/// reserved or locked balance of the sender that was preventing the transfer.
|
||||
TransferFailed = 4,
|
||||
/// The subcall ran out of weight or storage deposit.
|
||||
OutOfResources = 5,
|
||||
/// ECDSA public key recovery failed. Most probably wrong recovery id or signature.
|
||||
EcdsaRecoveryFailed = 7,
|
||||
/// sr25519 signature verification failed.
|
||||
Sr25519VerifyFailed = 8,
|
||||
/// Contract instantiation failed because the address already exists.
|
||||
/// Occurs when instantiating the same contract with the same salt more than once.
|
||||
DuplicateContractAddress = 11,
|
||||
}
|
||||
|
||||
/// The raw return code returned by the host side.
|
||||
#[repr(transparent)]
|
||||
pub struct ReturnCode(u32);
|
||||
|
||||
/// Used as a sentinel value when reading and writing contract memory.
|
||||
///
|
||||
/// We use this value to signal `None` to a contract when only a primitive is
|
||||
/// allowed and we don't want to go through encoding a full Rust type.
|
||||
/// Using `u32::Max` is a safe sentinel because contracts are never
|
||||
/// allowed to use such a large amount of resources. So this value doesn't
|
||||
/// make sense for a memory location or length.
|
||||
const SENTINEL: u32 = u32::MAX;
|
||||
|
||||
impl From<ReturnCode> for Option<u32> {
|
||||
fn from(code: ReturnCode) -> Self {
|
||||
(code.0 < SENTINEL).then_some(code.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReturnCode {
|
||||
/// Returns the raw underlying `u32` representation.
|
||||
pub fn into_u32(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
/// Returns the underlying `u32` converted into `bool`.
|
||||
pub fn into_bool(self) -> bool {
|
||||
self.0.ne(&0)
|
||||
}
|
||||
}
|
||||
|
||||
type Result = core::result::Result<(), ReturnErrorCode>;
|
||||
|
||||
/// Helper to pack two `u32` values into a `u64` register.
|
||||
///
|
||||
/// Pointers to PVM memory are always 32 bit in size. Thus contracts can pack two
|
||||
/// pointers into a single register when calling a syscall API method.
|
||||
///
|
||||
/// This is done in syscall API methods where the number of arguments is exceeding
|
||||
/// the available registers.
|
||||
pub fn pack_hi_lo(hi: u32, lo: u32) -> u64 {
|
||||
((hi as u64) << 32) | lo as u64
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// 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.
|
||||
|
||||
/// Utility macro to read input passed to a contract.
|
||||
///
|
||||
/// Example:
|
||||
/// ```ignore
|
||||
/// input!(
|
||||
/// var1: u32, // [0, 4) var1 decoded as u32
|
||||
/// var2: [u8; 32], // [4, 36) var2 decoded as a [u8] slice
|
||||
/// var3: u8, // [36, 37) var3 decoded as a u8
|
||||
/// );
|
||||
///
|
||||
/// // Input and size can be specified as well:
|
||||
/// input!(
|
||||
/// input, // input buffer (optional)
|
||||
/// 512, // input size (optional)
|
||||
/// var4: u32, // [0, 4) var4 decoded as u32
|
||||
/// var5: [u8], // [4, ..) var5 decoded as a [u8] slice
|
||||
/// );
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! input {
|
||||
(@inner $input:expr, $cursor:expr,) => {};
|
||||
(@size $size:expr, ) => { $size };
|
||||
|
||||
// Match a u8 variable.
|
||||
// e.g input!(var1: u8, );
|
||||
(@inner $input:expr, $cursor:expr, $var:ident: u8, $($rest:tt)*) => {
|
||||
let $var = $input[$cursor];
|
||||
input!(@inner $input, $cursor + 1, $($rest)*);
|
||||
};
|
||||
|
||||
// Size of u8 variable.
|
||||
(@size $size:expr, $var:ident: u8, $($rest:tt)*) => {
|
||||
input!(@size $size + 1, $($rest)*)
|
||||
};
|
||||
|
||||
// Match a u64 variable.
|
||||
// e.g input!(var1: u64, );
|
||||
(@inner $input:expr, $cursor:expr, $var:ident: u64, $($rest:tt)*) => {
|
||||
let $var = u64::from_le_bytes($input[$cursor..$cursor + 8].try_into().unwrap());
|
||||
input!(@inner $input, $cursor + 8, $($rest)*);
|
||||
};
|
||||
|
||||
// Size of u64 variable.
|
||||
(@size $size:expr, $var:ident: u64, $($rest:tt)*) => {
|
||||
input!(@size $size + 8, $($rest)*)
|
||||
};
|
||||
|
||||
// Match a u32 variable.
|
||||
// e.g input!(var1: u32, );
|
||||
(@inner $input:expr, $cursor:expr, $var:ident: u32, $($rest:tt)*) => {
|
||||
let $var = u32::from_le_bytes($input[$cursor..$cursor + 4].try_into().unwrap());
|
||||
input!(@inner $input, $cursor + 4, $($rest)*);
|
||||
};
|
||||
|
||||
// Size of u32 variable.
|
||||
(@size $size:expr, $var:ident: u32, $($rest:tt)*) => {
|
||||
input!(@size $size + 4, $($rest)*)
|
||||
};
|
||||
|
||||
// Match a u8 slice with the remaining bytes.
|
||||
// e.g input!(512, var1: [u8; 32], var2: [u8], );
|
||||
(@inner $input:expr, $cursor:expr, $var:ident: [u8],) => {
|
||||
let $var = &$input[$cursor..];
|
||||
};
|
||||
|
||||
// Match a u8 slice of the given size.
|
||||
// e.g input!(var1: [u8; 32], );
|
||||
(@inner $input:expr, $cursor:expr, $var:ident: [u8; $n:expr], $($rest:tt)*) => {
|
||||
let $var = &$input[$cursor..$cursor+$n];
|
||||
input!(@inner $input, $cursor + $n, $($rest)*);
|
||||
};
|
||||
|
||||
// Match an array reference of the given size.
|
||||
// e.g input!(var1: &[u8; 32], );
|
||||
(@inner $input:expr, $cursor:expr, $var:ident: &[u8; $n:expr], $($rest:tt)*) => {
|
||||
let $var: &[u8; $n] = &$input[$cursor..$cursor+$n].try_into().unwrap();
|
||||
input!(@inner $input, $cursor + $n, $($rest)*);
|
||||
};
|
||||
|
||||
// Size of a u8 slice.
|
||||
(@size $size:expr, $var:ident: [u8; $n:expr], $($rest:tt)*) => {
|
||||
input!(@size $size + $n, $($rest)*)
|
||||
};
|
||||
|
||||
// Size of an array reference.
|
||||
(@size $size:expr, $var:ident: &[u8; $n:expr], $($rest:tt)*) => {
|
||||
input!(@size $size + $n, $($rest)*)
|
||||
};
|
||||
|
||||
// Entry point, with the buffer and it's size specified first.
|
||||
// e.g input!(buffer, 512, var1: u32, var2: [u8], );
|
||||
($buffer:ident, $size:expr, $($rest:tt)*) => {
|
||||
let mut $buffer = [0u8; $size];
|
||||
let input_size = $crate::HostFnImpl::call_data_size();
|
||||
let $buffer = &mut &mut $buffer[..$size.min(input_size as usize)];
|
||||
$crate::HostFnImpl::call_data_copy($buffer, 0);
|
||||
input!(@inner $buffer, 0, $($rest)*);
|
||||
};
|
||||
|
||||
// Entry point, with the name of the buffer specified and size of the input buffer computed.
|
||||
// e.g input!(buffer, var1: u32, var2: u64, );
|
||||
($buffer: ident, $($rest:tt)*) => {
|
||||
input!($buffer, input!(@size 0, $($rest)*), $($rest)*);
|
||||
};
|
||||
|
||||
// Entry point, with the size of the input buffer computed.
|
||||
// e.g input!(var1: u32, var2: u64, );
|
||||
($($rest:tt)*) => {
|
||||
input!(buffer, $($rest)*);
|
||||
};
|
||||
}
|
||||
|
||||
/// Utility macro to invoke a host function that expect a `output: &mut &mut [u8]` as last argument.
|
||||
///
|
||||
/// Example:
|
||||
/// ```ignore
|
||||
/// use pezpallet_revive_uapi::{output, HostFn, HostFnImpl as api};
|
||||
///
|
||||
/// // call `api::caller` and store the output in `caller`
|
||||
/// output!(caller, [0u8; 32], api::caller,);
|
||||
///
|
||||
/// // call `api::get_storage` and store the output in `address`
|
||||
/// output!(address, [0u8; 32], api::get_storage, &[1u8; 32]);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! output {
|
||||
($output: ident, $buffer: expr, $host_fn:path, $($arg:expr),*) => {
|
||||
let mut $output = $buffer;
|
||||
let $output = &mut &mut $output[..];
|
||||
$host_fn($($arg,)* $output);
|
||||
};
|
||||
}
|
||||
|
||||
/// Similar to `output!` but unwraps the result.
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_output {
|
||||
($output: ident, $buffer: expr, $host_fn:path, $($arg:expr),*) => {
|
||||
let mut $output = $buffer;
|
||||
let $output = &mut &mut $output[..];
|
||||
$host_fn($($arg,)* $output).unwrap();
|
||||
};
|
||||
}
|
||||
|
||||
/// Call the host function and convert the [u8; 32] output to u64.
|
||||
#[macro_export]
|
||||
macro_rules! u64_output {
|
||||
($host_fn:path, $($arg:expr),*) => {{
|
||||
let mut buffer = [1u8; 32];
|
||||
$host_fn($($arg,)* &mut buffer);
|
||||
assert!(buffer[8..].iter().all(|&x| x == 0));
|
||||
u64::from_le_bytes(buffer[..8].try_into().unwrap())
|
||||
}};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
pub mod storage;
|
||||
pub mod system;
|
||||
pub mod utils;
|
||||
|
||||
/// The directory with all the precompile interface files.
|
||||
pub const INTERFACE_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/sol/");
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// 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.
|
||||
|
||||
//! Information around the `Storage` pre-compile.
|
||||
|
||||
/// Address for the `Storage` pre-compile.
|
||||
pub const STORAGE_PRECOMPILE_ADDR: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000901");
|
||||
|
||||
#[cfg(feature = "precompiles-sol-interfaces")]
|
||||
alloy_core::sol!("sol/IStorage.sol");
|
||||
@@ -0,0 +1,25 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// 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.
|
||||
|
||||
//! Information around the `System` pre-compile.
|
||||
|
||||
/// Address for the System pre-compile.
|
||||
pub const SYSTEM_PRECOMPILE_ADDR: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000900");
|
||||
|
||||
#[cfg(feature = "precompiles-sol-interfaces")]
|
||||
alloy_core::sol!("sol/ISystem.sol");
|
||||
@@ -0,0 +1,152 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// 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.
|
||||
|
||||
//! Helper utilities around pre-compiles.
|
||||
//!
|
||||
//! This file contains a number of functions for Solidity type encoding
|
||||
//! and decoding. Notably these implementations don't require an allocator,
|
||||
//! which is why they are here in the first place (e.g. `alloy-core` requires
|
||||
//! an allocator for the Solidity `bytes` type).
|
||||
|
||||
/// Returns the Solidity selector for `fn_sig`.
|
||||
///
|
||||
/// Note that this is a const function, it is evaluated at compile time.
|
||||
///
|
||||
/// # Usage
|
||||
///
|
||||
/// ```
|
||||
/// # use pezpallet_revive_uapi::solidity_selector;
|
||||
/// let sel = solidity_selector("ownCodeHash()");
|
||||
/// assert_eq!(sel, [219, 107, 220, 138]);
|
||||
/// ```
|
||||
pub const fn solidity_selector(fn_sig: &str) -> [u8; 4] {
|
||||
let output: [u8; 32] =
|
||||
const_crypto::sha3::Keccak256::new().update(fn_sig.as_bytes()).finalize();
|
||||
[output[0], output[1], output[2], output[3]]
|
||||
}
|
||||
|
||||
/// When encoding a Rust `[u8]` to Solidity `bytes`, a small amount
|
||||
/// of overhead space is required (for padding and the length word).
|
||||
const SOLIDITY_BYTES_ENCODING_OVERHEAD: usize = 64;
|
||||
|
||||
/// Encodes a `u32` to big-endian `[u8; 32]` with padded zeros.
|
||||
pub fn encode_u32(value: u32) -> [u8; 32] {
|
||||
let mut buf = [0u8; 32];
|
||||
buf[28..].copy_from_slice(&value.to_be_bytes()); // last 4 bytes
|
||||
buf
|
||||
}
|
||||
|
||||
/// Encodes a `bool` to big-endian `[u8; 32]` with padded zeros.
|
||||
pub fn encode_bool(value: bool, out: &mut [u8]) {
|
||||
let mut buf = [0u8; 32];
|
||||
if value {
|
||||
buf[31] = 1;
|
||||
}
|
||||
out[..32].copy_from_slice(&buf[..32]);
|
||||
}
|
||||
|
||||
/// Encodes the `bytes` argument for the Solidity ABI.
|
||||
/// The result is written to `out`.
|
||||
///
|
||||
/// Returns the number of bytes written.
|
||||
///
|
||||
/// # Important
|
||||
///
|
||||
/// This function assumes that the encoded bytes argument follows
|
||||
/// two previous other argument that takes up 32 bytes.
|
||||
///
|
||||
/// So e.g. `function(uint32, bool, bytes)` (with `uint32` and `bool`
|
||||
/// being of word size 32 bytes). This assumption is made to calculate
|
||||
/// the `offset` word.
|
||||
///
|
||||
/// # Developer Note
|
||||
///
|
||||
/// The returned layout will be
|
||||
///
|
||||
/// ```no_compile
|
||||
/// [offset (32 bytes)] [len (32 bytes)] [data (padded to 32)]
|
||||
/// ```
|
||||
///
|
||||
/// The `out` byte array needs to be able to hold (in the worst case)
|
||||
/// 95 bytes more than `input.len()`. This is because we write the
|
||||
/// following to `out`:
|
||||
///
|
||||
/// * The offset word → always 32 bytes.
|
||||
/// * The length word → always 32 bytes.
|
||||
/// * The input itself → exactly `input.len()` bytes.
|
||||
/// * We pad the input to a multiple of 32 → between 0 and 31 extra bytes.
|
||||
pub fn encode_bytes(input: &[u8], out: &mut [u8]) -> usize {
|
||||
let len = input.len();
|
||||
let padded_len = ((len + 31).div_ceil(32)) * 32;
|
||||
|
||||
// out_len = 32 + padded_len
|
||||
// = 32 + ceil(input_len / 32) * 32
|
||||
assert!(out.len() >= padded_len + SOLIDITY_BYTES_ENCODING_OVERHEAD);
|
||||
|
||||
// Encode offset as a 32-byte big-endian word.
|
||||
// The offset points to the start of the bytes payload in the ABI.
|
||||
//
|
||||
// Important:
|
||||
// This function assumes that the `bytes` argument to the Solidity function follows
|
||||
// two prior argument of word size 32 bytes (e.g. `function(uint32, bool, bytes)`!
|
||||
//
|
||||
// Then the offset will be
|
||||
// * 32 bytes for `uint32`
|
||||
// * 32 bytes for `bool`
|
||||
// * Another 32 bytes for this offset word
|
||||
// The 96 then points to the start of the `bytes` data segment (specifically
|
||||
// its `len` field (`bytes = offset (32 bytes) | len (32 bytes) | data (variable)`).
|
||||
let assumed_offset: u32 = 96;
|
||||
out[28..32].copy_from_slice(&assumed_offset.to_be_bytes()[..4]);
|
||||
out[..28].copy_from_slice(&[0u8; 28]); // make sure the first bytes are zeroed
|
||||
|
||||
// Encode length as a 32-byte big-endian word
|
||||
let mut len_word = [0u8; 32];
|
||||
let len_bytes = (len as u128).to_be_bytes(); // 16 bytes
|
||||
len_word[32 - len_bytes.len()..].copy_from_slice(&len_bytes);
|
||||
out[32..64].copy_from_slice(&len_word);
|
||||
|
||||
// Write data
|
||||
out[64..64 + len].copy_from_slice(input);
|
||||
|
||||
// Zero padding
|
||||
assert!(padded_len >= len);
|
||||
for i in 64 + len..64 + padded_len - len {
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
64 + padded_len
|
||||
}
|
||||
|
||||
/// Simple decoder for a Solidity `bytes` type.
|
||||
///
|
||||
/// Returns the number of bytes written to `out`.
|
||||
pub fn decode_bytes(input: &[u8], out: &mut [u8]) -> usize {
|
||||
let mut buf = [0u8; 4];
|
||||
buf[..].copy_from_slice(&input[28..32]);
|
||||
let offset = u32::from_be_bytes(buf) as usize;
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
buf[..].copy_from_slice(&input[60..64]);
|
||||
let bytes_len = u32::from_be_bytes(buf) as usize;
|
||||
|
||||
// we start decoding at the start of the payload.
|
||||
// the payload starts at the `len` word here:
|
||||
// `bytes = offset (32 bytes) | len (32 bytes) | data`
|
||||
out[..bytes_len].copy_from_slice(&input[32 + offset..32 + offset + bytes_len]);
|
||||
bytes_len
|
||||
}
|
||||
Reference in New Issue
Block a user