diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dde2a10276..9642390a35 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ env: # We plan to fully support RISC-V as a bytecode for contracts soon. # RISC-V does not have a standard library in contrast to Wasm. Compiling against # this target also makes sure that we don't pull in `std` by accident (through dependencies). - # RISC-V is a modular archtitecture. We might switch to a different flavor with more features + # RISC-V is a modular architecture. We might switch to a different flavor with more features # later. For example, `riscv32imc-unknown-none-elf`. RISCV_TARGET: riscv32i-unknown-none-elf diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0a27c30ab8..f1e4fec3c7 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -153,8 +153,8 @@ If you look at the implementations you'll see a common pattern of for contract developers. ### The pallet API -The function signature of the `pallet-contracts` API functions is defined in -[`ink_env/src/engine/on_chain/ext.rs`](https://github.com/paritytech/ink/blob/master/crates/env/src/engine/on_chain/ext.rs). +Signatures of host API functions are defined in +[`pallet-contracts-uapi`](https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/contracts/uapi/src/host/wasm32.rs). You'll see that we import different versions of API functions, something like the following excerpt: diff --git a/Cargo.lock b/Cargo.lock index 89fac01484..be1795ca8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2542,6 +2542,7 @@ name = "ink" version = "5.0.0-rc" dependencies = [ "derive_more", + "ink-pallet-contracts-uapi", "ink_env 5.0.0-rc", "ink_ir", "ink_macro", @@ -2554,6 +2555,17 @@ dependencies = [ "trybuild", ] +[[package]] +name = "ink-pallet-contracts-uapi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd3e608f5410d03e529145875eb736305e0d7cae4b989faf54f932eff31bc048" +dependencies = [ + "bitflags 1.3.2", + "paste", + "polkavm-derive", +] + [[package]] name = "ink_allocator" version = "5.0.0-rc" @@ -2646,6 +2658,7 @@ version = "5.0.0-rc" dependencies = [ "blake2", "derive_more", + "ink-pallet-contracts-uapi", "ink_primitives 5.0.0-rc", "parity-scale-codec", "secp256k1 0.28.0", @@ -2677,6 +2690,7 @@ dependencies = [ "const_env", "derive_more", "ink", + "ink-pallet-contracts-uapi", "ink_allocator 5.0.0-rc", "ink_engine 5.0.0-rc", "ink_prelude 5.0.0-rc", @@ -2842,6 +2856,7 @@ dependencies = [ "cfg-if", "derive_more", "ink", + "ink-pallet-contracts-uapi", "ink_env 5.0.0-rc", "ink_metadata 5.0.0-rc", "ink_prelude 5.0.0-rc", @@ -3881,6 +3896,34 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +[[package]] +name = "polkavm-common" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecd2caacfc4a7ee34243758dd7348859e6dec73f5e5df059890f5742ee46f0e" + +[[package]] +name = "polkavm-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db65a500d4adf574893c726ae365e37e4fbb7f2cbd403f6eaa1b665457456adc" +dependencies = [ + "polkavm-derive-impl", + "syn 2.0.46", +] + +[[package]] +name = "polkavm-derive-impl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c99f4e7a9ff434ef9c885b874c99d824c3a5693bf5e3e8569bb1d2245a8c1b7f" +dependencies = [ + "polkavm-common", + "proc-macro2", + "quote", + "syn 2.0.46", +] + [[package]] name = "polling" version = "2.8.0" diff --git a/Cargo.toml b/Cargo.toml index 69860cb885..649da09d91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,6 +84,7 @@ const_env = { version = "0.1"} # Substrate dependencies pallet-contracts-primitives = { version = "26.0.0", default-features = false } +pallet-contracts-uapi = { package = "ink-pallet-contracts-uapi", version = "6.0.0", default-features = false } sp-core = { version = "23.0.0", default-features = false } sp-keyring = { version = "26.0.0", default-features = false } sp-runtime = { version = "26.0.0", default-features = false } diff --git a/crates/engine/Cargo.toml b/crates/engine/Cargo.toml index 9e6876bb5e..7bf3c1886b 100644 --- a/crates/engine/Cargo.toml +++ b/crates/engine/Cargo.toml @@ -17,6 +17,7 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] [dependencies] ink_primitives = { workspace = true } scale = { workspace = true } +pallet-contracts-uapi = { workspace = true } derive_more = { workspace = true, features = ["from", "display"] } sha2 = { workspace = true } diff --git a/crates/engine/src/ext.rs b/crates/engine/src/ext.rs index 475d5fa128..0e53d5c485 100644 --- a/crates/engine/src/ext.rs +++ b/crates/engine/src/ext.rs @@ -31,85 +31,10 @@ use crate::{ BlockTimestamp, }, }; +pub use pallet_contracts_uapi::ReturnErrorCode as Error; use scale::Encode; use std::panic::panic_any; -macro_rules! define_error_codes { - ( - $( - $( #[$attr:meta] )* - $name:ident = $discr:literal, - )* - ) => { - /// Every error that can be returned to a contract when it calls any of the host functions. - #[cfg_attr(test, derive(PartialEq, Eq))] - #[derive(Debug)] - #[repr(u32)] - pub enum Error { - $( - $( #[$attr] )* - $name = $discr, - )* - /// Returns if an unknown error was received from the host module. - Unknown, - } - - impl From for Result<(), Error> { - #[inline] - fn from(return_code: ReturnCode) -> Self { - match return_code.0 { - 0 => Ok(()), - $( - $discr => Err(Error::$name), - )* - _ => Err(Error::Unknown), - } - } - } - }; -} -define_error_codes! { - /// The called function trapped and has its state changes reverted. - /// In this case no output buffer is returned. - /// Can only be returned from `call` and `instantiate`. - CalleeTrapped = 1, - /// The called function ran to completion but decided to revert its state. - /// An output buffer is returned when one was supplied. - /// Can only be returned from `call` and `instantiate`. - CalleeReverted = 2, - /// The passed key does not exist in storage. - KeyNotFound = 3, - /// Deprecated and no longer returned: There is only the minimum balance. - _BelowSubsistenceThreshold = 4, - /// Transfer failed for other not further specified reason. Most probably - /// reserved or locked balance of the sender that was preventing the transfer. - TransferFailed = 5, - /// Deprecated and no longer returned: Endowment is no longer required. - _EndowmentTooLow = 6, - /// No code could be found at the supplied code hash. - CodeNotFound = 7, - /// The account that was called is no contract. - NotCallable = 8, - /// The call to `debug_message` had no effect because debug message - /// recording was disabled. - LoggingDisabled = 9, - /// ECDSA public key recovery failed. Most probably wrong recovery id or signature. - EcdsaRecoveryFailed = 11, - /// sr25519 signature verification failed. This may be because of an invalid public key, invalid message or invalid signature. - Sr25519VerifyFailed = 12, -} - -/// The raw return code returned by the host side. -#[repr(transparent)] -pub struct ReturnCode(u32); - -impl ReturnCode { - /// Returns the raw underlying `u32` representation. - pub fn into_u32(self) -> u32 { - self.0 - } -} - /// The off-chain engine. pub struct Engine { /// The environment database. diff --git a/crates/env/Cargo.toml b/crates/env/Cargo.toml index 568b6eb16e..969753946e 100644 --- a/crates/env/Cargo.toml +++ b/crates/env/Cargo.toml @@ -20,6 +20,7 @@ ink_allocator = { workspace = true } ink_storage_traits = { workspace = true } ink_prelude = { workspace = true } ink_primitives = { workspace = true } +pallet-contracts-uapi = { workspace = true } scale = { workspace = true } derive_more = { workspace = true, features = ["from", "display"] } diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index c0880cc9e8..6168240818 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -17,7 +17,6 @@ use crate::{ backend::{ EnvBackend, - ReturnFlags, TypedEnvBackend, }, call::{ @@ -42,6 +41,7 @@ use crate::{ Result, }; use ink_storage_traits::Storable; +use pallet_contracts_uapi::ReturnFlags; /// Returns the address of the caller of the executed contract. /// diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 880e7c1da0..d5da075d80 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -30,142 +30,7 @@ use crate::{ Result, }; use ink_storage_traits::Storable; - -/// The flags to indicate further information about the end of a contract execution. -#[derive(Default)] -pub struct ReturnFlags { - value: u32, -} - -impl ReturnFlags { - /// Initialize [`ReturnFlags`] with the reverted flag. - pub fn new_with_reverted(has_reverted: bool) -> Self { - Self::default().set_reverted(has_reverted) - } - - /// Sets the bit to indicate that the execution is going to be reverted. - #[must_use] - pub fn set_reverted(mut self, has_reverted: bool) -> Self { - match has_reverted { - true => self.value |= has_reverted as u32, - false => self.value &= !has_reverted as u32, - } - self - } - - /// Returns the underlying `u32` representation. - #[cfg(not(feature = "std"))] - pub(crate) fn into_u32(self) -> u32 { - self.value - } -} - -/// The flags used to change the behavior of a contract call. -#[must_use] -#[derive(Copy, Clone, Debug, Default)] -pub struct CallFlags { - forward_input: bool, - clone_input: bool, - tail_call: bool, - allow_reentry: bool, -} - -impl CallFlags { - /// Forwards the input for the current function to the callee. - /// - /// # Note - /// - /// A forwarding call will consume the current contracts input. Any attempt to - /// access the input after this call returns (e.g. by trying another forwarding call) - /// will lead to a contract revert. - /// Consider using [`Self::set_clone_input`] in order to preserve the input. - pub const fn set_forward_input(mut self, forward_input: bool) -> Self { - self.forward_input = forward_input; - self - } - - /// Identical to [`Self::set_forward_input`] but without consuming the input. - /// - /// This adds some additional weight costs to the call. - /// - /// # Note - /// - /// This implies [`Self::set_forward_input`] and takes precedence when both are set. - pub const fn set_clone_input(mut self, clone_input: bool) -> Self { - self.clone_input = clone_input; - self - } - - /// Do not return from the call but rather return the result of the callee to the - /// callers caller. - /// - /// # Note - /// - /// This makes the current contract completely transparent to its caller by replacing - /// this contracts potential output with the callee ones. Any code after the contract - /// calls has been invoked can be safely considered unreachable. - pub const fn set_tail_call(mut self, tail_call: bool) -> Self { - self.tail_call = tail_call; - self - } - - /// Allow the callee to reenter into the current contract. - /// - /// Without this flag any reentrancy into the current contract that originates from - /// the callee (or any of its callees) is denied. This includes the first callee: - /// You cannot call into yourself with this flag set. - pub const fn set_allow_reentry(mut self, allow_reentry: bool) -> Self { - self.allow_reentry = allow_reentry; - self - } - - /// Returns the underlying `u32` representation of the call flags. - /// - /// This value is used to forward the call flag information to the - /// `contracts` pallet. - pub(crate) const fn into_u32(self) -> u32 { - self.forward_input as u32 - | ((self.clone_input as u32) << 1) - | ((self.tail_call as u32) << 2) - | ((self.allow_reentry as u32) << 3) - } - - /// Returns `true` if input forwarding is set. - /// - /// # Note - /// - /// See [`Self::set_forward_input`] for more information. - pub const fn forward_input(&self) -> bool { - self.forward_input - } - - /// Returns `true` if input cloning is set. - /// - /// # Note - /// - /// See [`Self::set_clone_input`] for more information. - pub const fn clone_input(&self) -> bool { - self.clone_input - } - - /// Returns `true` if the tail call property is set. - /// - /// # Note - /// - /// See [`Self::set_tail_call`] for more information. - pub const fn tail_call(&self) -> bool { - self.tail_call - } - - /// Returns `true` if call reentry is allowed. - /// - /// # Note - /// - /// See [`Self::set_allow_reentry`] for more information. - pub const fn allow_reentry(&self) -> bool { - self.allow_reentry - } -} +pub use pallet_contracts_uapi::ReturnFlags; /// Environmental contract functionality that does not require `Environment`. pub trait EnvBackend { diff --git a/crates/env/src/call/call_builder.rs b/crates/env/src/call/call_builder.rs index c5e803a25d..55d98219bb 100644 --- a/crates/env/src/call/call_builder.rs +++ b/crates/env/src/call/call_builder.rs @@ -13,7 +13,6 @@ // limitations under the License. use crate::{ - backend::CallFlags, call::{ utils::{ EmptyArgumentList, @@ -29,6 +28,7 @@ use crate::{ }; use core::marker::PhantomData; use num_traits::Zero; +use pallet_contracts_uapi::CallFlags; /// The final parameters to the cross-contract call. #[derive(Debug)] @@ -328,7 +328,7 @@ where { CallBuilder { call_type: Default::default(), - call_flags: Default::default(), + call_flags: CallFlags::empty(), exec_input: Default::default(), return_type: Default::default(), _phantom: Default::default(), diff --git a/crates/env/src/engine/mod.rs b/crates/env/src/engine/mod.rs index 3d94f99678..52065b413b 100644 --- a/crates/env/src/engine/mod.rs +++ b/crates/env/src/engine/mod.rs @@ -21,7 +21,6 @@ use crate::{ ConstructorReturnType, FromAccountId, }, - Error as EnvError, Error, Result as EnvResult, }; @@ -31,6 +30,8 @@ use ink_primitives::{ LangError, }; +use pallet_contracts_uapi::ReturnErrorCode; + /// Convert a slice into an array reference. /// /// Creates an array reference of size `$len` pointing to `$offset` within `$arr`. @@ -89,7 +90,7 @@ where let output = >::ok(contract_ref); Ok(Ok(output)) } - Err(EnvError::CalleeReverted) => { + Err(Error::ReturnError(ReturnErrorCode::CalleeReverted)) => { decode_instantiate_err::(out_return_value) } Err(actual_error) => Err(actual_error), @@ -150,7 +151,6 @@ mod decode_instantiate_result_tests { use crate::{ DefaultEnvironment, Environment, - Error, }; use scale::Encode; @@ -193,7 +193,11 @@ mod decode_instantiate_result_tests { DefaultEnvironment, TestContractRef, Result, - >(Err(Error::CalleeReverted), out_address, out_return_value) + >( + Err(ReturnErrorCode::CalleeReverted.into()), + out_address, + out_return_value, + ) } #[test] diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index d0757d1781..df1a332102 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -37,19 +37,18 @@ use crate::{ Clear, EnvBackend, Environment, - Error, Result, - ReturnFlags, TypedEnvBackend, }; -use ink_engine::{ - ext, - ext::Engine, -}; +use ink_engine::ext::Engine; use ink_storage_traits::{ decode_all, Storable, }; +use pallet_contracts_uapi::{ + ReturnErrorCode, + ReturnFlags, +}; use schnorrkel::{ PublicKey, Signature, @@ -108,25 +107,6 @@ impl CryptoHash for Keccak256 { } } -impl From for crate::Error { - fn from(ext_error: ext::Error) -> Self { - match ext_error { - ext::Error::Unknown => Self::Unknown, - ext::Error::CalleeTrapped => Self::CalleeTrapped, - ext::Error::CalleeReverted => Self::CalleeReverted, - ext::Error::KeyNotFound => Self::KeyNotFound, - ext::Error::_BelowSubsistenceThreshold => Self::_BelowSubsistenceThreshold, - ext::Error::TransferFailed => Self::TransferFailed, - ext::Error::_EndowmentTooLow => Self::_EndowmentTooLow, - ext::Error::CodeNotFound => Self::CodeNotFound, - ext::Error::NotCallable => Self::NotCallable, - ext::Error::LoggingDisabled => Self::LoggingDisabled, - ext::Error::EcdsaRecoveryFailed => Self::EcdsaRecoveryFailed, - ext::Error::Sr25519VerifyFailed => Self::Sr25519VerifyFailed, - } - } -} - #[derive(Default)] pub struct TopicsBuilder { pub topics: Vec>, @@ -210,7 +190,7 @@ impl EnvBackend for EnvInstance { let decoded = decode_all(&mut &res[..])?; Ok(Some(decoded)) } - Err(ext::Error::KeyNotFound) => Ok(None), + Err(ReturnErrorCode::KeyNotFound) => Ok(None), Err(_) => panic!("encountered unexpected error"), } } @@ -225,7 +205,7 @@ impl EnvBackend for EnvInstance { let decoded = decode_all(&mut &output[..])?; Ok(Some(decoded)) } - Err(ext::Error::KeyNotFound) => Ok(None), + Err(ReturnErrorCode::KeyNotFound) => Ok(None), Err(_) => panic!("encountered unexpected error"), } } @@ -316,7 +296,7 @@ impl EnvBackend for EnvInstance { *output = pub_key.serialize(); Ok(()) } - Err(_) => Err(Error::EcdsaRecoveryFailed), + Err(_) => Err(ReturnErrorCode::EcdsaRecoveryFailed.into()), } } @@ -326,7 +306,7 @@ impl EnvBackend for EnvInstance { output: &mut [u8; 20], ) -> Result<()> { let pk = secp256k1::PublicKey::from_slice(pubkey) - .map_err(|_| Error::EcdsaRecoveryFailed)?; + .map_err(|_| ReturnErrorCode::EcdsaRecoveryFailed)?; let uncompressed = pk.serialize_uncompressed(); let mut hash = ::Type::default(); ::hash(&uncompressed[1..], &mut hash); @@ -345,15 +325,15 @@ impl EnvBackend for EnvInstance { // https://github.com/paritytech/substrate/blob/c32f5ed2ae6746d6f791f08cecbfc22fa188f5f9/primitives/core/src/sr25519.rs#L60 let context = b"substrate"; // attempt to parse a signature from bytes - let signature: Signature = - Signature::from_bytes(signature).map_err(|_| Error::Sr25519VerifyFailed)?; + let signature: Signature = Signature::from_bytes(signature) + .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?; // attempt to parse a public key from bytes - let public_key: PublicKey = - PublicKey::from_bytes(pub_key).map_err(|_| Error::Sr25519VerifyFailed)?; + let public_key: PublicKey = PublicKey::from_bytes(pub_key) + .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?; // verify the signature public_key .verify_simple(context, message, &signature) - .map_err(|_| Error::Sr25519VerifyFailed) + .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed.into()) } fn call_chain_extension( @@ -458,18 +438,13 @@ impl TypedEnvBackend for EnvInstance { fn invoke_contract( &mut self, - params: &CallParams, Args, R>, + _params: &CallParams, Args, R>, ) -> Result> where E: Environment, Args: scale::Encode, R: scale::Decode, { - let _gas_limit = params.gas_limit(); - let _callee = params.callee(); - let _call_flags = params.call_flags().into_u32(); - let _transferred_value = params.transferred_value(); - let _input = params.exec_input(); unimplemented!("off-chain environment does not support contract invocation") } diff --git a/crates/env/src/engine/on_chain/ext.rs b/crates/env/src/engine/on_chain/ext.rs deleted file mode 100644 index 3d30ce02d3..0000000000 --- a/crates/env/src/engine/on_chain/ext.rs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! External C API to communicate with substrate contracts runtime module. -//! -//! Refer to substrate FRAME contract module for more documentation. - -use core::marker::PhantomData; -use scale::Encode; - -cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - mod wasm32; - pub use wasm32::*; - } else if #[cfg(target_arch = "riscv32")] { - mod riscv32; - pub use riscv32::*; - } -} - -macro_rules! define_error_codes { - ( - $( - $( #[$attr:meta] )* - $name:ident = $discr:literal, - )* - ) => { - /// Every error that can be returned to a contract when it calls any of the host functions. - #[repr(u32)] - pub enum Error { - $( - $( #[$attr] )* - $name = $discr, - )* - /// Returns if an unknown error was received from the host module. - Unknown, - } - - impl From for Result { - #[inline] - fn from(return_code: ReturnCode) -> Self { - match return_code.0 { - 0 => Ok(()), - $( - $discr => Err(Error::$name), - )* - _ => Err(Error::Unknown), - } - } - } - }; -} -define_error_codes! { - /// The called function trapped and has its state changes reverted. - /// In this case no output buffer is returned. - /// Can only be returned from `call` and `instantiate`. - CalleeTrapped = 1, - /// The called function ran to completion but decided to revert its state. - /// An output buffer is returned when one was supplied. - /// Can only be returned from `call` and `instantiate`. - CalleeReverted = 2, - /// The passed key does not exist in storage. - KeyNotFound = 3, - /// Deprecated and no longer returned: There is only the minimum balance. - _BelowSubsistenceThreshold = 4, - /// Transfer failed for other not further specified reason. Most probably - /// reserved or locked balance of the sender that was preventing the transfer. - TransferFailed = 5, - /// Deprecated and no longer returned: Endowment is no longer required. - _EndowmentTooLow = 6, - /// No code could be found at the supplied code hash. - CodeNotFound = 7, - /// The account that was called is no contract. - NotCallable = 8, - /// The call to `debug_message` had no effect because debug message - /// recording was disabled. - LoggingDisabled = 9, - /// The call dispatched by `call_runtime` was executed but returned an error. - CallRuntimeFailed = 10, - /// ECDSA public key recovery failed. Most probably wrong recovery id or signature. - EcdsaRecoveryFailed = 11, -} - -/// Thin-wrapper around a `u32` representing a pointer for Wasm32. -/// -/// Only for shared references. -/// -/// # Note -/// -/// Can only be constructed from shared reference types and encapsulates the -/// conversion from reference to raw `u32`. -/// Does not allow accessing the internal `u32` value. -#[derive(Debug, Encode)] -#[repr(transparent)] -pub struct Ptr32<'a, T> -where - T: ?Sized, -{ - /// The internal Wasm32 raw pointer value. - /// - /// Must not be readable or directly usable by any safe Rust code. - _value: u32, - /// We handle types like these as if the associated lifetime was exclusive. - marker: PhantomData &'a T>, -} - -impl<'a, T> Ptr32<'a, T> -where - T: ?Sized, -{ - /// Creates a new Wasm32 pointer for the given raw pointer value. - fn new(value: u32) -> Self { - Self { - _value: value, - marker: Default::default(), - } - } -} - -impl<'a, T> Ptr32<'a, [T]> { - /// Creates a new Wasm32 pointer from the given shared slice. - pub fn from_slice(slice: &'a [T]) -> Self { - Self::new(slice.as_ptr() as u32) - } -} - -/// Thin-wrapper around a `u32` representing a pointer for Wasm32. -/// -/// Only for exclusive references. -/// -/// # Note -/// -/// Can only be constructed from exclusive reference types and encapsulates the -/// conversion from reference to raw `u32`. -/// Does not allow accessing the internal `u32` value. -#[derive(Debug, Encode)] -#[repr(transparent)] -pub struct Ptr32Mut<'a, T> -where - T: ?Sized, -{ - /// The internal Wasm32 raw pointer value. - /// - /// Must not be readable or directly usable by any safe Rust code. - _value: u32, - /// We handle types like these as if the associated lifetime was exclusive. - marker: PhantomData &'a mut T>, -} - -impl<'a, T> Ptr32Mut<'a, T> -where - T: ?Sized, -{ - /// Creates a new Wasm32 pointer for the given raw pointer value. - fn new(value: u32) -> Self { - Self { - _value: value, - marker: Default::default(), - } - } -} - -impl<'a, T> Ptr32Mut<'a, [T]> { - /// Creates a new Wasm32 pointer from the given exclusive slice. - pub fn from_slice(slice: &'a mut [T]) -> Self { - Self::new(slice.as_ptr() as u32) - } -} - -impl<'a, T> Ptr32Mut<'a, T> -where - T: Sized, -{ - /// Creates a new Wasm32 pointer from the given exclusive reference. - pub fn from_ref(a_ref: &'a mut T) -> Self { - let a_ptr: *mut T = a_ref; - Self::new(a_ptr as u32) - } -} - -/// The raw return code returned by the host side. -#[repr(transparent)] -pub struct ReturnCode(u32); - -impl From for Option { - fn from(code: ReturnCode) -> Self { - /// Used as a sentinel value when reading and writing contract memory. - /// - /// We use this value to signal `None` to a contract when only a primitive is - /// allowed and we don't want to go through encoding a full Rust type. - /// Using `u32::Max` is a safe sentinel because contracts are never - /// allowed to use such a large amount of resources. So this value doesn't - /// make sense for a memory location or length. - const SENTINEL: u32 = u32::MAX; - - (code.0 < SENTINEL).then_some(code.0) - } -} - -impl ReturnCode { - /// Returns the raw underlying `u32` representation. - pub fn into_u32(self) -> u32 { - self.0 - } - /// Returns the underlying `u32` converted into `bool`. - pub fn into_bool(self) -> bool { - self.0.ne(&0) - } -} - -type Result = core::result::Result<(), Error>; - -#[inline(always)] -fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { - debug_assert!(new_len <= output.len()); - let tmp = core::mem::take(output); - *output = &mut tmp[..new_len]; -} diff --git a/crates/env/src/engine/on_chain/ext/riscv32.rs b/crates/env/src/engine/on_chain/ext/riscv32.rs deleted file mode 100644 index 90809da9c1..0000000000 --- a/crates/env/src/engine/on_chain/ext/riscv32.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - extract_from_slice, - Ptr32, - Ptr32Mut, - Result, - ReturnCode, -}; -use crate::ReturnFlags; -use scale::Encode; - -// TODO: Remove the constant and use the real func ids. -const FUNC_ID: u32 = 0; - -mod sys { - use super::{ - Ptr32, - ReturnCode, - }; - use core::arch::asm; - - fn ecall(mut a0: u32, a1: u32) -> u32 { - unsafe { - asm!( - "ecall", - inout("a0") a0, - in("a1") a1, - ); - } - a0 - } - - fn ecall0(mut a0: u32) -> u32 { - unsafe { - asm!( - "ecall", - inout("a0") a0, - ); - } - a0 - } - - pub fn call(func_id: u32, in_ptr: Ptr32<[u8]>) -> ReturnCode { - ReturnCode(ecall(func_id, in_ptr._value)) - } - - pub fn call0(func_id: u32) -> ReturnCode { - ReturnCode(ecall0(func_id)) - } -} - -pub fn instantiate( - code_hash: &[u8], - gas_limit: u64, - endowment: &[u8], - input: &[u8], - out_address: &mut &mut [u8], - out_return_value: &mut &mut [u8], - salt: &[u8], -) -> Result { - let mut address_len = out_address.len() as u32; - let mut return_value_len = out_return_value.len() as u32; - let ret_code = ( - Ptr32::from_slice(code_hash), - gas_limit, - Ptr32::from_slice(endowment), - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(out_address), - Ptr32Mut::from_ref(&mut address_len), - Ptr32Mut::from_slice(out_return_value), - Ptr32Mut::from_ref(&mut return_value_len), - Ptr32::from_slice(salt), - salt.len() as u32, - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - extract_from_slice(out_address, address_len as usize); - extract_from_slice(out_return_value, return_value_len as usize); - ret_code.into() -} - -pub fn call( - flags: u32, - callee: &[u8], - gas_limit: u64, - value: &[u8], - input: &[u8], - output: &mut &mut [u8], -) -> Result { - let mut output_len = output.len() as u32; - let ret_code = ( - flags, - Ptr32::from_slice(callee), - gas_limit, - Ptr32::from_slice(value), - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -pub fn delegate_call( - flags: u32, - code_hash: &[u8], - input: &[u8], - output: &mut &mut [u8], -) -> Result { - let mut output_len = output.len() as u32; - let ret_code = ( - flags, - Ptr32::from_slice(code_hash), - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -pub fn transfer(account_id: &[u8], value: &[u8]) -> Result { - let ret_code = ( - Ptr32::from_slice(account_id), - account_id.len() as u32, - Ptr32::from_slice(value), - value.len() as u32, - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -pub fn deposit_event(topics: &[u8], data: &[u8]) { - ( - Ptr32::from_slice(topics), - topics.len() as u32, - Ptr32::from_slice(data), - data.len() as u32, - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); -} - -pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { - let ret_code = ( - Ptr32::from_slice(key), - key.len() as u32, - Ptr32::from_slice(encoded_value), - encoded_value.len() as u32, - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -pub fn clear_storage(key: &[u8]) -> Option { - let ret_code = (Ptr32::from_slice(key), key.len() as u32) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_code = ( - Ptr32::from_slice(key), - key.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -pub fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_code = ( - Ptr32::from_slice(key), - key.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -pub fn storage_contains(key: &[u8]) -> Option { - let ret_code = (Ptr32::from_slice(key), key.len() as u32) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -pub fn terminate(beneficiary: &[u8]) -> ! { - (Ptr32::from_slice(beneficiary)) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - unsafe { - core::hint::unreachable_unchecked(); - } -} - -pub fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 { - let mut output_len = output.len() as u32; - let ret_code = ( - func_id, - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - extract_from_slice(output, output_len as usize); - ret_code.into_u32() -} - -pub fn input(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - ( - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - extract_from_slice(output, output_len as usize); -} - -pub fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - ( - flags.into_u32(), - Ptr32::from_slice(return_value), - return_value.len() as u32, - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - unsafe { - core::hint::unreachable_unchecked(); - } -} - -pub fn call_runtime(call: &[u8]) -> Result { - let ret_code = (Ptr32::from_slice(call), call.len() as u32) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -macro_rules! impl_wrapper_for { - ( $( $name:ident, )* ) => { - $( - - pub fn $name(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - ( - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ).using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - } - )* - } -} -impl_wrapper_for! { - caller, - block_number, - address, - balance, - gas_left, - value_transferred, - now, - minimum_balance, -} - -pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - { - ( - gas, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - } - extract_from_slice(output, output_len as usize); -} - -#[cfg(feature = "ink-debug")] -/// Call `debug_message` with the supplied UTF-8 encoded message. -/// -/// If debug message recording is disabled in the contracts pallet, the first call will -/// return a `LoggingDisabled` error, and further calls will be a no-op to avoid the cost -/// of calling into the supervisor. -/// -/// # Note -/// -/// This depends on the `debug_message` interface which requires the -/// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. -pub fn debug_message(message: &str) { - static mut DEBUG_ENABLED: bool = false; - static mut FIRST_RUN: bool = true; - - // SAFETY: safe because executing in a single threaded context - // We need those two variables in order to make sure that the assignment is performed - // in the "logging enabled" case. This is because during RPC execution logging might - // be enabled while it is disabled during the actual execution as part of a - // transaction. The gas estimation takes place during RPC execution. We want to - // overestimate instead of underestimate gas usage. Otherwise using this estimate - // could lead to a out of gas error. - if unsafe { DEBUG_ENABLED || FIRST_RUN } { - let bytes = message.as_bytes(); - let ret_code = (Ptr32::from_slice(bytes), bytes.len() as u32) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - if !matches!(ret_code.into(), Err(super::Error::LoggingDisabled)) { - // SAFETY: safe because executing in a single threaded context - unsafe { DEBUG_ENABLED = true } - } - // SAFETY: safe because executing in a single threaded context - unsafe { FIRST_RUN = false } - } -} - -#[cfg(not(feature = "ink-debug"))] -/// A no-op. Enable the `ink-debug` feature for debug messages. -pub fn debug_message(_message: &str) {} - -macro_rules! impl_hash_fn { - ( $name:ident, $bytes_result:literal ) => { - paste::item! { - pub fn [](input: &[u8], output: &mut [u8; $bytes_result]) { - ( - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - ).using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - } - } - }; -} -impl_hash_fn!(sha2_256, 32); -impl_hash_fn!(keccak_256, 32); -impl_hash_fn!(blake2_256, 32); -impl_hash_fn!(blake2_128, 16); - -pub fn ecdsa_recover( - signature: &[u8; 65], - message_hash: &[u8; 32], - output: &mut [u8; 33], -) -> Result { - let ret_code = ( - Ptr32::from_slice(signature), - Ptr32::from_slice(message_hash), - Ptr32Mut::from_slice(output), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { - let ret_code = (Ptr32::from_slice(pubkey), Ptr32Mut::from_slice(output)) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -/// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces), -/// which is unsafe and normally is not available on production chains. -pub fn sr25519_verify( - signature: &[u8; 64], - message: &[u8], - pub_key: &[u8; 32], -) -> Result { - let ret_code = ( - Ptr32::from_slice(signature), - Ptr32::from_slice(pub_key), - message.len() as u32, - Ptr32::from_slice(message), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_code.into() -} - -pub fn is_contract(account_id: &[u8]) -> bool { - let ret_val = sys::call(FUNC_ID, Ptr32::from_slice(account_id)); - ret_val.into_bool() -} - -pub fn caller_is_origin() -> bool { - let ret_val = sys::call0(FUNC_ID); - ret_val.into_bool() -} - -pub fn set_code_hash(code_hash: &[u8]) -> Result { - let ret_val = sys::call(FUNC_ID, Ptr32::from_slice(code_hash)); - ret_val.into() -} - -pub fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = ( - Ptr32::from_slice(account_id), - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); - ret_val.into() -} - -pub fn own_code_hash(output: &mut [u8]) { - let mut output_len = output.len() as u32; - ( - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); -} diff --git a/crates/env/src/engine/on_chain/ext/wasm32.rs b/crates/env/src/engine/on_chain/ext/wasm32.rs deleted file mode 100644 index 49f4dc37bc..0000000000 --- a/crates/env/src/engine/on_chain/ext/wasm32.rs +++ /dev/null @@ -1,658 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Copyright (C) Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - extract_from_slice, - Ptr32, - Ptr32Mut, - Result, - ReturnCode, -}; -use crate::ReturnFlags; - -mod sys { - use super::{ - Ptr32, - Ptr32Mut, - ReturnCode, - }; - - #[link(wasm_import_module = "seal0")] - extern "C" { - pub fn transfer( - account_id_ptr: Ptr32<[u8]>, - account_id_len: u32, - transferred_value_ptr: Ptr32<[u8]>, - transferred_value_len: u32, - ) -> ReturnCode; - - pub fn deposit_event( - topics_ptr: Ptr32<[u8]>, - topics_len: u32, - data_ptr: Ptr32<[u8]>, - data_len: u32, - ); - - pub fn call_chain_extension( - func_id: u32, - input_ptr: Ptr32<[u8]>, - input_len: u32, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - ) -> ReturnCode; - - pub fn input(buf_ptr: Ptr32Mut<[u8]>, buf_len_ptr: Ptr32Mut); - pub fn seal_return(flags: u32, data_ptr: Ptr32<[u8]>, data_len: u32) -> !; - - pub fn caller(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - pub fn block_number(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - pub fn address(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - pub fn balance(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - pub fn weight_to_fee( - gas: u64, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - ); - pub fn gas_left(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - pub fn value_transferred( - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - ); - pub fn now(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - pub fn minimum_balance(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - - pub fn hash_keccak_256( - input_ptr: Ptr32<[u8]>, - input_len: u32, - output_ptr: Ptr32Mut<[u8]>, - ); - pub fn hash_blake2_256( - input_ptr: Ptr32<[u8]>, - input_len: u32, - output_ptr: Ptr32Mut<[u8]>, - ); - pub fn hash_blake2_128( - input_ptr: Ptr32<[u8]>, - input_len: u32, - output_ptr: Ptr32Mut<[u8]>, - ); - pub fn hash_sha2_256( - input_ptr: Ptr32<[u8]>, - input_len: u32, - output_ptr: Ptr32Mut<[u8]>, - ); - - pub fn is_contract(account_id_ptr: Ptr32<[u8]>) -> ReturnCode; - - pub fn caller_is_origin() -> ReturnCode; - - pub fn set_code_hash(code_hash_ptr: Ptr32<[u8]>) -> ReturnCode; - - pub fn code_hash( - account_id_ptr: Ptr32<[u8]>, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - ) -> ReturnCode; - - pub fn own_code_hash(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); - - #[cfg(feature = "ink-debug")] - pub fn debug_message(str_ptr: Ptr32<[u8]>, str_len: u32) -> ReturnCode; - - pub fn delegate_call( - flags: u32, - code_hash_ptr: Ptr32<[u8]>, - input_data_ptr: Ptr32<[u8]>, - input_data_len: u32, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - ) -> ReturnCode; - - pub fn ecdsa_recover( - // 65 bytes of ecdsa signature - signature_ptr: Ptr32<[u8]>, - // 32 bytes hash of the message - message_hash_ptr: Ptr32<[u8]>, - output_ptr: Ptr32Mut<[u8]>, - ) -> ReturnCode; - - pub fn ecdsa_to_eth_address( - public_key_ptr: Ptr32<[u8]>, - output_ptr: Ptr32Mut<[u8]>, - ) -> ReturnCode; - - /// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces), - /// which is unsafe and normally is not available on production chains. - pub fn sr25519_verify( - signature_ptr: Ptr32<[u8]>, - public_key_ptr: Ptr32<[u8]>, - message_len: u32, - message_ptr: Ptr32<[u8]>, - ) -> ReturnCode; - - pub fn take_storage( - key_ptr: Ptr32<[u8]>, - key_len: u32, - out_ptr: Ptr32Mut<[u8]>, - out_len_ptr: Ptr32Mut, - ) -> ReturnCode; - - pub fn call_runtime(call_ptr: Ptr32<[u8]>, call_len: u32) -> ReturnCode; - } - - #[link(wasm_import_module = "seal1")] - extern "C" { - pub fn instantiate( - init_code_ptr: Ptr32<[u8]>, - gas: u64, - endowment_ptr: Ptr32<[u8]>, - input_ptr: Ptr32<[u8]>, - input_len: u32, - address_ptr: Ptr32Mut<[u8]>, - address_len_ptr: Ptr32Mut, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - salt_ptr: Ptr32<[u8]>, - salt_len: u32, - ) -> ReturnCode; - - pub fn terminate(beneficiary_ptr: Ptr32<[u8]>) -> !; - - pub fn call( - flags: u32, - callee_ptr: Ptr32<[u8]>, - gas: u64, - transferred_value_ptr: Ptr32<[u8]>, - input_data_ptr: Ptr32<[u8]>, - input_data_len: u32, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - ) -> ReturnCode; - - // # 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. - pub fn clear_storage(key_ptr: Ptr32<[u8]>, key_len: u32) -> ReturnCode; - - // # 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. - pub fn contains_storage(key_ptr: Ptr32<[u8]>, key_len: u32) -> ReturnCode; - - // # 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` - pub fn get_storage( - key_ptr: Ptr32<[u8]>, - key_len: u32, - out_ptr: Ptr32Mut<[u8]>, - out_len_ptr: Ptr32Mut, - ) -> ReturnCode; - } - - #[link(wasm_import_module = "seal2")] - extern "C" { - // # 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. - pub fn set_storage( - key_ptr: Ptr32<[u8]>, - key_len: u32, - value_ptr: Ptr32<[u8]>, - value_len: u32, - ) -> ReturnCode; - } -} - -#[inline(always)] -pub fn instantiate( - code_hash: &[u8], - gas_limit: u64, - endowment: &[u8], - input: &[u8], - out_address: &mut &mut [u8], - out_return_value: &mut &mut [u8], - salt: &[u8], -) -> Result { - let mut address_len = out_address.len() as u32; - let mut return_value_len = out_return_value.len() as u32; - let ret_code = { - unsafe { - sys::instantiate( - Ptr32::from_slice(code_hash), - gas_limit, - Ptr32::from_slice(endowment), - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(out_address), - Ptr32Mut::from_ref(&mut address_len), - Ptr32Mut::from_slice(out_return_value), - Ptr32Mut::from_ref(&mut return_value_len), - Ptr32::from_slice(salt), - salt.len() as u32, - ) - } - }; - extract_from_slice(out_address, address_len as usize); - extract_from_slice(out_return_value, return_value_len as usize); - ret_code.into() -} - -#[inline(always)] -pub fn call( - flags: u32, - callee: &[u8], - gas_limit: u64, - value: &[u8], - input: &[u8], - output: &mut &mut [u8], -) -> Result { - let mut output_len = output.len() as u32; - let ret_code = { - unsafe { - sys::call( - flags, - Ptr32::from_slice(callee), - gas_limit, - Ptr32::from_slice(value), - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -#[inline(always)] -pub fn delegate_call( - flags: u32, - code_hash: &[u8], - input: &[u8], - output: &mut &mut [u8], -) -> Result { - let mut output_len = output.len() as u32; - let ret_code = { - unsafe { - sys::delegate_call( - flags, - Ptr32::from_slice(code_hash), - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -pub fn transfer(account_id: &[u8], value: &[u8]) -> Result { - let ret_code = unsafe { - sys::transfer( - Ptr32::from_slice(account_id), - account_id.len() as u32, - Ptr32::from_slice(value), - value.len() as u32, - ) - }; - ret_code.into() -} - -pub fn deposit_event(topics: &[u8], data: &[u8]) { - unsafe { - sys::deposit_event( - Ptr32::from_slice(topics), - topics.len() as u32, - Ptr32::from_slice(data), - data.len() as u32, - ) - } -} - -pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { - let ret_code = unsafe { - sys::set_storage( - Ptr32::from_slice(key), - key.len() as u32, - Ptr32::from_slice(encoded_value), - encoded_value.len() as u32, - ) - }; - ret_code.into() -} - -pub fn clear_storage(key: &[u8]) -> Option { - let ret_code = - unsafe { sys::clear_storage(Ptr32::from_slice(key), key.len() as u32) }; - ret_code.into() -} - -#[inline(always)] -pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_code = { - unsafe { - sys::get_storage( - Ptr32::from_slice(key), - key.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -#[inline(always)] -pub fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_code = { - unsafe { - sys::take_storage( - Ptr32::from_slice(key), - key.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into() -} - -pub fn storage_contains(key: &[u8]) -> Option { - let ret_code = - unsafe { sys::contains_storage(Ptr32::from_slice(key), key.len() as u32) }; - ret_code.into() -} - -pub fn terminate(beneficiary: &[u8]) -> ! { - unsafe { sys::terminate(Ptr32::from_slice(beneficiary)) } -} - -#[inline(always)] -pub fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 { - let mut output_len = output.len() as u32; - let ret_code = { - unsafe { - sys::call_chain_extension( - func_id, - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into_u32() -} - -#[inline(always)] -pub fn input(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - { - unsafe { - sys::input( - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - }; - } - extract_from_slice(output, output_len as usize); -} - -pub fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - unsafe { - sys::seal_return( - flags.into_u32(), - Ptr32::from_slice(return_value), - return_value.len() as u32, - ) - } -} - -pub fn call_runtime(call: &[u8]) -> Result { - let ret_code = - unsafe { sys::call_runtime(Ptr32::from_slice(call), call.len() as u32) }; - ret_code.into() -} - -macro_rules! impl_wrapper_for { - ( $( $name:ident, )* ) => { - $( - #[inline(always)] - pub fn $name(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - { - unsafe { - sys::$name( - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - }; - } - } - )* - } -} -impl_wrapper_for! { - caller, - block_number, - address, - balance, - gas_left, - value_transferred, - now, - minimum_balance, -} - -#[inline(always)] -pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - { - unsafe { - sys::weight_to_fee( - gas, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - }; - } - extract_from_slice(output, output_len as usize); -} - -#[cfg(feature = "ink-debug")] -/// Call `debug_message` with the supplied UTF-8 encoded message. -/// -/// If debug message recording is disabled in the contracts pallet, the first call will -/// return a `LoggingDisabled` error, and further calls will be a no-op to avoid the cost -/// of calling into the supervisor. -/// -/// # Note -/// -/// This depends on the `debug_message` interface which requires the -/// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. -pub fn debug_message(message: &str) { - static mut DEBUG_ENABLED: bool = false; - static mut FIRST_RUN: bool = true; - - // SAFETY: safe because executing in a single threaded context - // We need those two variables in order to make sure that the assignment is performed - // in the "logging enabled" case. This is because during RPC execution logging might - // be enabled while it is disabled during the actual execution as part of a - // transaction. The gas estimation takes place during RPC execution. We want to - // overestimate instead of underestimate gas usage. Otherwise using this estimate - // could lead to a out of gas error. - if unsafe { DEBUG_ENABLED || FIRST_RUN } { - let bytes = message.as_bytes(); - let ret_code = - unsafe { sys::debug_message(Ptr32::from_slice(bytes), bytes.len() as u32) }; - if !matches!(ret_code.into(), Err(super::Error::LoggingDisabled)) { - // SAFETY: safe because executing in a single threaded context - unsafe { DEBUG_ENABLED = true } - } - // SAFETY: safe because executing in a single threaded context - unsafe { FIRST_RUN = false } - } -} - -#[cfg(not(feature = "ink-debug"))] -/// A no-op. Enable the `ink-debug` feature for debug messages. -pub fn debug_message(_message: &str) {} - -macro_rules! impl_hash_fn { - ( $name:ident, $bytes_result:literal ) => { - paste::item! { - pub fn [](input: &[u8], output: &mut [u8; $bytes_result]) { - unsafe { - sys::[]( - Ptr32::from_slice(input), - input.len() as u32, - Ptr32Mut::from_slice(output), - ) - } - } - } - }; -} -impl_hash_fn!(sha2_256, 32); -impl_hash_fn!(keccak_256, 32); -impl_hash_fn!(blake2_256, 32); -impl_hash_fn!(blake2_128, 16); - -pub fn ecdsa_recover( - signature: &[u8; 65], - message_hash: &[u8; 32], - output: &mut [u8; 33], -) -> Result { - let ret_code = unsafe { - sys::ecdsa_recover( - Ptr32::from_slice(signature), - Ptr32::from_slice(message_hash), - Ptr32Mut::from_slice(output), - ) - }; - ret_code.into() -} - -pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { - let ret_code = unsafe { - sys::ecdsa_to_eth_address(Ptr32::from_slice(pubkey), Ptr32Mut::from_slice(output)) - }; - ret_code.into() -} - -pub fn sr25519_verify( - signature: &[u8; 64], - message: &[u8], - pub_key: &[u8; 32], -) -> Result { - let ret_code = unsafe { - sys::sr25519_verify( - Ptr32::from_slice(signature), - Ptr32::from_slice(pub_key), - message.len() as u32, - Ptr32::from_slice(message), - ) - }; - ret_code.into() -} - -pub fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(Ptr32::from_slice(account_id)) }; - ret_val.into_bool() -} - -pub fn caller_is_origin() -> bool { - let ret_val = unsafe { sys::caller_is_origin() }; - ret_val.into_bool() -} - -pub fn set_code_hash(code_hash: &[u8]) -> Result { - let ret_val = unsafe { sys::set_code_hash(Ptr32::from_slice(code_hash)) }; - ret_val.into() -} - -pub fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::code_hash( - Ptr32::from_slice(account_id), - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - }; - ret_val.into() -} - -pub fn own_code_hash(output: &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { - sys::own_code_hash( - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), - ) - } -} diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index babfada8d1..b1c39c3779 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -13,9 +13,7 @@ // limitations under the License. use super::{ - ext, EnvInstance, - Error as ExtError, ScopedBuffer, }; use crate::{ @@ -42,16 +40,21 @@ use crate::{ Clear, EnvBackend, Environment, - Error, FromLittleEndian, Result, - ReturnFlags, TypedEnvBackend, }; use ink_storage_traits::{ decode_all, Storable, }; +use pallet_contracts_uapi::{ + CallFlags, + HostFn, + HostFnImpl as ext, + ReturnErrorCode, + ReturnFlags, +}; impl CryptoHash for Blake2x128 { fn hash(input: &[u8], output: &mut ::Type) { @@ -101,25 +104,6 @@ impl CryptoHash for Keccak256 { } } -impl From for Error { - fn from(ext_error: ext::Error) -> Self { - match ext_error { - ext::Error::Unknown => Self::Unknown, - ext::Error::CalleeTrapped => Self::CalleeTrapped, - ext::Error::CalleeReverted => Self::CalleeReverted, - ext::Error::KeyNotFound => Self::KeyNotFound, - ext::Error::_BelowSubsistenceThreshold => Self::_BelowSubsistenceThreshold, - ext::Error::TransferFailed => Self::TransferFailed, - ext::Error::_EndowmentTooLow => Self::_EndowmentTooLow, - ext::Error::CodeNotFound => Self::CodeNotFound, - ext::Error::NotCallable => Self::NotCallable, - ext::Error::LoggingDisabled => Self::LoggingDisabled, - ext::Error::CallRuntimeFailed => Self::CallRuntimeFailed, - ext::Error::EcdsaRecoveryFailed => Self::EcdsaRecoveryFailed, - } - } -} - pub struct TopicsBuilder<'a, E> { scoped_buffer: ScopedBuffer<'a>, marker: core::marker::PhantomData E>, @@ -223,7 +207,7 @@ impl EnvBackend for EnvInstance { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); let value = buffer.take_storable_encoded(value); - ext::set_storage(key, value) + ext::set_storage_v2(key, value) } fn get_contract_storage(&mut self, key: &K) -> Result> @@ -234,9 +218,9 @@ impl EnvBackend for EnvInstance { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); let output = &mut buffer.take_rest(); - match ext::get_storage(key, output) { + match ext::get_storage_v1(key, output) { Ok(_) => (), - Err(ExtError::KeyNotFound) => return Ok(None), + Err(ReturnErrorCode::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), } let decoded = decode_all(&mut &output[..])?; @@ -253,7 +237,7 @@ impl EnvBackend for EnvInstance { let output = &mut buffer.take_rest(); match ext::take_storage(key, output) { Ok(_) => (), - Err(ExtError::KeyNotFound) => return Ok(None), + Err(ReturnErrorCode::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), } let decoded = decode_all(&mut &output[..])?; @@ -266,7 +250,7 @@ impl EnvBackend for EnvInstance { { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); - ext::storage_contains(key) + ext::contains_storage_v1(key) } fn clear_contract_storage(&mut self, key: &K) -> Option @@ -275,7 +259,7 @@ impl EnvBackend for EnvInstance { { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); - ext::clear_storage(key) + ext::clear_storage_v1(key) } fn decode_input(&mut self) -> Result @@ -295,8 +279,32 @@ impl EnvBackend for EnvInstance { ext::return_value(flags, &self.buffer[..][..len]); } + #[cfg(not(feature = "ink-debug"))] + /// A no-op. Enable the `ink-debug` feature for debug messages. + fn debug_message(&mut self, _content: &str) {} + + #[cfg(feature = "ink-debug")] fn debug_message(&mut self, content: &str) { - ext::debug_message(content) + static mut DEBUG_ENABLED: bool = false; + static mut FIRST_RUN: bool = true; + + // SAFETY: safe because executing in a single threaded context + // We need those two variables in order to make sure that the assignment is + // performed in the "logging enabled" case. This is because during RPC + // execution logging might be enabled while it is disabled during the + // actual execution as part of a transaction. The gas estimation takes + // place during RPC execution. We want to overestimate instead + // of underestimate gas usage. Otherwise using this estimate could lead to a out + // of gas error. + if unsafe { DEBUG_ENABLED || FIRST_RUN } { + let ret_code = ext::debug_message(content.as_bytes()); + if !matches!(ret_code, Err(ReturnErrorCode::LoggingDisabled)) { + // SAFETY: safe because executing in a single threaded context + unsafe { DEBUG_ENABLED = true } + } + // SAFETY: safe because executing in a single threaded context + unsafe { FIRST_RUN = false } + } } fn hash_bytes(&mut self, input: &[u8], output: &mut ::Type) @@ -359,8 +367,8 @@ impl EnvBackend for EnvInstance { let mut scope = self.scoped_buffer(); let enc_input = scope.take_encoded(input); let output = &mut scope.take_rest(); - status_to_result(ext::call_chain_extension(id, enc_input, output))?; - let decoded = decode_to_result(&output[..])?; + status_to_result(ext::call_chain_extension(id, enc_input, Some(output)))?; + let decoded = decode_to_result(output)?; Ok(decoded) } @@ -429,23 +437,25 @@ impl TypedEnvBackend for EnvInstance { let enc_callee = scope.take_encoded(params.callee()); let enc_transferred_value = scope.take_encoded(params.transferred_value()); let call_flags = params.call_flags(); - let enc_input = if !call_flags.forward_input() && !call_flags.clone_input() { + let enc_input = if !call_flags.contains(CallFlags::FORWARD_INPUT) + && !call_flags.contains(CallFlags::CLONE_INPUT) + { scope.take_encoded(params.exec_input()) } else { &mut [] }; let output = &mut scope.take_rest(); - let flags = params.call_flags().into_u32(); - let call_result = ext::call( - flags, + let flags = params.call_flags(); + let call_result = ext::call_v1( + *flags, enc_callee, gas_limit, enc_transferred_value, enc_input, - output, + Some(output), ); match call_result { - Ok(()) | Err(ext::Error::CalleeReverted) => { + Ok(()) | Err(ReturnErrorCode::CalleeReverted) => { let decoded = scale::DecodeAll::decode_all(&mut &output[..])?; Ok(decoded) } @@ -465,16 +475,19 @@ impl TypedEnvBackend for EnvInstance { let mut scope = self.scoped_buffer(); let call_flags = params.call_flags(); let enc_code_hash = scope.take_encoded(params.code_hash()); - let enc_input = if !call_flags.forward_input() && !call_flags.clone_input() { + let enc_input = if !call_flags.contains(CallFlags::FORWARD_INPUT) + && !call_flags.contains(CallFlags::CLONE_INPUT) + { scope.take_encoded(params.exec_input()) } else { &mut [] }; let output = &mut scope.take_rest(); - let flags = params.call_flags().into_u32(); - let call_result = ext::delegate_call(flags, enc_code_hash, enc_input, output); + let flags = params.call_flags(); + let call_result = + ext::delegate_call(*flags, enc_code_hash, enc_input, Some(output)); match call_result { - Ok(()) | Err(ext::Error::CalleeReverted) => { + Ok(()) | Err(ReturnErrorCode::CalleeReverted) => { let decoded = scale::DecodeAll::decode_all(&mut &output[..])?; Ok(decoded) } @@ -509,13 +522,13 @@ impl TypedEnvBackend for EnvInstance { let salt = params.salt_bytes().as_ref(); let out_return_value = &mut scoped.take_rest(); - let instantiate_result = ext::instantiate( + let instantiate_result = ext::instantiate_v1( enc_code_hash, gas_limit, enc_endowment, enc_input, - out_address, - out_return_value, + Some(out_address), + Some(out_return_value), salt, ); @@ -531,7 +544,7 @@ impl TypedEnvBackend for EnvInstance { E: Environment, { let buffer = self.scoped_buffer().take_encoded(&beneficiary); - ext::terminate(buffer); + ext::terminate_v1(buffer); } fn transfer(&mut self, destination: E::AccountId, value: E::Balance) -> Result<()> diff --git a/crates/env/src/engine/on_chain/mod.rs b/crates/env/src/engine/on_chain/mod.rs index 8a42b90445..1a5cff3231 100644 --- a/crates/env/src/engine/on_chain/mod.rs +++ b/crates/env/src/engine/on_chain/mod.rs @@ -13,16 +13,12 @@ // limitations under the License. mod buffer; -mod ext; mod impls; -use self::{ - buffer::{ - EncodeScope, - ScopedBuffer, - StaticBuffer, - }, - ext::Error, +use self::buffer::{ + EncodeScope, + ScopedBuffer, + StaticBuffer, }; use super::OnInstance; diff --git a/crates/env/src/error.rs b/crates/env/src/error.rs index 977a9116a8..9632568354 100644 --- a/crates/env/src/error.rs +++ b/crates/env/src/error.rs @@ -16,6 +16,7 @@ use derive_more::From; #[cfg(any(feature = "std", test, doc))] use crate::engine::off_chain::OffChainError; +use pallet_contracts_uapi::ReturnErrorCode; /// Errors that can be encountered upon environmental interaction. #[derive(Debug, From, PartialEq, Eq)] @@ -27,34 +28,8 @@ pub enum Error { /// An error that can only occur in the off-chain environment. #[cfg(any(feature = "std", test, doc))] OffChain(OffChainError), - /// The call to another contract has trapped. - CalleeTrapped, - /// The call to another contract has been reverted. - CalleeReverted, - /// The queried contract storage entry is missing. - KeyNotFound, - /// Deprecated and no longer returned: There is only the minimum balance. - _BelowSubsistenceThreshold, - /// Transfer failed for other not further specified reason. Most probably - /// reserved or locked balance of the sender that was preventing the transfer. - TransferFailed, - /// Deprecated and no longer returned: Endowment is no longer required. - _EndowmentTooLow, - /// No code could be found at the supplied code hash. - CodeNotFound, - /// The account that was called is no contract, but a plain account. - NotCallable, - /// An unknown error has occurred. - Unknown, - /// The call to `debug_message` had no effect because debug message - /// recording was disabled. - LoggingDisabled, - /// The call dispatched by `call_runtime` was executed but returned an error. - CallRuntimeFailed, - /// ECDSA pubkey recovery failed. Most probably wrong recovery id or signature. - EcdsaRecoveryFailed, - /// sr25519 signature verification failed. - Sr25519VerifyFailed, + /// The error returned by the contract. + ReturnError(ReturnErrorCode), } /// A result of environmental operations. diff --git a/crates/env/src/lib.rs b/crates/env/src/lib.rs index 2f0bbf5277..f5adef5763 100644 --- a/crates/env/src/lib.rs +++ b/crates/env/src/lib.rs @@ -102,16 +102,19 @@ mod tests; #[doc(inline)] pub use self::engine::off_chain::test_api as test; +#[doc(inline)] +pub use pallet_contracts_uapi::{ + CallFlags, + ReturnErrorCode, + ReturnFlags, +}; + use self::backend::{ EnvBackend, TypedEnvBackend, }; pub use self::{ api::*, - backend::{ - CallFlags, - ReturnFlags, - }, contract::{ ContractEnv, ContractReference, diff --git a/crates/env/src/tests.rs b/crates/env/src/tests.rs index 9bebca43b0..2fde5c4c24 100644 --- a/crates/env/src/tests.rs +++ b/crates/env/src/tests.rs @@ -62,42 +62,3 @@ fn test_hash_blake2_128() { [180, 158, 48, 21, 171, 163, 217, 175, 145, 160, 25, 159, 213, 142, 103, 242] ); } - -#[test] -fn test_call_flags() { - let flags = crate::CallFlags::default(); - - // enable each flag one after the other - let flags = flags.set_forward_input(true); - assert!(flags.forward_input()); - assert_eq!(flags.into_u32(), 0b0000_0001); - - let flags = flags.set_clone_input(true); - assert!(flags.clone_input()); - assert_eq!(flags.into_u32(), 0b0000_0011); - - let flags = flags.set_tail_call(true); - assert!(flags.tail_call()); - assert_eq!(flags.into_u32(), 0b0000_0111); - - let flags = flags.set_allow_reentry(true); - assert!(flags.allow_reentry()); - assert_eq!(flags.into_u32(), 0b0000_1111); - - // disable each flag one after the other - let flags = flags.set_allow_reentry(false); - assert!(!flags.allow_reentry()); - assert_eq!(flags.into_u32(), 0b0000_0111); - - let flags = flags.set_tail_call(false); - assert!(!flags.tail_call()); - assert_eq!(flags.into_u32(), 0b0000_0011); - - let flags = flags.set_clone_input(false); - assert!(!flags.clone_input()); - assert_eq!(flags.into_u32(), 0b0000_0001); - - let flags = flags.set_forward_input(false); - assert!(!flags.forward_input()); - assert_eq!(flags.into_u32(), 0b0000_0000); -} diff --git a/crates/ink/Cargo.toml b/crates/ink/Cargo.toml index ddc58e0967..7d86373933 100644 --- a/crates/ink/Cargo.toml +++ b/crates/ink/Cargo.toml @@ -22,7 +22,7 @@ ink_primitives = { workspace = true } ink_metadata = { workspace = true, optional = true } ink_prelude = { workspace = true } ink_macro = { workspace = true } - +pallet-contracts-uapi = { workspace = true } scale = { workspace = true } scale-info = { workspace = true, default-features = false, features = ["derive"], optional = true } derive_more = { workspace = true, features = ["from"] } diff --git a/crates/ink/codegen/src/generator/dispatch.rs b/crates/ink/codegen/src/generator/dispatch.rs index 40711d1a9a..eb88b07f75 100644 --- a/crates/ink/codegen/src/generator/dispatch.rs +++ b/crates/ink/codegen/src/generator/dispatch.rs @@ -374,7 +374,7 @@ impl Dispatch<'_> { // This is okay since we're going to only be encoding the `Err` variant // into the output buffer anyways. ::ink::env::return_value::<::ink::ConstructorResult<()>>( - ::ink::env::ReturnFlags::new_with_reverted(true), + ::ink::env::ReturnFlags::REVERT, &error, ); } @@ -410,7 +410,7 @@ impl Dispatch<'_> { // This is okay since we're going to only be encoding the `Err` variant // into the output buffer anyways. ::ink::env::return_value::<::ink::MessageResult<()>>( - ::ink::env::ReturnFlags::new_with_reverted(true), + ::ink::env::ReturnFlags::REVERT, &error, ); } @@ -588,12 +588,20 @@ impl Dispatch<'_> { ); } + // NOTE: we can't use an if/else expression here + // It fails inside quote_spanned! macro. + // See https://github.com/rust-lang/rust-clippy/issues/6249 + let mut flag = ::ink::env::ReturnFlags::empty(); + if output_result.is_err() { + flag = ::ink::env::ReturnFlags::REVERT; + } + ::ink::env::return_value::< ::ink::ConstructorResult< ::core::result::Result<(), &#constructor_value::Error> >, >( - ::ink::env::ReturnFlags::new_with_reverted(output_result.is_err()), + flag, // Currently no `LangError`s are raised at this level of the // dispatch logic so `Ok` is always returned to the caller. &::ink::ConstructorResult::Ok(output_result.map(|_| ())), @@ -773,7 +781,6 @@ impl Dispatch<'_> { quote_spanned!(message_span=> #( #cfg_attrs )* Self::#message_ident(input) => { - if #any_message_accepts_payment && #deny_payment { ::ink::codegen::deny_payment::< <#storage_ident as ::ink::env::ContractEnv>::Env>()?; @@ -783,13 +790,19 @@ impl Dispatch<'_> { let is_reverted = ::ink::is_result_type!(#message_output) && ::ink::is_result_err!(result); + // NOTE: we can't use an if/else expression here + // It fails inside quote_spanned! macro. + // See https://github.com/rust-lang/rust-clippy/issues/6249 + let mut flag = ::ink::env::ReturnFlags::REVERT; + // no need to push back results: transaction gets reverted anyways if !is_reverted { + flag = ::ink::env::ReturnFlags::empty(); push_contract(contract, #mutates_storage); } ::ink::env::return_value::<::ink::MessageResult::<#message_output>>( - ::ink::env::ReturnFlags::new_with_reverted(is_reverted), + flag, // Currently no `LangError`s are raised at this level of the // dispatch logic so `Ok` is always returned to the caller. &::ink::MessageResult::Ok(result), diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 0cc8e871e8..a7a705882d 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -28,9 +28,9 @@ use ink_env::{ HashOutput, }, Environment, - Error, Result, }; +use pallet_contracts_uapi::ReturnErrorCode; /// The API behind the `self.env()` and `Self::env()` syntax in ink!. /// @@ -824,7 +824,7 @@ where /// let failed_result = self.env().ecdsa_recover(&signature, &[0; 32]); /// assert!(failed_result.is_err()); /// if let Err(e) = failed_result { - /// assert_eq!(e, ink::env::Error::EcdsaRecoveryFailed); + /// assert_eq!(e, ink::env::ReturnErrorCode::EcdsaRecoveryFailed.into()); /// } /// } /// # @@ -839,7 +839,7 @@ where let mut output = [0; 33]; ink_env::ecdsa_recover(signature, message_hash, &mut output) .map(|_| output) - .map_err(|_| Error::EcdsaRecoveryFailed) + .map_err(|_| ReturnErrorCode::EcdsaRecoveryFailed.into()) } /// Returns an Ethereum address from the ECDSA compressed public key. @@ -886,7 +886,7 @@ where let mut output = [0; 20]; ink_env::ecdsa_to_eth_address(pubkey, &mut output) .map(|_| output) - .map_err(|_| Error::EcdsaRecoveryFailed) + .map_err(|_| ReturnErrorCode::EcdsaRecoveryFailed.into()) } /// Verifies a SR25519 signature against a message and a public key. @@ -946,7 +946,7 @@ where pub_key: &[u8; 32], ) -> Result<()> { ink_env::sr25519_verify(signature, message, pub_key) - .map_err(|_| Error::Sr25519VerifyFailed) + .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed.into()) } /// Checks whether a specified account belongs to a contract. diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index 98117c5ccc..c04d4cb066 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -20,6 +20,7 @@ ink_metadata = { workspace = true, optional = true } ink_primitives = { workspace = true } ink_storage_traits = { workspace = true } ink_prelude = { workspace = true } +pallet-contracts-uapi = { workspace = true} scale = { workspace = true } derive_more = { workspace = true, features = ["from", "display"] } diff --git a/crates/storage/src/lazy/vec.rs b/crates/storage/src/lazy/vec.rs index d6abb47abf..f113550220 100644 --- a/crates/storage/src/lazy/vec.rs +++ b/crates/storage/src/lazy/vec.rs @@ -20,7 +20,6 @@ //! Instead it is just a simple wrapper around the contract storage facilities. use core::cell::Cell; - use ink_primitives::Key; use ink_storage_traits::{ AutoKey, @@ -29,6 +28,7 @@ use ink_storage_traits::{ StorableHint, StorageKey, }; +use pallet_contracts_uapi::ReturnErrorCode; use scale::EncodeLike; use crate::{ @@ -403,9 +403,10 @@ where /// * `Ok(Some(_))` if the value was inserted successfully, containing the size in /// bytes of the pre-existing value at the specified key if any. /// * `Ok(None)` if the insert was successful but there was no pre-existing value. - /// * Err([ink_env::Error::BufferTooSmall]) if the encoded value exceeds the static + /// * Err([`ink_env::Error::BufferTooSmall`]) if the encoded value exceeds the static /// buffer size - /// * Err([ink_env::Error::KeyNotFound]) if the `index` is out of bounds. + /// * Err([`ink_env::Error::ReturnError`]\([`ink_env::ReturnErrorCode::KeyNotFound`])) + /// if the `index` is out of bounds. /// /// # Panics /// @@ -419,7 +420,7 @@ where T: Storable + EncodeLike, { if index >= self.len() { - return Err(ink_env::Error::KeyNotFound); + return Err(ReturnErrorCode::KeyNotFound.into()); } self.elements.try_insert(index, value) @@ -661,7 +662,12 @@ mod tests { let mut array: StorageVec = (0..10).collect(); assert_eq!(array.try_set(0, &1), Ok(Some(4))); - assert_eq!(array.try_set(10, &1), Err(ink_env::Error::KeyNotFound)); + assert_eq!( + array.try_set(10, &1), + Err(ink_env::Error::ReturnError( + ink_env::ReturnErrorCode::KeyNotFound + )) + ); array.clear_at(0); assert_eq!(array.try_set(0, &1), Ok(None)); diff --git a/integration-tests/call-runtime/lib.rs b/integration-tests/call-runtime/lib.rs index b655f13adf..0fa1830003 100644 --- a/integration-tests/call-runtime/lib.rs +++ b/integration-tests/call-runtime/lib.rs @@ -62,8 +62,11 @@ mod runtime_call { impl From for RuntimeError { fn from(e: EnvError) -> Self { + use ink::env::ReturnErrorCode; match e { - EnvError::CallRuntimeFailed => RuntimeError::CallRuntimeFailed, + EnvError::ReturnError(ReturnErrorCode::CallRuntimeFailed) => { + RuntimeError::CallRuntimeFailed + } _ => panic!("Unexpected error from `pallet-contracts`."), } } diff --git a/integration-tests/erc1155/lib.rs b/integration-tests/erc1155/lib.rs index 27b9f2161e..ba06c1ea05 100644 --- a/integration-tests/erc1155/lib.rs +++ b/integration-tests/erc1155/lib.rs @@ -413,9 +413,13 @@ mod erc1155 { ) } Err(e) => { + use ink::env::ReturnErrorCode; + match e { - ink::env::Error::CodeNotFound - | ink::env::Error::NotCallable => { + ink::env::Error::ReturnError( + ReturnErrorCode::CodeNotFound + | ReturnErrorCode::NotCallable, + ) => { // Our recipient wasn't a smart contract, so there's // nothing more for // us to do diff --git a/integration-tests/lang-err-integration-tests/constructors-return-value/lib.rs b/integration-tests/lang-err-integration-tests/constructors-return-value/lib.rs index 96c0a8ec1e..76cd2bcfdc 100644 --- a/integration-tests/lang-err-integration-tests/constructors-return-value/lib.rs +++ b/integration-tests/lang-err-integration-tests/constructors-return-value/lib.rs @@ -39,7 +39,7 @@ pub mod constructors_return_value { #[ink(constructor)] pub fn revert_new(_init_value: bool) -> Self { ink::env::return_value::>( - ink::env::ReturnFlags::new_with_reverted(true), + ink::env::ReturnFlags::REVERT, &Ok(AccountId::from([0u8; 32])), ) } @@ -56,7 +56,7 @@ pub mod constructors_return_value { ink::env::return_value::< ink::ConstructorResult>, - >(ink::env::ReturnFlags::new_with_reverted(true), &value) + >(ink::env::ReturnFlags::REVERT, &value) } /// Returns the current value of the contract storage. diff --git a/integration-tests/multisig/lib.rs b/integration-tests/multisig/lib.rs index f79fd16db7..623919891e 100755 --- a/integration-tests/multisig/lib.rs +++ b/integration-tests/multisig/lib.rs @@ -545,11 +545,17 @@ mod multisig { self.ensure_confirmed(trans_id); let t = self.take_transaction(trans_id).expect(WRONG_TRANSACTION_ID); assert!(self.env().transferred_value() == t.transferred_value); + let call_flags = if t.allow_reentry { + CallFlags::ALLOW_REENTRY + } else { + CallFlags::empty() + }; + let result = build_call::<::Env>() .call(t.callee) .gas_limit(t.gas_limit) .transferred_value(t.transferred_value) - .call_flags(CallFlags::default().set_allow_reentry(t.allow_reentry)) + .call_flags(call_flags) .exec_input( ExecutionInput::new(t.selector.into()).push_arg(CallInput(&t.input)), ) @@ -580,11 +586,17 @@ mod multisig { ) -> Result, Error> { self.ensure_confirmed(trans_id); let t = self.take_transaction(trans_id).expect(WRONG_TRANSACTION_ID); + let call_flags = if t.allow_reentry { + CallFlags::ALLOW_REENTRY + } else { + CallFlags::empty() + }; + let result = build_call::<::Env>() .call(t.callee) .gas_limit(t.gas_limit) .transferred_value(t.transferred_value) - .call_flags(CallFlags::default().set_allow_reentry(t.allow_reentry)) + .call_flags(call_flags) .exec_input( ExecutionInput::new(t.selector.into()).push_arg(CallInput(&t.input)), ) diff --git a/integration-tests/sr25519-verification/lib.rs b/integration-tests/sr25519-verification/lib.rs index a8322b0149..e31445a391 100644 --- a/integration-tests/sr25519-verification/lib.rs +++ b/integration-tests/sr25519-verification/lib.rs @@ -73,7 +73,10 @@ pub mod sr25519_verification { 143, ]; let result = ink::env::sr25519_verify(&signature, &message, &public_key); - assert_eq!(result, Err(ink::env::Error::Sr25519VerifyFailed)); + assert_eq!( + result, + Err(ink::env::ReturnErrorCode::Sr25519VerifyFailed.into()) + ); } #[ink::test] @@ -99,7 +102,10 @@ pub mod sr25519_verification { 143, ]; let result = ink::env::sr25519_verify(&signature, &message, &public_key); - assert_eq!(result, Err(ink::env::Error::Sr25519VerifyFailed)); + assert_eq!( + result, + Err(ink::env::ReturnErrorCode::Sr25519VerifyFailed.into()) + ); } #[ink::test] @@ -125,7 +131,10 @@ pub mod sr25519_verification { 143, ]; let result = ink::env::sr25519_verify(&signature, &message, &public_key); - assert_eq!(result, Err(ink::env::Error::Sr25519VerifyFailed)); + assert_eq!( + result, + Err(ink::env::ReturnErrorCode::Sr25519VerifyFailed.into()) + ); } } } diff --git a/integration-tests/upgradeable-contracts/README.md b/integration-tests/upgradeable-contracts/README.md index b6fb8ca298..85bd7b5c81 100644 --- a/integration-tests/upgradeable-contracts/README.md +++ b/integration-tests/upgradeable-contracts/README.md @@ -25,11 +25,11 @@ However, there are certain nuances associated with using `delegate_call`. First of all, as demonstrated in the example, if the delegated code intends to mutate the caller's storage, a developer needs to be mindful. If the delegated code modifies layout-full storage -(i.e. it contains at least non-`Lazy`, non-`Mapping` field), the `.set_tail_call(true)` flag of `CallFlags` needs to be specified and the storage layouts must match. +(i.e. it contains at least non-`Lazy`, non-`Mapping` field), the `CallFlags::TAIL_CALL` flag needs to be specified and the storage layouts must match. This is due to the way ink! execution call stack is operated (see [Stack Exchange Answer](https://substrate.stackexchange.com/a/3352/3098) for more explanation). -If the delegated code only modifies `Lazy` or `Mapping` field, the keys must be identical and `.set_tail_call(true)` is optional. +If the delegated code only modifies `Lazy` or `Mapping` field, the keys must be identical and `CallFlags::TAIL_CALL` is optional. This is because `Lazy` and `Mapping` interact with the storage directly instead of loading and flushing storage states. If your storage is completely layoutless (it only contains `Lazy` and `Mapping` fields), the order of fields and layout do not need to match for the same reason as mentioned above. diff --git a/integration-tests/upgradeable-contracts/delegator/lib.rs b/integration-tests/upgradeable-contracts/delegator/lib.rs index 15106b014e..b6d95c3a1c 100644 --- a/integration-tests/upgradeable-contracts/delegator/lib.rs +++ b/integration-tests/upgradeable-contracts/delegator/lib.rs @@ -48,7 +48,7 @@ pub mod delegator { let selector = ink::selector_bytes!("inc"); let _ = build_call::() .delegate(hash) - // We specify `set_tail_call(true)` to use the delegatee last memory frame + // We specify `CallFlags::TAIL_CALL` to use the delegatee last memory frame // as the end of the execution cycle. // So any mutations to `Packed` types, made by delegatee, // will be flushed to storage. @@ -56,14 +56,14 @@ pub mod delegator { // If we don't specify this flag. // The storage state before the delegate call will be flushed to storage instead. // See https://substrate.stackexchange.com/questions/3336/i-found-set-allow-reentry-may-have-some-problems/3352#3352 - .call_flags(CallFlags::default().set_tail_call(true)) + .call_flags(CallFlags::TAIL_CALL) .exec_input(ExecutionInput::new(Selector::new(selector))) .returns::<()>() .try_invoke(); } /// Adds entry to `addresses` using delegate call. - /// Note that we don't need `set_tail_call(true)` flag + /// Note that we don't need `CallFlags::TAIL_CALL` flag /// because `Mapping` updates the storage instantly on-demand. #[ink(message)] pub fn add_entry_delegate(&mut self, hash: Hash) {