From 717e56a6a0fe699c78be7130733253690d9e74d9 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 18 Jul 2022 20:17:33 +0300 Subject: [PATCH 01/21] define_env proc macro basics + can_satisfy part ready --- frame/contracts/proc-macro/src/lib.rs | 186 +- frame/contracts/src/wasm/runtime.rs | 2620 +++++++++++++------------ 2 files changed, 1525 insertions(+), 1281 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index dca29c805cec4..1e5a00d2e1d93 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -21,10 +21,15 @@ extern crate alloc; -use alloc::string::ToString; +use alloc::{ + boxed::Box, + string::{String, ToString}, + vec, + vec::Vec, +}; use proc_macro2::TokenStream; -use quote::{quote, quote_spanned}; -use syn::{parse_macro_input, Data, DeriveInput, Ident}; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Ident, Item, ItemFn}; /// This derives `Debug` for a struct where each field must be of some numeric type. /// It interprets each field as its represents some weight and formats it as times so that @@ -85,7 +90,7 @@ fn derive_debug( /// This is only used then the `full` feature is activated. #[cfg(feature = "full")] fn iterate_fields(data: &syn::DataStruct, fmt: impl Fn(&Ident) -> TokenStream) -> TokenStream { - use syn::{spanned::Spanned, Fields}; + use syn::Fields; match &data.fields { Fields::Named(fields) => { @@ -140,3 +145,176 @@ fn format_default(field: &Ident) -> TokenStream { &self.#field } } + +// define_env! macro re-write +// first we parse env mod +// then we expand, i.e. +// should generate code for: +// 1. can_satisfy checks: #can_satisfy +// expand def, so just add parts related to the new func to it, and return updated def as a token +// stream see how it's done in pallet proc macro, e.g. in constants +// 2. impls() for the set of host functions: #impls + +/// parsed definition of env +/// (inspired by pallet attribute macro, see /frame/support/procedural/src/pallet/) +struct EnvDef { + pub item: syn::ItemMod, // the whole env module + pub host_funcs: Vec, // set of host fuctions +} + +struct HostFn { + item: syn::ItemFn, + module: String, + name: String, +} + +trait ToWasmSig { + fn to_wasm_sig(&self) -> TokenStream; +} + +impl ToWasmSig for HostFn { + fn to_wasm_sig(&self) -> TokenStream { + let args = self.item.sig.inputs.iter().skip(1).filter_map(|a| match a { + syn::FnArg::Typed(pt) => Some(&pt.ty), + _ => None, + }); + + let returns = match &self.item.sig.output { + syn::ReturnType::Type(_, bt) => quote! { vec![ #bt::VALUE_TYPE ] }, + _ => quote! { vec![] }, + }; + + quote! { + wasm_instrument::parity_wasm::elements::FunctionType::new( + vec! [ #(<#args>::VALUE_TYPE),* ], + #returns, + ) + } + } +} + +impl ToTokens for HostFn { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.item.to_tokens(tokens); + } +} + +impl HostFn { + pub fn try_from(mut item: syn::Item) -> syn::Result { + let span = item.span(); + let err = || { + let msg = "Invalid environment definition, only fn with #[host(\"...\")] attribute are allowed."; + syn::Error::new(span, msg) + }; + + let mut item = match item { + syn::Item::Fn(i_fn) => Ok(i_fn), + _ => Err(err()), + }?; + + let attr = item.attrs.pop().ok_or(err())?; + let module = attr.parse_args().map(|a: syn::LitStr| a.value())?; + let name = item.sig.ident.to_string(); + attr.path + .get_ident() + .ok_or(err())? + .to_string() + .eq("host") + .then(|| Ok(Self { item, module, name })) + .ok_or(err())? + } +} + +impl EnvDef { + pub fn try_from(mut item: syn::ItemMod) -> syn::Result { + let item_span = item.span(); + let items = &mut item + .content + .as_mut() + .ok_or_else(|| { + let msg = "Invalid environment definition, expected mod to be inlined."; + syn::Error::new(item_span, msg) + })? + .1; + let mut host_funcs = Vec::::default(); + + for i in items.iter() { + host_funcs.push(HostFn::try_from(i.clone())?); + } + + Ok(Self { item, host_funcs }) + } +} + +fn expand_env(def: &mut EnvDef) -> proc_macro2::TokenStream { + // should generate code for: + // 1. can_satisfy checks: #can_satisfy + // expand def, so just add parts related to the new func to it, and return updated def as a + // token stream see how it's done in pallet proc macro, e.g. in constants + // 2. impls() for the set of host functions: #impls + let can_satisfy = expand_can_satisfy(def); + // expand_impls(def); + quote! { + pub struct Env; + #can_satisfy + } + // expand_impls(def); +} + +// Adds check to can_satisfy for a new host fn +fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { + let checks = def.host_funcs.iter().map(|f| { + let (module, name, signature) = (&f.module, &f.name, &f.to_wasm_sig()); + quote! { + if module == #module.as_bytes() + && name == #name.as_bytes() + && signature == #signature + { + return true; + } + } + }); + + let satisfy_checks = quote! { + #( #checks )* + }; + + quote! { + impl crate::wasm::env_def::ImportSatisfyCheck for Env { + fn can_satisfy( + module: &[u8], + name: &[u8], + signature: &wasm_instrument::parity_wasm::elements::FunctionType, + ) -> bool { + use crate::wasm::env_def::ConvertibleToWasm; + #[cfg(not(feature = "unstable-interface"))] + if module == b"__unstable__" { + return false; + } + #satisfy_checks + return false; + } + } + } +} +//pub fn expand_impls +// TDB + +#[proc_macro_attribute] +pub fn define_env( + attr: proc_macro::TokenStream, + item: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + if !attr.is_empty() { + let msg = "Invalid define_env macro call: expected no attributes, the call must be just \ + `#[define_env]`"; + let span = proc_macro2::TokenStream::from(attr).span(); + return syn::Error::new(span, msg).to_compile_error().into() + } + + let i = syn::parse_macro_input!(item as syn::ItemMod); + match EnvDef::try_from(i) { + Ok(mut def) => expand_env(&mut def).into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index c1757ba06412a..1dc503f02d77a 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -29,6 +29,7 @@ use bitflags::bitflags; use codec::{Decode, DecodeAll, Encode, MaxEncodedLen}; use frame_support::{dispatch::DispatchError, ensure, traits::Get, weights::Weight}; use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags}; +use pallet_contracts_proc_macro::define_env; use sp_core::{crypto::UncheckedFrom, Bytes}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; use sp_runtime::traits::{Bounded, Zero}; @@ -929,6 +930,44 @@ where } } +// // How new API will look like +// #[define(seal0)] +// fn gas(ctx: Runtime, amount: u32) -> Result { +// ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; +// Ok(sp_sandbox::ReturnValue::Unit) +// } + +// #[define(seal0)] +// fn seal_set_storage( +// ctx: Runtime, +// key_ptr: u32, +// value_ptr: u32, +// value_len: u32, +// ) -> Result { +// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +// } + +// #[define(seal1)] +// fn seal_set_storage( +// ctx: Runtime, +// key_ptr: u32, +// value_ptr: u32, +// value_len: u32, +// ) -> Result { +// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) +// } + +// #[define(__unstable__)] +// fn seal_set_storage( +// ctx: Runtime, +// key_ptr: u32, +// key_len: u32, +// value_ptr: u32, +// value_len: u32, +// ) -> Result { +// ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) +// } + // This is the API exposed to contracts. // // # Note @@ -936,1284 +975,1311 @@ where // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -define_env!(Env, , - // Account for used gas. Traps if gas used is greater than gas limit. - // - // NOTE: This is a implementation defined call and is NOT a part of the public API. - // This call is supposed to be called only by instrumentation injected code. - // - // - amount: How much gas is used. - [seal0] gas(ctx, amount: u32) => { + +// New implementation with proc macro +// here: create a module and add host funcs in it, like +// it is done for pallet (see e.g. lib.rs in contracts/) +#[define_env] +pub mod env { + #[host("seal0")] + fn gas(ctx: Runtime, amount: u32) { ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; - Ok(()) - }, + Ok(sp_sandbox::ReturnValue::Unit) + } - // Set the value at the given key in the contract storage. - // - // Equivalent to the newer version of `seal_set_storage` with the exception of the return - // type. Still a valid thing to call when not interested in the return value. - [seal0] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => { + #[host("seal1")] + fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) - }, - - // Set the value at the given key in the contract storage. - // - // This version is to be used with a fixed sized storage key. For runtimes supporting transparent - // hashing, please use the newer version of this function. - // - // The value length must not exceed the maximum defined by the contracts module parameters. - // Specifying a `value_len` of zero will store an empty value. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the location to store the value is placed. - // - `value_ptr`: pointer into the linear memory where the value to set is placed. - // - `value_len`: the length of the value in bytes. - // - // # Return Value - // - // Returns the size of the pre-existing value at the specified key if any. Otherwise - // `SENTINEL` is returned as a sentinel value. - [seal1] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 => { - ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) - }, - - // Set the value at the given key in the contract storage. - // - // The key and value lengths must not exceed the maximums defined by the contracts module parameters. - // Specifying a `value_len` of zero will store an empty value. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the location to store the value is placed. - // - `key_len`: the length of the key in bytes. - // - `value_ptr`: pointer into the linear memory where the value to set is placed. - // - `value_len`: the length of the value in bytes. - // - // # Return Value - // - // Returns the size of the pre-existing value at the specified key if any. Otherwise - // `SENTINEL` is returned as a sentinel value. - [__unstable__] seal_set_storage(ctx, key_ptr: u32, key_len: u32, value_ptr: u32, value_len: u32) -> u32 => { - ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) - }, - - // Clear the value at the given key in the contract storage. - // - // Equivalent to the newer version of `seal_clear_storage` with the exception of the return - // type. Still a valid thing to call when not interested in the return value. - [seal0] seal_clear_storage(ctx, key_ptr: u32) => { - ctx.clear_storage(KeyType::Fix, key_ptr).map(|_| ()) - }, - - // Clear the value at the given key in the contract storage. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the key is placed. - // - `key_len`: the length of the key in bytes. - // - // # Return Value - // - // Returns the size of the pre-existing value at the specified key if any. Otherwise - // `SENTINEL` is returned as a sentinel value. - [__unstable__] seal_clear_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => { - ctx.clear_storage(KeyType::Variable(key_len), key_ptr) - }, - - // Retrieve the value under the given key from storage. - // - // This version is to be used with a fixed sized storage key. For runtimes supporting transparent - // hashing, please use the newer version of this function. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. - // - `out_ptr`: pointer to the linear memory where the value is written to. - // - `out_len_ptr`: in-out pointer into linear memory where the buffer length - // is read from and the value length is written to. - // - // # Errors - // - // `ReturnCode::KeyNotFound` - [seal0] seal_get_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { - ctx.get_storage(KeyType::Fix, key_ptr, out_ptr, out_len_ptr) - }, - - // Retrieve the value under the given key from storage. - // - // This version is to be used with a fixed sized storage key. For runtimes supporting transparent - // hashing, please use the newer version of this function. - // - // The key length must not exceed the maximum defined by the contracts module parameter. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. - // - `key_len`: the length of the key in bytes. - // - `out_ptr`: pointer to the linear memory where the value is written to. - // - `out_len_ptr`: in-out pointer into linear memory where the buffer length - // is read from and the value length is written to. - // - // # Errors - // - // `ReturnCode::KeyNotFound` - [__unstable__] seal_get_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { - ctx.get_storage(KeyType::Variable(key_len), key_ptr, out_ptr, out_len_ptr) - }, - - // Checks whether there is a value stored under the given key. - // - // This version is to be used with a fixed sized storage key. For runtimes supporting transparent - // hashing, please use the newer version of this function. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. - // - // # Return Value - // - // Returns the size of the pre-existing value at the specified key if any. Otherwise - // `SENTINEL` is returned as a sentinel value. - [seal0] seal_contains_storage(ctx, key_ptr: u32) -> u32 => { - ctx.contains_storage(KeyType::Fix, key_ptr) - }, - - // 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. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. - // - `key_len`: the length of the key in bytes. - // - // # Return Value - // - // Returns the size of the pre-existing value at the specified key if any. Otherwise - // `SENTINEL` is returned as a sentinel value. - [__unstable__] seal_contains_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => { - ctx.contains_storage(KeyType::Variable(key_len), key_ptr) - }, - - // Retrieve and remove the value under the given key from storage. - // - // # Parameters - // - // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. - // - `key_len`: the length of the key in bytes. - // - `out_ptr`: pointer to the linear memory where the value is written to. - // - `out_len_ptr`: in-out pointer into linear memory where the buffer length - // is read from and the value length is written to. - // - // # Errors - // - // `ReturnCode::KeyNotFound` - [__unstable__] seal_take_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { - let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; - let key = ctx.read_sandbox_memory(key_ptr, key_len)?; - if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage_transparent(&VarSizedKey::::try_from(key).map_err(|_| Error::::DecodingFailed)?, None, true)? { - ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(value.len() as u32)); - ctx.write_sandbox_output(out_ptr, out_len_ptr, &value, false, already_charged)?; - Ok(ReturnCode::Success) - } else { - ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(0)); - Ok(ReturnCode::KeyNotFound) - } - }, - - // Transfer some value to another account. - // - // # Parameters - // - // - account_ptr: a pointer to the address of the beneficiary account - // Should be decodable as an `T::AccountId`. Traps otherwise. - // - account_len: length of the address buffer. - // - value_ptr: a pointer to the buffer with value, how much value to send. - // Should be decodable as a `T::Balance`. Traps otherwise. - // - value_len: length of the value buffer. - // - // # Errors - // - // `ReturnCode::TransferFailed` - [seal0] seal_transfer( - ctx, - account_ptr: u32, - _account_len: u32, - value_ptr: u32, - _value_len: u32 - ) -> ReturnCode => { - ctx.charge_gas(RuntimeCosts::Transfer)?; - let callee: <::T as frame_system::Config>::AccountId = - ctx.read_sandbox_memory_as(account_ptr)?; - let value: BalanceOf<::T> = - ctx.read_sandbox_memory_as(value_ptr)?; - - let result = ctx.ext.transfer(&callee, value); - match result { - Ok(()) => Ok(ReturnCode::Success), - Err(err) => { - let code = Runtime::::err_into_return_code(err)?; - Ok(code) - } - } - }, - - // Make a call to another contract. - // - // # Deprecation - // - // This is equivalent to calling the newer version of this function with - // `flags` set to `ALLOW_REENTRY`. See the newer version for documentation. - // - // # Note - // - // The values `_callee_len` and `_value_len` are ignored because the encoded sizes - // of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards - // compatibility. Consider switching to the newest version of this function. - [seal0] seal_call( - ctx, - callee_ptr: u32, - _callee_len: u32, - gas: u64, - value_ptr: u32, - _value_len: u32, - input_data_ptr: u32, - input_data_len: u32, - output_ptr: u32, - output_len_ptr: u32 - ) -> ReturnCode => { - ctx.call( - CallFlags::ALLOW_REENTRY, - CallType::Call{callee_ptr, value_ptr, gas}, - input_data_ptr, - input_data_len, - output_ptr, - output_len_ptr, - ) - }, - - // Make a call to another contract. - // - // The callees output buffer is copied to `output_ptr` and its length to `output_len_ptr`. - // The copy of the output buffer can be skipped by supplying the sentinel value - // of `SENTINEL` to `output_ptr`. - // - // # Parameters - // - // - flags: See [`CallFlags`] for a documenation of the supported flags. - // - callee_ptr: a pointer to the address of the callee contract. - // Should be decodable as an `T::AccountId`. Traps otherwise. - // - gas: how much gas to devote to the execution. - // - value_ptr: a pointer to the buffer with value, how much value to send. - // Should be decodable as a `T::Balance`. Traps otherwise. - // - input_data_ptr: a pointer to a buffer to be used as input data to the callee. - // - input_data_len: length of the input data buffer. - // - output_ptr: a pointer where the output buffer is copied to. - // - output_len_ptr: in-out pointer to where the length of the buffer is read from - // and the actual length is written to. - // - // # Errors - // - // An error means that the call wasn't successful output buffer is returned unless - // stated otherwise. - // - // `ReturnCode::CalleeReverted`: Output buffer is returned. - // `ReturnCode::CalleeTrapped` - // `ReturnCode::TransferFailed` - // `ReturnCode::NotCallable` - [seal1] seal_call( - ctx, - flags: u32, - callee_ptr: u32, - gas: u64, - value_ptr: u32, - input_data_ptr: u32, - input_data_len: u32, - output_ptr: u32, - output_len_ptr: u32 - ) -> ReturnCode => { - ctx.call( - CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, - CallType::Call{callee_ptr, value_ptr, gas}, - input_data_ptr, - input_data_len, - output_ptr, - output_len_ptr, - ) - }, - - // 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. - // - code_hash: a pointer to the hash of the code to be called. - // - input_data_ptr: a pointer to a buffer to be used as input data to the callee. - // - input_data_len: length of the input data buffer. - // - output_ptr: a pointer where the output buffer is copied to. - // - output_len_ptr: in-out pointer to where the length of the buffer is read from - // and the actual length is written to. - // - // # Errors - // - // An error means that the call wasn't successful and no output buffer is returned unless - // stated otherwise. - // - // `ReturnCode::CalleeReverted`: Output buffer is returned. - // `ReturnCode::CalleeTrapped` - // `ReturnCode::CodeNotFound` - [seal0] seal_delegate_call( - ctx, - flags: u32, - code_hash_ptr: u32, - input_data_ptr: u32, - input_data_len: u32, - output_ptr: u32, - output_len_ptr: u32 - ) -> ReturnCode => { - ctx.call( - CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, - CallType::DelegateCall{code_hash_ptr}, - input_data_ptr, - input_data_len, - output_ptr, - output_len_ptr, - ) - }, - - // Instantiate a contract with the specified code hash. - // - // # Deprecation - // - // This is equivalent to calling the newer version of this function. The newer version - // drops the now unnecessary length fields. - // - // # Note - // - // The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes - // of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards - // compatibility. Consider switching to the newest version of this function. - [seal0] seal_instantiate( - ctx, - code_hash_ptr: u32, - _code_hash_len: u32, - gas: u64, - value_ptr: u32, - _value_len: u32, - input_data_ptr: u32, - input_data_len: u32, - address_ptr: u32, - address_len_ptr: u32, - output_ptr: u32, - output_len_ptr: u32, - salt_ptr: u32, - salt_len: u32 - ) -> ReturnCode => { - ctx.instantiate ( - code_hash_ptr, - gas, - value_ptr, - input_data_ptr, - input_data_len, - address_ptr, - address_len_ptr, - output_ptr, - output_len_ptr, - salt_ptr, - salt_len, - ) - }, - - // Instantiate a contract with the specified code hash. - // - // This function creates an account and executes the constructor defined in the code specified - // by the code hash. The address of this new account is copied to `address_ptr` and its length - // to `address_len_ptr`. The constructors output buffer is copied to `output_ptr` and its - // length to `output_len_ptr`. The copy of the output buffer and address can be skipped by - // supplying the sentinel value of `SENTINEL` to `output_ptr` or `address_ptr`. - // - // `value` must be at least the minimum balance. Otherwise the instantiation fails and the - // contract is not created. - // - // # Parameters - // - // - code_hash_ptr: a pointer to the buffer that contains the initializer code. - // - gas: how much gas to devote to the execution of the initializer code. - // - value_ptr: a pointer to the buffer with value, how much value to send. - // Should be decodable as a `T::Balance`. Traps otherwise. - // - input_data_ptr: a pointer to a buffer to be used as input data to the initializer code. - // - input_data_len: length of the input data buffer. - // - address_ptr: a pointer where the new account's address is copied to. - // - address_len_ptr: in-out pointer to where the length of the buffer is read from - // and the actual length is written to. - // - output_ptr: a pointer where the output buffer is copied to. - // - output_len_ptr: in-out pointer to where the length of the buffer is read from - // and the actual length is written to. - // - salt_ptr: Pointer to raw bytes used for address derivation. See `fn contract_address`. - // - salt_len: length in bytes of the supplied salt. - // - // # Errors - // - // Please consult the `ReturnCode` 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. - // - // `ReturnCode::CalleeReverted`: Output buffer is returned. - // `ReturnCode::CalleeTrapped` - // `ReturnCode::TransferFailed` - // `ReturnCode::CodeNotFound` - [seal1] seal_instantiate( - ctx, - code_hash_ptr: u32, - gas: u64, - value_ptr: u32, - input_data_ptr: u32, - input_data_len: u32, - address_ptr: u32, - address_len_ptr: u32, - output_ptr: u32, - output_len_ptr: u32, - salt_ptr: u32, - salt_len: u32 - ) -> ReturnCode => { - ctx.instantiate( - code_hash_ptr, - gas, - value_ptr, - input_data_ptr, - input_data_len, - address_ptr, - address_len_ptr, - output_ptr, - output_len_ptr, - salt_ptr, - salt_len, - ) - }, - - // Remove the calling account and transfer remaining balance. - // - // # Deprecation - // - // This is equivalent to calling the newer version of this function. The newer version - // drops the now unnecessary length fields. - // - // # Note - // - // The value `_beneficiary_len` is ignored because the encoded sizes - // this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards - // compatibility. Consider switching to the newest version of this function. - [seal0] seal_terminate(ctx, beneficiary_ptr: u32, _beneficiary_len: u32) => { - ctx.terminate(beneficiary_ptr) - }, - - // 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. - // - // - beneficiary_ptr: a pointer to the address of the beneficiary account where all - // where all remaining funds of the caller are transferred. - // Should be decodable as an `T::AccountId`. Traps otherwise. - // - // # 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. - [seal1] seal_terminate(ctx, beneficiary_ptr: u32) => { - ctx.terminate(beneficiary_ptr) - }, - - // Stores the input passed by the caller into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // # Note - // - // This function traps if the input was previously forwarded by a `seal_call`. - [seal0] seal_input(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::InputBase)?; - if let Some(input) = ctx.input_data.take() { - ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| { - Some(RuntimeCosts::CopyToContract(len)) - })?; - ctx.input_data = Some(input); - Ok(()) - } else { - Err(Error::::InputForwarded.into()) - } - }, - - // Cease contract execution and save a data buffer as a result of the execution. - // - // 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: - // ``` - // seal_return(0, 0, 0); - // ``` - // - // The flags argument is a bitfield that can be used to signal special return - // conditions to the supervisor: - // --- lsb --- - // bit 0 : REVERT - Revert all storage changes made by the caller. - // bit [1, 31]: Reserved for future use. - // --- msb --- - // - // Using a reserved bit triggers a trap. - [seal0] seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => { - ctx.charge_gas(RuntimeCosts::Return(data_len))?; - Err(TrapReason::Return(ReturnData { - flags, - data: ctx.read_sandbox_memory(data_ptr, data_len)?, - })) - }, - - // Stores the address of the caller into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // 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. The value is encoded as T::AccountId. - [seal0] seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Caller)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.caller().encode(), false, already_charged - )?) - }, - - // Checks whether a specified address belongs to a contract. - // - // # Parameters - // - // - account_ptr: a pointer to the address of the beneficiary account - // Should be decodable as an `T::AccountId`. Traps otherwise. - // - // Returned value is a u32-encoded boolean: (0 = false, 1 = true). - [seal0] seal_is_contract(ctx, account_ptr: u32) -> u32 => { - ctx.charge_gas(RuntimeCosts::IsContract)?; - let address: <::T as frame_system::Config>::AccountId = - ctx.read_sandbox_memory_as(account_ptr)?; - - Ok(ctx.ext.is_contract(&address) as u32) - }, - - // Retrieve the code hash for a specified contract address. - // - // # Parameters - // - // - `account_ptr`: a pointer to the address in question. - // Should be decodable as an `T::AccountId`. Traps otherwise. - // - `out_ptr`: pointer to the linear memory where the returning value is written to. - // - `out_len_ptr`: in-out pointer into linear memory where the buffer length - // is read from and the value length is written to. - // - // # Errors - // - // `ReturnCode::KeyNotFound` - [seal0] seal_code_hash(ctx, account_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { - ctx.charge_gas(RuntimeCosts::CodeHash)?; - let address: <::T as frame_system::Config>::AccountId = - ctx.read_sandbox_memory_as(account_ptr)?; - if let Some(value) = ctx.ext.code_hash(&address) { - ctx.write_sandbox_output(out_ptr, out_len_ptr, &value.encode(), false, already_charged)?; - Ok(ReturnCode::Success) - } else { - Ok(ReturnCode::KeyNotFound) - } - }, - - // Retrieve the code hash of the currently executing contract. - // - // # Parameters - // - // - `out_ptr`: pointer to the linear memory where the returning value is written to. - // - `out_len_ptr`: in-out pointer into linear memory where the buffer length - // is read from and the value length is written to. - [seal0] seal_own_code_hash(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; - let code_hash_encoded = &ctx.ext.own_code_hash().encode(); - Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, code_hash_encoded, false, already_charged)?) - }, - - // Checks whether the caller of the current contract is the origin of the whole call stack. - // - // Prefer this over `seal_is_contract` when checking whether your contract is being called by a contract - // or a plain account. The reason is that it performs better since it does not need to - // do any storage lookups. - // - // A return value of`true` indicates that this contract is being called by a plain account - // and `false` indicates that the caller is another contract. - // - // Returned value is a u32-encoded boolean: (0 = false, 1 = true). - [seal0] seal_caller_is_origin(ctx) -> u32 => { - ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; - Ok(ctx.ext.caller_is_origin() as u32) - }, - - // Stores the address of the current contract into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - [seal0] seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Address)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged - )?) - }, - - // Stores the price for the specified amount of gas into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // The data is encoded as T::Balance. - // - // # Note - // - // It is recommended to avoid specifying very small values for `gas` as the prices for a single - // gas can be smaller than one. - [seal0] seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::WeightToFee)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged - )?) - }, - - // Stores the amount of gas left into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // The data is encoded as Gas. - [seal0] seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::GasLeft)?; - let gas_left = &ctx.ext.gas_meter().gas_left().encode(); - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, gas_left, false, already_charged, - )?) - }, - - // Stores the **free* balance of the current account into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // The data is encoded as T::Balance. - [seal0] seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Balance)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged - )?) - }, - - // Stores the value transferred along with this call/instantiate into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // The data is encoded as T::Balance. - [seal0] seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::ValueTransferred)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged - )?) - }, - - // Stores a random number for the current block and the given subject into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // The data is encoded as T::Hash. - // - // # Deprecation - // - // This function is deprecated. Users should migrate to the version in the "seal1" module. - [seal0] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Random)?; - if subject_len > ctx.ext.schedule().limits.subject_len { - return Err(Error::::RandomSubjectTooLong.into()); - } - let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).0.encode(), false, already_charged - )?) - }, - - // Stores a random number for the current block and the given subject into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // The data is encoded as (T::Hash, T::BlockNumber). - // - // # Changes from v0 - // - // In addition to the seed it returns the block number since which it was determinable - // by chain observers. - // - // # Note - // - // The returned seed should only be used to distinguish commitments made before - // the returned block number. If the block number is too early (i.e. commitments were - // made afterwards), then ensure no further commitments may be made and repeatedly - // call this on later blocks until the block number returned is later than the latest - // commitment. - [seal1] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Random)?; - if subject_len > ctx.ext.schedule().limits.subject_len { - return Err(Error::::RandomSubjectTooLong.into()); - } - let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).encode(), false, already_charged - )?) - }, - - // Load the latest block timestamp into the supplied buffer - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - [seal0] seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Now)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged - )?) - }, - - // Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - // - // The data is encoded as T::Balance. - [seal0] seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::MinimumBalance)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged - )?) - }, - - // Stores the tombstone deposit into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - // - // # Deprecation - // - // There is no longer a tombstone deposit. This function always returns 0. - [seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Balance)?; - let deposit = >::zero().encode(); - Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?) - }, - - // Was used to restore the given destination contract sacrificing the caller. - // - // # Note - // - // The state rent functionality was removed. This is stub only exists for - // backwards compatiblity - [seal0] seal_restore_to( - ctx, - _dest_ptr: u32, - _dest_len: u32, - _code_hash_ptr: u32, - _code_hash_len: u32, - _rent_allowance_ptr: u32, - _rent_allowance_len: u32, - _delta_ptr: u32, - _delta_count: u32 - ) => { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; - Ok(()) - }, - - // Was used to restore the given destination contract sacrificing the caller. - // - // # Note - // - // The state rent functionality was removed. This is stub only exists for - // backwards compatiblity - [seal1] seal_restore_to( - ctx, - _dest_ptr: u32, - _code_hash_ptr: u32, - _rent_allowance_ptr: u32, - _delta_ptr: u32, - _delta_count: u32 - ) => { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; - Ok(()) - }, - - // 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`. - // - // - topics_ptr - a pointer to the buffer of topics encoded as `Vec`. The value of this - // is ignored if `topics_len` is set to 0. The topics list can't contain duplicates. - // - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector. - // - data_ptr - a pointer to a raw data buffer which will saved along the event. - // - data_len - the length of the data buffer. - [seal0] seal_deposit_event( - ctx, - topics_ptr: u32, - topics_len: u32, - data_ptr: u32, - data_len: u32 - ) => { - fn has_duplicates(items: &mut Vec) -> bool { - // # Warning - // - // Unstable sorts are non-deterministic across architectures. The usage here is OK - // because we are rejecting duplicates which removes the non determinism. - items.sort_unstable(); - // Find any two consecutive equal elements. - items.windows(2).any(|w| { - match &w { - &[a, b] => a == b, - _ => false, - } - }) - } - - let num_topic = topics_len - .checked_div(sp_std::mem::size_of::>() as u32) - .ok_or("Zero sized topics are not allowed")?; - ctx.charge_gas(RuntimeCosts::DepositEvent { - num_topic, - len: data_len, - })?; - if data_len > ctx.ext.max_value_size() { - return Err(Error::::ValueTooLarge.into()); - } - - let mut topics: Vec::::T>> = match topics_len { - 0 => Vec::new(), - _ => ctx.read_sandbox_memory_as_unbounded(topics_ptr, topics_len)?, - }; - - // If there are more than `event_topics`, then trap. - if topics.len() > ctx.ext.schedule().limits.event_topics as usize { - return Err(Error::::TooManyTopics.into()); - } - - // Check for duplicate topics. If there are any, then trap. - // Complexity O(n * log(n)) and no additional allocations. - // This also sorts the topics. - if has_duplicates(&mut topics) { - return Err(Error::::DuplicateTopics.into()); - } - - let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?; - - ctx.ext.deposit_event(topics, event_data); - - Ok(()) - }, - - // Was used to set rent allowance of the contract. - // - // # Note - // - // The state rent functionality was removed. This is stub only exists for - // backwards compatiblity. - [seal0] seal_set_rent_allowance(ctx, _value_ptr: u32, _value_len: u32) => { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; - Ok(()) - }, - - // Was used to set rent allowance of the contract. - // - // # Note - // - // The state rent functionality was removed. This is stub only exists for - // backwards compatiblity. - [seal1] seal_set_rent_allowance(ctx, _value_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; - Ok(()) - }, - - // Was used to store the rent allowance into the supplied buffer. - // - // # Note - // - // The state rent functionality was removed. This is stub only exists for - // backwards compatiblity. - [seal0] seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::Balance)?; - let rent_allowance = >::max_value().encode(); - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &rent_allowance, false, already_charged - )?) - }, - - // Stores the current block number of the current contract into the supplied buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // `out_len_ptr` must point to a u32 value that describes the available space at - // `out_ptr`. This call overwrites it with the size of the value. If the available - // space at `out_ptr` is less than the size of the value a trap is triggered. - [seal0] seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::BlockNumber)?; - Ok(ctx.write_sandbox_output( - out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged - )?) - }, - - // Computes the SHA2 256-bit hash on the given input buffer. - // - // Returns the result directly into the given output buffer. - // - // # Note - // - // - The `input` and `output` buffer may overlap. - // - The output buffer is expected to hold at least 32 bytes (256 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 - // chosen hash function. - // - // # Parameters - // - // - `input_ptr`: the pointer into the linear memory where the input - // data is placed. - // - `input_len`: the length of the input data in bytes. - // - `output_ptr`: the pointer into the linear memory where the output - // data is placed. The function will write the result - // directly into this buffer. - [seal0] seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?; - Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?) - }, - - // Computes the KECCAK 256-bit hash on the given input buffer. - // - // Returns the result directly into the given output buffer. - // - // # Note - // - // - The `input` and `output` buffer may overlap. - // - The output buffer is expected to hold at least 32 bytes (256 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 - // chosen hash function. - // - // # Parameters - // - // - `input_ptr`: the pointer into the linear memory where the input - // data is placed. - // - `input_len`: the length of the input data in bytes. - // - `output_ptr`: the pointer into the linear memory where the output - // data is placed. The function will write the result - // directly into this buffer. - [seal0] seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; - Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?) - }, - - // Computes the BLAKE2 256-bit hash on the given input buffer. - // - // Returns the result directly into the given output buffer. - // - // # Note - // - // - The `input` and `output` buffer may overlap. - // - The output buffer is expected to hold at least 32 bytes (256 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 - // chosen hash function. - // - // # Parameters - // - // - `input_ptr`: the pointer into the linear memory where the input - // data is placed. - // - `input_len`: the length of the input data in bytes. - // - `output_ptr`: the pointer into the linear memory where the output - // data is placed. The function will write the result - // directly into this buffer. - [seal0] seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?; - Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?) - }, - - // Computes the BLAKE2 128-bit hash on the given input buffer. - // - // Returns the result directly into the given output buffer. - // - // # Note - // - // - The `input` and `output` buffer may overlap. - // - The output buffer is expected to hold at least 16 bytes (128 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 - // chosen hash function. - // - // # Parameters - // - // - `input_ptr`: the pointer into the linear memory where the input - // data is placed. - // - `input_len`: the length of the input data in bytes. - // - `output_ptr`: the pointer into the linear memory where the output - // data is placed. The function will write the result - // directly into this buffer. - [seal0] seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { - ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?; - Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?) - }, - - // Call into the chain extension provided by the chain if any. - // - // Handling of the input values is up to the specific chain extension and so is the - // return value. The extension can decide to use the inputs as primitive inputs or as - // in/out arguments by interpreting them as pointers. Any caller of this function - // must therefore coordinate with the chain that it targets. - // - // # Note - // - // If no chain extension exists the contract will trap with the `NoChainExtension` - // module error. - [seal0] seal_call_chain_extension( - ctx, - func_id: u32, - input_ptr: u32, - input_len: u32, - output_ptr: u32, - output_len_ptr: u32 - ) -> u32 => { - use crate::chain_extension::{ChainExtension, Environment, RetVal}; - if !::ChainExtension::enabled() { - return Err(Error::::NoChainExtension.into()); - } - let env = Environment::new(ctx, input_ptr, input_len, output_ptr, output_len_ptr); - match ::ChainExtension::call(func_id, env)? { - RetVal::Converging(val) => Ok(val), - RetVal::Diverging{flags, data} => Err(TrapReason::Return(ReturnData { - flags: flags.bits(), - data, - })), - } - }, - - // Emit a custom debug message. - // - // No newlines are added to the supplied message. - // Specifying invalid UTF-8 triggers a trap. - // - // This is a no-op if debug message recording is disabled which is always the case - // when the code is executing on-chain. The message is interpreted as UTF-8 and - // appended to the debug buffer which is then supplied to the calling RPC client. - // - // # Note - // - // Even though no action is taken when debug message recording is disabled there is still - // a non trivial overhead (and weight cost) associated with calling this function. Contract - // languages should remove calls to this function (either at runtime or compile time) when - // not being executed as an RPC. For example, they could allow users to disable logging - // through compile time flags (cargo features) for on-chain deployment. Additionally, the - // return value of this function can be cached in order to prevent further calls at runtime. - [seal0] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => { - ctx.charge_gas(RuntimeCosts::DebugMessage)?; - if ctx.ext.append_debug_buffer("") { - let data = ctx.read_sandbox_memory(str_ptr, str_len)?; - let msg = core::str::from_utf8(&data) - .map_err(|_| >::DebugMessageInvalidUTF8)?; - ctx.ext.append_debug_buffer(msg); - return Ok(ReturnCode::Success); - } - Ok(ReturnCode::LoggingDisabled) - }, - - // Call some dispatchable of the runtime. - // - // This function decodes the passed in data as the overarching `Call` type of the - // runtime and dispatches it. The weight as specified in the runtime is charged - // from the gas meter. Any weight refunds made by the dispatchable are considered. - // - // The filter specified by `Config::CallFilter` is attached to the origin of - // the dispatched call. - // - // # Parameters - // - // - `input_ptr`: the pointer into the linear memory where the input data is placed. - // - `input_len`: the length of the input data in bytes. - // - // # Return Value - // - // Returns `ReturnCode::Success` when the dispatchable was succesfully executed and - // returned `Ok`. When the dispatchable was exeuted but returned an error - // `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not - // provided because it is not guaranteed to be stable. - // - // # Comparison with `ChainExtension` - // - // Just as a chain extension this API allows the runtime to extend the functionality - // of contracts. While making use of this function is generelly easier it cannot be - // used in call cases. Consider writing a chain extension if you need to do perform - // one of the following tasks: - // - // - Return data. - // - Provide functionality **exclusively** to contracts. - // - Provide custom weights. - // - Avoid the need to keep the `Call` data structure stable. - // - // # Unstable - // - // This function is unstable and subject to change (or removal) in the future. Do not - // deploy a contract using it to a production chain. - [__unstable__] seal_call_runtime(ctx, call_ptr: u32, call_len: u32) -> ReturnCode => { - use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight}; - ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; - let call: ::Call = ctx.read_sandbox_memory_as_unbounded( - call_ptr, call_len - )?; - let dispatch_info = call.get_dispatch_info(); - let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?; - let result = ctx.ext.call_runtime(call); - let actual_weight = extract_actual_weight(&result, &dispatch_info); - ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); - match result { - Ok(_) => Ok(ReturnCode::Success), - Err(_) => Ok(ReturnCode::CallRuntimeReturnedError), - } - }, - - // Recovers the ECDSA public key from the given message hash and signature. - // - // Writes the public key into the given output buffer. - // Assumes the secp256k1 curve. - // - // # Parameters - // - // - `signature_ptr`: the pointer into the linear memory where the signature - // is placed. Should be decodable as a 65 bytes. Traps otherwise. - // - `message_hash_ptr`: the pointer into the linear memory where the message - // hash is placed. Should be decodable as a 32 bytes. Traps otherwise. - // - `output_ptr`: the pointer into the linear memory where the output - // data is placed. The buffer should be 33 bytes. The function - // will write the result directly into this buffer. - // - // # Errors - // - // `ReturnCode::EcdsaRecoverFailed` - [seal0] seal_ecdsa_recover(ctx, signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32) -> ReturnCode => { - ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?; - - let mut signature: [u8; 65] = [0; 65]; - ctx.read_sandbox_memory_into_buf(signature_ptr, &mut signature)?; - let mut message_hash: [u8; 32] = [0; 32]; - ctx.read_sandbox_memory_into_buf(message_hash_ptr, &mut message_hash)?; - - let result = ctx.ext.ecdsa_recover(&signature, &message_hash); - - match result { - Ok(pub_key) => { - // Write the recovered compressed ecdsa public key back into the sandboxed output - // buffer. - ctx.write_sandbox_memory(output_ptr, pub_key.as_ref())?; - - Ok(ReturnCode::Success) - }, - Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), - } - }, - - // 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 `seal_set_code_hash` and the next caller would use - // the old code. - // - // # Parameters - // - // - `code_hash_ptr`: A pointer to the buffer that contains the new code hash. - // - // # Errors - // - // `ReturnCode::CodeNotFound` - [seal0] seal_set_code_hash(ctx, code_hash_ptr: u32) -> ReturnCode => { - ctx.charge_gas(RuntimeCosts::SetCodeHash)?; - let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?; - match ctx.ext.set_code_hash(code_hash) { - Err(err) => { - let code = Runtime::::err_into_return_code(err)?; - Ok(code) - }, - Ok(()) => Ok(ReturnCode::Success) - } - }, + } +} - // Calculates Ethereum address from the ECDSA compressed public key and stores - // it into the supplied buffer. - // - // # Parameters - // - // - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes value. - // Traps otherwise. - // - `out_ptr`: the pointer into the linear memory where the output - // data is placed. The function will write the result - // directly into this buffer. - // - // The value is stored to linear memory at the address pointed to by `out_ptr`. - // If the available space at `out_ptr` is less than the size of the value a trap is triggered. - // - // # Errors - // - // `ReturnCode::EcdsaRecoverFailed` - [seal0] seal_ecdsa_to_eth_address(ctx, key_ptr: u32, out_ptr: u32) -> ReturnCode => { - ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; - let mut compressed_key: [u8; 33] = [0;33]; - ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?; - let result = ctx.ext.ecdsa_to_eth_address(&compressed_key); - match result { - Ok(eth_address) => { - ctx.write_sandbox_memory(out_ptr, eth_address.as_ref())?; - Ok(ReturnCode::Success) - }, - Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), - } - }, -); +// impl can_satisfy +//impl crate::wasm::env_def::ImportSatisfyCheck for Env { +// fn can_satisfy( + +// impl impls() +// TBD + +// Old implementation with MBE +// define_env!(Env, , +// // Account for used gas. Traps if gas used is greater than gas limit. +// // +// // NOTE: This is a implementation defined call and is NOT a part of the public API. +// // This call is supposed to be called only by instrumentation injected code. +// // +// // - amount: How much gas is used. +// [seal0] gas(ctx, amount: u32) => { +// ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; +// Ok(()) +// }, + +// // Set the value at the given key in the contract storage. +// // +// // Equivalent to the newer version of `seal_set_storage` with the exception of the return +// // type. Still a valid thing to call when not interested in the return value. +// [seal0] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => { +// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +// }, + +// // Set the value at the given key in the contract storage. +// // +// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent +// // hashing, please use the newer version of this function. +// // +// // The value length must not exceed the maximum defined by the contracts module parameters. +// // Specifying a `value_len` of zero will store an empty value. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the location to store the value is placed. +// // - `value_ptr`: pointer into the linear memory where the value to set is placed. +// // - `value_len`: the length of the value in bytes. +// // +// // # Return Value +// // +// // Returns the size of the pre-existing value at the specified key if any. Otherwise +// // `SENTINEL` is returned as a sentinel value. +// [seal1] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 => { +// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) +// }, + +// // Set the value at the given key in the contract storage. +// // +// // The key and value lengths must not exceed the maximums defined by the contracts module +// parameters. // Specifying a `value_len` of zero will store an empty value. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the location to store the value is placed. +// // - `key_len`: the length of the key in bytes. +// // - `value_ptr`: pointer into the linear memory where the value to set is placed. +// // - `value_len`: the length of the value in bytes. +// // +// // # Return Value +// // +// // Returns the size of the pre-existing value at the specified key if any. Otherwise +// // `SENTINEL` is returned as a sentinel value. +// [__unstable__] seal_set_storage(ctx, key_ptr: u32, key_len: u32, value_ptr: u32, value_len: u32) +// -> u32 => { ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) +// }, + +// // Clear the value at the given key in the contract storage. +// // +// // Equivalent to the newer version of `seal_clear_storage` with the exception of the return +// // type. Still a valid thing to call when not interested in the return value. +// [seal0] seal_clear_storage(ctx, key_ptr: u32) => { +// ctx.clear_storage(KeyType::Fix, key_ptr).map(|_| ()) +// }, + +// // Clear the value at the given key in the contract storage. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the key is placed. +// // - `key_len`: the length of the key in bytes. +// // +// // # Return Value +// // +// // Returns the size of the pre-existing value at the specified key if any. Otherwise +// // `SENTINEL` is returned as a sentinel value. +// [__unstable__] seal_clear_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => { +// ctx.clear_storage(KeyType::Variable(key_len), key_ptr) +// }, + +// // Retrieve the value under the given key from storage. +// // +// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent +// // hashing, please use the newer version of this function. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. +// // - `out_ptr`: pointer to the linear memory where the value is written to. +// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length +// // is read from and the value length is written to. +// // +// // # Errors +// // +// // `ReturnCode::KeyNotFound` +// [seal0] seal_get_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { +// ctx.get_storage(KeyType::Fix, key_ptr, out_ptr, out_len_ptr) +// }, + +// // Retrieve the value under the given key from storage. +// // +// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent +// // hashing, please use the newer version of this function. +// // +// // The key length must not exceed the maximum defined by the contracts module parameter. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. +// // - `key_len`: the length of the key in bytes. +// // - `out_ptr`: pointer to the linear memory where the value is written to. +// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length +// // is read from and the value length is written to. +// // +// // # Errors +// // +// // `ReturnCode::KeyNotFound` +// [__unstable__] seal_get_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) +// -> ReturnCode => { ctx.get_storage(KeyType::Variable(key_len), key_ptr, out_ptr, out_len_ptr) +// }, + +// // Checks whether there is a value stored under the given key. +// // +// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent +// // hashing, please use the newer version of this function. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. +// // +// // # Return Value +// // +// // Returns the size of the pre-existing value at the specified key if any. Otherwise +// // `SENTINEL` is returned as a sentinel value. +// [seal0] seal_contains_storage(ctx, key_ptr: u32) -> u32 => { +// ctx.contains_storage(KeyType::Fix, key_ptr) +// }, + +// // 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. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. +// // - `key_len`: the length of the key in bytes. +// // +// // # Return Value +// // +// // Returns the size of the pre-existing value at the specified key if any. Otherwise +// // `SENTINEL` is returned as a sentinel value. +// [__unstable__] seal_contains_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => { +// ctx.contains_storage(KeyType::Variable(key_len), key_ptr) +// }, + +// // Retrieve and remove the value under the given key from storage. +// // +// // # Parameters +// // +// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. +// // - `key_len`: the length of the key in bytes. +// // - `out_ptr`: pointer to the linear memory where the value is written to. +// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length +// // is read from and the value length is written to. +// // +// // # Errors +// // +// // `ReturnCode::KeyNotFound` +// [__unstable__] seal_take_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) +// -> ReturnCode => { let charged = +// ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; let key = +// ctx.read_sandbox_memory(key_ptr, key_len)?; if let crate::storage::WriteOutcome::Taken(value) = +// ctx.ext.set_storage_transparent(&VarSizedKey::::try_from(key).map_err(|_| +// Error::::DecodingFailed)?, None, true)? { ctx.adjust_gas(charged, +// RuntimeCosts::TakeStorage(value.len() as u32)); ctx.write_sandbox_output(out_ptr, out_len_ptr, +// &value, false, already_charged)?; Ok(ReturnCode::Success) +// } else { +// ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(0)); +// Ok(ReturnCode::KeyNotFound) +// } +// }, + +// // Transfer some value to another account. +// // +// // # Parameters +// // +// // - account_ptr: a pointer to the address of the beneficiary account +// // Should be decodable as an `T::AccountId`. Traps otherwise. +// // - account_len: length of the address buffer. +// // - value_ptr: a pointer to the buffer with value, how much value to send. +// // Should be decodable as a `T::Balance`. Traps otherwise. +// // - value_len: length of the value buffer. +// // +// // # Errors +// // +// // `ReturnCode::TransferFailed` +// [seal0] seal_transfer( +// ctx, +// account_ptr: u32, +// _account_len: u32, +// value_ptr: u32, +// _value_len: u32 +// ) -> ReturnCode => { +// ctx.charge_gas(RuntimeCosts::Transfer)?; +// let callee: <::T as frame_system::Config>::AccountId = +// ctx.read_sandbox_memory_as(account_ptr)?; +// let value: BalanceOf<::T> = +// ctx.read_sandbox_memory_as(value_ptr)?; + +// let result = ctx.ext.transfer(&callee, value); +// match result { +// Ok(()) => Ok(ReturnCode::Success), +// Err(err) => { +// let code = Runtime::::err_into_return_code(err)?; +// Ok(code) +// } +// } +// }, + +// // Make a call to another contract. +// // +// // # Deprecation +// // +// // This is equivalent to calling the newer version of this function with +// // `flags` set to `ALLOW_REENTRY`. See the newer version for documentation. +// // +// // # Note +// // +// // The values `_callee_len` and `_value_len` are ignored because the encoded sizes +// // of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards +// // compatibility. Consider switching to the newest version of this function. +// [seal0] seal_call( +// ctx, +// callee_ptr: u32, +// _callee_len: u32, +// gas: u64, +// value_ptr: u32, +// _value_len: u32, +// input_data_ptr: u32, +// input_data_len: u32, +// output_ptr: u32, +// output_len_ptr: u32 +// ) -> ReturnCode => { +// ctx.call( +// CallFlags::ALLOW_REENTRY, +// CallType::Call{callee_ptr, value_ptr, gas}, +// input_data_ptr, +// input_data_len, +// output_ptr, +// output_len_ptr, +// ) +// }, + +// // Make a call to another contract. +// // +// // The callees output buffer is copied to `output_ptr` and its length to `output_len_ptr`. +// // The copy of the output buffer can be skipped by supplying the sentinel value +// // of `SENTINEL` to `output_ptr`. +// // +// // # Parameters +// // +// // - flags: See [`CallFlags`] for a documenation of the supported flags. +// // - callee_ptr: a pointer to the address of the callee contract. +// // Should be decodable as an `T::AccountId`. Traps otherwise. +// // - gas: how much gas to devote to the execution. +// // - value_ptr: a pointer to the buffer with value, how much value to send. +// // Should be decodable as a `T::Balance`. Traps otherwise. +// // - input_data_ptr: a pointer to a buffer to be used as input data to the callee. +// // - input_data_len: length of the input data buffer. +// // - output_ptr: a pointer where the output buffer is copied to. +// // - output_len_ptr: in-out pointer to where the length of the buffer is read from +// // and the actual length is written to. +// // +// // # Errors +// // +// // An error means that the call wasn't successful output buffer is returned unless +// // stated otherwise. +// // +// // `ReturnCode::CalleeReverted`: Output buffer is returned. +// // `ReturnCode::CalleeTrapped` +// // `ReturnCode::TransferFailed` +// // `ReturnCode::NotCallable` +// [seal1] seal_call( +// ctx, +// flags: u32, +// callee_ptr: u32, +// gas: u64, +// value_ptr: u32, +// input_data_ptr: u32, +// input_data_len: u32, +// output_ptr: u32, +// output_len_ptr: u32 +// ) -> ReturnCode => { +// ctx.call( +// CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, +// CallType::Call{callee_ptr, value_ptr, gas}, +// input_data_ptr, +// input_data_len, +// output_ptr, +// output_len_ptr, +// ) +// }, + +// // 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. +// // - code_hash: a pointer to the hash of the code to be called. +// // - input_data_ptr: a pointer to a buffer to be used as input data to the callee. +// // - input_data_len: length of the input data buffer. +// // - output_ptr: a pointer where the output buffer is copied to. +// // - output_len_ptr: in-out pointer to where the length of the buffer is read from +// // and the actual length is written to. +// // +// // # Errors +// // +// // An error means that the call wasn't successful and no output buffer is returned unless +// // stated otherwise. +// // +// // `ReturnCode::CalleeReverted`: Output buffer is returned. +// // `ReturnCode::CalleeTrapped` +// // `ReturnCode::CodeNotFound` +// [seal0] seal_delegate_call( +// ctx, +// flags: u32, +// code_hash_ptr: u32, +// input_data_ptr: u32, +// input_data_len: u32, +// output_ptr: u32, +// output_len_ptr: u32 +// ) -> ReturnCode => { +// ctx.call( +// CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, +// CallType::DelegateCall{code_hash_ptr}, +// input_data_ptr, +// input_data_len, +// output_ptr, +// output_len_ptr, +// ) +// }, + +// // Instantiate a contract with the specified code hash. +// // +// // # Deprecation +// // +// // This is equivalent to calling the newer version of this function. The newer version +// // drops the now unnecessary length fields. +// // +// // # Note +// // +// // The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes +// // of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards +// // compatibility. Consider switching to the newest version of this function. +// [seal0] seal_instantiate( +// ctx, +// code_hash_ptr: u32, +// _code_hash_len: u32, +// gas: u64, +// value_ptr: u32, +// _value_len: u32, +// input_data_ptr: u32, +// input_data_len: u32, +// address_ptr: u32, +// address_len_ptr: u32, +// output_ptr: u32, +// output_len_ptr: u32, +// salt_ptr: u32, +// salt_len: u32 +// ) -> ReturnCode => { +// ctx.instantiate ( +// code_hash_ptr, +// gas, +// value_ptr, +// input_data_ptr, +// input_data_len, +// address_ptr, +// address_len_ptr, +// output_ptr, +// output_len_ptr, +// salt_ptr, +// salt_len, +// ) +// }, + +// // Instantiate a contract with the specified code hash. +// // +// // This function creates an account and executes the constructor defined in the code specified +// // by the code hash. The address of this new account is copied to `address_ptr` and its length +// // to `address_len_ptr`. The constructors output buffer is copied to `output_ptr` and its +// // length to `output_len_ptr`. The copy of the output buffer and address can be skipped by +// // supplying the sentinel value of `SENTINEL` to `output_ptr` or `address_ptr`. +// // +// // `value` must be at least the minimum balance. Otherwise the instantiation fails and the +// // contract is not created. +// // +// // # Parameters +// // +// // - code_hash_ptr: a pointer to the buffer that contains the initializer code. +// // - gas: how much gas to devote to the execution of the initializer code. +// // - value_ptr: a pointer to the buffer with value, how much value to send. +// // Should be decodable as a `T::Balance`. Traps otherwise. +// // - input_data_ptr: a pointer to a buffer to be used as input data to the initializer code. +// // - input_data_len: length of the input data buffer. +// // - address_ptr: a pointer where the new account's address is copied to. +// // - address_len_ptr: in-out pointer to where the length of the buffer is read from +// // and the actual length is written to. +// // - output_ptr: a pointer where the output buffer is copied to. +// // - output_len_ptr: in-out pointer to where the length of the buffer is read from +// // and the actual length is written to. +// // - salt_ptr: Pointer to raw bytes used for address derivation. See `fn contract_address`. +// // - salt_len: length in bytes of the supplied salt. +// // +// // # Errors +// // +// // Please consult the `ReturnCode` 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. +// // +// // `ReturnCode::CalleeReverted`: Output buffer is returned. +// // `ReturnCode::CalleeTrapped` +// // `ReturnCode::TransferFailed` +// // `ReturnCode::CodeNotFound` +// [seal1] seal_instantiate( +// ctx, +// code_hash_ptr: u32, +// gas: u64, +// value_ptr: u32, +// input_data_ptr: u32, +// input_data_len: u32, +// address_ptr: u32, +// address_len_ptr: u32, +// output_ptr: u32, +// output_len_ptr: u32, +// salt_ptr: u32, +// salt_len: u32 +// ) -> ReturnCode => { +// ctx.instantiate( +// code_hash_ptr, +// gas, +// value_ptr, +// input_data_ptr, +// input_data_len, +// address_ptr, +// address_len_ptr, +// output_ptr, +// output_len_ptr, +// salt_ptr, +// salt_len, +// ) +// }, + +// // Remove the calling account and transfer remaining balance. +// // +// // # Deprecation +// // +// // This is equivalent to calling the newer version of this function. The newer version +// // drops the now unnecessary length fields. +// // +// // # Note +// // +// // The value `_beneficiary_len` is ignored because the encoded sizes +// // this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards +// // compatibility. Consider switching to the newest version of this function. +// [seal0] seal_terminate(ctx, beneficiary_ptr: u32, _beneficiary_len: u32) => { +// ctx.terminate(beneficiary_ptr) +// }, + +// // 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. +// // +// // - beneficiary_ptr: a pointer to the address of the beneficiary account where all +// // where all remaining funds of the caller are transferred. +// // Should be decodable as an `T::AccountId`. Traps otherwise. +// // +// // # 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. +// [seal1] seal_terminate(ctx, beneficiary_ptr: u32) => { +// ctx.terminate(beneficiary_ptr) +// }, + +// // Stores the input passed by the caller into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // # Note +// // +// // This function traps if the input was previously forwarded by a `seal_call`. +// [seal0] seal_input(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::InputBase)?; +// if let Some(input) = ctx.input_data.take() { +// ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| { +// Some(RuntimeCosts::CopyToContract(len)) +// })?; +// ctx.input_data = Some(input); +// Ok(()) +// } else { +// Err(Error::::InputForwarded.into()) +// } +// }, + +// // Cease contract execution and save a data buffer as a result of the execution. +// // +// // 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: +// // ``` +// // seal_return(0, 0, 0); +// // ``` +// // +// // The flags argument is a bitfield that can be used to signal special return +// // conditions to the supervisor: +// // --- lsb --- +// // bit 0 : REVERT - Revert all storage changes made by the caller. +// // bit [1, 31]: Reserved for future use. +// // --- msb --- +// // +// // Using a reserved bit triggers a trap. +// [seal0] seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => { +// ctx.charge_gas(RuntimeCosts::Return(data_len))?; +// Err(TrapReason::Return(ReturnData { +// flags, +// data: ctx.read_sandbox_memory(data_ptr, data_len)?, +// })) +// }, + +// // Stores the address of the caller into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // 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. The value is encoded as T::AccountId. +// [seal0] seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Caller)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.caller().encode(), false, already_charged +// )?) +// }, + +// // Checks whether a specified address belongs to a contract. +// // +// // # Parameters +// // +// // - account_ptr: a pointer to the address of the beneficiary account +// // Should be decodable as an `T::AccountId`. Traps otherwise. +// // +// // Returned value is a u32-encoded boolean: (0 = false, 1 = true). +// [seal0] seal_is_contract(ctx, account_ptr: u32) -> u32 => { +// ctx.charge_gas(RuntimeCosts::IsContract)?; +// let address: <::T as frame_system::Config>::AccountId = +// ctx.read_sandbox_memory_as(account_ptr)?; + +// Ok(ctx.ext.is_contract(&address) as u32) +// }, + +// // Retrieve the code hash for a specified contract address. +// // +// // # Parameters +// // +// // - `account_ptr`: a pointer to the address in question. +// // Should be decodable as an `T::AccountId`. Traps otherwise. +// // - `out_ptr`: pointer to the linear memory where the returning value is written to. +// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length +// // is read from and the value length is written to. +// // +// // # Errors +// // +// // `ReturnCode::KeyNotFound` +// [seal0] seal_code_hash(ctx, account_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { +// ctx.charge_gas(RuntimeCosts::CodeHash)?; +// let address: <::T as frame_system::Config>::AccountId = +// ctx.read_sandbox_memory_as(account_ptr)?; +// if let Some(value) = ctx.ext.code_hash(&address) { +// ctx.write_sandbox_output(out_ptr, out_len_ptr, &value.encode(), false, already_charged)?; +// Ok(ReturnCode::Success) +// } else { +// Ok(ReturnCode::KeyNotFound) +// } +// }, + +// // Retrieve the code hash of the currently executing contract. +// // +// // # Parameters +// // +// // - `out_ptr`: pointer to the linear memory where the returning value is written to. +// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length +// // is read from and the value length is written to. +// [seal0] seal_own_code_hash(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; +// let code_hash_encoded = &ctx.ext.own_code_hash().encode(); +// Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, code_hash_encoded, false, already_charged)?) +// }, + +// // Checks whether the caller of the current contract is the origin of the whole call stack. +// // +// // Prefer this over `seal_is_contract` when checking whether your contract is being called by a +// contract // or a plain account. The reason is that it performs better since it does not need to +// // do any storage lookups. +// // +// // A return value of`true` indicates that this contract is being called by a plain account +// // and `false` indicates that the caller is another contract. +// // +// // Returned value is a u32-encoded boolean: (0 = false, 1 = true). +// [seal0] seal_caller_is_origin(ctx) -> u32 => { +// ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; +// Ok(ctx.ext.caller_is_origin() as u32) +// }, + +// // Stores the address of the current contract into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// [seal0] seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Address)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged +// )?) +// }, + +// // Stores the price for the specified amount of gas into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // The data is encoded as T::Balance. +// // +// // # Note +// // +// // It is recommended to avoid specifying very small values for `gas` as the prices for a single +// // gas can be smaller than one. +// [seal0] seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::WeightToFee)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged +// )?) +// }, + +// // Stores the amount of gas left into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // The data is encoded as Gas. +// [seal0] seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::GasLeft)?; +// let gas_left = &ctx.ext.gas_meter().gas_left().encode(); +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, gas_left, false, already_charged, +// )?) +// }, + +// // Stores the **free* balance of the current account into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // The data is encoded as T::Balance. +// [seal0] seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Balance)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged +// )?) +// }, + +// // Stores the value transferred along with this call/instantiate into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // The data is encoded as T::Balance. +// [seal0] seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::ValueTransferred)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged +// )?) +// }, + +// // Stores a random number for the current block and the given subject into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // The data is encoded as T::Hash. +// // +// // # Deprecation +// // +// // This function is deprecated. Users should migrate to the version in the "seal1" module. +// [seal0] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Random)?; +// if subject_len > ctx.ext.schedule().limits.subject_len { +// return Err(Error::::RandomSubjectTooLong.into()); +// } +// let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).0.encode(), false, already_charged +// )?) +// }, + +// // Stores a random number for the current block and the given subject into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // The data is encoded as (T::Hash, T::BlockNumber). +// // +// // # Changes from v0 +// // +// // In addition to the seed it returns the block number since which it was determinable +// // by chain observers. +// // +// // # Note +// // +// // The returned seed should only be used to distinguish commitments made before +// // the returned block number. If the block number is too early (i.e. commitments were +// // made afterwards), then ensure no further commitments may be made and repeatedly +// // call this on later blocks until the block number returned is later than the latest +// // commitment. +// [seal1] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Random)?; +// if subject_len > ctx.ext.schedule().limits.subject_len { +// return Err(Error::::RandomSubjectTooLong.into()); +// } +// let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).encode(), false, already_charged +// )?) +// }, + +// // Load the latest block timestamp into the supplied buffer +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// [seal0] seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Now)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged +// )?) +// }, + +// // Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. +// // +// // The data is encoded as T::Balance. +// [seal0] seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::MinimumBalance)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged +// )?) +// }, + +// // Stores the tombstone deposit into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // # Deprecation +// // +// // There is no longer a tombstone deposit. This function always returns 0. +// [seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Balance)?; +// let deposit = >::zero().encode(); +// Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?) +// }, + +// // Was used to restore the given destination contract sacrificing the caller. +// // +// // # Note +// // +// // The state rent functionality was removed. This is stub only exists for +// // backwards compatiblity +// [seal0] seal_restore_to( +// ctx, +// _dest_ptr: u32, +// _dest_len: u32, +// _code_hash_ptr: u32, +// _code_hash_len: u32, +// _rent_allowance_ptr: u32, +// _rent_allowance_len: u32, +// _delta_ptr: u32, +// _delta_count: u32 +// ) => { +// ctx.charge_gas(RuntimeCosts::DebugMessage)?; +// Ok(()) +// }, + +// // Was used to restore the given destination contract sacrificing the caller. +// // +// // # Note +// // +// // The state rent functionality was removed. This is stub only exists for +// // backwards compatiblity +// [seal1] seal_restore_to( +// ctx, +// _dest_ptr: u32, +// _code_hash_ptr: u32, +// _rent_allowance_ptr: u32, +// _delta_ptr: u32, +// _delta_count: u32 +// ) => { +// ctx.charge_gas(RuntimeCosts::DebugMessage)?; +// Ok(()) +// }, + +// // 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`. +// // +// // - topics_ptr - a pointer to the buffer of topics encoded as `Vec`. The value of this +// // is ignored if `topics_len` is set to 0. The topics list can't contain duplicates. +// // - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector. +// // - data_ptr - a pointer to a raw data buffer which will saved along the event. +// // - data_len - the length of the data buffer. +// [seal0] seal_deposit_event( +// ctx, +// topics_ptr: u32, +// topics_len: u32, +// data_ptr: u32, +// data_len: u32 +// ) => { +// fn has_duplicates(items: &mut Vec) -> bool { +// // # Warning +// // +// // Unstable sorts are non-deterministic across architectures. The usage here is OK +// // because we are rejecting duplicates which removes the non determinism. +// items.sort_unstable(); +// // Find any two consecutive equal elements. +// items.windows(2).any(|w| { +// match &w { +// &[a, b] => a == b, +// _ => false, +// } +// }) +// } + +// let num_topic = topics_len +// .checked_div(sp_std::mem::size_of::>() as u32) +// .ok_or("Zero sized topics are not allowed")?; +// ctx.charge_gas(RuntimeCosts::DepositEvent { +// num_topic, +// len: data_len, +// })?; +// if data_len > ctx.ext.max_value_size() { +// return Err(Error::::ValueTooLarge.into()); +// } + +// let mut topics: Vec::::T>> = match topics_len { +// 0 => Vec::new(), +// _ => ctx.read_sandbox_memory_as_unbounded(topics_ptr, topics_len)?, +// }; + +// // If there are more than `event_topics`, then trap. +// if topics.len() > ctx.ext.schedule().limits.event_topics as usize { +// return Err(Error::::TooManyTopics.into()); +// } + +// // Check for duplicate topics. If there are any, then trap. +// // Complexity O(n * log(n)) and no additional allocations. +// // This also sorts the topics. +// if has_duplicates(&mut topics) { +// return Err(Error::::DuplicateTopics.into()); +// } + +// let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?; + +// ctx.ext.deposit_event(topics, event_data); + +// Ok(()) +// }, + +// // Was used to set rent allowance of the contract. +// // +// // # Note +// // +// // The state rent functionality was removed. This is stub only exists for +// // backwards compatiblity. +// [seal0] seal_set_rent_allowance(ctx, _value_ptr: u32, _value_len: u32) => { +// ctx.charge_gas(RuntimeCosts::DebugMessage)?; +// Ok(()) +// }, + +// // Was used to set rent allowance of the contract. +// // +// // # Note +// // +// // The state rent functionality was removed. This is stub only exists for +// // backwards compatiblity. +// [seal1] seal_set_rent_allowance(ctx, _value_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::DebugMessage)?; +// Ok(()) +// }, + +// // Was used to store the rent allowance into the supplied buffer. +// // +// // # Note +// // +// // The state rent functionality was removed. This is stub only exists for +// // backwards compatiblity. +// [seal0] seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::Balance)?; +// let rent_allowance = >::max_value().encode(); +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &rent_allowance, false, already_charged +// )?) +// }, + +// // Stores the current block number of the current contract into the supplied buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // `out_len_ptr` must point to a u32 value that describes the available space at +// // `out_ptr`. This call overwrites it with the size of the value. If the available +// // space at `out_ptr` is less than the size of the value a trap is triggered. +// [seal0] seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::BlockNumber)?; +// Ok(ctx.write_sandbox_output( +// out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged +// )?) +// }, + +// // Computes the SHA2 256-bit hash on the given input buffer. +// // +// // Returns the result directly into the given output buffer. +// // +// // # Note +// // +// // - The `input` and `output` buffer may overlap. +// // - The output buffer is expected to hold at least 32 bytes (256 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 +// // chosen hash function. +// // +// // # Parameters +// // +// // - `input_ptr`: the pointer into the linear memory where the input +// // data is placed. +// // - `input_len`: the length of the input data in bytes. +// // - `output_ptr`: the pointer into the linear memory where the output +// // data is placed. The function will write the result +// // directly into this buffer. +// [seal0] seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?; +// Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?) +// }, + +// // Computes the KECCAK 256-bit hash on the given input buffer. +// // +// // Returns the result directly into the given output buffer. +// // +// // # Note +// // +// // - The `input` and `output` buffer may overlap. +// // - The output buffer is expected to hold at least 32 bytes (256 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 +// // chosen hash function. +// // +// // # Parameters +// // +// // - `input_ptr`: the pointer into the linear memory where the input +// // data is placed. +// // - `input_len`: the length of the input data in bytes. +// // - `output_ptr`: the pointer into the linear memory where the output +// // data is placed. The function will write the result +// // directly into this buffer. +// [seal0] seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; +// Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?) +// }, + +// // Computes the BLAKE2 256-bit hash on the given input buffer. +// // +// // Returns the result directly into the given output buffer. +// // +// // # Note +// // +// // - The `input` and `output` buffer may overlap. +// // - The output buffer is expected to hold at least 32 bytes (256 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 +// // chosen hash function. +// // +// // # Parameters +// // +// // - `input_ptr`: the pointer into the linear memory where the input +// // data is placed. +// // - `input_len`: the length of the input data in bytes. +// // - `output_ptr`: the pointer into the linear memory where the output +// // data is placed. The function will write the result +// // directly into this buffer. +// [seal0] seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?; +// Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?) +// }, + +// // Computes the BLAKE2 128-bit hash on the given input buffer. +// // +// // Returns the result directly into the given output buffer. +// // +// // # Note +// // +// // - The `input` and `output` buffer may overlap. +// // - The output buffer is expected to hold at least 16 bytes (128 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 +// // chosen hash function. +// // +// // # Parameters +// // +// // - `input_ptr`: the pointer into the linear memory where the input +// // data is placed. +// // - `input_len`: the length of the input data in bytes. +// // - `output_ptr`: the pointer into the linear memory where the output +// // data is placed. The function will write the result +// // directly into this buffer. +// [seal0] seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { +// ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?; +// Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?) +// }, + +// // Call into the chain extension provided by the chain if any. +// // +// // Handling of the input values is up to the specific chain extension and so is the +// // return value. The extension can decide to use the inputs as primitive inputs or as +// // in/out arguments by interpreting them as pointers. Any caller of this function +// // must therefore coordinate with the chain that it targets. +// // +// // # Note +// // +// // If no chain extension exists the contract will trap with the `NoChainExtension` +// // module error. +// [seal0] seal_call_chain_extension( +// ctx, +// func_id: u32, +// input_ptr: u32, +// input_len: u32, +// output_ptr: u32, +// output_len_ptr: u32 +// ) -> u32 => { +// use crate::chain_extension::{ChainExtension, Environment, RetVal}; +// if !::ChainExtension::enabled() { +// return Err(Error::::NoChainExtension.into()); +// } +// let env = Environment::new(ctx, input_ptr, input_len, output_ptr, output_len_ptr); +// match ::ChainExtension::call(func_id, env)? { +// RetVal::Converging(val) => Ok(val), +// RetVal::Diverging{flags, data} => Err(TrapReason::Return(ReturnData { +// flags: flags.bits(), +// data, +// })), +// } +// }, + +// // Emit a custom debug message. +// // +// // No newlines are added to the supplied message. +// // Specifying invalid UTF-8 triggers a trap. +// // +// // This is a no-op if debug message recording is disabled which is always the case +// // when the code is executing on-chain. The message is interpreted as UTF-8 and +// // appended to the debug buffer which is then supplied to the calling RPC client. +// // +// // # Note +// // +// // Even though no action is taken when debug message recording is disabled there is still +// // a non trivial overhead (and weight cost) associated with calling this function. Contract +// // languages should remove calls to this function (either at runtime or compile time) when +// // not being executed as an RPC. For example, they could allow users to disable logging +// // through compile time flags (cargo features) for on-chain deployment. Additionally, the +// // return value of this function can be cached in order to prevent further calls at runtime. +// [seal0] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => { +// ctx.charge_gas(RuntimeCosts::DebugMessage)?; +// if ctx.ext.append_debug_buffer("") { +// let data = ctx.read_sandbox_memory(str_ptr, str_len)?; +// let msg = core::str::from_utf8(&data) +// .map_err(|_| >::DebugMessageInvalidUTF8)?; +// ctx.ext.append_debug_buffer(msg); +// return Ok(ReturnCode::Success); +// } +// Ok(ReturnCode::LoggingDisabled) +// }, + +// // Call some dispatchable of the runtime. +// // +// // This function decodes the passed in data as the overarching `Call` type of the +// // runtime and dispatches it. The weight as specified in the runtime is charged +// // from the gas meter. Any weight refunds made by the dispatchable are considered. +// // +// // The filter specified by `Config::CallFilter` is attached to the origin of +// // the dispatched call. +// // +// // # Parameters +// // +// // - `input_ptr`: the pointer into the linear memory where the input data is placed. +// // - `input_len`: the length of the input data in bytes. +// // +// // # Return Value +// // +// // Returns `ReturnCode::Success` when the dispatchable was succesfully executed and +// // returned `Ok`. When the dispatchable was exeuted but returned an error +// // `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not +// // provided because it is not guaranteed to be stable. +// // +// // # Comparison with `ChainExtension` +// // +// // Just as a chain extension this API allows the runtime to extend the functionality +// // of contracts. While making use of this function is generelly easier it cannot be +// // used in call cases. Consider writing a chain extension if you need to do perform +// // one of the following tasks: +// // +// // - Return data. +// // - Provide functionality **exclusively** to contracts. +// // - Provide custom weights. +// // - Avoid the need to keep the `Call` data structure stable. +// // +// // # Unstable +// // +// // This function is unstable and subject to change (or removal) in the future. Do not +// // deploy a contract using it to a production chain. +// [__unstable__] seal_call_runtime(ctx, call_ptr: u32, call_len: u32) -> ReturnCode => { +// use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight}; +// ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; +// let call: ::Call = ctx.read_sandbox_memory_as_unbounded( +// call_ptr, call_len +// )?; +// let dispatch_info = call.get_dispatch_info(); +// let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?; +// let result = ctx.ext.call_runtime(call); +// let actual_weight = extract_actual_weight(&result, &dispatch_info); +// ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); +// match result { +// Ok(_) => Ok(ReturnCode::Success), +// Err(_) => Ok(ReturnCode::CallRuntimeReturnedError), +// } +// }, + +// // Recovers the ECDSA public key from the given message hash and signature. +// // +// // Writes the public key into the given output buffer. +// // Assumes the secp256k1 curve. +// // +// // # Parameters +// // +// // - `signature_ptr`: the pointer into the linear memory where the signature +// // is placed. Should be decodable as a 65 bytes. Traps otherwise. +// // - `message_hash_ptr`: the pointer into the linear memory where the message +// // hash is placed. Should be decodable as a 32 bytes. Traps otherwise. +// // - `output_ptr`: the pointer into the linear memory where the output +// // data is placed. The buffer should be 33 bytes. The function +// // will write the result directly into this buffer. +// // +// // # Errors +// // +// // `ReturnCode::EcdsaRecoverFailed` +// [seal0] seal_ecdsa_recover(ctx, signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32) -> +// ReturnCode => { ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?; + +// let mut signature: [u8; 65] = [0; 65]; +// ctx.read_sandbox_memory_into_buf(signature_ptr, &mut signature)?; +// let mut message_hash: [u8; 32] = [0; 32]; +// ctx.read_sandbox_memory_into_buf(message_hash_ptr, &mut message_hash)?; + +// let result = ctx.ext.ecdsa_recover(&signature, &message_hash); + +// match result { +// Ok(pub_key) => { +// // Write the recovered compressed ecdsa public key back into the sandboxed output +// // buffer. +// ctx.write_sandbox_memory(output_ptr, pub_key.as_ref())?; + +// Ok(ReturnCode::Success) +// }, +// Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), +// } +// }, + +// // 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 `seal_set_code_hash` and the next caller would use +// // the old code. +// // +// // # Parameters +// // +// // - `code_hash_ptr`: A pointer to the buffer that contains the new code hash. +// // +// // # Errors +// // +// // `ReturnCode::CodeNotFound` +// [seal0] seal_set_code_hash(ctx, code_hash_ptr: u32) -> ReturnCode => { +// ctx.charge_gas(RuntimeCosts::SetCodeHash)?; +// let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?; +// match ctx.ext.set_code_hash(code_hash) { +// Err(err) => { +// let code = Runtime::::err_into_return_code(err)?; +// Ok(code) +// }, +// Ok(()) => Ok(ReturnCode::Success) +// } +// }, + +// // Calculates Ethereum address from the ECDSA compressed public key and stores +// // it into the supplied buffer. +// // +// // # Parameters +// // +// // - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes +// value. // Traps otherwise. +// // - `out_ptr`: the pointer into the linear memory where the output +// // data is placed. The function will write the result +// // directly into this buffer. +// // +// // The value is stored to linear memory at the address pointed to by `out_ptr`. +// // If the available space at `out_ptr` is less than the size of the value a trap is triggered. +// // +// // # Errors +// // +// // `ReturnCode::EcdsaRecoverFailed` +// [seal0] seal_ecdsa_to_eth_address(ctx, key_ptr: u32, out_ptr: u32) -> ReturnCode => { +// ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; +// let mut compressed_key: [u8; 33] = [0;33]; +// ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?; +// let result = ctx.ext.ecdsa_to_eth_address(&compressed_key); +// match result { +// Ok(eth_address) => { +// ctx.write_sandbox_memory(out_ptr, eth_address.as_ref())?; +// Ok(ReturnCode::Success) +// }, +// Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), +// } +// }, +// ); From c7d6f85862ea685b39dd23a8fd61787128af2df4 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 21 Jul 2022 15:41:45 +0300 Subject: [PATCH 02/21] expand_impls part done --- frame/contracts/proc-macro/src/lib.rs | 100 ++++++++++++++++++++++++-- frame/contracts/src/wasm/runtime.rs | 2 +- 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 1e5a00d2e1d93..7b9edeb6bed88 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -253,12 +253,12 @@ fn expand_env(def: &mut EnvDef) -> proc_macro2::TokenStream { // token stream see how it's done in pallet proc macro, e.g. in constants // 2. impls() for the set of host functions: #impls let can_satisfy = expand_can_satisfy(def); - // expand_impls(def); + let impls = expand_impls(def); quote! { - pub struct Env; - #can_satisfy + pub struct Env; + #can_satisfy + #impls } - // expand_impls(def); } // Adds check to can_satisfy for a new host fn @@ -297,8 +297,96 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { } } } -//pub fn expand_impls -// TDB + +fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { + let impls = def.host_funcs.iter().map(|f| { + let params = &f.item.sig.inputs.iter().skip(1).map(|arg| { + match arg { + syn::FnArg::Typed(pt) => { + if let syn::Pat::Ident(ident) = &*pt.pat { + let p_type = &pt.ty; + let p_name = ident.ident.clone(); + quote! { + let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = + args.next() + .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) + // TBD: update this msg + .expect( + "precondition: all imports should be checked against the signatures of corresponding + functions defined by `#[define_env]` proc macro by the user of the macro; + signatures of these functions defined by `$params`; + calls always made with arguments types of which are defined by the corresponding imports; + thus types of arguments should be equal to type list in `$params` and + length of argument list and $params should be equal; + thus this can never be `None`; + qed; + " + ); + } + } else { quote! { } } + }, + _ => quote! { }} + }); + + let outline = match &f.item.sig.output { + syn::ReturnType::Default => quote! { + body().map_err(|reason| { + ctx.set_trap_reason(reason); + sp_sandbox::HostError + })?; + return Ok(sp_sandbox::ReturnValue::Unit); + }, + syn::ReturnType::Type(_,_) => quote! { + let r = body().map_err(|reason| { + ctx.set_trap_reason(reason); + sp_sandbox::HostError + })?; + return Ok(sp_sandbox::ReturnValue::Value({ + use crate::wasm::env_def::ConvertibleToWasm; + r.to_typed_value() + })); + }, + }; + + let p = params.clone(); + let (module, name, ident, body) = (&f.module, &f.name, &f.item.sig.ident, &f.item.block); + quote! { + f(#module.as_bytes(), #name.as_bytes(), { + fn #ident( + ctx: &mut crate::wasm::Runtime, + args: &[sp_sandbox::Value], + ) -> Result + where + ::AccountId: sp_core::crypto::UncheckedFrom<::Hash> + + AsRef<[u8]>, + { + #[allow(unused)] + let mut args = args.iter(); + let body = crate::wasm::env_def::macros::constrain_closure::<(), _>(|| { + #( #p )* + #body + }); + #outline + } + #ident:: + }); + } + }); + let packed_impls = quote! { + #( #impls )* + }; + quote! { + impl crate::wasm::env_def::FunctionImplProvider for Env + where + ::AccountId: + sp_core::crypto::UncheckedFrom<::Hash> + AsRef<[u8]>, + { + fn impls)>(f: &mut F) { + #packed_impls + } + } + } +} #[proc_macro_attribute] pub fn define_env( diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 1dc503f02d77a..b8c6511f9135b 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -984,7 +984,7 @@ pub mod env { #[host("seal0")] fn gas(ctx: Runtime, amount: u32) { ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; - Ok(sp_sandbox::ReturnValue::Unit) + Ok(()) } #[host("seal1")] From 86ff3f1953fe9e3aa80ad87fe67c1efc3a7e67b2 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 21 Jul 2022 16:00:41 +0300 Subject: [PATCH 03/21] fix of the &FunctionType bug --- frame/contracts/proc-macro/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 7b9edeb6bed88..7582af169de95 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -268,7 +268,7 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { quote! { if module == #module.as_bytes() && name == #name.as_bytes() - && signature == #signature + && signature == &#signature { return true; } From 2bf1cd0ad1eae5daae5360706828ee375d0ee0bf Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 21 Jul 2022 18:10:59 +0300 Subject: [PATCH 04/21] pallet is compiled --- frame/contracts/proc-macro/src/lib.rs | 13 ++++++------- frame/contracts/src/wasm/runtime.rs | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 7582af169de95..acb208ea488b6 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -328,24 +328,23 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { _ => quote! { }} }); - let outline = match &f.item.sig.output { - syn::ReturnType::Default => quote! { + let (outline, ret_ty) = match &f.item.sig.output { + syn::ReturnType::Default => (quote! { body().map_err(|reason| { ctx.set_trap_reason(reason); sp_sandbox::HostError })?; return Ok(sp_sandbox::ReturnValue::Unit); - }, - syn::ReturnType::Type(_,_) => quote! { + }, quote! {()}), + syn::ReturnType::Type(_,ty) => (quote! { let r = body().map_err(|reason| { ctx.set_trap_reason(reason); sp_sandbox::HostError })?; return Ok(sp_sandbox::ReturnValue::Value({ - use crate::wasm::env_def::ConvertibleToWasm; r.to_typed_value() })); - }, + }, quote! {#ty}), }; let p = params.clone(); @@ -362,7 +361,7 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { { #[allow(unused)] let mut args = args.iter(); - let body = crate::wasm::env_def::macros::constrain_closure::<(), _>(|| { + let body = crate::wasm::env_def::macros::constrain_closure::<#ret_ty, _>(|| { #( #p )* #body }); diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index b8c6511f9135b..9d384c119e008 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -989,7 +989,7 @@ pub mod env { #[host("seal1")] fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 { - ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) + ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) } } From 76fb3953d5ee3cb052657e10243e409bff2a1d40 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 14:34:58 +0300 Subject: [PATCH 05/21] updated host fn definition syntax --- frame/contracts/proc-macro/src/lib.rs | 34 +++++++++++++++++++-------- frame/contracts/src/wasm/runtime.rs | 18 ++++++++++++-- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index acb208ea488b6..6e14838801aec 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -23,6 +23,7 @@ extern crate alloc; use alloc::{ boxed::Box, + format, string::{String, ToString}, vec, vec::Vec, @@ -203,7 +204,7 @@ impl HostFn { pub fn try_from(mut item: syn::Item) -> syn::Result { let span = item.span(); let err = || { - let msg = "Invalid environment definition, only fn with #[host(\"...\")] attribute are allowed."; + let msg = "Invalid host function definition, only #[v()] or #[unstable] attribute is allowed."; syn::Error::new(span, msg) }; @@ -212,16 +213,29 @@ impl HostFn { _ => Err(err()), }?; - let attr = item.attrs.pop().ok_or(err())?; - let module = attr.parse_args().map(|a: syn::LitStr| a.value())?; let name = item.sig.ident.to_string(); - attr.path - .get_ident() - .ok_or(err())? - .to_string() - .eq("host") - .then(|| Ok(Self { item, module, name })) - .ok_or(err())? + let mut module = "seal0".to_string(); + let attrs = &item.attrs; + match attrs.len() { + 0 => (), + 1 => { + let attr = &attrs[0]; + let ident = attr.path.get_ident().ok_or(err())?.to_string(); + match ident.as_str() { + "v" => { + let ver: syn::LitInt = attr.parse_args()?; + module = format!("seal{}", ver.base10_parse::().map_err(|_| err())?); + }, + "unstable" => { + module = "__unstable__".to_string(); + }, + _ => return Err(err()), + } + }, + _ => return Err(err()), + } + + Ok(Self { item, module, name }) } } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 9d384c119e008..09d1b1efb4d48 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -981,16 +981,30 @@ where // it is done for pallet (see e.g. lib.rs in contracts/) #[define_env] pub mod env { - #[host("seal0")] fn gas(ctx: Runtime, amount: u32) { ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; Ok(()) } - #[host("seal1")] + fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) { + ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) + } + + #[v(1)] fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) } + + #[unstable] + fn seal_set_storage( + ctx: Runtime, + key_ptr: u32, + key_len: u32, + value_ptr: u32, + value_len: u32, + ) -> u32 { + ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) + } } // impl can_satisfy From e197a9f54cf217a80e4f91d13f67040578c8d208 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 17:29:51 +0300 Subject: [PATCH 06/21] docs comments allowed to host fn definitions --- frame/contracts/proc-macro/src/lib.rs | 41 +++++++++++++-------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 6e14838801aec..7d002630a6eb8 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -156,19 +156,20 @@ fn format_default(field: &Ident) -> TokenStream { // stream see how it's done in pallet proc macro, e.g. in constants // 2. impls() for the set of host functions: #impls -/// parsed definition of env -/// (inspired by pallet attribute macro, see /frame/support/procedural/src/pallet/) +/// Parsed envirnoment definition. struct EnvDef { - pub item: syn::ItemMod, // the whole env module - pub host_funcs: Vec, // set of host fuctions + pub item: syn::ItemMod, + pub host_funcs: Vec, } +/// Parsed host function definition. struct HostFn { item: syn::ItemFn, module: String, name: String, } +/// Helper trait to convert a host function definition into its wasm signature. trait ToWasmSig { fn to_wasm_sig(&self) -> TokenStream; } @@ -179,7 +180,6 @@ impl ToWasmSig for HostFn { syn::FnArg::Typed(pt) => Some(&pt.ty), _ => None, }); - let returns = match &self.item.sig.output { syn::ReturnType::Type(_, bt) => quote! { vec![ #bt::VALUE_TYPE ] }, _ => quote! { vec![] }, @@ -215,7 +215,9 @@ impl HostFn { let name = item.sig.ident.to_string(); let mut module = "seal0".to_string(); - let attrs = &item.attrs; + let attrs: Vec<&syn::Attribute> = + item.attrs.iter().filter(|m| !m.path.is_ident("doc")).collect(); + match attrs.len() { 0 => (), 1 => { @@ -246,7 +248,7 @@ impl EnvDef { .content .as_mut() .ok_or_else(|| { - let msg = "Invalid environment definition, expected mod to be inlined."; + let msg = "Invalid environment definition, expected `mod` to be inlined."; syn::Error::new(item_span, msg) })? .1; @@ -260,12 +262,12 @@ impl EnvDef { } } +/// Expands environment definiton. +/// Should generate source code for: +/// - wasm import satisfy checks (see `expand_can_satisfy()`); +/// - implementations of the host functions to be added to the wasm runtime environment (see +/// `expand_impls()`). fn expand_env(def: &mut EnvDef) -> proc_macro2::TokenStream { - // should generate code for: - // 1. can_satisfy checks: #can_satisfy - // expand def, so just add parts related to the new func to it, and return updated def as a - // token stream see how it's done in pallet proc macro, e.g. in constants - // 2. impls() for the set of host functions: #impls let can_satisfy = expand_can_satisfy(def); let impls = expand_impls(def); quote! { @@ -275,7 +277,9 @@ fn expand_env(def: &mut EnvDef) -> proc_macro2::TokenStream { } } -// Adds check to can_satisfy for a new host fn +/// Generates `can_satisfy()` method for every host function, to be used to check +/// these functions versus expected module, name and signatures when imporing them from a wasm +/// module. fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { let checks = def.host_funcs.iter().map(|f| { let (module, name, signature) = (&f.module, &f.name, &f.to_wasm_sig()); @@ -288,7 +292,6 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { } } }); - let satisfy_checks = quote! { #( #checks )* }; @@ -312,6 +315,8 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { } } +/// Generates implementation for every host function, to register it in the contract execution +/// environment. fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { let impls = def.host_funcs.iter().map(|f| { let params = &f.item.sig.inputs.iter().skip(1).map(|arg| { @@ -324,17 +329,11 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = args.next() .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) - // TBD: update this msg .expect( "precondition: all imports should be checked against the signatures of corresponding functions defined by `#[define_env]` proc macro by the user of the macro; - signatures of these functions defined by `$params`; - calls always made with arguments types of which are defined by the corresponding imports; - thus types of arguments should be equal to type list in `$params` and - length of argument list and $params should be equal; thus this can never be `None`; - qed; - " + qed;" ); } } else { quote! { } } From 34108ce0e93bb9d3d110ce370996c3ae338e97f8 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 17:30:22 +0300 Subject: [PATCH 07/21] all 53 host funcs re-defined by the new macro --- frame/contracts/src/wasm/runtime.rs | 2685 ++++++++++++++------------- 1 file changed, 1352 insertions(+), 1333 deletions(-) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 09d1b1efb4d48..b7366dd41c4c4 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -930,44 +930,6 @@ where } } -// // How new API will look like -// #[define(seal0)] -// fn gas(ctx: Runtime, amount: u32) -> Result { -// ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; -// Ok(sp_sandbox::ReturnValue::Unit) -// } - -// #[define(seal0)] -// fn seal_set_storage( -// ctx: Runtime, -// key_ptr: u32, -// value_ptr: u32, -// value_len: u32, -// ) -> Result { -// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) -// } - -// #[define(seal1)] -// fn seal_set_storage( -// ctx: Runtime, -// key_ptr: u32, -// value_ptr: u32, -// value_len: u32, -// ) -> Result { -// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) -// } - -// #[define(__unstable__)] -// fn seal_set_storage( -// ctx: Runtime, -// key_ptr: u32, -// key_len: u32, -// value_ptr: u32, -// value_len: u32, -// ) -> Result { -// ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) -// } - // This is the API exposed to contracts. // // # Note @@ -975,26 +937,66 @@ where // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. - -// New implementation with proc macro -// here: create a module and add host funcs in it, like -// it is done for pallet (see e.g. lib.rs in contracts/) #[define_env] pub mod env { + /// Account for used gas. Traps if gas used is greater than gas limit. + /// + /// NOTE: This is a implementation defined call and is NOT a part of the public API. + /// This call is supposed to be called only by instrumentation injected code. + /// + /// - amount: How much gas is used. fn gas(ctx: Runtime, amount: u32) { ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; Ok(()) } + /// Set the value at the given key in the contract storage. + /// + /// Equivalent to the newer version of `seal_set_storage` with the exception of the return + /// type. Still a valid thing to call when not interested in the return value. fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) } + /// Set the value at the given key in the contract storage. + /// + /// This version is to be used with a fixed sized storage key. For runtimes supporting + /// transparent hashing, please use the newer version of this function. + /// + /// The value length must not exceed the maximum defined by the contracts module parameters. + /// Specifying a `value_len` of zero will store an empty value. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the location to store the value is placed. + /// - `value_ptr`: pointer into the linear memory where the value to set is placed. + /// - `value_len`: the length of the value in bytes. + /// + /// # Return Value + /// + /// Returns the size of the pre-existing value at the specified key if any. Otherwise + /// `SENTINEL` is returned as a sentinel value. #[v(1)] fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) } + /// Set the value at the given key in the contract storage. + /// + /// The key and value lengths must not exceed the maximums defined by the contracts module + /// parameters. Specifying a `value_len` of zero will store an empty value. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the location to store the value is placed. + /// - `key_len`: the length of the key in bytes. + /// - `value_ptr`: pointer into the linear memory where the value to set is placed. + /// - `value_len`: the length of the value in bytes. + /// + /// # Return Value + /// + /// Returns the size of the pre-existing value at the specified key if any. Otherwise + /// `SENTINEL` is returned as a sentinel value. #[unstable] fn seal_set_storage( ctx: Runtime, @@ -1005,1295 +1007,1312 @@ pub mod env { ) -> u32 { ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) } -} -// impl can_satisfy -//impl crate::wasm::env_def::ImportSatisfyCheck for Env { -// fn can_satisfy( - -// impl impls() -// TBD - -// Old implementation with MBE -// define_env!(Env, , -// // Account for used gas. Traps if gas used is greater than gas limit. -// // -// // NOTE: This is a implementation defined call and is NOT a part of the public API. -// // This call is supposed to be called only by instrumentation injected code. -// // -// // - amount: How much gas is used. -// [seal0] gas(ctx, amount: u32) => { -// ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; -// Ok(()) -// }, - -// // Set the value at the given key in the contract storage. -// // -// // Equivalent to the newer version of `seal_set_storage` with the exception of the return -// // type. Still a valid thing to call when not interested in the return value. -// [seal0] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) => { -// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) -// }, - -// // Set the value at the given key in the contract storage. -// // -// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent -// // hashing, please use the newer version of this function. -// // -// // The value length must not exceed the maximum defined by the contracts module parameters. -// // Specifying a `value_len` of zero will store an empty value. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the location to store the value is placed. -// // - `value_ptr`: pointer into the linear memory where the value to set is placed. -// // - `value_len`: the length of the value in bytes. -// // -// // # Return Value -// // -// // Returns the size of the pre-existing value at the specified key if any. Otherwise -// // `SENTINEL` is returned as a sentinel value. -// [seal1] seal_set_storage(ctx, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 => { -// ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) -// }, - -// // Set the value at the given key in the contract storage. -// // -// // The key and value lengths must not exceed the maximums defined by the contracts module -// parameters. // Specifying a `value_len` of zero will store an empty value. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the location to store the value is placed. -// // - `key_len`: the length of the key in bytes. -// // - `value_ptr`: pointer into the linear memory where the value to set is placed. -// // - `value_len`: the length of the value in bytes. -// // -// // # Return Value -// // -// // Returns the size of the pre-existing value at the specified key if any. Otherwise -// // `SENTINEL` is returned as a sentinel value. -// [__unstable__] seal_set_storage(ctx, key_ptr: u32, key_len: u32, value_ptr: u32, value_len: u32) -// -> u32 => { ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) -// }, - -// // Clear the value at the given key in the contract storage. -// // -// // Equivalent to the newer version of `seal_clear_storage` with the exception of the return -// // type. Still a valid thing to call when not interested in the return value. -// [seal0] seal_clear_storage(ctx, key_ptr: u32) => { -// ctx.clear_storage(KeyType::Fix, key_ptr).map(|_| ()) -// }, - -// // Clear the value at the given key in the contract storage. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the key is placed. -// // - `key_len`: the length of the key in bytes. -// // -// // # Return Value -// // -// // Returns the size of the pre-existing value at the specified key if any. Otherwise -// // `SENTINEL` is returned as a sentinel value. -// [__unstable__] seal_clear_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => { -// ctx.clear_storage(KeyType::Variable(key_len), key_ptr) -// }, - -// // Retrieve the value under the given key from storage. -// // -// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent -// // hashing, please use the newer version of this function. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. -// // - `out_ptr`: pointer to the linear memory where the value is written to. -// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length -// // is read from and the value length is written to. -// // -// // # Errors -// // -// // `ReturnCode::KeyNotFound` -// [seal0] seal_get_storage(ctx, key_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { -// ctx.get_storage(KeyType::Fix, key_ptr, out_ptr, out_len_ptr) -// }, - -// // Retrieve the value under the given key from storage. -// // -// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent -// // hashing, please use the newer version of this function. -// // -// // The key length must not exceed the maximum defined by the contracts module parameter. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. -// // - `key_len`: the length of the key in bytes. -// // - `out_ptr`: pointer to the linear memory where the value is written to. -// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length -// // is read from and the value length is written to. -// // -// // # Errors -// // -// // `ReturnCode::KeyNotFound` -// [__unstable__] seal_get_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) -// -> ReturnCode => { ctx.get_storage(KeyType::Variable(key_len), key_ptr, out_ptr, out_len_ptr) -// }, - -// // Checks whether there is a value stored under the given key. -// // -// // This version is to be used with a fixed sized storage key. For runtimes supporting transparent -// // hashing, please use the newer version of this function. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. -// // -// // # Return Value -// // -// // Returns the size of the pre-existing value at the specified key if any. Otherwise -// // `SENTINEL` is returned as a sentinel value. -// [seal0] seal_contains_storage(ctx, key_ptr: u32) -> u32 => { -// ctx.contains_storage(KeyType::Fix, key_ptr) -// }, - -// // 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. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. -// // - `key_len`: the length of the key in bytes. -// // -// // # Return Value -// // -// // Returns the size of the pre-existing value at the specified key if any. Otherwise -// // `SENTINEL` is returned as a sentinel value. -// [__unstable__] seal_contains_storage(ctx, key_ptr: u32, key_len: u32) -> u32 => { -// ctx.contains_storage(KeyType::Variable(key_len), key_ptr) -// }, - -// // Retrieve and remove the value under the given key from storage. -// // -// // # Parameters -// // -// // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. -// // - `key_len`: the length of the key in bytes. -// // - `out_ptr`: pointer to the linear memory where the value is written to. -// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length -// // is read from and the value length is written to. -// // -// // # Errors -// // -// // `ReturnCode::KeyNotFound` -// [__unstable__] seal_take_storage(ctx, key_ptr: u32, key_len: u32, out_ptr: u32, out_len_ptr: u32) -// -> ReturnCode => { let charged = -// ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; let key = -// ctx.read_sandbox_memory(key_ptr, key_len)?; if let crate::storage::WriteOutcome::Taken(value) = -// ctx.ext.set_storage_transparent(&VarSizedKey::::try_from(key).map_err(|_| -// Error::::DecodingFailed)?, None, true)? { ctx.adjust_gas(charged, -// RuntimeCosts::TakeStorage(value.len() as u32)); ctx.write_sandbox_output(out_ptr, out_len_ptr, -// &value, false, already_charged)?; Ok(ReturnCode::Success) -// } else { -// ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(0)); -// Ok(ReturnCode::KeyNotFound) -// } -// }, - -// // Transfer some value to another account. -// // -// // # Parameters -// // -// // - account_ptr: a pointer to the address of the beneficiary account -// // Should be decodable as an `T::AccountId`. Traps otherwise. -// // - account_len: length of the address buffer. -// // - value_ptr: a pointer to the buffer with value, how much value to send. -// // Should be decodable as a `T::Balance`. Traps otherwise. -// // - value_len: length of the value buffer. -// // -// // # Errors -// // -// // `ReturnCode::TransferFailed` -// [seal0] seal_transfer( -// ctx, -// account_ptr: u32, -// _account_len: u32, -// value_ptr: u32, -// _value_len: u32 -// ) -> ReturnCode => { -// ctx.charge_gas(RuntimeCosts::Transfer)?; -// let callee: <::T as frame_system::Config>::AccountId = -// ctx.read_sandbox_memory_as(account_ptr)?; -// let value: BalanceOf<::T> = -// ctx.read_sandbox_memory_as(value_ptr)?; - -// let result = ctx.ext.transfer(&callee, value); -// match result { -// Ok(()) => Ok(ReturnCode::Success), -// Err(err) => { -// let code = Runtime::::err_into_return_code(err)?; -// Ok(code) -// } -// } -// }, - -// // Make a call to another contract. -// // -// // # Deprecation -// // -// // This is equivalent to calling the newer version of this function with -// // `flags` set to `ALLOW_REENTRY`. See the newer version for documentation. -// // -// // # Note -// // -// // The values `_callee_len` and `_value_len` are ignored because the encoded sizes -// // of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards -// // compatibility. Consider switching to the newest version of this function. -// [seal0] seal_call( -// ctx, -// callee_ptr: u32, -// _callee_len: u32, -// gas: u64, -// value_ptr: u32, -// _value_len: u32, -// input_data_ptr: u32, -// input_data_len: u32, -// output_ptr: u32, -// output_len_ptr: u32 -// ) -> ReturnCode => { -// ctx.call( -// CallFlags::ALLOW_REENTRY, -// CallType::Call{callee_ptr, value_ptr, gas}, -// input_data_ptr, -// input_data_len, -// output_ptr, -// output_len_ptr, -// ) -// }, - -// // Make a call to another contract. -// // -// // The callees output buffer is copied to `output_ptr` and its length to `output_len_ptr`. -// // The copy of the output buffer can be skipped by supplying the sentinel value -// // of `SENTINEL` to `output_ptr`. -// // -// // # Parameters -// // -// // - flags: See [`CallFlags`] for a documenation of the supported flags. -// // - callee_ptr: a pointer to the address of the callee contract. -// // Should be decodable as an `T::AccountId`. Traps otherwise. -// // - gas: how much gas to devote to the execution. -// // - value_ptr: a pointer to the buffer with value, how much value to send. -// // Should be decodable as a `T::Balance`. Traps otherwise. -// // - input_data_ptr: a pointer to a buffer to be used as input data to the callee. -// // - input_data_len: length of the input data buffer. -// // - output_ptr: a pointer where the output buffer is copied to. -// // - output_len_ptr: in-out pointer to where the length of the buffer is read from -// // and the actual length is written to. -// // -// // # Errors -// // -// // An error means that the call wasn't successful output buffer is returned unless -// // stated otherwise. -// // -// // `ReturnCode::CalleeReverted`: Output buffer is returned. -// // `ReturnCode::CalleeTrapped` -// // `ReturnCode::TransferFailed` -// // `ReturnCode::NotCallable` -// [seal1] seal_call( -// ctx, -// flags: u32, -// callee_ptr: u32, -// gas: u64, -// value_ptr: u32, -// input_data_ptr: u32, -// input_data_len: u32, -// output_ptr: u32, -// output_len_ptr: u32 -// ) -> ReturnCode => { -// ctx.call( -// CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, -// CallType::Call{callee_ptr, value_ptr, gas}, -// input_data_ptr, -// input_data_len, -// output_ptr, -// output_len_ptr, -// ) -// }, - -// // 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. -// // - code_hash: a pointer to the hash of the code to be called. -// // - input_data_ptr: a pointer to a buffer to be used as input data to the callee. -// // - input_data_len: length of the input data buffer. -// // - output_ptr: a pointer where the output buffer is copied to. -// // - output_len_ptr: in-out pointer to where the length of the buffer is read from -// // and the actual length is written to. -// // -// // # Errors -// // -// // An error means that the call wasn't successful and no output buffer is returned unless -// // stated otherwise. -// // -// // `ReturnCode::CalleeReverted`: Output buffer is returned. -// // `ReturnCode::CalleeTrapped` -// // `ReturnCode::CodeNotFound` -// [seal0] seal_delegate_call( -// ctx, -// flags: u32, -// code_hash_ptr: u32, -// input_data_ptr: u32, -// input_data_len: u32, -// output_ptr: u32, -// output_len_ptr: u32 -// ) -> ReturnCode => { -// ctx.call( -// CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, -// CallType::DelegateCall{code_hash_ptr}, -// input_data_ptr, -// input_data_len, -// output_ptr, -// output_len_ptr, -// ) -// }, - -// // Instantiate a contract with the specified code hash. -// // -// // # Deprecation -// // -// // This is equivalent to calling the newer version of this function. The newer version -// // drops the now unnecessary length fields. -// // -// // # Note -// // -// // The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes -// // of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards -// // compatibility. Consider switching to the newest version of this function. -// [seal0] seal_instantiate( -// ctx, -// code_hash_ptr: u32, -// _code_hash_len: u32, -// gas: u64, -// value_ptr: u32, -// _value_len: u32, -// input_data_ptr: u32, -// input_data_len: u32, -// address_ptr: u32, -// address_len_ptr: u32, -// output_ptr: u32, -// output_len_ptr: u32, -// salt_ptr: u32, -// salt_len: u32 -// ) -> ReturnCode => { -// ctx.instantiate ( -// code_hash_ptr, -// gas, -// value_ptr, -// input_data_ptr, -// input_data_len, -// address_ptr, -// address_len_ptr, -// output_ptr, -// output_len_ptr, -// salt_ptr, -// salt_len, -// ) -// }, - -// // Instantiate a contract with the specified code hash. -// // -// // This function creates an account and executes the constructor defined in the code specified -// // by the code hash. The address of this new account is copied to `address_ptr` and its length -// // to `address_len_ptr`. The constructors output buffer is copied to `output_ptr` and its -// // length to `output_len_ptr`. The copy of the output buffer and address can be skipped by -// // supplying the sentinel value of `SENTINEL` to `output_ptr` or `address_ptr`. -// // -// // `value` must be at least the minimum balance. Otherwise the instantiation fails and the -// // contract is not created. -// // -// // # Parameters -// // -// // - code_hash_ptr: a pointer to the buffer that contains the initializer code. -// // - gas: how much gas to devote to the execution of the initializer code. -// // - value_ptr: a pointer to the buffer with value, how much value to send. -// // Should be decodable as a `T::Balance`. Traps otherwise. -// // - input_data_ptr: a pointer to a buffer to be used as input data to the initializer code. -// // - input_data_len: length of the input data buffer. -// // - address_ptr: a pointer where the new account's address is copied to. -// // - address_len_ptr: in-out pointer to where the length of the buffer is read from -// // and the actual length is written to. -// // - output_ptr: a pointer where the output buffer is copied to. -// // - output_len_ptr: in-out pointer to where the length of the buffer is read from -// // and the actual length is written to. -// // - salt_ptr: Pointer to raw bytes used for address derivation. See `fn contract_address`. -// // - salt_len: length in bytes of the supplied salt. -// // -// // # Errors -// // -// // Please consult the `ReturnCode` 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. -// // -// // `ReturnCode::CalleeReverted`: Output buffer is returned. -// // `ReturnCode::CalleeTrapped` -// // `ReturnCode::TransferFailed` -// // `ReturnCode::CodeNotFound` -// [seal1] seal_instantiate( -// ctx, -// code_hash_ptr: u32, -// gas: u64, -// value_ptr: u32, -// input_data_ptr: u32, -// input_data_len: u32, -// address_ptr: u32, -// address_len_ptr: u32, -// output_ptr: u32, -// output_len_ptr: u32, -// salt_ptr: u32, -// salt_len: u32 -// ) -> ReturnCode => { -// ctx.instantiate( -// code_hash_ptr, -// gas, -// value_ptr, -// input_data_ptr, -// input_data_len, -// address_ptr, -// address_len_ptr, -// output_ptr, -// output_len_ptr, -// salt_ptr, -// salt_len, -// ) -// }, - -// // Remove the calling account and transfer remaining balance. -// // -// // # Deprecation -// // -// // This is equivalent to calling the newer version of this function. The newer version -// // drops the now unnecessary length fields. -// // -// // # Note -// // -// // The value `_beneficiary_len` is ignored because the encoded sizes -// // this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards -// // compatibility. Consider switching to the newest version of this function. -// [seal0] seal_terminate(ctx, beneficiary_ptr: u32, _beneficiary_len: u32) => { -// ctx.terminate(beneficiary_ptr) -// }, - -// // 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. -// // -// // - beneficiary_ptr: a pointer to the address of the beneficiary account where all -// // where all remaining funds of the caller are transferred. -// // Should be decodable as an `T::AccountId`. Traps otherwise. -// // -// // # 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. -// [seal1] seal_terminate(ctx, beneficiary_ptr: u32) => { -// ctx.terminate(beneficiary_ptr) -// }, - -// // Stores the input passed by the caller into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // # Note -// // -// // This function traps if the input was previously forwarded by a `seal_call`. -// [seal0] seal_input(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::InputBase)?; -// if let Some(input) = ctx.input_data.take() { -// ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| { -// Some(RuntimeCosts::CopyToContract(len)) -// })?; -// ctx.input_data = Some(input); -// Ok(()) -// } else { -// Err(Error::::InputForwarded.into()) -// } -// }, - -// // Cease contract execution and save a data buffer as a result of the execution. -// // -// // 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: -// // ``` -// // seal_return(0, 0, 0); -// // ``` -// // -// // The flags argument is a bitfield that can be used to signal special return -// // conditions to the supervisor: -// // --- lsb --- -// // bit 0 : REVERT - Revert all storage changes made by the caller. -// // bit [1, 31]: Reserved for future use. -// // --- msb --- -// // -// // Using a reserved bit triggers a trap. -// [seal0] seal_return(ctx, flags: u32, data_ptr: u32, data_len: u32) => { -// ctx.charge_gas(RuntimeCosts::Return(data_len))?; -// Err(TrapReason::Return(ReturnData { -// flags, -// data: ctx.read_sandbox_memory(data_ptr, data_len)?, -// })) -// }, - -// // Stores the address of the caller into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // 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. The value is encoded as T::AccountId. -// [seal0] seal_caller(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Caller)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.caller().encode(), false, already_charged -// )?) -// }, - -// // Checks whether a specified address belongs to a contract. -// // -// // # Parameters -// // -// // - account_ptr: a pointer to the address of the beneficiary account -// // Should be decodable as an `T::AccountId`. Traps otherwise. -// // -// // Returned value is a u32-encoded boolean: (0 = false, 1 = true). -// [seal0] seal_is_contract(ctx, account_ptr: u32) -> u32 => { -// ctx.charge_gas(RuntimeCosts::IsContract)?; -// let address: <::T as frame_system::Config>::AccountId = -// ctx.read_sandbox_memory_as(account_ptr)?; - -// Ok(ctx.ext.is_contract(&address) as u32) -// }, - -// // Retrieve the code hash for a specified contract address. -// // -// // # Parameters -// // -// // - `account_ptr`: a pointer to the address in question. -// // Should be decodable as an `T::AccountId`. Traps otherwise. -// // - `out_ptr`: pointer to the linear memory where the returning value is written to. -// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length -// // is read from and the value length is written to. -// // -// // # Errors -// // -// // `ReturnCode::KeyNotFound` -// [seal0] seal_code_hash(ctx, account_ptr: u32, out_ptr: u32, out_len_ptr: u32) -> ReturnCode => { -// ctx.charge_gas(RuntimeCosts::CodeHash)?; -// let address: <::T as frame_system::Config>::AccountId = -// ctx.read_sandbox_memory_as(account_ptr)?; -// if let Some(value) = ctx.ext.code_hash(&address) { -// ctx.write_sandbox_output(out_ptr, out_len_ptr, &value.encode(), false, already_charged)?; -// Ok(ReturnCode::Success) -// } else { -// Ok(ReturnCode::KeyNotFound) -// } -// }, - -// // Retrieve the code hash of the currently executing contract. -// // -// // # Parameters -// // -// // - `out_ptr`: pointer to the linear memory where the returning value is written to. -// // - `out_len_ptr`: in-out pointer into linear memory where the buffer length -// // is read from and the value length is written to. -// [seal0] seal_own_code_hash(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; -// let code_hash_encoded = &ctx.ext.own_code_hash().encode(); -// Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, code_hash_encoded, false, already_charged)?) -// }, - -// // Checks whether the caller of the current contract is the origin of the whole call stack. -// // -// // Prefer this over `seal_is_contract` when checking whether your contract is being called by a -// contract // or a plain account. The reason is that it performs better since it does not need to -// // do any storage lookups. -// // -// // A return value of`true` indicates that this contract is being called by a plain account -// // and `false` indicates that the caller is another contract. -// // -// // Returned value is a u32-encoded boolean: (0 = false, 1 = true). -// [seal0] seal_caller_is_origin(ctx) -> u32 => { -// ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; -// Ok(ctx.ext.caller_is_origin() as u32) -// }, - -// // Stores the address of the current contract into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// [seal0] seal_address(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Address)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.address().encode(), false, already_charged -// )?) -// }, - -// // Stores the price for the specified amount of gas into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // The data is encoded as T::Balance. -// // -// // # Note -// // -// // It is recommended to avoid specifying very small values for `gas` as the prices for a single -// // gas can be smaller than one. -// [seal0] seal_weight_to_fee(ctx, gas: u64, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::WeightToFee)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.get_weight_price(gas).encode(), false, already_charged -// )?) -// }, - -// // Stores the amount of gas left into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // The data is encoded as Gas. -// [seal0] seal_gas_left(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::GasLeft)?; -// let gas_left = &ctx.ext.gas_meter().gas_left().encode(); -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, gas_left, false, already_charged, -// )?) -// }, - -// // Stores the **free* balance of the current account into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // The data is encoded as T::Balance. -// [seal0] seal_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Balance)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.balance().encode(), false, already_charged -// )?) -// }, - -// // Stores the value transferred along with this call/instantiate into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // The data is encoded as T::Balance. -// [seal0] seal_value_transferred(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::ValueTransferred)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.value_transferred().encode(), false, already_charged -// )?) -// }, - -// // Stores a random number for the current block and the given subject into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // The data is encoded as T::Hash. -// // -// // # Deprecation -// // -// // This function is deprecated. Users should migrate to the version in the "seal1" module. -// [seal0] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Random)?; -// if subject_len > ctx.ext.schedule().limits.subject_len { -// return Err(Error::::RandomSubjectTooLong.into()); -// } -// let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).0.encode(), false, already_charged -// )?) -// }, - -// // Stores a random number for the current block and the given subject into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // The data is encoded as (T::Hash, T::BlockNumber). -// // -// // # Changes from v0 -// // -// // In addition to the seed it returns the block number since which it was determinable -// // by chain observers. -// // -// // # Note -// // -// // The returned seed should only be used to distinguish commitments made before -// // the returned block number. If the block number is too early (i.e. commitments were -// // made afterwards), then ensure no further commitments may be made and repeatedly -// // call this on later blocks until the block number returned is later than the latest -// // commitment. -// [seal1] seal_random(ctx, subject_ptr: u32, subject_len: u32, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Random)?; -// if subject_len > ctx.ext.schedule().limits.subject_len { -// return Err(Error::::RandomSubjectTooLong.into()); -// } -// let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.random(&subject_buf).encode(), false, already_charged -// )?) -// }, - -// // Load the latest block timestamp into the supplied buffer -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// [seal0] seal_now(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Now)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.now().encode(), false, already_charged -// )?) -// }, - -// // Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. -// // -// // The data is encoded as T::Balance. -// [seal0] seal_minimum_balance(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::MinimumBalance)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.minimum_balance().encode(), false, already_charged -// )?) -// }, - -// // Stores the tombstone deposit into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // # Deprecation -// // -// // There is no longer a tombstone deposit. This function always returns 0. -// [seal0] seal_tombstone_deposit(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Balance)?; -// let deposit = >::zero().encode(); -// Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?) -// }, - -// // Was used to restore the given destination contract sacrificing the caller. -// // -// // # Note -// // -// // The state rent functionality was removed. This is stub only exists for -// // backwards compatiblity -// [seal0] seal_restore_to( -// ctx, -// _dest_ptr: u32, -// _dest_len: u32, -// _code_hash_ptr: u32, -// _code_hash_len: u32, -// _rent_allowance_ptr: u32, -// _rent_allowance_len: u32, -// _delta_ptr: u32, -// _delta_count: u32 -// ) => { -// ctx.charge_gas(RuntimeCosts::DebugMessage)?; -// Ok(()) -// }, - -// // Was used to restore the given destination contract sacrificing the caller. -// // -// // # Note -// // -// // The state rent functionality was removed. This is stub only exists for -// // backwards compatiblity -// [seal1] seal_restore_to( -// ctx, -// _dest_ptr: u32, -// _code_hash_ptr: u32, -// _rent_allowance_ptr: u32, -// _delta_ptr: u32, -// _delta_count: u32 -// ) => { -// ctx.charge_gas(RuntimeCosts::DebugMessage)?; -// Ok(()) -// }, - -// // 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`. -// // -// // - topics_ptr - a pointer to the buffer of topics encoded as `Vec`. The value of this -// // is ignored if `topics_len` is set to 0. The topics list can't contain duplicates. -// // - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector. -// // - data_ptr - a pointer to a raw data buffer which will saved along the event. -// // - data_len - the length of the data buffer. -// [seal0] seal_deposit_event( -// ctx, -// topics_ptr: u32, -// topics_len: u32, -// data_ptr: u32, -// data_len: u32 -// ) => { -// fn has_duplicates(items: &mut Vec) -> bool { -// // # Warning -// // -// // Unstable sorts are non-deterministic across architectures. The usage here is OK -// // because we are rejecting duplicates which removes the non determinism. -// items.sort_unstable(); -// // Find any two consecutive equal elements. -// items.windows(2).any(|w| { -// match &w { -// &[a, b] => a == b, -// _ => false, -// } -// }) -// } - -// let num_topic = topics_len -// .checked_div(sp_std::mem::size_of::>() as u32) -// .ok_or("Zero sized topics are not allowed")?; -// ctx.charge_gas(RuntimeCosts::DepositEvent { -// num_topic, -// len: data_len, -// })?; -// if data_len > ctx.ext.max_value_size() { -// return Err(Error::::ValueTooLarge.into()); -// } - -// let mut topics: Vec::::T>> = match topics_len { -// 0 => Vec::new(), -// _ => ctx.read_sandbox_memory_as_unbounded(topics_ptr, topics_len)?, -// }; - -// // If there are more than `event_topics`, then trap. -// if topics.len() > ctx.ext.schedule().limits.event_topics as usize { -// return Err(Error::::TooManyTopics.into()); -// } - -// // Check for duplicate topics. If there are any, then trap. -// // Complexity O(n * log(n)) and no additional allocations. -// // This also sorts the topics. -// if has_duplicates(&mut topics) { -// return Err(Error::::DuplicateTopics.into()); -// } - -// let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?; - -// ctx.ext.deposit_event(topics, event_data); - -// Ok(()) -// }, - -// // Was used to set rent allowance of the contract. -// // -// // # Note -// // -// // The state rent functionality was removed. This is stub only exists for -// // backwards compatiblity. -// [seal0] seal_set_rent_allowance(ctx, _value_ptr: u32, _value_len: u32) => { -// ctx.charge_gas(RuntimeCosts::DebugMessage)?; -// Ok(()) -// }, - -// // Was used to set rent allowance of the contract. -// // -// // # Note -// // -// // The state rent functionality was removed. This is stub only exists for -// // backwards compatiblity. -// [seal1] seal_set_rent_allowance(ctx, _value_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::DebugMessage)?; -// Ok(()) -// }, - -// // Was used to store the rent allowance into the supplied buffer. -// // -// // # Note -// // -// // The state rent functionality was removed. This is stub only exists for -// // backwards compatiblity. -// [seal0] seal_rent_allowance(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::Balance)?; -// let rent_allowance = >::max_value().encode(); -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &rent_allowance, false, already_charged -// )?) -// }, - -// // Stores the current block number of the current contract into the supplied buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // `out_len_ptr` must point to a u32 value that describes the available space at -// // `out_ptr`. This call overwrites it with the size of the value. If the available -// // space at `out_ptr` is less than the size of the value a trap is triggered. -// [seal0] seal_block_number(ctx, out_ptr: u32, out_len_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::BlockNumber)?; -// Ok(ctx.write_sandbox_output( -// out_ptr, out_len_ptr, &ctx.ext.block_number().encode(), false, already_charged -// )?) -// }, - -// // Computes the SHA2 256-bit hash on the given input buffer. -// // -// // Returns the result directly into the given output buffer. -// // -// // # Note -// // -// // - The `input` and `output` buffer may overlap. -// // - The output buffer is expected to hold at least 32 bytes (256 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 -// // chosen hash function. -// // -// // # Parameters -// // -// // - `input_ptr`: the pointer into the linear memory where the input -// // data is placed. -// // - `input_len`: the length of the input data in bytes. -// // - `output_ptr`: the pointer into the linear memory where the output -// // data is placed. The function will write the result -// // directly into this buffer. -// [seal0] seal_hash_sha2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?; -// Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?) -// }, - -// // Computes the KECCAK 256-bit hash on the given input buffer. -// // -// // Returns the result directly into the given output buffer. -// // -// // # Note -// // -// // - The `input` and `output` buffer may overlap. -// // - The output buffer is expected to hold at least 32 bytes (256 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 -// // chosen hash function. -// // -// // # Parameters -// // -// // - `input_ptr`: the pointer into the linear memory where the input -// // data is placed. -// // - `input_len`: the length of the input data in bytes. -// // - `output_ptr`: the pointer into the linear memory where the output -// // data is placed. The function will write the result -// // directly into this buffer. -// [seal0] seal_hash_keccak_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; -// Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?) -// }, - -// // Computes the BLAKE2 256-bit hash on the given input buffer. -// // -// // Returns the result directly into the given output buffer. -// // -// // # Note -// // -// // - The `input` and `output` buffer may overlap. -// // - The output buffer is expected to hold at least 32 bytes (256 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 -// // chosen hash function. -// // -// // # Parameters -// // -// // - `input_ptr`: the pointer into the linear memory where the input -// // data is placed. -// // - `input_len`: the length of the input data in bytes. -// // - `output_ptr`: the pointer into the linear memory where the output -// // data is placed. The function will write the result -// // directly into this buffer. -// [seal0] seal_hash_blake2_256(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?; -// Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?) -// }, - -// // Computes the BLAKE2 128-bit hash on the given input buffer. -// // -// // Returns the result directly into the given output buffer. -// // -// // # Note -// // -// // - The `input` and `output` buffer may overlap. -// // - The output buffer is expected to hold at least 16 bytes (128 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 -// // chosen hash function. -// // -// // # Parameters -// // -// // - `input_ptr`: the pointer into the linear memory where the input -// // data is placed. -// // - `input_len`: the length of the input data in bytes. -// // - `output_ptr`: the pointer into the linear memory where the output -// // data is placed. The function will write the result -// // directly into this buffer. -// [seal0] seal_hash_blake2_128(ctx, input_ptr: u32, input_len: u32, output_ptr: u32) => { -// ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?; -// Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?) -// }, - -// // Call into the chain extension provided by the chain if any. -// // -// // Handling of the input values is up to the specific chain extension and so is the -// // return value. The extension can decide to use the inputs as primitive inputs or as -// // in/out arguments by interpreting them as pointers. Any caller of this function -// // must therefore coordinate with the chain that it targets. -// // -// // # Note -// // -// // If no chain extension exists the contract will trap with the `NoChainExtension` -// // module error. -// [seal0] seal_call_chain_extension( -// ctx, -// func_id: u32, -// input_ptr: u32, -// input_len: u32, -// output_ptr: u32, -// output_len_ptr: u32 -// ) -> u32 => { -// use crate::chain_extension::{ChainExtension, Environment, RetVal}; -// if !::ChainExtension::enabled() { -// return Err(Error::::NoChainExtension.into()); -// } -// let env = Environment::new(ctx, input_ptr, input_len, output_ptr, output_len_ptr); -// match ::ChainExtension::call(func_id, env)? { -// RetVal::Converging(val) => Ok(val), -// RetVal::Diverging{flags, data} => Err(TrapReason::Return(ReturnData { -// flags: flags.bits(), -// data, -// })), -// } -// }, - -// // Emit a custom debug message. -// // -// // No newlines are added to the supplied message. -// // Specifying invalid UTF-8 triggers a trap. -// // -// // This is a no-op if debug message recording is disabled which is always the case -// // when the code is executing on-chain. The message is interpreted as UTF-8 and -// // appended to the debug buffer which is then supplied to the calling RPC client. -// // -// // # Note -// // -// // Even though no action is taken when debug message recording is disabled there is still -// // a non trivial overhead (and weight cost) associated with calling this function. Contract -// // languages should remove calls to this function (either at runtime or compile time) when -// // not being executed as an RPC. For example, they could allow users to disable logging -// // through compile time flags (cargo features) for on-chain deployment. Additionally, the -// // return value of this function can be cached in order to prevent further calls at runtime. -// [seal0] seal_debug_message(ctx, str_ptr: u32, str_len: u32) -> ReturnCode => { -// ctx.charge_gas(RuntimeCosts::DebugMessage)?; -// if ctx.ext.append_debug_buffer("") { -// let data = ctx.read_sandbox_memory(str_ptr, str_len)?; -// let msg = core::str::from_utf8(&data) -// .map_err(|_| >::DebugMessageInvalidUTF8)?; -// ctx.ext.append_debug_buffer(msg); -// return Ok(ReturnCode::Success); -// } -// Ok(ReturnCode::LoggingDisabled) -// }, - -// // Call some dispatchable of the runtime. -// // -// // This function decodes the passed in data as the overarching `Call` type of the -// // runtime and dispatches it. The weight as specified in the runtime is charged -// // from the gas meter. Any weight refunds made by the dispatchable are considered. -// // -// // The filter specified by `Config::CallFilter` is attached to the origin of -// // the dispatched call. -// // -// // # Parameters -// // -// // - `input_ptr`: the pointer into the linear memory where the input data is placed. -// // - `input_len`: the length of the input data in bytes. -// // -// // # Return Value -// // -// // Returns `ReturnCode::Success` when the dispatchable was succesfully executed and -// // returned `Ok`. When the dispatchable was exeuted but returned an error -// // `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not -// // provided because it is not guaranteed to be stable. -// // -// // # Comparison with `ChainExtension` -// // -// // Just as a chain extension this API allows the runtime to extend the functionality -// // of contracts. While making use of this function is generelly easier it cannot be -// // used in call cases. Consider writing a chain extension if you need to do perform -// // one of the following tasks: -// // -// // - Return data. -// // - Provide functionality **exclusively** to contracts. -// // - Provide custom weights. -// // - Avoid the need to keep the `Call` data structure stable. -// // -// // # Unstable -// // -// // This function is unstable and subject to change (or removal) in the future. Do not -// // deploy a contract using it to a production chain. -// [__unstable__] seal_call_runtime(ctx, call_ptr: u32, call_len: u32) -> ReturnCode => { -// use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight}; -// ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; -// let call: ::Call = ctx.read_sandbox_memory_as_unbounded( -// call_ptr, call_len -// )?; -// let dispatch_info = call.get_dispatch_info(); -// let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?; -// let result = ctx.ext.call_runtime(call); -// let actual_weight = extract_actual_weight(&result, &dispatch_info); -// ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); -// match result { -// Ok(_) => Ok(ReturnCode::Success), -// Err(_) => Ok(ReturnCode::CallRuntimeReturnedError), -// } -// }, - -// // Recovers the ECDSA public key from the given message hash and signature. -// // -// // Writes the public key into the given output buffer. -// // Assumes the secp256k1 curve. -// // -// // # Parameters -// // -// // - `signature_ptr`: the pointer into the linear memory where the signature -// // is placed. Should be decodable as a 65 bytes. Traps otherwise. -// // - `message_hash_ptr`: the pointer into the linear memory where the message -// // hash is placed. Should be decodable as a 32 bytes. Traps otherwise. -// // - `output_ptr`: the pointer into the linear memory where the output -// // data is placed. The buffer should be 33 bytes. The function -// // will write the result directly into this buffer. -// // -// // # Errors -// // -// // `ReturnCode::EcdsaRecoverFailed` -// [seal0] seal_ecdsa_recover(ctx, signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32) -> -// ReturnCode => { ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?; - -// let mut signature: [u8; 65] = [0; 65]; -// ctx.read_sandbox_memory_into_buf(signature_ptr, &mut signature)?; -// let mut message_hash: [u8; 32] = [0; 32]; -// ctx.read_sandbox_memory_into_buf(message_hash_ptr, &mut message_hash)?; - -// let result = ctx.ext.ecdsa_recover(&signature, &message_hash); - -// match result { -// Ok(pub_key) => { -// // Write the recovered compressed ecdsa public key back into the sandboxed output -// // buffer. -// ctx.write_sandbox_memory(output_ptr, pub_key.as_ref())?; - -// Ok(ReturnCode::Success) -// }, -// Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), -// } -// }, - -// // 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 `seal_set_code_hash` and the next caller would use -// // the old code. -// // -// // # Parameters -// // -// // - `code_hash_ptr`: A pointer to the buffer that contains the new code hash. -// // -// // # Errors -// // -// // `ReturnCode::CodeNotFound` -// [seal0] seal_set_code_hash(ctx, code_hash_ptr: u32) -> ReturnCode => { -// ctx.charge_gas(RuntimeCosts::SetCodeHash)?; -// let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?; -// match ctx.ext.set_code_hash(code_hash) { -// Err(err) => { -// let code = Runtime::::err_into_return_code(err)?; -// Ok(code) -// }, -// Ok(()) => Ok(ReturnCode::Success) -// } -// }, - -// // Calculates Ethereum address from the ECDSA compressed public key and stores -// // it into the supplied buffer. -// // -// // # Parameters -// // -// // - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes -// value. // Traps otherwise. -// // - `out_ptr`: the pointer into the linear memory where the output -// // data is placed. The function will write the result -// // directly into this buffer. -// // -// // The value is stored to linear memory at the address pointed to by `out_ptr`. -// // If the available space at `out_ptr` is less than the size of the value a trap is triggered. -// // -// // # Errors -// // -// // `ReturnCode::EcdsaRecoverFailed` -// [seal0] seal_ecdsa_to_eth_address(ctx, key_ptr: u32, out_ptr: u32) -> ReturnCode => { -// ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; -// let mut compressed_key: [u8; 33] = [0;33]; -// ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?; -// let result = ctx.ext.ecdsa_to_eth_address(&compressed_key); -// match result { -// Ok(eth_address) => { -// ctx.write_sandbox_memory(out_ptr, eth_address.as_ref())?; -// Ok(ReturnCode::Success) -// }, -// Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), -// } -// }, -// ); + /// Clear the value at the given key in the contract storage. + /// + /// Equivalent to the newer version of `seal_clear_storage` with the exception of the return + /// type. Still a valid thing to call when not interested in the return value. + fn seal_clear_storage(ctx: Runtime, key_ptr: u32) { + ctx.clear_storage(KeyType::Fix, key_ptr).map(|_| ()) + } + + /// Clear the value at the given key in the contract storage. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the key is placed. + /// - `key_len`: the length of the key in bytes. + /// + /// # Return Value + /// + /// Returns the size of the pre-existing value at the specified key if any. Otherwise + /// `SENTINEL` is returned as a sentinel value. + #[unstable] + fn seal_clear_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> u32 { + ctx.clear_storage(KeyType::Variable(key_len), key_ptr) + } + + /// Retrieve the value under the given key from storage. + /// + /// This version is to be used with a fixed sized storage key. For runtimes supporting + /// transparent hashing, please use the newer version of this function. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. + /// - `out_ptr`: pointer to the linear memory where the value is written to. + /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and + /// the value length is written to. + /// + /// # Errors + /// + /// `ReturnCode::KeyNotFound` + fn seal_get_storage( + ctx: Runtime, + key_ptr: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> ReturnCode { + ctx.get_storage(KeyType::Fix, key_ptr, out_ptr, out_len_ptr) + } + + /// Retrieve the value under the given key from storage. + /// + /// This version is to be used with a fixed sized storage key. For runtimes supporting + /// transparent hashing, please use the newer version of this function. + /// + /// The key length must not exceed the maximum defined by the contracts module parameter. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. + /// - `key_len`: the length of the key in bytes. + /// - `out_ptr`: pointer to the linear memory where the value is written to. + /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and + /// the value length is written to. + /// + /// # Errors + /// + /// `ReturnCode::KeyNotFound` + #[unstable] + fn seal_get_storage( + ctx: Runtime, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> ReturnCode { + ctx.get_storage(KeyType::Variable(key_len), key_ptr, out_ptr, out_len_ptr) + } + + /// Checks whether there is a value stored under the given key. + /// + /// This version is to be used with a fixed sized storage key. For runtimes supporting + /// transparent hashing, please use the newer version of this function. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. + /// + /// # Return Value + /// + /// Returns the size of the pre-existing value at the specified key if any. Otherwise + /// `SENTINEL` is returned as a sentinel value. + fn seal_contains_storage(ctx: Runtime, key_ptr: u32) -> u32 { + ctx.contains_storage(KeyType::Fix, key_ptr) + } + + /// 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. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. + /// - `key_len`: the length of the key in bytes. + /// + /// # Return Value + /// + /// Returns the size of the pre-existing value at the specified key if any. Otherwise + /// `SENTINEL` is returned as a sentinel value. + #[unstable] + fn seal_contains_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> u32 { + ctx.contains_storage(KeyType::Variable(key_len), key_ptr) + } + + /// Retrieve and remove the value under the given key from storage. + /// + /// # Parameters + /// + /// - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. + /// - `key_len`: the length of the key in bytes. + /// - `out_ptr`: pointer to the linear memory where the value is written to. + /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and + /// the value length is written to. + /// + /// # Errors + /// + /// `ReturnCode::KeyNotFound` + #[unstable] + fn seal_take_storage( + ctx: Runtime, + key_ptr: u32, + key_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> ReturnCode { + let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; + let key = ctx.read_sandbox_memory(key_ptr, key_len)?; + if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage_transparent( + &VarSizedKey::::try_from(key).map_err(|_| Error::::DecodingFailed)?, + None, + true, + )? { + ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(value.len() as u32)); + ctx.write_sandbox_output(out_ptr, out_len_ptr, &value, false, already_charged)?; + Ok(ReturnCode::Success) + } else { + ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(0)); + Ok(ReturnCode::KeyNotFound) + } + } + /// Transfer some value to another account. + /// + /// # Parameters + /// + /// - account_ptr: a pointer to the address of the beneficiary account Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// - account_len: length of the address buffer. + /// - value_ptr: a pointer to the buffer with value, how much value to send. Should be decodable + /// as a `T::Balance`. Traps otherwise. + /// - value_len: length of the value buffer. + /// + /// # Errors + /// + /// `ReturnCode::TransferFailed` + fn seal_transfer( + ctx: Runtime, + account_ptr: u32, + _account_len: u32, + value_ptr: u32, + _value_len: u32, + ) -> ReturnCode { + ctx.charge_gas(RuntimeCosts::Transfer)?; + let callee: <::T as frame_system::Config>::AccountId = + ctx.read_sandbox_memory_as(account_ptr)?; + let value: BalanceOf<::T> = ctx.read_sandbox_memory_as(value_ptr)?; + let result = ctx.ext.transfer(&callee, value); + match result { + Ok(()) => Ok(ReturnCode::Success), + Err(err) => { + let code = Runtime::::err_into_return_code(err)?; + Ok(code) + }, + } + } + + /// Make a call to another contract. + /// + /// # Deprecation + /// + /// This is equivalent to calling the newer version of this function with + /// `flags` set to `ALLOW_REENTRY`. See the newer version for documentation. + /// + /// # Note + /// + /// The values `_callee_len` and `_value_len` are ignored because the encoded sizes + /// of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards + /// compatibility. Consider switching to the newest version of this function. + fn seal_call( + ctx: Runtime, + callee_ptr: u32, + _callee_len: u32, + gas: u64, + value_ptr: u32, + _value_len: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> ReturnCode { + ctx.call( + CallFlags::ALLOW_REENTRY, + CallType::Call { callee_ptr, value_ptr, gas }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + + /// Make a call to another contract. + /// + /// The callees output buffer is copied to `output_ptr` and its length to `output_len_ptr`. + /// The copy of the output buffer can be skipped by supplying the sentinel value + /// of `SENTINEL` to `output_ptr`. + /// + /// # Parameters + /// + /// - flags: See [`CallFlags`] for a documenation of the supported flags. + /// - callee_ptr: a pointer to the address of the callee contract. Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// - gas: how much gas to devote to the execution. + /// - value_ptr: a pointer to the buffer with value, how much value to send. Should be decodable + /// as a `T::Balance`. Traps otherwise. + /// - input_data_ptr: a pointer to a buffer to be used as input data to the callee. + /// - input_data_len: length of the input data buffer. + /// - output_ptr: a pointer where the output buffer is copied to. + /// - output_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// actual length is written to. + /// + /// # Errors + /// + /// An error means that the call wasn't successful output buffer is returned unless + /// stated otherwise. + /// + /// `ReturnCode::CalleeReverted`: Output buffer is returned. + /// `ReturnCode::CalleeTrapped` + /// `ReturnCode::TransferFailed` + /// `ReturnCode::NotCallable` + #[v(1)] + fn seal_call( + ctx: Runtime, + flags: u32, + callee_ptr: u32, + gas: u64, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> ReturnCode { + ctx.call( + CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, + CallType::Call { callee_ptr, value_ptr, gas }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + + // // 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. + /// - code_hash: a pointer to the hash of the code to be called. + /// - input_data_ptr: a pointer to a buffer to be used as input data to the callee. + /// - input_data_len: length of the input data buffer. + /// - output_ptr: a pointer where the output buffer is copied to. + /// - output_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// actual length is written to. + /// + /// # Errors + /// + /// An error means that the call wasn't successful and no output buffer is returned unless + /// stated otherwise. + /// + /// `ReturnCode::CalleeReverted`: Output buffer is returned. + /// `ReturnCode::CalleeTrapped` + /// `ReturnCode::CodeNotFound` + fn seal_delegate_call( + ctx: Runtime, + flags: u32, + code_hash_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> ReturnCode { + ctx.call( + CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, + CallType::DelegateCall { code_hash_ptr }, + input_data_ptr, + input_data_len, + output_ptr, + output_len_ptr, + ) + } + /// Instantiate a contract with the specified code hash. + /// + /// # Deprecation + /// + /// This is equivalent to calling the newer version of this function. The newer version + /// drops the now unnecessary length fields. + /// + /// # Note + /// + /// The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes + /// of those types are fixed through `[`MaxEncodedLen`]. The fields exist for backwards + /// compatibility. Consider switching to the newest version of this function. + fn seal_instantiate( + ctx: Runtime, + code_hash_ptr: u32, + _code_hash_len: u32, + gas: u64, + value_ptr: u32, + _value_len: u32, + input_data_ptr: u32, + input_data_len: u32, + address_ptr: u32, + address_len_ptr: u32, + output_ptr: u32, + output_len_ptr: u32, + salt_ptr: u32, + salt_len: u32, + ) -> ReturnCode { + ctx.instantiate( + code_hash_ptr, + gas, + value_ptr, + input_data_ptr, + input_data_len, + address_ptr, + address_len_ptr, + output_ptr, + output_len_ptr, + salt_ptr, + salt_len, + ) + } + + /// Instantiate a contract with the specified code hash. + /// + /// This function creates an account and executes the constructor defined in the code specified + /// by the code hash. The address of this new account is copied to `address_ptr` and its length + /// to `address_len_ptr`. The constructors output buffer is copied to `output_ptr` and its + /// length to `output_len_ptr`. The copy of the output buffer and address can be skipped by + /// supplying the sentinel value of `SENTINEL` to `output_ptr` or `address_ptr`. + /// + /// `value` must be at least the minimum balance. Otherwise the instantiation fails and the + /// contract is not created. + /// + /// # Parameters + /// + /// - code_hash_ptr: a pointer to the buffer that contains the initializer code. + /// - gas: how much gas to devote to the execution of the initializer code. + /// - value_ptr: a pointer to the buffer with value, how much value to send. Should be decodable + /// as a `T::Balance`. Traps otherwise. + /// - input_data_ptr: a pointer to a buffer to be used as input data to the initializer code. + /// - input_data_len: length of the input data buffer. + /// - address_ptr: a pointer where the new account's address is copied to. + /// - address_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// actual length is written to. + /// - output_ptr: a pointer where the output buffer is copied to. + /// - output_len_ptr: in-out pointer to where the length of the buffer is read from and the + /// actual length is written to. + /// - salt_ptr: Pointer to raw bytes used for address derivation. See `fn contract_address`. + /// - salt_len: length in bytes of the supplied salt. + /// + /// # Errors + /// + /// Please consult the `ReturnCode` 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. + /// + /// `ReturnCode::CalleeReverted`: Output buffer is returned. + /// `ReturnCode::CalleeTrapped` + /// `ReturnCode::TransferFailed` + /// `ReturnCode::CodeNotFound` + #[v(1)] + fn seal_instantiate( + ctx: Runtime, + code_hash_ptr: u32, + gas: u64, + value_ptr: u32, + input_data_ptr: u32, + input_data_len: u32, + address_ptr: u32, + address_len_ptr: u32, + output_ptr: u32, + output_len_ptr: u32, + salt_ptr: u32, + salt_len: u32, + ) -> ReturnCode { + ctx.instantiate( + code_hash_ptr, + gas, + value_ptr, + input_data_ptr, + input_data_len, + address_ptr, + address_len_ptr, + output_ptr, + output_len_ptr, + salt_ptr, + salt_len, + ) + } + + /// Remove the calling account and transfer remaining balance. + /// + /// # Deprecation + /// + /// This is equivalent to calling the newer version of this function. The newer version + /// drops the now unnecessary length fields. + /// + /// # Note + /// + /// The value `_beneficiary_len` is ignored because the encoded sizes + /// this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards + /// compatibility. Consider switching to the newest version of this function. + fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32, _beneficiary_len: u32) { + ctx.terminate(beneficiary_ptr) + } + + /// 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. + /// + /// - beneficiary_ptr: a pointer to the address of the beneficiary account where all where all + /// remaining funds of the caller are transferred. Should be decodable as an `T::AccountId`. + /// Traps otherwise. + /// + /// # 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. + #[v(1)] + fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32) { + ctx.terminate(beneficiary_ptr) + } + + /// Stores the input passed by the caller into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// # Note + /// + /// This function traps if the input was previously forwarded by a `seal_call`. + fn seal_input(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::InputBase)?; + if let Some(input) = ctx.input_data.take() { + ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| { + Some(RuntimeCosts::CopyToContract(len)) + })?; + ctx.input_data = Some(input); + Ok(()) + } else { + Err(Error::::InputForwarded.into()) + } + } + + /// Cease contract execution and save a data buffer as a result of the execution. + /// + /// 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: + /// ``` + /// seal_return(0, 0, 0); + /// ``` + /// + /// The flags argument is a bitfield that can be used to signal special return + /// conditions to the supervisor: + /// --- lsb --- + /// bit 0 : REVERT - Revert all storage changes made by the caller. + /// bit [1, 31]: Reserved for future use. + /// --- msb --- + /// + /// Using a reserved bit triggers a trap. + fn seal_return(ctx: Runtime, flags: u32, data_ptr: u32, data_len: u32) { + ctx.charge_gas(RuntimeCosts::Return(data_len))?; + Err(TrapReason::Return(ReturnData { + flags, + data: ctx.read_sandbox_memory(data_ptr, data_len)?, + })) + } + + /// Stores the address of the caller into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// 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. The value is encoded as T::AccountId. + fn seal_caller(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::Caller)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.caller().encode(), + false, + already_charged, + )?) + } + + /// Checks whether a specified address belongs to a contract. + /// + /// # Parameters + /// + /// - account_ptr: a pointer to the address of the beneficiary account Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// + /// Returned value is a u32-encoded boolean: (0 = false, 1 = true). + fn seal_is_contract(ctx: Runtime, account_ptr: u32) -> u32 { + ctx.charge_gas(RuntimeCosts::IsContract)?; + let address: <::T as frame_system::Config>::AccountId = + ctx.read_sandbox_memory_as(account_ptr)?; + + Ok(ctx.ext.is_contract(&address) as u32) + } + + /// Retrieve the code hash for a specified contract address. + /// + /// # Parameters + /// + /// - `account_ptr`: a pointer to the address in question. Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// - `out_ptr`: pointer to the linear memory where the returning value is written to. + /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and + /// the value length is written to. + /// + /// # Errors + /// + /// `ReturnCode::KeyNotFound` + fn seal_code_hash( + ctx: Runtime, + account_ptr: u32, + out_ptr: u32, + out_len_ptr: u32, + ) -> ReturnCode { + ctx.charge_gas(RuntimeCosts::CodeHash)?; + let address: <::T as frame_system::Config>::AccountId = + ctx.read_sandbox_memory_as(account_ptr)?; + if let Some(value) = ctx.ext.code_hash(&address) { + ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &value.encode(), + false, + already_charged, + )?; + Ok(ReturnCode::Success) + } else { + Ok(ReturnCode::KeyNotFound) + } + } + + /// Retrieve the code hash of the currently executing contract. + /// + /// # Parameters + /// + /// - `out_ptr`: pointer to the linear memory where the returning value is written to. + /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and + /// the value length is written to. + fn seal_own_code_hash(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; + let code_hash_encoded = &ctx.ext.own_code_hash().encode(); + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + code_hash_encoded, + false, + already_charged, + )?) + } + + /// Checks whether the caller of the current contract is the origin of the whole call stack. + /// + /// Prefer this over `seal_is_contract` when checking whether your contract is being called by a + /// contract or a plain account. The reason is that it performs better since it does not need to + /// do any storage lookups. + /// + /// A return value of`true` indicates that this contract is being called by a plain account + /// and `false` indicates that the caller is another contract. + /// + /// Returned value is a u32-encoded boolean: (0 = false, 1 = true). + fn seal_caller_is_origin(ctx: Runtime) -> u32 { + ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; + Ok(ctx.ext.caller_is_origin() as u32) + } + + /// Stores the address of the current contract into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + fn seal_address(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::Address)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.address().encode(), + false, + already_charged, + )?) + } + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// The data is encoded as T::Balance. + /// + /// # Note + /// + /// It is recommended to avoid specifying very small values for `gas` as the prices for a single + /// gas can be smaller than one. + fn seal_weight_to_fee(ctx: Runtime, gas: u64, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::WeightToFee)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.get_weight_price(gas).encode(), + false, + already_charged, + )?) + } + + /// Stores the amount of gas left into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// The data is encoded as Gas. + fn seal_gas_left(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::GasLeft)?; + let gas_left = &ctx.ext.gas_meter().gas_left().encode(); + Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, gas_left, false, already_charged)?) + } + + /// Stores the **free* balance of the current account into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// The data is encoded as T::Balance. + fn seal_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::Balance)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.balance().encode(), + false, + already_charged, + )?) + } + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// The data is encoded as T::Balance. + fn seal_value_transferred(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::ValueTransferred)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.value_transferred().encode(), + false, + already_charged, + )?) + } + + /// Stores a random number for the current block and the given subject into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// The data is encoded as T::Hash. + /// + /// # Deprecation + /// + /// This function is deprecated. Users should migrate to the version in the "seal1" module. + fn seal_random( + ctx: Runtime, + subject_ptr: u32, + subject_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) { + ctx.charge_gas(RuntimeCosts::Random)?; + if subject_len > ctx.ext.schedule().limits.subject_len { + return Err(Error::::RandomSubjectTooLong.into()) + } + let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.random(&subject_buf).0.encode(), + false, + already_charged, + )?) + } + + /// Stores a random number for the current block and the given subject into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// The data is encoded as (T::Hash, T::BlockNumber). + /// + /// # Changes from v0 + /// + /// In addition to the seed it returns the block number since which it was determinable + /// by chain observers. + /// + /// # Note + /// + /// The returned seed should only be used to distinguish commitments made before + /// the returned block number. If the block number is too early (i.e. commitments were + /// made afterwards), then ensure no further commitments may be made and repeatedly + /// call this on later blocks until the block number returned is later than the latest + /// commitment. + #[v(1)] + fn seal_random( + ctx: Runtime, + subject_ptr: u32, + subject_len: u32, + out_ptr: u32, + out_len_ptr: u32, + ) { + ctx.charge_gas(RuntimeCosts::Random)?; + if subject_len > ctx.ext.schedule().limits.subject_len { + return Err(Error::::RandomSubjectTooLong.into()) + } + let subject_buf = ctx.read_sandbox_memory(subject_ptr, subject_len)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.random(&subject_buf).encode(), + false, + already_charged, + )?) + } + + /// Load the latest block timestamp into the supplied buffer + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + fn seal_now(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::Now)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.now().encode(), + false, + already_charged, + )?) + } + + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// + /// The data is encoded as T::Balance. + fn seal_minimum_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::MinimumBalance)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.minimum_balance().encode(), + false, + already_charged, + )?) + } + + /// Stores the tombstone deposit into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// # Deprecation + /// + /// There is no longer a tombstone deposit. This function always returns 0. + fn seal_tombstone_deposit(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::Balance)?; + let deposit = >::zero().encode(); + Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?) + } + + /// Was used to restore the given destination contract sacrificing the caller. + /// + /// # Note + /// + /// The state rent functionality was removed. This is stub only exists for + /// backwards compatiblity + fn seal_restore_to( + ctx: Runtime, + _dest_ptr: u32, + _dest_len: u32, + _code_hash_ptr: u32, + _code_hash_len: u32, + _rent_allowance_ptr: u32, + _rent_allowance_len: u32, + _delta_ptr: u32, + _delta_count: u32, + ) { + ctx.charge_gas(RuntimeCosts::DebugMessage)?; + Ok(()) + } + + /// Was used to restore the given destination contract sacrificing the caller. + /// + /// # Note + /// + /// The state rent functionality was removed. This is stub only exists for + /// backwards compatiblity + #[v(1)] + fn seal_restore_to( + ctx: Runtime, + _dest_ptr: u32, + _code_hash_ptr: u32, + _rent_allowance_ptr: u32, + _delta_ptr: u32, + _delta_count: u32, + ) { + ctx.charge_gas(RuntimeCosts::DebugMessage)?; + Ok(()) + } + + /// 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`. + /// + /// - topics_ptr - a pointer to the buffer of topics encoded as `Vec`. The value of + /// this is ignored if `topics_len` is set to 0. The topics list can't contain duplicates. + /// - topics_len - the length of the topics buffer. Pass 0 if you want to pass an empty vector. + /// - data_ptr - a pointer to a raw data buffer which will saved along the event. + /// - data_len - the length of the data buffer. + fn seal_deposit_event( + ctx: Runtime, + topics_ptr: u32, + topics_len: u32, + data_ptr: u32, + data_len: u32, + ) { + fn has_duplicates(items: &mut Vec) -> bool { + // # Warning + // + // Unstable sorts are non-deterministic across architectures. The usage here is OK + // because we are rejecting duplicates which removes the non determinism. + items.sort_unstable(); + // Find any two consecutive equal elements. + items.windows(2).any(|w| match &w { + &[a, b] => a == b, + _ => false, + }) + } + + let num_topic = topics_len + .checked_div(sp_std::mem::size_of::>() as u32) + .ok_or("Zero sized topics are not allowed")?; + ctx.charge_gas(RuntimeCosts::DepositEvent { num_topic, len: data_len })?; + if data_len > ctx.ext.max_value_size() { + return Err(Error::::ValueTooLarge.into()) + } + + let mut topics: Vec::T>> = match topics_len { + 0 => Vec::new(), + _ => ctx.read_sandbox_memory_as_unbounded(topics_ptr, topics_len)?, + }; + + // If there are more than `event_topics`, then trap. + if topics.len() > ctx.ext.schedule().limits.event_topics as usize { + return Err(Error::::TooManyTopics.into()) + } + + // Check for duplicate topics. If there are any, then trap. + // Complexity O(n * log(n)) and no additional allocations. + // This also sorts the topics. + if has_duplicates(&mut topics) { + return Err(Error::::DuplicateTopics.into()) + } + + let event_data = ctx.read_sandbox_memory(data_ptr, data_len)?; + + ctx.ext.deposit_event(topics, event_data); + + Ok(()) + } + + /// Was used to set rent allowance of the contract. + /// + /// # Note + /// + /// The state rent functionality was removed. This is stub only exists for + /// backwards compatiblity. + fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32, _value_len: u32) { + ctx.charge_gas(RuntimeCosts::DebugMessage)?; + Ok(()) + } + + /// Was used to set rent allowance of the contract. + /// + /// # Note + /// + /// The state rent functionality was removed. This is stub only exists for + /// backwards compatiblity. + #[v(1)] + fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32) { + ctx.charge_gas(RuntimeCosts::DebugMessage)?; + Ok(()) + } + + /// Was used to store the rent allowance into the supplied buffer. + /// + /// # Note + /// + /// The state rent functionality was removed. This is stub only exists for + /// backwards compatiblity. + fn seal_rent_allowance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::Balance)?; + let rent_allowance = >::max_value().encode(); + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &rent_allowance, + false, + already_charged, + )?) + } + + /// Stores the current block number of the current contract into the supplied buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// `out_len_ptr` must point to a u32 value that describes the available space at + /// `out_ptr`. This call overwrites it with the size of the value. If the available + /// space at `out_ptr` is less than the size of the value a trap is triggered. + fn seal_block_number(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + ctx.charge_gas(RuntimeCosts::BlockNumber)?; + Ok(ctx.write_sandbox_output( + out_ptr, + out_len_ptr, + &ctx.ext.block_number().encode(), + false, + already_charged, + )?) + } + + /// Computes the SHA2 256-bit hash on the given input buffer. + /// + /// Returns the result directly into the given output buffer. + /// + /// # Note + /// + /// - The `input` and `output` buffer may overlap. + /// - The output buffer is expected to hold at least 32 bytes (256 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 chosen hash function. + /// + /// # Parameters + /// + /// - `input_ptr`: the pointer into the linear memory where the input data is placed. + /// - `input_len`: the length of the input data in bytes. + /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The + /// function will write the result directly into this buffer. + fn seal_hash_sha2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?; + Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?) + } + + /// Computes the KECCAK 256-bit hash on the given input buffer. + /// + /// Returns the result directly into the given output buffer. + /// + /// # Note + /// + /// - The `input` and `output` buffer may overlap. + /// - The output buffer is expected to hold at least 32 bytes (256 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 chosen hash function. + /// + /// # Parameters + /// + /// - `input_ptr`: the pointer into the linear memory where the input data is placed. + /// - `input_len`: the length of the input data in bytes. + /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The + /// function will write the result directly into this buffer. + fn seal_hash_keccak_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; + Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?) + } + + /// Computes the BLAKE2 256-bit hash on the given input buffer. + /// + /// Returns the result directly into the given output buffer. + /// + /// # Note + /// + /// - The `input` and `output` buffer may overlap. + /// - The output buffer is expected to hold at least 32 bytes (256 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 chosen hash function. + /// + /// # Parameters + /// + /// - `input_ptr`: the pointer into the linear memory where the input data is placed. + /// - `input_len`: the length of the input data in bytes. + /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The + /// function will write the result directly into this buffer. + fn seal_hash_blake2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?; + Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?) + } + + /// Computes the BLAKE2 128-bit hash on the given input buffer. + /// + /// Returns the result directly into the given output buffer. + /// + /// # Note + /// + /// - The `input` and `output` buffer may overlap. + /// - The output buffer is expected to hold at least 16 bytes (128 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 chosen hash function. + /// + /// # Parameters + /// + /// - `input_ptr`: the pointer into the linear memory where the input data is placed. + /// - `input_len`: the length of the input data in bytes. + /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The + /// function will write the result directly into this buffer. + fn seal_hash_blake2_128(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?; + Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?) + } + + /// Call into the chain extension provided by the chain if any. + /// + /// Handling of the input values is up to the specific chain extension and so is the + /// return value. The extension can decide to use the inputs as primitive inputs or as + /// in/out arguments by interpreting them as pointers. Any caller of this function + /// must therefore coordinate with the chain that it targets. + /// + /// # Note + /// + /// If no chain extension exists the contract will trap with the `NoChainExtension` + /// module error. + fn seal_call_chain_extension( + ctx: Runtime, + func_id: u32, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + output_len_ptr: u32, + ) -> u32 { + use crate::chain_extension::{ChainExtension, Environment, RetVal}; + if !::ChainExtension::enabled() { + return Err(Error::::NoChainExtension.into()) + } + let env = Environment::new( + ctx: Runtime, + input_ptr, + input_len, + output_ptr, + output_len_ptr, + ); + match ::ChainExtension::call(func_id, env)? { + RetVal::Converging(val) => Ok(val), + RetVal::Diverging { flags, data } => + Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })), + } + } + + /// Emit a custom debug message. + /// + /// No newlines are added to the supplied message. + /// Specifying invalid UTF-8 triggers a trap. + /// + /// This is a no-op if debug message recording is disabled which is always the case + /// when the code is executing on-chain. The message is interpreted as UTF-8 and + /// appended to the debug buffer which is then supplied to the calling RPC client. + /// + /// # Note + /// + /// Even though no action is taken when debug message recording is disabled there is still + /// a non trivial overhead (and weight cost) associated with calling this function. Contract + /// languages should remove calls to this function (either at runtime or compile time) when + /// not being executed as an RPC. For example, they could allow users to disable logging + /// through compile time flags (cargo features) for on-chain deployment. Additionally, the + /// return value of this function can be cached in order to prevent further calls at runtime. + fn seal_debug_message(ctx: Runtime, str_ptr: u32, str_len: u32) -> ReturnCode { + ctx.charge_gas(RuntimeCosts::DebugMessage)?; + if ctx.ext.append_debug_buffer("") { + let data = ctx.read_sandbox_memory(str_ptr, str_len)?; + let msg = + core::str::from_utf8(&data).map_err(|_| >::DebugMessageInvalidUTF8)?; + ctx.ext.append_debug_buffer(msg); + return Ok(ReturnCode::Success) + } + Ok(ReturnCode::LoggingDisabled) + } + + /// Call some dispatchable of the runtime. + /// + /// This function decodes the passed in data as the overarching `Call` type of the + /// runtime and dispatches it. The weight as specified in the runtime is charged + /// from the gas meter. Any weight refunds made by the dispatchable are considered. + /// + /// The filter specified by `Config::CallFilter` is attached to the origin of + /// the dispatched call. + /// + /// # Parameters + /// + /// - `input_ptr`: the pointer into the linear memory where the input data is placed. + /// - `input_len`: the length of the input data in bytes. + /// + /// # Return Value + /// + /// Returns `ReturnCode::Success` when the dispatchable was succesfully executed and + /// returned `Ok`. When the dispatchable was exeuted but returned an error + /// `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not + /// provided because it is not guaranteed to be stable. + /// + /// # Comparison with `ChainExtension` + /// + /// Just as a chain extension this API allows the runtime to extend the functionality + /// of contracts. While making use of this function is generelly easier it cannot be + /// used in call cases. Consider writing a chain extension if you need to do perform + /// one of the following tasks: + /// + /// - Return data. + /// - Provide functionality **exclusively** to contracts. + /// - Provide custom weights. + /// - Avoid the need to keep the `Call` data structure stable. + /// + /// # Unstable + /// + /// This function is unstable and subject to change (or removal) in the future. Do not + /// deploy a contract using it to a production chain. + #[unstable] + fn seal_call_runtime(ctx: Runtime, call_ptr: u32, call_len: u32) -> ReturnCode { + use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight}; + ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; + let call: ::Call = + ctx.read_sandbox_memory_as_unbounded(call_ptr, call_len)?; + let dispatch_info = call.get_dispatch_info(); + let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?; + let result = ctx.ext.call_runtime(call); + let actual_weight = extract_actual_weight(&result, &dispatch_info); + ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); + match result { + Ok(_) => Ok(ReturnCode::Success), + Err(_) => Ok(ReturnCode::CallRuntimeReturnedError), + } + } + + /// Recovers the ECDSA public key from the given message hash and signature. + /// + /// Writes the public key into the given output buffer. + /// Assumes the secp256k1 curve. + /// + /// # Parameters + /// + /// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should + /// be decodable as a 65 bytes. Traps otherwise. + /// - `message_hash_ptr`: the pointer into the linear memory where the message hash is placed. + /// Should be decodable as a 32 bytes. Traps otherwise. + /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The + /// buffer should be 33 bytes. The function will write the result directly into this buffer. + /// + /// # Errors + /// + /// `ReturnCode::EcdsaRecoverFailed` + fn seal_ecdsa_recover( + ctx: Runtime, + signature_ptr: u32, + message_hash_ptr: u32, + output_ptr: u32, + ) -> ReturnCode { + ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?; + + let mut signature: [u8; 65] = [0; 65]; + ctx.read_sandbox_memory_into_buf(signature_ptr, &mut signature)?; + let mut message_hash: [u8; 32] = [0; 32]; + ctx.read_sandbox_memory_into_buf(message_hash_ptr, &mut message_hash)?; + + let result = ctx.ext.ecdsa_recover(&signature, &message_hash); + + match result { + Ok(pub_key) => { + // Write the recovered compressed ecdsa public key back into the sandboxed output + // buffer. + ctx.write_sandbox_memory(output_ptr, pub_key.as_ref())?; + + Ok(ReturnCode::Success) + }, + Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), + } + } + + /// 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 `seal_set_code_hash` and the next caller would use + /// the old code. + /// + /// # Parameters + /// + /// - `code_hash_ptr`: A pointer to the buffer that contains the new code hash. + /// + /// # Errors + /// + /// `ReturnCode::CodeNotFound` + fn seal_set_code_hash(ctx: Runtime, code_hash_ptr: u32) -> ReturnCode { + ctx.charge_gas(RuntimeCosts::SetCodeHash)?; + let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?; + match ctx.ext.set_code_hash(code_hash) { + Err(err) => { + let code = Runtime::::err_into_return_code(err)?; + Ok(code) + }, + Ok(()) => Ok(ReturnCode::Success), + } + } + + /// Calculates Ethereum address from the ECDSA compressed public key and stores + /// it into the supplied buffer. + /// + /// # Parameters + /// + /// - `key_ptr`: a pointer to the ECDSA compressed public key. Should be decodable as a 33 bytes + /// value. Traps otherwise. + /// - `out_ptr`: the pointer into the linear memory where the output data is placed. The + /// function will write the result directly into this buffer. + /// + /// The value is stored to linear memory at the address pointed to by `out_ptr`. + /// If the available space at `out_ptr` is less than the size of the value a trap is triggered. + /// + /// # Errors + /// + /// `ReturnCode::EcdsaRecoverFailed` + fn seal_ecdsa_to_eth_address(ctx: Runtime, key_ptr: u32, out_ptr: u32) -> ReturnCode { + ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; + let mut compressed_key: [u8; 33] = [0; 33]; + ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?; + let result = ctx.ext.ecdsa_to_eth_address(&compressed_key); + match result { + Ok(eth_address) => { + ctx.write_sandbox_memory(out_ptr, eth_address.as_ref())?; + Ok(ReturnCode::Success) + }, + Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), + } + } +} From 45e43cfb726aa47392473bab2086dd48851b9af3 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 18:11:41 +0300 Subject: [PATCH 08/21] unstable feat fix --- frame/contracts/proc-macro/src/lib.rs | 122 ++++++++++++++------------ frame/contracts/src/wasm/runtime.rs | 8 +- 2 files changed, 67 insertions(+), 63 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 7d002630a6eb8..9f17a149ecbdd 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -320,70 +320,80 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { let impls = def.host_funcs.iter().map(|f| { let params = &f.item.sig.inputs.iter().skip(1).map(|arg| { - match arg { - syn::FnArg::Typed(pt) => { - if let syn::Pat::Ident(ident) = &*pt.pat { - let p_type = &pt.ty; - let p_name = ident.ident.clone(); - quote! { - let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = - args.next() - .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) - .expect( - "precondition: all imports should be checked against the signatures of corresponding - functions defined by `#[define_env]` proc macro by the user of the macro; - thus this can never be `None`; - qed;" - ); - } - } else { quote! { } } - }, - _ => quote! { }} + match arg { + syn::FnArg::Typed(pt) => { + if let syn::Pat::Ident(ident) = &*pt.pat { + let p_type = &pt.ty; + let p_name = ident.ident.clone(); + quote! { + let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = + args.next() + .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) + .expect( + "precondition: all imports should be checked against the signatures of corresponding + functions defined by `#[define_env]` proc macro by the user of the macro; + thus this can never be `None`; + qed;" + ); + } + } else { quote! { } } + }, + _ => quote! { }, + } }); let (outline, ret_ty) = match &f.item.sig.output { - syn::ReturnType::Default => (quote! { - body().map_err(|reason| { - ctx.set_trap_reason(reason); - sp_sandbox::HostError - })?; - return Ok(sp_sandbox::ReturnValue::Unit); - }, quote! {()}), - syn::ReturnType::Type(_,ty) => (quote! { - let r = body().map_err(|reason| { - ctx.set_trap_reason(reason); - sp_sandbox::HostError - })?; - return Ok(sp_sandbox::ReturnValue::Value({ - r.to_typed_value() - })); - }, quote! {#ty}), + syn::ReturnType::Default => ( + quote! { + body().map_err(|reason| { + ctx.set_trap_reason(reason); + sp_sandbox::HostError + })?; + return Ok(sp_sandbox::ReturnValue::Unit); + }, + quote! {()}), + syn::ReturnType::Type(_,ty) => ( + quote! { + let r = body().map_err(|reason| { + ctx.set_trap_reason(reason); + sp_sandbox::HostError + })?; + return Ok(sp_sandbox::ReturnValue::Value({ + r.to_typed_value() + })); + }, + quote! {#ty}), }; - let p = params.clone(); + let params = params.clone(); let (module, name, ident, body) = (&f.module, &f.name, &f.item.sig.ident, &f.item.block); + let unstable_feat = match module.as_str() { + "__unstable__" => quote! { #[cfg(feature = "unstable-interface")] }, + _ => quote! { }, + }; quote! { - f(#module.as_bytes(), #name.as_bytes(), { - fn #ident( - ctx: &mut crate::wasm::Runtime, - args: &[sp_sandbox::Value], - ) -> Result - where - ::AccountId: sp_core::crypto::UncheckedFrom<::Hash> - + AsRef<[u8]>, - { - #[allow(unused)] - let mut args = args.iter(); - let body = crate::wasm::env_def::macros::constrain_closure::<#ret_ty, _>(|| { - #( #p )* - #body - }); - #outline - } - #ident:: - }); + #unstable_feat + f(#module.as_bytes(), #name.as_bytes(), { + fn #ident( + ctx: &mut crate::wasm::Runtime, + args: &[sp_sandbox::Value], + ) -> Result + where + ::AccountId: sp_core::crypto::UncheckedFrom<::Hash> + + AsRef<[u8]>, + { + #[allow(unused)] + let mut args = args.iter(); + let body = crate::wasm::env_def::macros::constrain_closure::<#ret_ty, _>(|| { + #( #params )* + #body + }); + #outline + } + #ident:: + }); } - }); + }); let packed_impls = quote! { #( #impls )* }; diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index b7366dd41c4c4..0a7ea056e2f50 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -2107,13 +2107,7 @@ pub mod env { if !::ChainExtension::enabled() { return Err(Error::::NoChainExtension.into()) } - let env = Environment::new( - ctx: Runtime, - input_ptr, - input_len, - output_ptr, - output_len_ptr, - ); + let env = Environment::new(ctx, input_ptr, input_len, output_ptr, output_len_ptr); match ::ChainExtension::call(func_id, env)? { RetVal::Converging(val) => Ok(val), RetVal::Diverging { flags, data } => From 7180ba8fbb0f7d5527e475d95bd1bb42ba1cc762 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 18:15:03 +0300 Subject: [PATCH 09/21] cleanup --- frame/contracts/proc-macro/src/lib.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 9f17a149ecbdd..cd1209c11936f 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -22,15 +22,13 @@ extern crate alloc; use alloc::{ - boxed::Box, format, string::{String, ToString}, - vec, vec::Vec, }; use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; -use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Ident, Item, ItemFn}; +use syn::{parse_macro_input, spanned::Spanned, Data, DeriveInput, Ident}; /// This derives `Debug` for a struct where each field must be of some numeric type. /// It interprets each field as its represents some weight and formats it as times so that @@ -158,7 +156,6 @@ fn format_default(field: &Ident) -> TokenStream { /// Parsed envirnoment definition. struct EnvDef { - pub item: syn::ItemMod, pub host_funcs: Vec, } @@ -201,14 +198,14 @@ impl ToTokens for HostFn { } impl HostFn { - pub fn try_from(mut item: syn::Item) -> syn::Result { + pub fn try_from(item: syn::Item) -> syn::Result { let span = item.span(); let err = || { let msg = "Invalid host function definition, only #[v()] or #[unstable] attribute is allowed."; syn::Error::new(span, msg) }; - let mut item = match item { + let item = match item { syn::Item::Fn(i_fn) => Ok(i_fn), _ => Err(err()), }?; @@ -258,7 +255,7 @@ impl EnvDef { host_funcs.push(HostFn::try_from(i.clone())?); } - Ok(Self { item, host_funcs }) + Ok(Self { host_funcs }) } } From 1f347e494958d02c6d746eb9969e155ec79c6013 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 18:25:16 +0300 Subject: [PATCH 10/21] legacy mbe macros cleaned up --- frame/contracts/proc-macro/src/lib.rs | 13 +- frame/contracts/src/wasm/env_def/macros.rs | 365 --------------------- 2 files changed, 4 insertions(+), 374 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index cd1209c11936f..dc0f108d265c3 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -16,6 +16,10 @@ // limitations under the License. //! Proc macros used in the contracts module. +//! The #[define_env] attribute macro hides boilerplate of defining external environment +//! for a wasm module. +//! +//! Most likely you should use the `define_env` macro. #![no_std] @@ -145,15 +149,6 @@ fn format_default(field: &Ident) -> TokenStream { } } -// define_env! macro re-write -// first we parse env mod -// then we expand, i.e. -// should generate code for: -// 1. can_satisfy checks: #can_satisfy -// expand def, so just add parts related to the new func to it, and return updated def as a token -// stream see how it's done in pallet proc macro, e.g. in constants -// 2. impls() for the set of host functions: #impls - /// Parsed envirnoment definition. struct EnvDef { pub host_funcs: Vec, diff --git a/frame/contracts/src/wasm/env_def/macros.rs b/frame/contracts/src/wasm/env_def/macros.rs index aa5a1626681f4..1a7d4ed1efa7f 100644 --- a/frame/contracts/src/wasm/env_def/macros.rs +++ b/frame/contracts/src/wasm/env_def/macros.rs @@ -15,81 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Definition of macros that hides boilerplate of defining external environment -//! for a wasm module. -//! -//! Most likely you should use `define_env` macro. - -macro_rules! convert_args { - () => (vec![]); - ( $( $t:ty ),* ) => ( vec![ $( { use $crate::wasm::env_def::ConvertibleToWasm; <$t>::VALUE_TYPE }, )* ] ); -} - -macro_rules! gen_signature { - ( ( $( $params: ty ),* ) ) => ( - { - wasm_instrument::parity_wasm::elements::FunctionType::new( - convert_args!($($params),*), vec![], - ) - } - ); - - ( ( $( $params: ty ),* ) -> $returns: ty ) => ( - { - wasm_instrument::parity_wasm::elements::FunctionType::new( - convert_args!($($params),*), - vec![{use $crate::wasm::env_def::ConvertibleToWasm; <$returns>::VALUE_TYPE}], - ) - } - ); -} - -macro_rules! gen_signature_dispatch { - ( - $needle_module:ident, - $needle_name:ident, - $needle_sig:ident ; - $module:ident, - $name:ident - ( $ctx:ident $( , $names:ident : $params:ty )* ) $( -> $returns:ty )* , $($rest:tt)* - ) => { - let module = stringify!($module).as_bytes(); - if module == $needle_module && stringify!($name).as_bytes() == $needle_name { - let signature = gen_signature!( ( $( $params ),* ) $( -> $returns )* ); - if $needle_sig == &signature { - return true; - } - } else { - gen_signature_dispatch!($needle_module, $needle_name, $needle_sig ; $($rest)*); - } - }; - ( $needle_module:ident, $needle_name:ident, $needle_sig:ident ; ) => {}; -} - -/// Unmarshall arguments and then execute `body` expression and return its result. -macro_rules! unmarshall_then_body { - ( $body:tt, $ctx:ident, $args_iter:ident, $( $names:ident : $params:ty ),* ) => ({ - $( - let $names : <$params as $crate::wasm::env_def::ConvertibleToWasm>::NativeType = - $args_iter.next() - .and_then(|v| <$params as $crate::wasm::env_def::ConvertibleToWasm> - ::from_typed_value(v.clone())) - .expect( - "precondition: all imports should be checked against the signatures of corresponding - functions defined by `define_env!` macro by the user of the macro; - signatures of these functions defined by `$params`; - calls always made with arguments types of which are defined by the corresponding imports; - thus types of arguments should be equal to type list in `$params` and - length of argument list and $params should be equal; - thus this can never be `None`; - qed; - " - ); - )* - $body - }) -} - /// Since we can't specify the type of closure directly at binding site: /// /// ```nocompile @@ -104,293 +29,3 @@ where { f } - -macro_rules! unmarshall_then_body_then_marshall { - ( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) -> $returns:ty => $body:tt ) => ({ - let body = $crate::wasm::env_def::macros::constrain_closure::< - <$returns as $crate::wasm::env_def::ConvertibleToWasm>::NativeType, _ - >(|| { - unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*) - }); - let r = body().map_err(|reason| { - $ctx.set_trap_reason(reason); - sp_sandbox::HostError - })?; - return Ok(sp_sandbox::ReturnValue::Value({ use $crate::wasm::env_def::ConvertibleToWasm; r.to_typed_value() })) - }); - ( $args_iter:ident, $ctx:ident, ( $( $names:ident : $params:ty ),* ) => $body:tt ) => ({ - let body = $crate::wasm::env_def::macros::constrain_closure::<(), _>(|| { - unmarshall_then_body!($body, $ctx, $args_iter, $( $names : $params ),*) - }); - body().map_err(|reason| { - $ctx.set_trap_reason(reason); - sp_sandbox::HostError - })?; - return Ok(sp_sandbox::ReturnValue::Unit) - }) -} - -macro_rules! define_func { - ( $trait:tt $name:ident ( $ctx: ident $(, $names:ident : $params:ty)*) $(-> $returns:ty)* => $body:tt ) => { - fn $name< E: $trait >( - $ctx: &mut $crate::wasm::Runtime, - args: &[sp_sandbox::Value], - ) -> Result - where - ::AccountId: - sp_core::crypto::UncheckedFrom<::Hash> + - AsRef<[u8]> - { - #[allow(unused)] - let mut args = args.iter(); - - unmarshall_then_body_then_marshall!( - args, - $ctx, - ( $( $names : $params ),* ) $( -> $returns )* => $body - ) - } - }; -} - -macro_rules! register_body { - ( $reg_cb:ident, $trait:tt; - $module:ident $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) - $( -> $returns:ty )* => $body:tt - ) => { - $reg_cb( - stringify!($module).as_bytes(), - stringify!($name).as_bytes(), - { - define_func!( - $trait $name ( $ctx $(, $names : $params )* ) $( -> $returns )* => $body - ); - $name:: - } - ); - } -} - -macro_rules! register_func { - ( $reg_cb:ident, $trait:tt; ) => {}; - - ( $reg_cb:ident, $trait:tt; - __unstable__ $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) - $( -> $returns:ty )* => $body:tt $($rest:tt)* - ) => { - #[cfg(feature = "unstable-interface")] - register_body!( - $reg_cb, $trait; - __unstable__ $name - ( $ctx $( , $names : $params )* ) - $( -> $returns )* => $body - ); - register_func!( $reg_cb, $trait; $($rest)* ); - }; - - ( $reg_cb:ident, $trait:tt; - $module:ident $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) - $( -> $returns:ty )* => $body:tt $($rest:tt)* - ) => { - register_body!( - $reg_cb, $trait; - $module $name - ( $ctx $( , $names : $params )* ) - $( -> $returns )* => $body - ); - register_func!( $reg_cb, $trait; $($rest)* ); - }; -} - -/// Define a function set that can be imported by executing wasm code. -/// -/// **NB**: Be advised that all functions defined by this macro -/// will panic if called with unexpected arguments. -/// -/// It's up to the user of this macro to check signatures of wasm code to be executed -/// and reject the code if any imported function has a mismatched signature. -macro_rules! define_env { - ( $init_name:ident , < E: $trait:tt > , - $( [$module:ident] $name:ident ( $ctx:ident $( , $names:ident : $params:ty )* ) - $( -> $returns:ty )* => $body:tt , )* - ) => { - pub struct $init_name; - - impl $crate::wasm::env_def::ImportSatisfyCheck for $init_name { - fn can_satisfy( - module: &[u8], - name: &[u8], - func_type: &wasm_instrument::parity_wasm::elements::FunctionType, - ) -> bool - { - #[cfg(not(feature = "unstable-interface"))] - if module == b"__unstable__" { - return false; - } - gen_signature_dispatch!( - module, name, func_type ; - $( $module, $name ( $ctx $(, $names : $params )* ) $( -> $returns )* , )* - ); - - return false; - } - } - - impl $crate::wasm::env_def::FunctionImplProvider for $init_name - where - ::AccountId: - sp_core::crypto::UncheckedFrom<::Hash> + - AsRef<[u8]> - { - fn impls)>(f: &mut F) { - register_func!( - f, - $trait; - $( $module $name ( $ctx $( , $names : $params )* ) $( -> $returns)* => $body )* - ); - } - } - }; -} - -#[cfg(test)] -mod tests { - use crate::{ - exec::Ext, - wasm::{runtime::TrapReason, tests::MockExt, Runtime}, - Weight, - }; - use sp_runtime::traits::Zero; - use sp_sandbox::{ReturnValue, Value}; - use wasm_instrument::parity_wasm::elements::{FunctionType, ValueType}; - - struct TestRuntime { - value: u32, - } - - impl TestRuntime { - fn set_trap_reason(&mut self, _reason: TrapReason) {} - } - - #[test] - fn macro_unmarshall_then_body_then_marshall_value_or_trap() { - fn test_value( - _ctx: &mut TestRuntime, - args: &[sp_sandbox::Value], - ) -> Result { - let mut args = args.iter(); - unmarshall_then_body_then_marshall!( - args, - _ctx, - (a: u32, b: u32) -> u32 => { - if b == 0 { - Err(crate::wasm::runtime::TrapReason::Termination) - } else { - Ok(a / b) - } - } - ) - } - - let ctx = &mut TestRuntime { value: 0 }; - assert_eq!( - test_value(ctx, &[Value::I32(15), Value::I32(3)]).unwrap(), - ReturnValue::Value(Value::I32(5)), - ); - assert!(test_value(ctx, &[Value::I32(15), Value::I32(0)]).is_err()); - } - - #[test] - fn macro_unmarshall_then_body_then_marshall_unit() { - fn test_unit( - ctx: &mut TestRuntime, - args: &[sp_sandbox::Value], - ) -> Result { - let mut args = args.iter(); - unmarshall_then_body_then_marshall!( - args, - ctx, - (a: u32, b: u32) => { - ctx.value = a + b; - Ok(()) - } - ) - } - - let ctx = &mut TestRuntime { value: 0 }; - let result = test_unit(ctx, &[Value::I32(2), Value::I32(3)]).unwrap(); - assert_eq!(result, ReturnValue::Unit); - assert_eq!(ctx.value, 5); - } - - #[test] - fn macro_define_func() { - define_func!( Ext seal_gas (_ctx, amount: u32) => { - let amount = Weight::from(amount); - if !amount.is_zero() { - Ok(()) - } else { - Err(TrapReason::Termination) - } - }); - let _f: fn( - &mut Runtime, - &[sp_sandbox::Value], - ) -> Result = seal_gas::; - } - - #[test] - fn macro_gen_signature() { - assert_eq!(gen_signature!((i32)), FunctionType::new(vec![ValueType::I32], vec![])); - - assert_eq!( - gen_signature!( (i32, u32) -> u32 ), - FunctionType::new(vec![ValueType::I32, ValueType::I32], vec![ValueType::I32]), - ); - } - - #[test] - fn macro_unmarshall_then_body() { - let args = vec![Value::I32(5), Value::I32(3)]; - let mut args = args.iter(); - - let ctx: &mut u32 = &mut 0; - - let r = unmarshall_then_body!( - { - *ctx = a + b; - a * b - }, - ctx, - args, - a: u32, - b: u32 - ); - - assert_eq!(*ctx, 8); - assert_eq!(r, 15); - } - - #[test] - fn macro_define_env() { - use crate::wasm::env_def::ImportSatisfyCheck; - - define_env!(Env, , - [seal0] seal_gas( _ctx, amount: u32 ) => { - let amount = Weight::from(amount); - if !amount.is_zero() { - Ok(()) - } else { - Err(crate::wasm::runtime::TrapReason::Termination) - } - }, - ); - - assert!(Env::can_satisfy( - b"seal0", - b"seal_gas", - &FunctionType::new(vec![ValueType::I32], vec![]) - )); - assert!(!Env::can_satisfy(b"seal0", b"not_exists", &FunctionType::new(vec![], vec![]))); - } -} From d10d12e3e1827f4a7f2fbda9770e1aa35bd1e6f6 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 20:41:30 +0300 Subject: [PATCH 11/21] Added Env ident to macro attribute; all tests pass! --- frame/contracts/proc-macro/src/lib.rs | 54 ++++++++++++++++++--------- frame/contracts/src/wasm/prepare.rs | 23 +++++++++--- frame/contracts/src/wasm/runtime.rs | 2 +- 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index dc0f108d265c3..5a8b69e73c55f 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -149,9 +149,16 @@ fn format_default(field: &Ident) -> TokenStream { } } +/// Unparsed envirnoment definition. +struct EnvDefInput { + item: syn::ItemMod, + ident: syn::Ident, +} + /// Parsed envirnoment definition. struct EnvDef { - pub host_funcs: Vec, + ident: syn::Ident, + host_funcs: Vec, } /// Parsed host function definition. @@ -234,23 +241,26 @@ impl HostFn { } impl EnvDef { - pub fn try_from(mut item: syn::ItemMod) -> syn::Result { - let item_span = item.span(); - let items = &mut item + pub fn try_from(input: EnvDefInput) -> syn::Result { + let item = &input.item; + let span = item.span(); + let err = |msg| syn::Error::new(span.clone(), msg); + let items = &item .content - .as_mut() - .ok_or_else(|| { - let msg = "Invalid environment definition, expected `mod` to be inlined."; - syn::Error::new(item_span, msg) - })? + .as_ref() + .ok_or( + err("Invalid environment definition, expected `mod` to be inlined.".to_string()), + )? .1; + + let ident = input.ident; let mut host_funcs = Vec::::default(); for i in items.iter() { host_funcs.push(HostFn::try_from(i.clone())?); } - Ok(Self { host_funcs }) + Ok(Self { ident, host_funcs }) } } @@ -262,8 +272,10 @@ impl EnvDef { fn expand_env(def: &mut EnvDef) -> proc_macro2::TokenStream { let can_satisfy = expand_can_satisfy(def); let impls = expand_impls(def); + let env = &def.ident; + quote! { - pub struct Env; + pub struct #env; #can_satisfy #impls } @@ -288,8 +300,9 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { #( #checks )* }; + let env = &def.ident; quote! { - impl crate::wasm::env_def::ImportSatisfyCheck for Env { + impl crate::wasm::env_def::ImportSatisfyCheck for #env { fn can_satisfy( module: &[u8], name: &[u8], @@ -389,8 +402,10 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { let packed_impls = quote! { #( #impls )* }; + let env = &def.ident; + quote! { - impl crate::wasm::env_def::FunctionImplProvider for Env + impl crate::wasm::env_def::FunctionImplProvider for #env where ::AccountId: sp_core::crypto::UncheckedFrom<::Hash> + AsRef<[u8]>, @@ -407,15 +422,18 @@ pub fn define_env( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - if !attr.is_empty() { - let msg = "Invalid define_env macro call: expected no attributes, the call must be just \ - `#[define_env]`"; + if attr.is_empty() { + let msg = "Invalid `define_env` attribute macro: expected enviroment identificator, \ + e.g. `#[define_env(Env)]`."; let span = proc_macro2::TokenStream::from(attr).span(); return syn::Error::new(span, msg).to_compile_error().into() } - let i = syn::parse_macro_input!(item as syn::ItemMod); - match EnvDef::try_from(i) { + let item = syn::parse_macro_input!(item as syn::ItemMod); + let ident = syn::parse_macro_input!(attr as syn::Ident); + let input = EnvDefInput { item, ident }; + + match EnvDef::try_from(input) { Ok(mut def) => expand_env(&mut def).into(), Err(e) => e.to_compile_error().into(), } diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index f6fff20de6b1a..c3b3d8286d60d 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -517,6 +517,7 @@ mod tests { schedule::Limits, tests::{Test, ALICE}, }; + use pallet_contracts_proc_macro::define_env; use std::fmt; impl fmt::Debug for PrefabWasmModule { @@ -532,17 +533,27 @@ mod tests { // Define test environment for tests. We need ImportSatisfyCheck // implementation from it. So actual implementations doesn't matter. - define_env!(Test, , - [seal0] panic(_ctx) => { unreachable!(); }, + #[define_env(Test)] + pub mod env { + fn panic(_ctx: _) { + unreachable!(); + } // gas is an implementation defined function and a contract can't import it. - [seal0] gas(_ctx, _amount: u32) => { unreachable!(); }, + fn gas(_ctx: _, _amount: u32) { + unreachable!(); + } - [seal0] nop(_ctx, _unused: u64) => { unreachable!(); }, + fn nop(_ctx: _, _unused: u64) { + unreachable!(); + } // new version of nop with other data type for argumebt - [seal1] nop(_ctx, _unused: i32) => { unreachable!(); }, - ); + #[v(1)] + fn nop(_ctx: _, _unused: i32) { + unreachable!(); + } + } } macro_rules! prepare_test { diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 0a7ea056e2f50..8a8d3ef7ac908 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -937,7 +937,7 @@ where // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env] +#[define_env(Env)] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// From f84e2706fc223310babef50d0617dbcca1f43106 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 20:49:38 +0300 Subject: [PATCH 12/21] \#[v(..)] -> \#[version(..)] --- frame/contracts/proc-macro/src/lib.rs | 4 ++-- frame/contracts/src/wasm/prepare.rs | 2 +- frame/contracts/src/wasm/runtime.rs | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 5a8b69e73c55f..318f0b9c387a8 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -203,7 +203,7 @@ impl HostFn { pub fn try_from(item: syn::Item) -> syn::Result { let span = item.span(); let err = || { - let msg = "Invalid host function definition, only #[v()] or #[unstable] attribute is allowed."; + let msg = "Invalid host function definition, only #[version()] or #[unstable] attribute is allowed."; syn::Error::new(span, msg) }; @@ -223,7 +223,7 @@ impl HostFn { let attr = &attrs[0]; let ident = attr.path.get_ident().ok_or(err())?.to_string(); match ident.as_str() { - "v" => { + "version" => { let ver: syn::LitInt = attr.parse_args()?; module = format!("seal{}", ver.base10_parse::().map_err(|_| err())?); }, diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index c3b3d8286d60d..271ac0a6f7ad9 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -549,7 +549,7 @@ mod tests { } // new version of nop with other data type for argumebt - #[v(1)] + #[version(1)] fn nop(_ctx: _, _unused: i32) { unreachable!(); } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 8a8d3ef7ac908..ef8cdafa9cd54 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -976,7 +976,7 @@ pub mod env { /// /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. - #[v(1)] + #[version(1)] fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) } @@ -1254,7 +1254,7 @@ pub mod env { /// `ReturnCode::CalleeTrapped` /// `ReturnCode::TransferFailed` /// `ReturnCode::NotCallable` - #[v(1)] + #[version(1)] fn seal_call( ctx: Runtime, flags: u32, @@ -1401,7 +1401,7 @@ pub mod env { /// `ReturnCode::CalleeTrapped` /// `ReturnCode::TransferFailed` /// `ReturnCode::CodeNotFound` - #[v(1)] + #[version(1)] fn seal_instantiate( ctx: Runtime, code_hash_ptr: u32, @@ -1462,7 +1462,7 @@ pub mod env { /// - 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. - #[v(1)] + #[version(1)] fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32) { ctx.terminate(beneficiary_ptr) } @@ -1769,7 +1769,7 @@ pub mod env { /// made afterwards), then ensure no further commitments may be made and repeatedly /// call this on later blocks until the block number returned is later than the latest /// commitment. - #[v(1)] + #[version(1)] fn seal_random( ctx: Runtime, subject_ptr: u32, @@ -1865,7 +1865,7 @@ pub mod env { /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity - #[v(1)] + #[version(1)] fn seal_restore_to( ctx: Runtime, _dest_ptr: u32, @@ -1955,7 +1955,7 @@ pub mod env { /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. - #[v(1)] + #[version(1)] fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32) { ctx.charge_gas(RuntimeCosts::DebugMessage)?; Ok(()) From 3052e3e7a168ad4cd7541adc7c56904eccecee86 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 22 Jul 2022 21:03:40 +0300 Subject: [PATCH 13/21] some tiny corrections --- frame/contracts/proc-macro/src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 318f0b9c387a8..0b8c1359b390f 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -149,13 +149,13 @@ fn format_default(field: &Ident) -> TokenStream { } } -/// Unparsed envirnoment definition. +/// Unparsed environment definition. struct EnvDefInput { item: syn::ItemMod, ident: syn::Ident, } -/// Parsed envirnoment definition. +/// Parsed environment definition. struct EnvDef { ident: syn::Ident, host_funcs: Vec, @@ -248,9 +248,7 @@ impl EnvDef { let items = &item .content .as_ref() - .ok_or( - err("Invalid environment definition, expected `mod` to be inlined.".to_string()), - )? + .ok_or(err("Invalid environment definition, expected `mod` to be inlined."))? .1; let ident = input.ident; @@ -299,8 +297,8 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { let satisfy_checks = quote! { #( #checks )* }; - let env = &def.ident; + quote! { impl crate::wasm::env_def::ImportSatisfyCheck for #env { fn can_satisfy( From a7cd7d8a098e345ba5a299f865b5580e9e7dc84a Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 29 Jul 2022 16:39:45 +0300 Subject: [PATCH 14/21] save --- frame/contracts/proc-macro/src/lib.rs | 164 ++++++++++++++++---------- frame/contracts/src/wasm/runtime.rs | 108 ++++++++--------- 2 files changed, 158 insertions(+), 114 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 0b8c1359b390f..d22a6b845109c 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -26,6 +26,7 @@ extern crate alloc; use alloc::{ + boxed::Box, format, string::{String, ToString}, vec::Vec, @@ -149,15 +150,9 @@ fn format_default(field: &Ident) -> TokenStream { } } -/// Unparsed environment definition. -struct EnvDefInput { - item: syn::ItemMod, - ident: syn::Ident, -} - /// Parsed environment definition. struct EnvDef { - ident: syn::Ident, + item: syn::ItemMod, host_funcs: Vec, } @@ -166,6 +161,25 @@ struct HostFn { item: syn::ItemFn, module: String, name: String, + ret_type: HostFnReturn, +} + +enum HostFnReturn { + Unit, + U32, + ReturnCode, +} + +impl syn::parse::Parse for HostFnReturn { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + // let lookahead = input.lookahead1(); + // if lookahead.peek(syn::Ident) { + // input.parse().map(HostFnReturn::Unit) + // } else { + // Err(lookahead.error()) + // } + input.parse::().map(|_| Self::Unit) + } } /// Helper trait to convert a host function definition into its wasm signature. @@ -236,13 +250,53 @@ impl HostFn { _ => return Err(err()), } - Ok(Self { item, module, name }) + let ret_ty = match item.clone().sig.output { + syn::ReturnType::Type(_, ty) => Ok(ty.clone()), + _ => { + let msg = "Invalid host function definition, return val is expected."; + Err(syn::Error::new(span, msg)) + }, + }?; + + // TODO: refactor errs + let err1 = |span| { + let msg = + format!("Invalid host function definition: expected Result<...> return value"); + syn::Error::new(span, msg) + }; + match *ret_ty { + syn::Type::Path(tp) => { + let tmp = &tp.path.segments.first().ok_or(err1(span))?; + let (id, span) = (tmp.ident.to_string(), tmp.ident.span()); + if id == "Result".to_string() { + match tmp.arguments { + syn::PathArguments::AngleBracketed(group) => { + (group.args.len() == 2).then_some(42).ok_or(err1(span))?; + + (group.args.last().ok_or(err1(span))?.to_string() == "TrapReason") + .then_some(42) + .ok_or(err1(span))?; + let ret_val = group.args.first().ok_or(err1(span))?.to_string(); + let ret_type = match ret_val { + "()" => Ok(HostFnReturn::Unit), + "u32" => Ok(HostFnReturn::U32), + "ReturnCode" => Ok(HostFnReturn::ReturnCode), + _ => Err(err1(span)), + }?; + Ok(Self { item, module, name, ret_type }) + }, + _ => Err(err1(span)), + } + } else { + Err(err1(span)) + } + }, + _ => Err(err1(span)), + } } } - impl EnvDef { - pub fn try_from(input: EnvDefInput) -> syn::Result { - let item = &input.item; + pub fn try_from(item: syn::ItemMod) -> syn::Result { let span = item.span(); let err = |msg| syn::Error::new(span.clone(), msg); let items = &item @@ -251,14 +305,13 @@ impl EnvDef { .ok_or(err("Invalid environment definition, expected `mod` to be inlined."))? .1; - let ident = input.ident; let mut host_funcs = Vec::::default(); for i in items.iter() { host_funcs.push(HostFn::try_from(i.clone())?); } - Ok(Self { ident, host_funcs }) + Ok(Self { item, host_funcs }) } } @@ -270,10 +323,9 @@ impl EnvDef { fn expand_env(def: &mut EnvDef) -> proc_macro2::TokenStream { let can_satisfy = expand_can_satisfy(def); let impls = expand_impls(def); - let env = &def.ident; quote! { - pub struct #env; + pub struct Env; #can_satisfy #impls } @@ -297,10 +349,9 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { let satisfy_checks = quote! { #( #checks )* }; - let env = &def.ident; quote! { - impl crate::wasm::env_def::ImportSatisfyCheck for #env { + impl crate::wasm::env_def::ImportSatisfyCheck for Env { fn can_satisfy( module: &[u8], name: &[u8], @@ -322,41 +373,39 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { /// environment. fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { let impls = def.host_funcs.iter().map(|f| { - let params = &f.item.sig.inputs.iter().skip(1).map(|arg| { - match arg { - syn::FnArg::Typed(pt) => { - if let syn::Pat::Ident(ident) = &*pt.pat { - let p_type = &pt.ty; - let p_name = ident.ident.clone(); - quote! { - let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = - args.next() - .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) - .expect( - "precondition: all imports should be checked against the signatures of corresponding - functions defined by `#[define_env]` proc macro by the user of the macro; - thus this can never be `None`; - qed;" - ); - } - } else { quote! { } } - }, - _ => quote! { }, - } - }); - - let (outline, ret_ty) = match &f.item.sig.output { - syn::ReturnType::Default => ( - quote! { + let params = &f.item.sig.inputs.iter().skip(1).map(|arg| { + match arg { + syn::FnArg::Typed(pt) => { + if let syn::Pat::Ident(ident) = &*pt.pat { + let p_type = &pt.ty; + let p_name = ident.ident.clone(); + quote! { + let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = + args.next() + .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) + .expect( + "precondition: all imports should be checked against the signatures of corresponding + functions defined by `#[define_env]` proc macro by the user of the macro; + thus this can never be `None`; + qed;" + ); + } + } else { quote! { whoo } } + }, + _ => quote! { beee }, + } + }); + + let outline = match &f.ret_type { + HostFnReturn::Unit => quote! { body().map_err(|reason| { ctx.set_trap_reason(reason); sp_sandbox::HostError })?; return Ok(sp_sandbox::ReturnValue::Unit); }, - quote! {()}), - syn::ReturnType::Type(_,ty) => ( - quote! { + + _ => quote! { let r = body().map_err(|reason| { ctx.set_trap_reason(reason); sp_sandbox::HostError @@ -365,9 +414,7 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { r.to_typed_value() })); }, - quote! {#ty}), }; - let params = params.clone(); let (module, name, ident, body) = (&f.module, &f.name, &f.item.sig.ident, &f.item.block); let unstable_feat = match module.as_str() { @@ -375,7 +422,7 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { _ => quote! { }, }; quote! { - #unstable_feat + #unstable_feat f(#module.as_bytes(), #name.as_bytes(), { fn #ident( ctx: &mut crate::wasm::Runtime, @@ -387,23 +434,23 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { { #[allow(unused)] let mut args = args.iter(); - let body = crate::wasm::env_def::macros::constrain_closure::<#ret_ty, _>(|| { + let body = || { #( #params )* #body - }); + }; #outline } #ident:: }); } }); + let packed_impls = quote! { - #( #impls )* + #( #impls )* }; - let env = &def.ident; quote! { - impl crate::wasm::env_def::FunctionImplProvider for #env + impl crate::wasm::env_def::FunctionImplProvider for Env where ::AccountId: sp_core::crypto::UncheckedFrom<::Hash> + AsRef<[u8]>, @@ -420,18 +467,15 @@ pub fn define_env( attr: proc_macro::TokenStream, item: proc_macro::TokenStream, ) -> proc_macro::TokenStream { - if attr.is_empty() { - let msg = "Invalid `define_env` attribute macro: expected enviroment identificator, \ - e.g. `#[define_env(Env)]`."; + if !attr.is_empty() { + let msg = "Invalid `define_env` attribute macro: expected no attributes: `#[define_env]`."; let span = proc_macro2::TokenStream::from(attr).span(); return syn::Error::new(span, msg).to_compile_error().into() } let item = syn::parse_macro_input!(item as syn::ItemMod); - let ident = syn::parse_macro_input!(attr as syn::Ident); - let input = EnvDefInput { item, ident }; - match EnvDef::try_from(input) { + match EnvDef::try_from(item) { Ok(mut def) => expand_env(&mut def).into(), Err(e) => e.to_compile_error().into(), } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index ef8cdafa9cd54..edd6f5fad59be 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -937,7 +937,7 @@ where // Any input that leads to a out of bound error (reading or writing) or failing to decode // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. -#[define_env(Env)] +#[define_env] pub mod env { /// Account for used gas. Traps if gas used is greater than gas limit. /// @@ -945,7 +945,7 @@ pub mod env { /// This call is supposed to be called only by instrumentation injected code. /// /// - amount: How much gas is used. - fn gas(ctx: Runtime, amount: u32) { + fn gas(ctx: Runtime, amount: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::MeteringBlock(amount))?; Ok(()) } @@ -954,7 +954,7 @@ pub mod env { /// /// Equivalent to the newer version of `seal_set_storage` with the exception of the return /// type. Still a valid thing to call when not interested in the return value. - fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) { + fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) } @@ -977,7 +977,7 @@ pub mod env { /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. #[version(1)] - fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> u32 { + fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) } @@ -1004,7 +1004,7 @@ pub mod env { key_len: u32, value_ptr: u32, value_len: u32, - ) -> u32 { + ) -> Result { ctx.set_storage(KeyType::Variable(key_len), key_ptr, value_ptr, value_len) } @@ -1012,7 +1012,7 @@ pub mod env { /// /// Equivalent to the newer version of `seal_clear_storage` with the exception of the return /// type. Still a valid thing to call when not interested in the return value. - fn seal_clear_storage(ctx: Runtime, key_ptr: u32) { + fn seal_clear_storage(ctx: Runtime, key_ptr: u32) -> Result<(), TrapReason> { ctx.clear_storage(KeyType::Fix, key_ptr).map(|_| ()) } @@ -1028,7 +1028,7 @@ pub mod env { /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. #[unstable] - fn seal_clear_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> u32 { + fn seal_clear_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> Result { ctx.clear_storage(KeyType::Variable(key_len), key_ptr) } @@ -1052,7 +1052,7 @@ pub mod env { key_ptr: u32, out_ptr: u32, out_len_ptr: u32, - ) -> ReturnCode { + ) -> Result { ctx.get_storage(KeyType::Fix, key_ptr, out_ptr, out_len_ptr) } @@ -1081,7 +1081,7 @@ pub mod env { key_len: u32, out_ptr: u32, out_len_ptr: u32, - ) -> ReturnCode { + ) -> Result { ctx.get_storage(KeyType::Variable(key_len), key_ptr, out_ptr, out_len_ptr) } @@ -1098,7 +1098,7 @@ pub mod env { /// /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. - fn seal_contains_storage(ctx: Runtime, key_ptr: u32) -> u32 { + fn seal_contains_storage(ctx: Runtime, key_ptr: u32) -> Result { ctx.contains_storage(KeyType::Fix, key_ptr) } @@ -1116,7 +1116,7 @@ pub mod env { /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. #[unstable] - fn seal_contains_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> u32 { + fn seal_contains_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> Result { ctx.contains_storage(KeyType::Variable(key_len), key_ptr) } @@ -1140,7 +1140,7 @@ pub mod env { key_len: u32, out_ptr: u32, out_len_ptr: u32, - ) -> ReturnCode { + ) -> Result { let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; let key = ctx.read_sandbox_memory(key_ptr, key_len)?; if let crate::storage::WriteOutcome::Taken(value) = ctx.ext.set_storage_transparent( @@ -1176,7 +1176,7 @@ pub mod env { _account_len: u32, value_ptr: u32, _value_len: u32, - ) -> ReturnCode { + ) -> Result { ctx.charge_gas(RuntimeCosts::Transfer)?; let callee: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(account_ptr)?; @@ -1214,7 +1214,7 @@ pub mod env { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> ReturnCode { + ) -> Result { ctx.call( CallFlags::ALLOW_REENTRY, CallType::Call { callee_ptr, value_ptr, gas }, @@ -1265,7 +1265,7 @@ pub mod env { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> ReturnCode { + ) -> Result { ctx.call( CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, CallType::Call { callee_ptr, value_ptr, gas }, @@ -1308,7 +1308,7 @@ pub mod env { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> ReturnCode { + ) -> Result { ctx.call( CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, CallType::DelegateCall { code_hash_ptr }, @@ -1345,7 +1345,7 @@ pub mod env { output_len_ptr: u32, salt_ptr: u32, salt_len: u32, - ) -> ReturnCode { + ) -> Result { ctx.instantiate( code_hash_ptr, gas, @@ -1415,7 +1415,7 @@ pub mod env { output_len_ptr: u32, salt_ptr: u32, salt_len: u32, - ) -> ReturnCode { + ) -> Result { ctx.instantiate( code_hash_ptr, gas, @@ -1443,7 +1443,7 @@ pub mod env { /// The value `_beneficiary_len` is ignored because the encoded sizes /// this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards /// compatibility. Consider switching to the newest version of this function. - fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32, _beneficiary_len: u32) { + fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32, _beneficiary_len: u32) -> Result<(), TrapReason> { ctx.terminate(beneficiary_ptr) } @@ -1463,7 +1463,7 @@ pub mod env { /// - Failed to send the balance to the beneficiary. /// - The deletion queue is full. #[version(1)] - fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32) { + fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32) -> Result<(), TrapReason> { ctx.terminate(beneficiary_ptr) } @@ -1477,7 +1477,7 @@ pub mod env { /// # Note /// /// This function traps if the input was previously forwarded by a `seal_call`. - fn seal_input(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_input(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::InputBase)?; if let Some(input) = ctx.input_data.take() { ctx.write_sandbox_output(out_ptr, out_len_ptr, &input, false, |len| { @@ -1507,7 +1507,7 @@ pub mod env { /// --- msb --- /// /// Using a reserved bit triggers a trap. - fn seal_return(ctx: Runtime, flags: u32, data_ptr: u32, data_len: u32) { + fn seal_return(ctx: Runtime, flags: u32, data_ptr: u32, data_len: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Return(data_len))?; Err(TrapReason::Return(ReturnData { flags, @@ -1525,7 +1525,7 @@ pub mod env { /// 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. The value is encoded as T::AccountId. - fn seal_caller(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_caller(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Caller)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1544,7 +1544,7 @@ pub mod env { /// `T::AccountId`. Traps otherwise. /// /// Returned value is a u32-encoded boolean: (0 = false, 1 = true). - fn seal_is_contract(ctx: Runtime, account_ptr: u32) -> u32 { + fn seal_is_contract(ctx: Runtime, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::IsContract)?; let address: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(account_ptr)?; @@ -1570,7 +1570,7 @@ pub mod env { account_ptr: u32, out_ptr: u32, out_len_ptr: u32, - ) -> ReturnCode { + ) -> Result { ctx.charge_gas(RuntimeCosts::CodeHash)?; let address: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(account_ptr)?; @@ -1595,7 +1595,7 @@ pub mod env { /// - `out_ptr`: pointer to the linear memory where the returning value is written to. /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and /// the value length is written to. - fn seal_own_code_hash(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_own_code_hash(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; let code_hash_encoded = &ctx.ext.own_code_hash().encode(); Ok(ctx.write_sandbox_output( @@ -1617,7 +1617,7 @@ pub mod env { /// and `false` indicates that the caller is another contract. /// /// Returned value is a u32-encoded boolean: (0 = false, 1 = true). - fn seal_caller_is_origin(ctx: Runtime) -> u32 { + fn seal_caller_is_origin(ctx: Runtime) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; Ok(ctx.ext.caller_is_origin() as u32) } @@ -1628,7 +1628,7 @@ pub mod env { /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. - fn seal_address(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_address(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Address)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1652,7 +1652,7 @@ pub mod env { /// /// It is recommended to avoid specifying very small values for `gas` as the prices for a single /// gas can be smaller than one. - fn seal_weight_to_fee(ctx: Runtime, gas: u64, out_ptr: u32, out_len_ptr: u32) { + fn seal_weight_to_fee(ctx: Runtime, gas: u64, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::WeightToFee)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1671,7 +1671,7 @@ pub mod env { /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as Gas. - fn seal_gas_left(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_gas_left(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::GasLeft)?; let gas_left = &ctx.ext.gas_meter().gas_left().encode(); Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, gas_left, false, already_charged)?) @@ -1685,7 +1685,7 @@ pub mod env { /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as T::Balance. - fn seal_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1704,7 +1704,7 @@ pub mod env { /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as T::Balance. - fn seal_value_transferred(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_value_transferred(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::ValueTransferred)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1733,7 +1733,7 @@ pub mod env { subject_len: u32, out_ptr: u32, out_len_ptr: u32, - ) { + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Random)?; if subject_len > ctx.ext.schedule().limits.subject_len { return Err(Error::::RandomSubjectTooLong.into()) @@ -1776,7 +1776,7 @@ pub mod env { subject_len: u32, out_ptr: u32, out_len_ptr: u32, - ) { + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Random)?; if subject_len > ctx.ext.schedule().limits.subject_len { return Err(Error::::RandomSubjectTooLong.into()) @@ -1797,7 +1797,7 @@ pub mod env { /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. - fn seal_now(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_now(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Now)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1811,7 +1811,7 @@ pub mod env { /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. /// /// The data is encoded as T::Balance. - fn seal_minimum_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_minimum_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::MinimumBalance)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1832,7 +1832,7 @@ pub mod env { /// # Deprecation /// /// There is no longer a tombstone deposit. This function always returns 0. - fn seal_tombstone_deposit(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_tombstone_deposit(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; let deposit = >::zero().encode(); Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?) @@ -1854,7 +1854,7 @@ pub mod env { _rent_allowance_len: u32, _delta_ptr: u32, _delta_count: u32, - ) { + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage)?; Ok(()) } @@ -1873,7 +1873,7 @@ pub mod env { _rent_allowance_ptr: u32, _delta_ptr: u32, _delta_count: u32, - ) { + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage)?; Ok(()) } @@ -1892,7 +1892,7 @@ pub mod env { topics_len: u32, data_ptr: u32, data_len: u32, - ) { + ) ->Result<(), TrapReason> { fn has_duplicates(items: &mut Vec) -> bool { // # Warning // @@ -1944,7 +1944,7 @@ pub mod env { /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. - fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32, _value_len: u32) { + fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32, _value_len: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage)?; Ok(()) } @@ -1956,7 +1956,7 @@ pub mod env { /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. #[version(1)] - fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32) { + fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage)?; Ok(()) } @@ -1967,7 +1967,7 @@ pub mod env { /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. - fn seal_rent_allowance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_rent_allowance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; let rent_allowance = >::max_value().encode(); Ok(ctx.write_sandbox_output( @@ -1985,7 +1985,7 @@ pub mod env { /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. - fn seal_block_number(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) { + fn seal_block_number(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::BlockNumber)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -2013,7 +2013,7 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_sha2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + fn seal_hash_sha2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?) } @@ -2035,7 +2035,7 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_keccak_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + fn seal_hash_keccak_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?) } @@ -2057,7 +2057,7 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_blake2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + fn seal_hash_blake2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?) } @@ -2079,7 +2079,7 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_blake2_128(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) { + fn seal_hash_blake2_128(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?) } @@ -2102,7 +2102,7 @@ pub mod env { input_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> u32 { + ) -> Result { use crate::chain_extension::{ChainExtension, Environment, RetVal}; if !::ChainExtension::enabled() { return Err(Error::::NoChainExtension.into()) @@ -2132,7 +2132,7 @@ pub mod env { /// not being executed as an RPC. For example, they could allow users to disable logging /// through compile time flags (cargo features) for on-chain deployment. Additionally, the /// return value of this function can be cached in order to prevent further calls at runtime. - fn seal_debug_message(ctx: Runtime, str_ptr: u32, str_len: u32) -> ReturnCode { + fn seal_debug_message(ctx: Runtime, str_ptr: u32, str_len: u32) -> Result { ctx.charge_gas(RuntimeCosts::DebugMessage)?; if ctx.ext.append_debug_buffer("") { let data = ctx.read_sandbox_memory(str_ptr, str_len)?; @@ -2182,7 +2182,7 @@ pub mod env { /// This function is unstable and subject to change (or removal) in the future. Do not /// deploy a contract using it to a production chain. #[unstable] - fn seal_call_runtime(ctx: Runtime, call_ptr: u32, call_len: u32) -> ReturnCode { + fn seal_call_runtime(ctx: Runtime, call_ptr: u32, call_len: u32) -> Result { use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight}; ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; let call: ::Call = @@ -2220,7 +2220,7 @@ pub mod env { signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32, - ) -> ReturnCode { + ) -> Result { ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?; let mut signature: [u8; 65] = [0; 65]; @@ -2268,7 +2268,7 @@ pub mod env { /// # Errors /// /// `ReturnCode::CodeNotFound` - fn seal_set_code_hash(ctx: Runtime, code_hash_ptr: u32) -> ReturnCode { + fn seal_set_code_hash(ctx: Runtime, code_hash_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::SetCodeHash)?; let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?; match ctx.ext.set_code_hash(code_hash) { @@ -2296,7 +2296,7 @@ pub mod env { /// # Errors /// /// `ReturnCode::EcdsaRecoverFailed` - fn seal_ecdsa_to_eth_address(ctx: Runtime, key_ptr: u32, out_ptr: u32) -> ReturnCode { + fn seal_ecdsa_to_eth_address(ctx: Runtime, key_ptr: u32, out_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; let mut compressed_key: [u8; 33] = [0; 33]; ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?; From 0a04f4e837fbef31db52f5c00a3a2db9e451037e Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Fri, 29 Jul 2022 22:18:36 +0300 Subject: [PATCH 15/21] builds with non-magic rt; tests fail --- frame/contracts/proc-macro/src/lib.rs | 113 +++++++++------ frame/contracts/src/wasm/env_def/macros.rs | 31 ---- frame/contracts/src/wasm/env_def/mod.rs | 3 - frame/contracts/src/wasm/prepare.rs | 14 +- frame/contracts/src/wasm/runtime.rs | 161 +++++++++++++++++---- 5 files changed, 212 insertions(+), 110 deletions(-) delete mode 100644 frame/contracts/src/wasm/env_def/macros.rs diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index d22a6b845109c..7840fdf4d8118 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -26,7 +26,6 @@ extern crate alloc; use alloc::{ - boxed::Box, format, string::{String, ToString}, vec::Vec, @@ -152,7 +151,6 @@ fn format_default(field: &Ident) -> TokenStream { /// Parsed environment definition. struct EnvDef { - item: syn::ItemMod, host_funcs: Vec, } @@ -170,18 +168,6 @@ enum HostFnReturn { ReturnCode, } -impl syn::parse::Parse for HostFnReturn { - fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { - // let lookahead = input.lookahead1(); - // if lookahead.peek(syn::Ident) { - // input.parse().map(HostFnReturn::Unit) - // } else { - // Err(lookahead.error()) - // } - input.parse::().map(|_| Self::Unit) - } -} - /// Helper trait to convert a host function definition into its wasm signature. trait ToWasmSig { fn to_wasm_sig(&self) -> TokenStream; @@ -193,9 +179,10 @@ impl ToWasmSig for HostFn { syn::FnArg::Typed(pt) => Some(&pt.ty), _ => None, }); - let returns = match &self.item.sig.output { - syn::ReturnType::Type(_, bt) => quote! { vec![ #bt::VALUE_TYPE ] }, - _ => quote! { vec![] }, + let returns = match &self.ret_type { + HostFnReturn::U32 => quote! { vec![ ::VALUE_TYPE ] }, + HostFnReturn::ReturnCode => quote! { vec![ ::VALUE_TYPE ] }, + HostFnReturn::Unit => quote! { vec![] }, }; quote! { @@ -249,39 +236,81 @@ impl HostFn { }, _ => return Err(err()), } + // TODO: refactor errs + let err1 = |span| { + let msg = format!( + r#"Invalid host function definition. + Should return one of the following: + - Result<(), TrapReason>, + - Result, + - Result"# + ); + syn::Error::new(span, msg) + }; let ret_ty = match item.clone().sig.output { syn::ReturnType::Type(_, ty) => Ok(ty.clone()), - _ => { - let msg = "Invalid host function definition, return val is expected."; - Err(syn::Error::new(span, msg)) - }, + _ => Err(err1(span)), }?; - // TODO: refactor errs - let err1 = |span| { - let msg = - format!("Invalid host function definition: expected Result<...> return value"); - syn::Error::new(span, msg) - }; + // TODO: try sync::parse_from_str("Result<(), TrapReason") and then .eq()? match *ret_ty { syn::Type::Path(tp) => { - let tmp = &tp.path.segments.first().ok_or(err1(span))?; - let (id, span) = (tmp.ident.to_string(), tmp.ident.span()); + let result = &tp.path.segments.first().ok_or(err1(span))?; + let (id, span) = (result.ident.to_string(), result.ident.span()); if id == "Result".to_string() { - match tmp.arguments { + match &result.arguments { syn::PathArguments::AngleBracketed(group) => { - (group.args.len() == 2).then_some(42).ok_or(err1(span))?; - - (group.args.last().ok_or(err1(span))?.to_string() == "TrapReason") - .then_some(42) - .ok_or(err1(span))?; - let ret_val = group.args.first().ok_or(err1(span))?.to_string(); - let ret_type = match ret_val { + group.args.len().eq(&2).then_some(42).ok_or(err1(span))?; + + let arg2 = group.args.last().ok_or(err1(span))?; // TrapReason + + let err_ty = match arg2 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err1(arg2.span())), + }?; + + match err_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err1(arg2.span()))? + .ident + .to_string()), + _ => Err(err1(tp.span())), + }? + .eq("TrapReason") + .then_some(()) + .ok_or(err1(span))?; + + let arg1 = group.args.first().ok_or(err1(span))?; // (), u32 or ReturnCode + let ok_ty = match arg1 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err1(arg1.span())), + }?; + let ok_ty_str = match ok_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err1(arg2.span()))? + .ident + .to_string()), + syn::Type::Tuple(tt) => { + if !tt.elems.is_empty() { + return Err(err1(arg1.span())) + }; + Ok("()".to_string()) + }, + _ => Err(err1(ok_ty.span())), + }?; + + let ret_type = match ok_ty_str.as_str() { "()" => Ok(HostFnReturn::Unit), "u32" => Ok(HostFnReturn::U32), "ReturnCode" => Ok(HostFnReturn::ReturnCode), - _ => Err(err1(span)), + _ => Err(err1(arg1.span())), }?; Ok(Self { item, module, name, ret_type }) }, @@ -311,7 +340,7 @@ impl EnvDef { host_funcs.push(HostFn::try_from(i.clone())?); } - Ok(Self { item, host_funcs }) + Ok(Self { host_funcs }) } } @@ -390,9 +419,9 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { qed;" ); } - } else { quote! { whoo } } + } else { quote! { let err = "whoo!"; } } }, - _ => quote! { beee }, + _ => quote! { let err = "beee"; }, } }); @@ -434,7 +463,7 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { { #[allow(unused)] let mut args = args.iter(); - let body = || { + let mut body = || { #( #params )* #body }; diff --git a/frame/contracts/src/wasm/env_def/macros.rs b/frame/contracts/src/wasm/env_def/macros.rs deleted file mode 100644 index 1a7d4ed1efa7f..0000000000000 --- a/frame/contracts/src/wasm/env_def/macros.rs +++ /dev/null @@ -1,31 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 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. - -/// Since we can't specify the type of closure directly at binding site: -/// -/// ```nocompile -/// let f: FnOnce() -> Result<::NativeType, _> = || { /* ... */ }; -/// ``` -/// -/// we use this function to constrain the type of the closure. -#[inline(always)] -pub fn constrain_closure(f: F) -> F -where - F: FnOnce() -> Result, -{ - f -} diff --git a/frame/contracts/src/wasm/env_def/mod.rs b/frame/contracts/src/wasm/env_def/mod.rs index b4c5ffe81e7c1..a9cb01c3dbb18 100644 --- a/frame/contracts/src/wasm/env_def/mod.rs +++ b/frame/contracts/src/wasm/env_def/mod.rs @@ -21,9 +21,6 @@ use crate::exec::Ext; use sp_sandbox::Value; use wasm_instrument::parity_wasm::elements::{FunctionType, ValueType}; -#[macro_use] -pub mod macros; - pub trait ConvertibleToWasm: Sized { const VALUE_TYPE: ValueType; type NativeType; diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 271ac0a6f7ad9..823fc0e9c50f9 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -533,24 +533,24 @@ mod tests { // Define test environment for tests. We need ImportSatisfyCheck // implementation from it. So actual implementations doesn't matter. - #[define_env(Test)] - pub mod env { - fn panic(_ctx: _) { + #[define_env] + pub mod test_env { + fn panic(_ctx: _) -> Result<(), TrapReason> { unreachable!(); } // gas is an implementation defined function and a contract can't import it. - fn gas(_ctx: _, _amount: u32) { + fn gas(_ctx: _, _amount: u32) -> Result<(), TrapReason> { unreachable!(); } - fn nop(_ctx: _, _unused: u64) { + fn nop(_ctx: _, _unused: u64) -> Result<(), TrapReason> { unreachable!(); } // new version of nop with other data type for argumebt #[version(1)] - fn nop(_ctx: _, _unused: i32) { + fn nop(_ctx: _, _unused: i32) -> Result<(), TrapReason> { unreachable!(); } } @@ -572,7 +572,7 @@ mod tests { }, .. Default::default() }; - let r = do_preparation::(wasm, &schedule, ALICE); + let r = do_preparation::(wasm, &schedule, ALICE); assert_matches::assert_matches!(r.map_err(|(_, msg)| msg), $($expected)*); } }; diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index edd6f5fad59be..4a4a196223a80 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -954,7 +954,12 @@ pub mod env { /// /// Equivalent to the newer version of `seal_set_storage` with the exception of the return /// type. Still a valid thing to call when not interested in the return value. - fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> { + fn seal_set_storage( + ctx: Runtime, + key_ptr: u32, + value_ptr: u32, + value_len: u32, + ) -> Result<(), TrapReason> { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) } @@ -977,7 +982,12 @@ pub mod env { /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. #[version(1)] - fn seal_set_storage(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { + fn seal_set_storage( + ctx: Runtime, + key_ptr: u32, + value_ptr: u32, + value_len: u32, + ) -> Result { ctx.set_storage(KeyType::Fix, key_ptr, value_ptr, value_len) } @@ -1028,7 +1038,11 @@ pub mod env { /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. #[unstable] - fn seal_clear_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> Result { + fn seal_clear_storage( + ctx: Runtime, + key_ptr: u32, + key_len: u32, + ) -> Result { ctx.clear_storage(KeyType::Variable(key_len), key_ptr) } @@ -1098,7 +1112,7 @@ pub mod env { /// /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. - fn seal_contains_storage(ctx: Runtime, key_ptr: u32) -> Result { + fn seal_contains_storage(ctx: Runtime, key_ptr: u32) -> Result { ctx.contains_storage(KeyType::Fix, key_ptr) } @@ -1116,7 +1130,11 @@ pub mod env { /// Returns the size of the pre-existing value at the specified key if any. Otherwise /// `SENTINEL` is returned as a sentinel value. #[unstable] - fn seal_contains_storage(ctx: Runtime, key_ptr: u32, key_len: u32) -> Result { + fn seal_contains_storage( + ctx: Runtime, + key_ptr: u32, + key_len: u32, + ) -> Result { ctx.contains_storage(KeyType::Variable(key_len), key_ptr) } @@ -1443,7 +1461,11 @@ pub mod env { /// The value `_beneficiary_len` is ignored because the encoded sizes /// this type is fixed through `[`MaxEncodedLen`]. The field exist for backwards /// compatibility. Consider switching to the newest version of this function. - fn seal_terminate(ctx: Runtime, beneficiary_ptr: u32, _beneficiary_len: u32) -> Result<(), TrapReason> { + fn seal_terminate( + ctx: Runtime, + beneficiary_ptr: u32, + _beneficiary_len: u32, + ) -> Result<(), TrapReason> { ctx.terminate(beneficiary_ptr) } @@ -1507,7 +1529,12 @@ pub mod env { /// --- msb --- /// /// Using a reserved bit triggers a trap. - fn seal_return(ctx: Runtime, flags: u32, data_ptr: u32, data_len: u32) -> Result<(), TrapReason> { + fn seal_return( + ctx: Runtime, + flags: u32, + data_ptr: u32, + data_len: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Return(data_len))?; Err(TrapReason::Return(ReturnData { flags, @@ -1595,7 +1622,11 @@ pub mod env { /// - `out_ptr`: pointer to the linear memory where the returning value is written to. /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and /// the value length is written to. - fn seal_own_code_hash(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_own_code_hash( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; let code_hash_encoded = &ctx.ext.own_code_hash().encode(); Ok(ctx.write_sandbox_output( @@ -1628,7 +1659,11 @@ pub mod env { /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. - fn seal_address(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_address( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Address)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1652,7 +1687,12 @@ pub mod env { /// /// It is recommended to avoid specifying very small values for `gas` as the prices for a single /// gas can be smaller than one. - fn seal_weight_to_fee(ctx: Runtime, gas: u64, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_weight_to_fee( + ctx: Runtime, + gas: u64, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::WeightToFee)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1671,7 +1711,11 @@ pub mod env { /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as Gas. - fn seal_gas_left(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_gas_left( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::GasLeft)?; let gas_left = &ctx.ext.gas_meter().gas_left().encode(); Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, gas_left, false, already_charged)?) @@ -1685,7 +1729,11 @@ pub mod env { /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as T::Balance. - fn seal_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_balance( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1704,7 +1752,11 @@ pub mod env { /// space at `out_ptr` is less than the size of the value a trap is triggered. /// /// The data is encoded as T::Balance. - fn seal_value_transferred(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_value_transferred( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::ValueTransferred)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1811,7 +1863,11 @@ pub mod env { /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. /// /// The data is encoded as T::Balance. - fn seal_minimum_balance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_minimum_balance( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::MinimumBalance)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -1832,7 +1888,11 @@ pub mod env { /// # Deprecation /// /// There is no longer a tombstone deposit. This function always returns 0. - fn seal_tombstone_deposit(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_tombstone_deposit( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; let deposit = >::zero().encode(); Ok(ctx.write_sandbox_output(out_ptr, out_len_ptr, &deposit, false, already_charged)?) @@ -1892,7 +1952,7 @@ pub mod env { topics_len: u32, data_ptr: u32, data_len: u32, - ) ->Result<(), TrapReason> { + ) -> Result<(), TrapReason> { fn has_duplicates(items: &mut Vec) -> bool { // # Warning // @@ -1944,7 +2004,11 @@ pub mod env { /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. - fn seal_set_rent_allowance(ctx: Runtime, _value_ptr: u32, _value_len: u32) -> Result<(), TrapReason> { + fn seal_set_rent_allowance( + ctx: Runtime, + _value_ptr: u32, + _value_len: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::DebugMessage)?; Ok(()) } @@ -1967,7 +2031,11 @@ pub mod env { /// /// The state rent functionality was removed. This is stub only exists for /// backwards compatiblity. - fn seal_rent_allowance(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_rent_allowance( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; let rent_allowance = >::max_value().encode(); Ok(ctx.write_sandbox_output( @@ -1985,7 +2053,11 @@ pub mod env { /// `out_len_ptr` must point to a u32 value that describes the available space at /// `out_ptr`. This call overwrites it with the size of the value. If the available /// space at `out_ptr` is less than the size of the value a trap is triggered. - fn seal_block_number(ctx: Runtime, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { + fn seal_block_number( + ctx: Runtime, + out_ptr: u32, + out_len_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::BlockNumber)?; Ok(ctx.write_sandbox_output( out_ptr, @@ -2013,7 +2085,12 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_sha2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { + fn seal_hash_sha2_256( + ctx: Runtime, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashSha256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(sha2_256, input_ptr, input_len, output_ptr)?) } @@ -2035,7 +2112,12 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_keccak_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { + fn seal_hash_keccak_256( + ctx: Runtime, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashKeccak256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(keccak_256, input_ptr, input_len, output_ptr)?) } @@ -2057,7 +2139,12 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_blake2_256(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { + fn seal_hash_blake2_256( + ctx: Runtime, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashBlake256(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(blake2_256, input_ptr, input_len, output_ptr)?) } @@ -2079,7 +2166,12 @@ pub mod env { /// - `input_len`: the length of the input data in bytes. /// - `output_ptr`: the pointer into the linear memory where the output data is placed. The /// function will write the result directly into this buffer. - fn seal_hash_blake2_128(ctx: Runtime, input_ptr: u32, input_len: u32, output_ptr: u32) -> Result<(), TrapReason> { + fn seal_hash_blake2_128( + ctx: Runtime, + input_ptr: u32, + input_len: u32, + output_ptr: u32, + ) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::HashBlake128(input_len))?; Ok(ctx.compute_hash_on_intermediate_buffer(blake2_128, input_ptr, input_len, output_ptr)?) } @@ -2132,7 +2224,11 @@ pub mod env { /// not being executed as an RPC. For example, they could allow users to disable logging /// through compile time flags (cargo features) for on-chain deployment. Additionally, the /// return value of this function can be cached in order to prevent further calls at runtime. - fn seal_debug_message(ctx: Runtime, str_ptr: u32, str_len: u32) -> Result { + fn seal_debug_message( + ctx: Runtime, + str_ptr: u32, + str_len: u32, + ) -> Result { ctx.charge_gas(RuntimeCosts::DebugMessage)?; if ctx.ext.append_debug_buffer("") { let data = ctx.read_sandbox_memory(str_ptr, str_len)?; @@ -2182,7 +2278,11 @@ pub mod env { /// This function is unstable and subject to change (or removal) in the future. Do not /// deploy a contract using it to a production chain. #[unstable] - fn seal_call_runtime(ctx: Runtime, call_ptr: u32, call_len: u32) -> Result { + fn seal_call_runtime( + ctx: Runtime, + call_ptr: u32, + call_len: u32, + ) -> Result { use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight}; ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; let call: ::Call = @@ -2268,7 +2368,10 @@ pub mod env { /// # Errors /// /// `ReturnCode::CodeNotFound` - fn seal_set_code_hash(ctx: Runtime, code_hash_ptr: u32) -> Result { + fn seal_set_code_hash( + ctx: Runtime, + code_hash_ptr: u32, + ) -> Result { ctx.charge_gas(RuntimeCosts::SetCodeHash)?; let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(code_hash_ptr)?; match ctx.ext.set_code_hash(code_hash) { @@ -2296,7 +2399,11 @@ pub mod env { /// # Errors /// /// `ReturnCode::EcdsaRecoverFailed` - fn seal_ecdsa_to_eth_address(ctx: Runtime, key_ptr: u32, out_ptr: u32) -> Result { + fn seal_ecdsa_to_eth_address( + ctx: Runtime, + key_ptr: u32, + out_ptr: u32, + ) -> Result { ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; let mut compressed_key: [u8; 33] = [0; 33]; ctx.read_sandbox_memory_into_buf(key_ptr, &mut compressed_key)?; From ddfabd9ff107875f3c55234444d777a051019e72 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 1 Aug 2022 17:13:53 +0300 Subject: [PATCH 16/21] tests pass --- frame/contracts/proc-macro/src/lib.rs | 2 +- frame/contracts/src/wasm/prepare.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 7840fdf4d8118..c6c83a6d4eee7 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -327,7 +327,7 @@ impl HostFn { impl EnvDef { pub fn try_from(item: syn::ItemMod) -> syn::Result { let span = item.span(); - let err = |msg| syn::Error::new(span.clone(), msg); + let err = |msg| syn::Error::new(span, msg); let items = &item .content .as_ref() diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 823fc0e9c50f9..5512ca484c8e6 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -535,23 +535,23 @@ mod tests { // implementation from it. So actual implementations doesn't matter. #[define_env] pub mod test_env { - fn panic(_ctx: _) -> Result<(), TrapReason> { - unreachable!(); + fn panic(_ctx: crate::wasm::Runtime) -> Result<(), TrapReason> { + Ok(()) } // gas is an implementation defined function and a contract can't import it. - fn gas(_ctx: _, _amount: u32) -> Result<(), TrapReason> { - unreachable!(); - } + fn gas(_ctx: crate::wasm::Runtime, _amount: u32) -> Result<(), TrapReason> { + Ok(()) + } - fn nop(_ctx: _, _unused: u64) -> Result<(), TrapReason> { - unreachable!(); + fn nop(_ctx: crate::wasm::Runtime, _unused: u64) -> Result<(), TrapReason> { + Ok(()) } // new version of nop with other data type for argumebt #[version(1)] - fn nop(_ctx: _, _unused: i32) -> Result<(), TrapReason> { - unreachable!(); + fn nop(_ctx: crate::wasm::Runtime, _unused: i32) -> Result<(), TrapReason> { + Ok(()) } } } From b0f4e274982f6c300d91d5c5fb29e3fdd114d908 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 1 Aug 2022 19:33:49 +0300 Subject: [PATCH 17/21] refactored errors + added docs --- frame/contracts/proc-macro/src/lib.rs | 224 +++++++++++++++----------- frame/contracts/src/wasm/prepare.rs | 2 +- 2 files changed, 134 insertions(+), 92 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index c6c83a6d4eee7..c3a185d71e5df 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -15,11 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Proc macros used in the contracts module. -//! The #[define_env] attribute macro hides boilerplate of defining external environment -//! for a wasm module. +//! Procedural macroses used in the contracts module. //! -//! Most likely you should use the `define_env` macro. +//! Most likely you should use the [`#[define_env]`][`macro@define_env`] attribute macro which hides +//! boilerplate of defining external environment for a wasm module. #![no_std] @@ -159,7 +158,7 @@ struct HostFn { item: syn::ItemFn, module: String, name: String, - ret_type: HostFnReturn, + returns: HostFnReturn, } enum HostFnReturn { @@ -179,7 +178,7 @@ impl ToWasmSig for HostFn { syn::FnArg::Typed(pt) => Some(&pt.ty), _ => None, }); - let returns = match &self.ret_type { + let returns = match &self.returns { HostFnReturn::U32 => quote! { vec![ ::VALUE_TYPE ] }, HostFnReturn::ReturnCode => quote! { vec![ ::VALUE_TYPE ] }, HostFnReturn::Unit => quote! { vec![] }, @@ -202,15 +201,15 @@ impl ToTokens for HostFn { impl HostFn { pub fn try_from(item: syn::Item) -> syn::Result { - let span = item.span(); - let err = || { - let msg = "Invalid host function definition, only #[version()] or #[unstable] attribute is allowed."; + let err = |span, msg| { + let msg = format!("Invalid host function definition. {}", msg); syn::Error::new(span, msg) }; - + let msg = "only #[version()] or #[unstable] attribute is allowed."; + let span = item.span(); let item = match item { syn::Item::Fn(i_fn) => Ok(i_fn), - _ => Err(err()), + _ => Err(err(span, msg)), }?; let name = item.sig.ident.to_string(); @@ -222,105 +221,101 @@ impl HostFn { 0 => (), 1 => { let attr = &attrs[0]; - let ident = attr.path.get_ident().ok_or(err())?.to_string(); + let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string(); match ident.as_str() { "version" => { let ver: syn::LitInt = attr.parse_args()?; - module = format!("seal{}", ver.base10_parse::().map_err(|_| err())?); + module = format!( + "seal{}", + ver.base10_parse::().map_err(|_| err(span, msg))? + ); }, "unstable" => { module = "__unstable__".to_string(); }, - _ => return Err(err()), + _ => return Err(err(span, msg)), } }, - _ => return Err(err()), + _ => return Err(err(span, msg)), } - // TODO: refactor errs - let err1 = |span| { - let msg = format!( - r#"Invalid host function definition. - Should return one of the following: - - Result<(), TrapReason>, - - Result, - - Result"# - ); - syn::Error::new(span, msg) - }; + + let msg = format!( + r#"Should return one of the following: + - Result<(), TrapReason>, + - Result, + - Result"# + ); let ret_ty = match item.clone().sig.output { syn::ReturnType::Type(_, ty) => Ok(ty.clone()), - _ => Err(err1(span)), + _ => Err(err(span, &msg)), }?; - // TODO: try sync::parse_from_str("Result<(), TrapReason") and then .eq()? match *ret_ty { syn::Type::Path(tp) => { - let result = &tp.path.segments.first().ok_or(err1(span))?; + let result = &tp.path.segments.first().ok_or(err(span, &msg))?; let (id, span) = (result.ident.to_string(), result.ident.span()); - if id == "Result".to_string() { - match &result.arguments { - syn::PathArguments::AngleBracketed(group) => { - group.args.len().eq(&2).then_some(42).ok_or(err1(span))?; - - let arg2 = group.args.last().ok_or(err1(span))?; // TrapReason - - let err_ty = match arg2 { - syn::GenericArgument::Type(ty) => Ok(ty.clone()), - _ => Err(err1(arg2.span())), - }?; - - match err_ty { - syn::Type::Path(tp) => Ok(tp - .path - .segments - .first() - .ok_or(err1(arg2.span()))? - .ident - .to_string()), - _ => Err(err1(tp.span())), - }? - .eq("TrapReason") - .then_some(()) - .ok_or(err1(span))?; - - let arg1 = group.args.first().ok_or(err1(span))?; // (), u32 or ReturnCode - let ok_ty = match arg1 { - syn::GenericArgument::Type(ty) => Ok(ty.clone()), - _ => Err(err1(arg1.span())), - }?; - let ok_ty_str = match ok_ty { - syn::Type::Path(tp) => Ok(tp - .path - .segments - .first() - .ok_or(err1(arg2.span()))? - .ident - .to_string()), - syn::Type::Tuple(tt) => { - if !tt.elems.is_empty() { - return Err(err1(arg1.span())) - }; - Ok("()".to_string()) - }, - _ => Err(err1(ok_ty.span())), - }?; - - let ret_type = match ok_ty_str.as_str() { - "()" => Ok(HostFnReturn::Unit), - "u32" => Ok(HostFnReturn::U32), - "ReturnCode" => Ok(HostFnReturn::ReturnCode), - _ => Err(err1(arg1.span())), - }?; - Ok(Self { item, module, name, ret_type }) - }, - _ => Err(err1(span)), - } - } else { - Err(err1(span)) + id.eq(&"Result".to_string()).then_some(()).ok_or(err(span, &msg))?; + + match &result.arguments { + syn::PathArguments::AngleBracketed(group) => { + group.args.len().eq(&2).then_some(42).ok_or(err(span, &msg))?; + + let arg2 = group.args.last().ok_or(err(span, &msg))?; + + let err_ty = match arg2 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err(arg2.span(), &msg)), + }?; + + match err_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err(arg2.span(), &msg))? + .ident + .to_string()), + _ => Err(err(tp.span(), &msg)), + }? + .eq("TrapReason") + .then_some(()) + .ok_or(err(span, &msg))?; + + let arg1 = group.args.first().ok_or(err(span, &msg))?; + let ok_ty = match arg1 { + syn::GenericArgument::Type(ty) => Ok(ty.clone()), + _ => Err(err(arg1.span(), &msg)), + }?; + let ok_ty_str = match ok_ty { + syn::Type::Path(tp) => Ok(tp + .path + .segments + .first() + .ok_or(err(arg2.span(), &msg))? + .ident + .to_string()), + syn::Type::Tuple(tt) => { + if !tt.elems.is_empty() { + return Err(err(arg1.span(), &msg)) + }; + Ok("()".to_string()) + }, + _ => Err(err(ok_ty.span(), &msg)), + }?; + + let returns = match ok_ty_str.as_str() { + "()" => Ok(HostFnReturn::Unit), + "u32" => Ok(HostFnReturn::U32), + "ReturnCode" => Ok(HostFnReturn::ReturnCode), + _ => Err(err(arg1.span(), &msg)), + }?; + Ok(Self { item, module, name, returns }) + }, + _ => Err(err(span, &msg)), } }, - _ => Err(err1(span)), + _ => Err(err(span, &msg)), } } } @@ -425,7 +420,7 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { } }); - let outline = match &f.ret_type { + let outline = match &f.returns { HostFnReturn::Unit => quote! { body().map_err(|reason| { ctx.set_trap_reason(reason); @@ -491,6 +486,53 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { } } +/// Defines a host functions set that can be imported by contract wasm code. +/// +/// **NB**: Be advised that all functions defined by this macro +/// will panic if called with unexpected arguments. +/// +/// It's up to you as the user of this macro to check signatures of wasm code to be executed +/// and reject the code if any imported function has a mismatched signature. +/// +/// ## Example +/// +/// ```nocompile +/// #[define_env] +/// pub mod some_env { +/// fn some_host_fn(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result<(), TrapReason> { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// } +/// ``` +/// This example will expand to the `some_host_fn()` defined in the wasm module named `seal0`. +/// To define a host function in `seal1` and `__unstable__` modules, it should be annotated with the +/// appropriate attribute as follows: +/// +/// ## Example +/// +/// ```nocompile +/// #[define_env] +/// pub mod some_env { +/// #[version(1)] +/// fn some_host_fn(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// +/// #[unstable] +/// fn some_host_fn(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) +/// } +/// } +/// ``` +/// +/// Only following return types are allowed for the host functions defined with the macro: +/// - `Result<(), TrapReason>`, +/// - `Result`, +/// - `Result`. +/// +/// The macro expands to `pub struct Env` declaration, with the following traits implementations: +/// - `pallet_contracts::wasm::env_def::ImportSatisfyCheck` +/// - `pallet_contracts::wasm::env_def::FunctionImplProvider` #[proc_macro_attribute] pub fn define_env( attr: proc_macro::TokenStream, diff --git a/frame/contracts/src/wasm/prepare.rs b/frame/contracts/src/wasm/prepare.rs index 5512ca484c8e6..7b81c1c55b3bd 100644 --- a/frame/contracts/src/wasm/prepare.rs +++ b/frame/contracts/src/wasm/prepare.rs @@ -542,7 +542,7 @@ mod tests { // gas is an implementation defined function and a contract can't import it. fn gas(_ctx: crate::wasm::Runtime, _amount: u32) -> Result<(), TrapReason> { Ok(()) - } + } fn nop(_ctx: crate::wasm::Runtime, _unused: u64) -> Result<(), TrapReason> { Ok(()) From e10fc9101666997ce837539f2c5830f732474bbb Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 1 Aug 2022 19:57:05 +0300 Subject: [PATCH 18/21] merge err fixed --- frame/contracts/src/wasm/runtime.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 2154a58ce3fab..a9870731fab32 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -2214,14 +2214,12 @@ pub mod env { let env = Environment::new(ctx, id, input_ptr, input_len, output_ptr, output_len_ptr); let ret = match chain_extension.call(env)? { RetVal::Converging(val) => Ok(val), - RetVal::Diverging{flags, data} => Err(TrapReason::Return(ReturnData { - flags: flags.bits(), - data, - })), + RetVal::Diverging { flags, data } => + Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })), }; ctx.chain_extension = Some(chain_extension); ret - }, + } /// Emit a custom debug message. /// From 0bb80148bad5aa90c8c3fb7515b1bc42044215f9 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Thu, 18 Aug 2022 20:14:41 +0300 Subject: [PATCH 19/21] fixes on @ascjones review, all except moving away from `pub mod env` syntax --- frame/contracts/proc-macro/src/lib.rs | 151 ++++++++++++-------------- 1 file changed, 69 insertions(+), 82 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index c3a185d71e5df..34eafc38649ae 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -167,32 +167,6 @@ enum HostFnReturn { ReturnCode, } -/// Helper trait to convert a host function definition into its wasm signature. -trait ToWasmSig { - fn to_wasm_sig(&self) -> TokenStream; -} - -impl ToWasmSig for HostFn { - fn to_wasm_sig(&self) -> TokenStream { - let args = self.item.sig.inputs.iter().skip(1).filter_map(|a| match a { - syn::FnArg::Typed(pt) => Some(&pt.ty), - _ => None, - }); - let returns = match &self.returns { - HostFnReturn::U32 => quote! { vec![ ::VALUE_TYPE ] }, - HostFnReturn::ReturnCode => quote! { vec![ ::VALUE_TYPE ] }, - HostFnReturn::Unit => quote! { vec![] }, - }; - - quote! { - wasm_instrument::parity_wasm::elements::FunctionType::new( - vec! [ #(<#args>::VALUE_TYPE),* ], - #returns, - ) - } - } -} - impl ToTokens for HostFn { fn to_tokens(&self, tokens: &mut TokenStream) { self.item.to_tokens(tokens); @@ -213,31 +187,25 @@ impl HostFn { }?; let name = item.sig.ident.to_string(); - let mut module = "seal0".to_string(); let attrs: Vec<&syn::Attribute> = item.attrs.iter().filter(|m| !m.path.is_ident("doc")).collect(); - match attrs.len() { - 0 => (), + let module = match attrs.len() { + 0 => Ok("seal0".to_string()), 1 => { let attr = &attrs[0]; let ident = attr.path.get_ident().ok_or(err(span, msg))?.to_string(); match ident.as_str() { "version" => { let ver: syn::LitInt = attr.parse_args()?; - module = format!( - "seal{}", - ver.base10_parse::().map_err(|_| err(span, msg))? - ); - }, - "unstable" => { - module = "__unstable__".to_string(); + Ok(format!("seal{}", ver.base10_parse::().map_err(|_| err(span, msg))?)) }, - _ => return Err(err(span, msg)), + "unstable" => Ok("__unstable__".to_string()), + _ => Err(err(span, msg)), } }, - _ => return Err(err(span, msg)), - } + _ => Err(err(span, msg)), + }?; let msg = format!( r#"Should return one of the following: @@ -253,13 +221,15 @@ impl HostFn { match *ret_ty { syn::Type::Path(tp) => { - let result = &tp.path.segments.first().ok_or(err(span, &msg))?; + let result = &tp.path.segments.last().ok_or(err(span, &msg))?; let (id, span) = (result.ident.to_string(), result.ident.span()); id.eq(&"Result".to_string()).then_some(()).ok_or(err(span, &msg))?; match &result.arguments { syn::PathArguments::AngleBracketed(group) => { - group.args.len().eq(&2).then_some(42).ok_or(err(span, &msg))?; + if group.args.len() != 2 { + return Err(err(span, &msg)) + }; let arg2 = group.args.last().ok_or(err(span, &msg))?; @@ -292,7 +262,7 @@ impl HostFn { .path .segments .first() - .ok_or(err(arg2.span(), &msg))? + .ok_or(err(arg1.span(), &msg))? .ident .to_string()), syn::Type::Tuple(tt) => { @@ -318,6 +288,25 @@ impl HostFn { _ => Err(err(span, &msg)), } } + + fn to_wasm_sig(&self) -> TokenStream { + let args = self.item.sig.inputs.iter().skip(1).filter_map(|a| match a { + syn::FnArg::Typed(pt) => Some(&pt.ty), + _ => None, + }); + let returns = match &self.returns { + HostFnReturn::U32 => quote! { vec![ ::VALUE_TYPE ] }, + HostFnReturn::ReturnCode => quote! { vec![ ::VALUE_TYPE ] }, + HostFnReturn::Unit => quote! { vec![] }, + }; + + quote! { + wasm_instrument::parity_wasm::elements::FunctionType::new( + vec! [ #(<#args>::VALUE_TYPE),* ], + #returns, + ) + } + } } impl EnvDef { pub fn try_from(item: syn::ItemMod) -> syn::Result { @@ -329,11 +318,10 @@ impl EnvDef { .ok_or(err("Invalid environment definition, expected `mod` to be inlined."))? .1; - let mut host_funcs = Vec::::default(); - - for i in items.iter() { - host_funcs.push(HostFn::try_from(i.clone())?); - } + let host_funcs = items + .iter() + .map(|i| HostFn::try_from(i.clone())) + .collect::, _>>()?; Ok(Self { host_funcs }) } @@ -397,47 +385,46 @@ fn expand_can_satisfy(def: &mut EnvDef) -> proc_macro2::TokenStream { /// environment. fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { let impls = def.host_funcs.iter().map(|f| { - let params = &f.item.sig.inputs.iter().skip(1).map(|arg| { - match arg { - syn::FnArg::Typed(pt) => { - if let syn::Pat::Ident(ident) = &*pt.pat { - let p_type = &pt.ty; - let p_name = ident.ident.clone(); - quote! { - let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = - args.next() - .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) - .expect( - "precondition: all imports should be checked against the signatures of corresponding - functions defined by `#[define_env]` proc macro by the user of the macro; - thus this can never be `None`; - qed;" - ); - } - } else { quote! { let err = "whoo!"; } } - }, - _ => quote! { let err = "beee"; }, - } - }); - - let outline = match &f.returns { - HostFnReturn::Unit => quote! { + let params = &f.item.sig.inputs.iter().skip(1).map(|arg| { + match arg { + syn::FnArg::Typed(pt) => { + if let syn::Pat::Ident(ident) = &*pt.pat { + let p_type = &pt.ty; + let p_name = ident.ident.clone(); + quote! { + let #p_name : <#p_type as crate::wasm::env_def::ConvertibleToWasm>::NativeType = + args.next() + .and_then(|v| <#p_type as crate::wasm::env_def::ConvertibleToWasm>::from_typed_value(v.clone())) + .expect( + "precondition: all imports should be checked against the signatures of corresponding + functions defined by `#[define_env]` proc macro by the user of the macro; + thus this can never be `None`; + qed;" + ); + } + } else { quote! { let err = "whoo!"; } } + }, + _ => quote! { let err = "beee"; }, + } + }); + + let outline = match &f.returns { + HostFnReturn::Unit => quote! { body().map_err(|reason| { ctx.set_trap_reason(reason); sp_sandbox::HostError })?; return Ok(sp_sandbox::ReturnValue::Unit); }, - - _ => quote! { - let r = body().map_err(|reason| { - ctx.set_trap_reason(reason); - sp_sandbox::HostError - })?; + _ => quote! { + let r = body().map_err(|reason| { + ctx.set_trap_reason(reason); + sp_sandbox::HostError + })?; return Ok(sp_sandbox::ReturnValue::Value({ r.to_typed_value() })); - }, + }, }; let params = params.clone(); let (module, name, ident, body) = (&f.module, &f.name, &f.item.sig.ident, &f.item.block); @@ -446,7 +433,7 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { _ => quote! { }, }; quote! { - #unstable_feat + #unstable_feat f(#module.as_bytes(), #name.as_bytes(), { fn #ident( ctx: &mut crate::wasm::Runtime, @@ -513,12 +500,12 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { /// ```nocompile /// #[define_env] /// pub mod some_env { -/// #[version(1)] +/// #[version(1)] /// fn some_host_fn(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { /// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) /// } /// -/// #[unstable] +/// #[unstable] /// fn some_host_fn(ctx: Runtime, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { /// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) /// } From 37c6c2efbf790f90b5d772ab14d83bab5ec83e2c Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 22 Aug 2022 13:02:22 +0300 Subject: [PATCH 20/21] debug printing cleared --- frame/contracts/proc-macro/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 34eafc38649ae..345f691cfd522 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -402,9 +402,9 @@ fn expand_impls(def: &mut EnvDef) -> proc_macro2::TokenStream { qed;" ); } - } else { quote! { let err = "whoo!"; } } + } else { quote! { } } }, - _ => quote! { let err = "beee"; }, + _ => quote! { }, } }); From 4d25569d92d27c52ac8e1ce3ae4b82e1a3242815 Mon Sep 17 00:00:00 2001 From: Alexander Gryaznov Date: Mon, 22 Aug 2022 15:39:45 +0300 Subject: [PATCH 21/21] clippy fix --- frame/contracts/proc-macro/src/lib.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index 345f691cfd522..65c13bb1fc607 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -207,12 +207,10 @@ impl HostFn { _ => Err(err(span, msg)), }?; - let msg = format!( - r#"Should return one of the following: - - Result<(), TrapReason>, - - Result, - - Result"# - ); + let msg = r#"Should return one of the following: + - Result<(), TrapReason>, + - Result, + - Result"#; let ret_ty = match item.clone().sig.output { syn::ReturnType::Type(_, ty) => Ok(ty.clone()),