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:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -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;
}
}
+519
View File
@@ -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 256bit key with a fixed 256bit value.
///
/// If the provided 32byte value is all zeros then the key is cleared (i.e. deleted),
/// mimicking Ethereums SSTORE behavior.
///
/// # Parameters
/// - `key`: The fixed 256bit storage key (32 bytes).
/// - `value`: The fixed 256bit storage value (32 bytes).
///
/// # Return
/// Returns the size (in bytes) of the preexisting 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 256bit key.
///
/// If the key does not exist, the output buffer is filled with 32 zero bytes.
///
/// # Parameters
/// - `key`: The fixed 256bit 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");
}
}
+163
View File
@@ -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
}