use crate::ReturnFlags;
use core::marker::PhantomData;
macro_rules! define_error_codes {
(
$(
$( #[$attr:meta] )*
$name:ident = $discr:literal,
)*
) => {
#[repr(u32)]
pub enum Error {
$(
$( #[$attr] )*
$name = $discr,
)*
Unknown,
}
impl From<ReturnCode> for Result {
#[inline]
fn from(return_code: ReturnCode) -> Self {
match return_code.0 {
0 => Ok(()),
$(
$discr => Err(Error::$name),
)*
_ => Err(Error::Unknown),
}
}
}
};
}
define_error_codes! {
CalleeTrapped = 1,
CalleeReverted = 2,
KeyNotFound = 3,
_BelowSubsistenceThreshold = 4,
TransferFailed = 5,
_EndowmentTooLow = 6,
CodeNotFound = 7,
NotCallable = 8,
LoggingDisabled = 9,
EcdsaRecoveryFailed = 11,
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Ptr32<'a, T>
where
T: ?Sized,
{
_value: u32,
marker: PhantomData<fn() -> &'a T>,
}
impl<'a, T> Ptr32<'a, T>
where
T: ?Sized,
{
fn new(value: u32) -> Self {
Self {
_value: value,
marker: Default::default(),
}
}
}
impl<'a, T> Ptr32<'a, [T]> {
pub fn from_slice(slice: &'a [T]) -> Self {
Self::new(slice.as_ptr() as u32)
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct Ptr32Mut<'a, T>
where
T: ?Sized,
{
_value: u32,
marker: PhantomData<fn() -> &'a mut T>,
}
impl<'a, T> Ptr32Mut<'a, T>
where
T: ?Sized,
{
fn new(value: u32) -> Self {
Self {
_value: value,
marker: Default::default(),
}
}
}
impl<'a, T> Ptr32Mut<'a, [T]> {
pub fn from_slice(slice: &'a mut [T]) -> Self {
Self::new(slice.as_ptr() as u32)
}
}
impl<'a, T> Ptr32Mut<'a, T>
where
T: Sized,
{
pub fn from_ref(a_ref: &'a mut T) -> Self {
let a_ptr: *mut T = a_ref;
Self::new(a_ptr as u32)
}
}
const SENTINEL: u32 = u32::MAX;
#[repr(transparent)]
pub struct ReturnCode(u32);
impl From<ReturnCode> for Option<u32> {
fn from(code: ReturnCode) -> Self {
(code.0 < SENTINEL).then_some(code.0)
}
}
impl ReturnCode {
pub fn into_u32(self) -> u32 {
self.0
}
pub fn into_bool(self) -> bool {
self.0.ne(&0)
}
}
type Result = core::result::Result<(), Error>;
mod sys {
use super::{
Ptr32,
Ptr32Mut,
ReturnCode,
};
#[link(wasm_import_module = "seal0")]
extern "C" {
pub fn transfer(
account_id_ptr: Ptr32<[u8]>,
account_id_len: u32,
transferred_value_ptr: Ptr32<[u8]>,
transferred_value_len: u32,
) -> ReturnCode;
pub fn deposit_event(
topics_ptr: Ptr32<[u8]>,
topics_len: u32,
data_ptr: Ptr32<[u8]>,
data_len: u32,
);
pub fn call_chain_extension(
func_id: u32,
input_ptr: Ptr32<[u8]>,
input_len: u32,
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
) -> ReturnCode;
pub fn input(buf_ptr: Ptr32Mut<[u8]>, buf_len_ptr: Ptr32Mut<u32>);
pub fn seal_return(flags: u32, data_ptr: Ptr32<[u8]>, data_len: u32) -> !;
pub fn caller(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
pub fn block_number(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
pub fn address(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
pub fn balance(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
pub fn weight_to_fee(
gas: u64,
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
);
pub fn gas_left(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
pub fn value_transferred(
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
);
pub fn now(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
pub fn minimum_balance(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
pub fn hash_keccak_256(
input_ptr: Ptr32<[u8]>,
input_len: u32,
output_ptr: Ptr32Mut<[u8]>,
);
pub fn hash_blake2_256(
input_ptr: Ptr32<[u8]>,
input_len: u32,
output_ptr: Ptr32Mut<[u8]>,
);
pub fn hash_blake2_128(
input_ptr: Ptr32<[u8]>,
input_len: u32,
output_ptr: Ptr32Mut<[u8]>,
);
pub fn hash_sha2_256(
input_ptr: Ptr32<[u8]>,
input_len: u32,
output_ptr: Ptr32Mut<[u8]>,
);
pub fn is_contract(account_id_ptr: Ptr32<[u8]>) -> ReturnCode;
pub fn caller_is_origin() -> ReturnCode;
pub fn set_code_hash(code_hash_ptr: Ptr32<[u8]>) -> ReturnCode;
pub fn code_hash(
account_id_ptr: Ptr32<[u8]>,
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
) -> ReturnCode;
pub fn own_code_hash(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut<u32>);
#[cfg(feature = "ink-debug")]
pub fn debug_message(str_ptr: Ptr32<[u8]>, str_len: u32) -> ReturnCode;
pub fn delegate_call(
flags: u32,
code_hash_ptr: Ptr32<[u8]>,
input_data_ptr: Ptr32<[u8]>,
input_data_len: u32,
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
) -> ReturnCode;
pub fn ecdsa_recover(
signature_ptr: Ptr32<[u8]>,
message_hash_ptr: Ptr32<[u8]>,
output_ptr: Ptr32Mut<[u8]>,
) -> ReturnCode;
pub fn ecdsa_to_eth_address(
public_key_ptr: Ptr32<[u8]>,
output_ptr: Ptr32Mut<[u8]>,
) -> ReturnCode;
pub fn take_storage(
key_ptr: Ptr32<[u8]>,
key_len: u32,
out_ptr: Ptr32Mut<[u8]>,
out_len_ptr: Ptr32Mut<u32>,
) -> ReturnCode;
}
#[link(wasm_import_module = "seal1")]
extern "C" {
pub fn instantiate(
init_code_ptr: Ptr32<[u8]>,
gas: u64,
endowment_ptr: Ptr32<[u8]>,
input_ptr: Ptr32<[u8]>,
input_len: u32,
address_ptr: Ptr32Mut<[u8]>,
address_len_ptr: Ptr32Mut<u32>,
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
salt_ptr: Ptr32<[u8]>,
salt_len: u32,
) -> ReturnCode;
pub fn terminate(beneficiary_ptr: Ptr32<[u8]>) -> !;
pub fn call(
flags: u32,
callee_ptr: Ptr32<[u8]>,
gas: u64,
transferred_value_ptr: Ptr32<[u8]>,
input_data_ptr: Ptr32<[u8]>,
input_data_len: u32,
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
) -> ReturnCode;
pub fn clear_storage(key_ptr: Ptr32<[u8]>, key_len: u32) -> ReturnCode;
pub fn contains_storage(key_ptr: Ptr32<[u8]>, key_len: u32) -> ReturnCode;
pub fn get_storage(
key_ptr: Ptr32<[u8]>,
key_len: u32,
out_ptr: Ptr32Mut<[u8]>,
out_len_ptr: Ptr32Mut<u32>,
) -> ReturnCode;
}
#[link(wasm_import_module = "seal2")]
extern "C" {
pub fn set_storage(
key_ptr: Ptr32<[u8]>,
key_len: u32,
value_ptr: Ptr32<[u8]>,
value_len: u32,
) -> ReturnCode;
}
}
#[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)]
pub fn instantiate(
code_hash: &[u8],
gas_limit: u64,
endowment: &[u8],
input: &[u8],
out_address: &mut &mut [u8],
out_return_value: &mut &mut [u8],
salt: &[u8],
) -> Result {
let mut address_len = out_address.len() as u32;
let mut return_value_len = out_return_value.len() as u32;
let ret_code = {
unsafe {
sys::instantiate(
Ptr32::from_slice(code_hash),
gas_limit,
Ptr32::from_slice(endowment),
Ptr32::from_slice(input),
input.len() as u32,
Ptr32Mut::from_slice(out_address),
Ptr32Mut::from_ref(&mut address_len),
Ptr32Mut::from_slice(out_return_value),
Ptr32Mut::from_ref(&mut return_value_len),
Ptr32::from_slice(salt),
salt.len() as u32,
)
}
};
extract_from_slice(out_address, address_len as usize);
extract_from_slice(out_return_value, return_value_len as usize);
ret_code.into()
}
#[inline(always)]
pub fn call(
flags: u32,
callee: &[u8],
gas_limit: u64,
value: &[u8],
input: &[u8],
output: &mut &mut [u8],
) -> Result {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
sys::call(
flags,
Ptr32::from_slice(callee),
gas_limit,
Ptr32::from_slice(value),
Ptr32::from_slice(input),
input.len() as u32,
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into()
}
#[inline(always)]
pub fn delegate_call(
flags: u32,
code_hash: &[u8],
input: &[u8],
output: &mut &mut [u8],
) -> Result {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
sys::delegate_call(
flags,
Ptr32::from_slice(code_hash),
Ptr32::from_slice(input),
input.len() as u32,
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into()
}
pub fn transfer(account_id: &[u8], value: &[u8]) -> Result {
let ret_code = unsafe {
sys::transfer(
Ptr32::from_slice(account_id),
account_id.len() as u32,
Ptr32::from_slice(value),
value.len() as u32,
)
};
ret_code.into()
}
pub fn deposit_event(topics: &[u8], data: &[u8]) {
unsafe {
sys::deposit_event(
Ptr32::from_slice(topics),
topics.len() as u32,
Ptr32::from_slice(data),
data.len() as u32,
)
}
}
pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option<u32> {
let ret_code = unsafe {
sys::set_storage(
Ptr32::from_slice(key),
key.len() as u32,
Ptr32::from_slice(encoded_value),
encoded_value.len() as u32,
)
};
ret_code.into()
}
pub fn clear_storage(key: &[u8]) -> Option<u32> {
let ret_code =
unsafe { sys::clear_storage(Ptr32::from_slice(key), key.len() as u32) };
ret_code.into()
}
#[inline(always)]
pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
sys::get_storage(
Ptr32::from_slice(key),
key.len() as u32,
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into()
}
#[inline(always)]
pub fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
sys::take_storage(
Ptr32::from_slice(key),
key.len() as u32,
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into()
}
pub fn storage_contains(key: &[u8]) -> Option<u32> {
let ret_code =
unsafe { sys::contains_storage(Ptr32::from_slice(key), key.len() as u32) };
ret_code.into()
}
pub fn terminate(beneficiary: &[u8]) -> ! {
unsafe { sys::terminate(Ptr32::from_slice(beneficiary)) }
}
#[inline(always)]
pub fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 {
let mut output_len = output.len() as u32;
let ret_code = {
unsafe {
sys::call_chain_extension(
func_id,
Ptr32::from_slice(input),
input.len() as u32,
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
}
};
extract_from_slice(output, output_len as usize);
ret_code.into_u32()
}
#[inline(always)]
pub fn input(output: &mut &mut [u8]) {
let mut output_len = output.len() as u32;
{
unsafe {
sys::input(
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
};
}
extract_from_slice(output, output_len as usize);
}
pub fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! {
unsafe {
sys::seal_return(
flags.into_u32(),
Ptr32::from_slice(return_value),
return_value.len() as u32,
)
}
}
macro_rules! impl_wrapper_for {
( $( $name:ident, )* ) => {
$(
#[inline(always)]
pub fn $name(output: &mut &mut [u8]) {
let mut output_len = output.len() as u32;
{
unsafe {
sys::$name(
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
};
}
}
)*
}
}
impl_wrapper_for! {
caller,
block_number,
address,
balance,
gas_left,
value_transferred,
now,
minimum_balance,
}
#[inline(always)]
pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) {
let mut output_len = output.len() as u32;
{
unsafe {
sys::weight_to_fee(
gas,
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
};
}
extract_from_slice(output, output_len as usize);
}
#[cfg(feature = "ink-debug")]
pub fn debug_message(message: &str) {
static mut DEBUG_ENABLED: bool = false;
static mut FIRST_RUN: bool = true;
if unsafe { DEBUG_ENABLED || FIRST_RUN } {
let bytes = message.as_bytes();
let ret_code =
unsafe { sys::debug_message(Ptr32::from_slice(bytes), bytes.len() as u32) };
if !matches!(ret_code.into(), Err(Error::LoggingDisabled)) {
unsafe { DEBUG_ENABLED = true }
}
unsafe { FIRST_RUN = false }
}
}
#[cfg(not(feature = "ink-debug"))]
pub fn debug_message(_message: &str) {}
macro_rules! impl_hash_fn {
( $name:ident, $bytes_result:literal ) => {
paste::item! {
pub fn [<hash_ $name>](input: &[u8], output: &mut [u8; $bytes_result]) {
unsafe {
sys::[<hash_ $name>](
Ptr32::from_slice(input),
input.len() as u32,
Ptr32Mut::from_slice(output),
)
}
}
}
};
}
impl_hash_fn!(sha2_256, 32);
impl_hash_fn!(keccak_256, 32);
impl_hash_fn!(blake2_256, 32);
impl_hash_fn!(blake2_128, 16);
pub fn ecdsa_recover(
signature: &[u8; 65],
message_hash: &[u8; 32],
output: &mut [u8; 33],
) -> Result {
let ret_code = unsafe {
sys::ecdsa_recover(
Ptr32::from_slice(signature),
Ptr32::from_slice(message_hash),
Ptr32Mut::from_slice(output),
)
};
ret_code.into()
}
pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result {
let ret_code = unsafe {
sys::ecdsa_to_eth_address(Ptr32::from_slice(pubkey), Ptr32Mut::from_slice(output))
};
ret_code.into()
}
pub fn is_contract(account_id: &[u8]) -> bool {
let ret_val = unsafe { sys::is_contract(Ptr32::from_slice(account_id)) };
ret_val.into_bool()
}
pub fn caller_is_origin() -> bool {
let ret_val = unsafe { sys::caller_is_origin() };
ret_val.into_bool()
}
pub fn set_code_hash(code_hash: &[u8]) -> Result {
let ret_val = unsafe { sys::set_code_hash(Ptr32::from_slice(code_hash)) };
ret_val.into()
}
pub fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result {
let mut output_len = output.len() as u32;
let ret_val = unsafe {
sys::code_hash(
Ptr32::from_slice(account_id),
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
};
ret_val.into()
}
pub fn own_code_hash(output: &mut [u8]) {
let mut output_len = output.len() as u32;
unsafe {
sys::own_code_hash(
Ptr32Mut::from_slice(output),
Ptr32Mut::from_ref(&mut output_len),
)
}
}