From 50d39a658b0701ffdbc9bc647269e1ab1b5fbc66 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 15 Nov 2023 15:36:50 +0100 Subject: [PATCH 01/81] Add uapi and tests-contracts pallets --- Cargo.lock | 16 + Cargo.toml | 2 + .../contracts/test-contracts/.cargo/config | 15 + .../frame/contracts/test-contracts/Cargo.toml | 21 + .../contracts/test-contracts/src/dummy.rs | 11 + substrate/frame/contracts/uapi/Cargo.toml | 25 + substrate/frame/contracts/uapi/src/lib.rs | 258 +++++++ substrate/frame/contracts/uapi/src/riscv32.rs | 424 +++++++++++ substrate/frame/contracts/uapi/src/wasm32.rs | 658 ++++++++++++++++++ 9 files changed, 1430 insertions(+) create mode 100755 substrate/frame/contracts/test-contracts/.cargo/config create mode 100644 substrate/frame/contracts/test-contracts/Cargo.toml create mode 100644 substrate/frame/contracts/test-contracts/src/dummy.rs create mode 100644 substrate/frame/contracts/uapi/Cargo.toml create mode 100644 substrate/frame/contracts/uapi/src/lib.rs create mode 100644 substrate/frame/contracts/uapi/src/riscv32.rs create mode 100644 substrate/frame/contracts/uapi/src/wasm32.rs diff --git a/Cargo.lock b/Cargo.lock index 4a45d5c602e8a..a1d6953b565f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9760,6 +9760,22 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "pallet-contracts-test-contracts" +version = "1.0.0" +dependencies = [ + "pallet-contracts-uapi", +] + +[[package]] +name = "pallet-contracts-uapi" +version = "4.0.0-dev" +dependencies = [ + "cfg-if", + "parity-scale-codec", + "paste", +] + [[package]] name = "pallet-conviction-voting" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 30445bd594504..56b6751a33576 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -294,6 +294,8 @@ members = [ "substrate/frame/collective", "substrate/frame/contracts", "substrate/frame/contracts/fixtures", + "substrate/frame/contracts/uapi", + "substrate/frame/contracts/test-contracts", "substrate/frame/contracts/primitives", "substrate/frame/contracts/proc-macro", "substrate/frame/conviction-voting", diff --git a/substrate/frame/contracts/test-contracts/.cargo/config b/substrate/frame/contracts/test-contracts/.cargo/config new file mode 100755 index 0000000000000..ff2419b7de6f1 --- /dev/null +++ b/substrate/frame/contracts/test-contracts/.cargo/config @@ -0,0 +1,15 @@ +[build] +target="wasm32-unknown-unknown" + +# see cargo-contract/crates/build/src/args.rs +rustflags = [ + "-C", "link-arg=-zstack-size=65536", + "-C", "link-arg=--import-memory", + "-C", "target-cpu=mvp", +] + +# does not seem to work for now... +# fails with duplicate lang item in crate `core` (which `pallet_contracts_uapi` depends on): +[unstable] +build-std=["core,alloc"] +build-std-features=["panic_immediate_abort"] diff --git a/substrate/frame/contracts/test-contracts/Cargo.toml b/substrate/frame/contracts/test-contracts/Cargo.toml new file mode 100644 index 0000000000000..c6922d2a6a2e9 --- /dev/null +++ b/substrate/frame/contracts/test-contracts/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "pallet-contracts-test-contracts" +publish = false +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +description = "Fixtures for testing contracts pallet." + +[[bin]] +name = "dummy" +path = "src/dummy.rs" + +[dependencies] +uapi = { package = "pallet-contracts-uapi", path = "../uapi", default-features = false} + +[features] +std = [ "uapi/std" ] +ink-debug = [ "uapi/ink-debug" ] + + diff --git a/substrate/frame/contracts/test-contracts/src/dummy.rs b/substrate/frame/contracts/test-contracts/src/dummy.rs new file mode 100644 index 0000000000000..f82c959d52c1a --- /dev/null +++ b/substrate/frame/contracts/test-contracts/src/dummy.rs @@ -0,0 +1,11 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[no_mangle] +pub fn deploy() { + uapi::debug_message("contract deployed"); +} + +#[no_mangle] +pub fn call() { + uapi::debug_message("contract called"); +} diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml new file mode 100644 index 0000000000000..ce0849615b78c --- /dev/null +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "pallet-contracts-uapi" +version = "4.0.0-dev" +authors.workspace = true +edition.workspace = true +license = "Apache-2.0" +homepage = "https://substrate.io" +repository.workspace = true +description = "This crates exposes all the host functions that a contract can import" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", + "max-encoded-len", +] } +cfg-if = { version = "1.0", default-features = false } +paste = { version = "1.0", default-features = false } + +[features] +default = [ "std" ] +std = [ "codec/std" ] + +# Enable contract debug messages via `debug_print!` and `debug_println!`. +ink-debug = [] + diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs new file mode 100644 index 0000000000000..097be5cc02e04 --- /dev/null +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -0,0 +1,258 @@ +// 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 codec::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, +} + +/// 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 + } +} + +/// 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/substrate/frame/contracts/uapi/src/riscv32.rs b/substrate/frame/contracts/uapi/src/riscv32.rs new file mode 100644 index 0000000000000..90809da9c1aaf --- /dev/null +++ b/substrate/frame/contracts/uapi/src/riscv32.rs @@ -0,0 +1,424 @@ +// 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/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs new file mode 100644 index 0000000000000..906912dc65c05 --- /dev/null +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -0,0 +1,658 @@ +// 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, + 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), + ) + } +} From 5396bf6b2bb28c08d08b89e09c5bccfb7c4e7377 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 15 Nov 2023 17:14:58 +0100 Subject: [PATCH 02/81] wip --- .../frame/contracts/uapi/src/allocator.rs | 6 + .../contracts/uapi/src/allocator/bump.rs | 554 ++++++++++++++++++ substrate/frame/contracts/uapi/src/lib.rs | 83 +++ 3 files changed, 643 insertions(+) create mode 100644 substrate/frame/contracts/uapi/src/allocator.rs create mode 100644 substrate/frame/contracts/uapi/src/allocator/bump.rs diff --git a/substrate/frame/contracts/uapi/src/allocator.rs b/substrate/frame/contracts/uapi/src/allocator.rs new file mode 100644 index 0000000000000..2c9e5bf6dbbe3 --- /dev/null +++ b/substrate/frame/contracts/uapi/src/allocator.rs @@ -0,0 +1,6 @@ +mod bump; + +#[cfg(not(any(feature = "std", feature = "no-allocator")))] +#[global_allocator] +static mut ALLOC: bump::BumpAllocator = bump::BumpAllocator {}; + diff --git a/substrate/frame/contracts/uapi/src/allocator/bump.rs b/substrate/frame/contracts/uapi/src/allocator/bump.rs new file mode 100644 index 0000000000000..a713cc5d04809 --- /dev/null +++ b/substrate/frame/contracts/uapi/src/allocator/bump.rs @@ -0,0 +1,554 @@ +// 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. + +//! A simple bump allocator. +//! +//! Its goal to have a much smaller footprint than the admittedly more full-featured +//! `wee_alloc` allocator which is currently being used by ink! smart contracts. +//! +//! The heap which is used by this allocator is built from pages of Wasm memory (each page +//! is `64KiB`). We will request new pages of memory as needed until we run out of memory, +//! at which point we will crash with an `OOM` error instead of freeing any memory. + +use core::alloc::{ + GlobalAlloc, + Layout, +}; + +/// A page in Wasm is `64KiB` +const PAGE_SIZE: usize = 64 * 1024; + +static mut INNER: Option = None; + +/// A bump allocator suitable for use in a Wasm environment. +pub struct BumpAllocator; + +unsafe impl GlobalAlloc for BumpAllocator { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if INNER.is_none() { + INNER = Some(InnerAlloc::new()); + }; + match INNER + .as_mut() + .expect("We just set the value above; qed") + .alloc(layout) + { + Some(start) => start as *mut u8, + None => core::ptr::null_mut(), + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // A new page in Wasm is guaranteed to already be zero initialized, so we can just + // use our regular `alloc` call here and save a bit of work. + // + // See: https://webassembly.github.io/spec/core/exec/modules.html#growing-memories + self.alloc(layout) + } + + #[inline] + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +#[cfg_attr(feature = "std", derive(Debug, Copy, Clone))] +struct InnerAlloc { + /// Points to the start of the next available allocation. + next: usize, + + /// The address of the upper limit of our heap. + upper_limit: usize, +} + +impl InnerAlloc { + fn new() -> Self { + Self { + next: Self::heap_start(), + upper_limit: Self::heap_end(), + } + } + + cfg_if::cfg_if! { + if #[cfg(test)] { + fn heap_start() -> usize { + 0 + } + + fn heap_end() -> usize { + 0 + } + + /// Request a `pages` number of page sized sections of Wasm memory. Each page is `64KiB` in size. + /// + /// Returns `None` if a page is not available. + /// + /// This implementation is only meant to be used for testing, since we cannot (easily) + /// test the `wasm32` implementation. + fn request_pages(&mut self, _pages: usize) -> Option { + Some(self.upper_limit) + } + } else if #[cfg(feature = "std")] { + fn heap_start() -> usize { + 0 + } + + fn heap_end() -> usize { + 0 + } + + fn request_pages(&mut self, _pages: usize) -> Option { + unreachable!( + "This branch is only used to keep the compiler happy when building tests, and + should never actually be called outside of a test run." + ) + } + } else if #[cfg(target_arch = "wasm32")] { + fn heap_start() -> usize { + extern "C" { + static __heap_base: usize; + } + // # SAFETY + // + // The `__heap_base` symbol is defined by the wasm linker and is guaranteed + // to point to the start of the heap. + let heap_start = unsafe { &__heap_base as *const usize as usize }; + // if the symbol isn't found it will resolve to 0 + // for that to happen the rust compiler or linker need to break or change + assert_ne!(heap_start, 0, "Can't find `__heap_base` symbol."); + heap_start + } + + fn heap_end() -> usize { + // Cannot overflow on this architecture + core::arch::wasm32::memory_size(0) * PAGE_SIZE + } + + /// Request a `pages` number of pages of Wasm memory. Each page is `64KiB` in size. + /// + /// Returns `None` if a page is not available. + fn request_pages(&mut self, pages: usize) -> Option { + let prev_page = core::arch::wasm32::memory_grow(0, pages); + if prev_page == usize::MAX { + return None; + } + + // Cannot overflow on this architecture + Some(prev_page * PAGE_SIZE) + } + } else if #[cfg(target_arch = "riscv32")] { + const fn heap_start() -> usize { + // Placeholder value until we specified our riscv VM + 0x7000_0000 + } + + const fn heap_end() -> usize { + // Placeholder value until we specified our riscv VM + // Let's just assume a cool megabyte of mem for now + 0x7000_0400 + } + + fn request_pages(&mut self, _pages: usize) -> Option { + // On riscv the memory can't be grown + None + } + } else { + core::compile_error!("ink! only supports wasm32 and riscv32"); + } + } + + /// Tries to allocate enough memory on the heap for the given `Layout`. If there is + /// not enough room on the heap it'll try and grow it by a page. + /// + /// Note: This implementation results in internal fragmentation when allocating across + /// pages. + fn alloc(&mut self, layout: Layout) -> Option { + let alloc_start = self.next; + + let aligned_size = layout.pad_to_align().size(); + let alloc_end = alloc_start.checked_add(aligned_size)?; + + if alloc_end > self.upper_limit { + let required_pages = required_pages(aligned_size)?; + let page_start = self.request_pages(required_pages)?; + + self.upper_limit = required_pages + .checked_mul(PAGE_SIZE) + .and_then(|pages| page_start.checked_add(pages))?; + self.next = page_start.checked_add(aligned_size)?; + + Some(page_start) + } else { + self.next = alloc_end; + Some(alloc_start) + } + } +} + +/// Calculates the number of pages of memory needed for an allocation of `size` bytes. +/// +/// This function rounds up to the next page. For example, if we have an allocation of +/// `size = PAGE_SIZE / 2` this function will indicate that one page is required to +/// satisfy the allocation. +#[inline] +fn required_pages(size: usize) -> Option { + size.checked_add(PAGE_SIZE - 1) + .and_then(|num| num.checked_div(PAGE_SIZE)) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::mem::size_of; + + #[test] + fn can_alloc_no_bytes() { + let mut inner = InnerAlloc::new(); + + let layout = Layout::new::<()>(); + assert_eq!(inner.alloc(layout), Some(0)); + + let expected_limit = + PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); + + let expected_alloc_start = size_of::<()>(); + assert_eq!(inner.next, expected_alloc_start); + } + + #[test] + fn can_alloc_a_byte() { + let mut inner = InnerAlloc::new(); + + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(0)); + + let expected_limit = + PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); + + let expected_alloc_start = size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } + + #[test] + fn can_alloc_a_foobarbaz() { + let mut inner = InnerAlloc::new(); + + struct FooBarBaz { + _foo: u32, + _bar: u128, + _baz: (u16, bool), + } + + let layout = Layout::new::(); + let mut total_size = 0; + + let allocations = 3; + for _ in 0..allocations { + assert!(inner.alloc(layout).is_some()); + total_size += layout.pad_to_align().size(); + } + + let expected_limit = PAGE_SIZE * required_pages(total_size).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); + + let expected_alloc_start = allocations * size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } + + #[test] + fn can_alloc_across_pages() { + let mut inner = InnerAlloc::new(); + + struct Foo { + _foo: [u8; PAGE_SIZE - 1], + } + + // First, let's allocate a struct which is _almost_ a full page + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(0)); + + let expected_limit = + PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); + + let expected_alloc_start = size_of::(); + assert_eq!(inner.next, expected_alloc_start); + + // Now we'll allocate two bytes which will push us over to the next page + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(PAGE_SIZE)); + + let expected_limit = 2 * PAGE_SIZE; + assert_eq!(inner.upper_limit, expected_limit); + + // Notice that we start the allocation on the second page, instead of making use + // of the remaining byte on the first page + let expected_alloc_start = PAGE_SIZE + size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } + + #[test] + fn can_alloc_multiple_pages() { + let mut inner = InnerAlloc::new(); + + struct Foo { + _foo: [u8; 2 * PAGE_SIZE], + } + + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(0)); + + let expected_limit = + PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); + + let expected_alloc_start = size_of::(); + assert_eq!(inner.next, expected_alloc_start); + + // Now we want to make sure that the state of our allocator is correct for any + // subsequent allocations + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(2 * PAGE_SIZE)); + + let expected_limit = 3 * PAGE_SIZE; + assert_eq!(inner.upper_limit, expected_limit); + + let expected_alloc_start = 2 * PAGE_SIZE + size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } +} + +#[cfg(all(test, feature = "ink-fuzz-tests"))] +mod fuzz_tests { + use super::*; + use quickcheck::{ + quickcheck, + TestResult, + }; + use std::mem::size_of; + + #[quickcheck] + fn should_allocate_arbitrary_sized_bytes(n: usize) -> TestResult { + let mut inner = InnerAlloc::new(); + + // If we're going to end up creating an invalid `Layout` we don't want to use + // these test inputs. + let layout = match Layout::from_size_align(n, size_of::()) { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + let size = layout.pad_to_align().size(); + assert_eq!( + inner.alloc(layout), + Some(0), + "The given pointer for the allocation doesn't match." + ); + + let expected_alloc_start = size; + assert_eq!( + inner.next, expected_alloc_start, + "Our next allocation doesn't match where it should start." + ); + + let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); + assert_eq!( + inner.upper_limit, expected_limit, + "The upper bound of our heap doesn't match." + ); + + TestResult::passed() + } + + #[quickcheck] + fn should_allocate_regardless_of_alignment_size( + n: usize, + align: usize, + ) -> TestResult { + let aligns = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]; + let align = aligns[align % aligns.len()]; + + let mut inner = InnerAlloc::new(); + + // If we're going to end up creating an invalid `Layout` we don't want to use + // these test inputs. + let layout = match Layout::from_size_align(n, align) { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + let size = layout.pad_to_align().size(); + assert_eq!( + inner.alloc(layout), + Some(0), + "The given pointer for the allocation doesn't match." + ); + + let expected_alloc_start = size; + assert_eq!( + inner.next, expected_alloc_start, + "Our next allocation doesn't match where it should start." + ); + + let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); + assert_eq!( + inner.upper_limit, expected_limit, + "The upper bound of our heap doesn't match." + ); + + TestResult::passed() + } + + /// The idea behind this fuzz test is to check a series of allocation sequences. For + /// example, we maybe have back to back runs as follows: + /// + /// 1. `vec![1, 2, 3]` + /// 2. `vec![4, 5, 6, 7]` + /// 3. `vec![8]` + /// + /// Each of the vectors represents one sequence of allocations. Within each sequence + /// the individual size of allocations will be randomly selected by `quickcheck`. + #[quickcheck] + fn should_allocate_arbitrary_byte_sequences(sequence: Vec) -> TestResult { + let mut inner = InnerAlloc::new(); + + if sequence.is_empty() { + return TestResult::discard() + } + + // We don't want any negative numbers so we can be sure our conversions to `usize` + // later are valid + if !sequence.iter().all(|n| n.is_positive()) { + return TestResult::discard() + } + + // We can't just use `required_pages(Iterator::sum())` here because it ends up + // underestimating the pages due to the ceil rounding at each step + let pages_required = sequence + .iter() + .fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); + let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); + + // We know this is going to end up overflowing, we'll check this case in a + // different test + if pages_required > max_pages { + return TestResult::discard() + } + + let mut expected_alloc_start = 0; + let mut total_bytes_requested = 0; + let mut total_bytes_fragmented = 0; + + for alloc in sequence { + let layout = Layout::from_size_align(alloc as usize, size_of::()); + let layout = match layout { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + let size = layout.pad_to_align().size(); + + let current_page_limit = PAGE_SIZE * required_pages(inner.next).unwrap(); + let is_too_big_for_current_page = inner.next + size > current_page_limit; + + if is_too_big_for_current_page { + let fragmented_in_current_page = current_page_limit - inner.next; + total_bytes_fragmented += fragmented_in_current_page; + + // We expect our next allocation to be aligned to the start of the next + // page boundary + expected_alloc_start = inner.upper_limit; + } + + assert_eq!( + inner.alloc(layout), + Some(expected_alloc_start), + "The given pointer for the allocation doesn't match." + ); + total_bytes_requested += size; + + expected_alloc_start = total_bytes_requested + total_bytes_fragmented; + assert_eq!( + inner.next, expected_alloc_start, + "Our next allocation doesn't match where it should start." + ); + + let pages_required = required_pages(expected_alloc_start).unwrap(); + let expected_limit = PAGE_SIZE * pages_required; + assert_eq!( + inner.upper_limit, expected_limit, + "The upper bound of our heap doesn't match." + ); + } + + TestResult::passed() + } + + // For this test we have sequences of allocations which will eventually overflow the + // maximum amount of pages (in practice this means our heap will be OOM). + // + // We don't care about the allocations that succeed (those are checked in other + // tests), we just care that eventually an allocation doesn't success. + #[quickcheck] + fn should_not_allocate_arbitrary_byte_sequences_which_eventually_overflow( + sequence: Vec, + ) -> TestResult { + let mut inner = InnerAlloc::new(); + + if sequence.is_empty() { + return TestResult::discard() + } + + // We don't want any negative numbers so we can be sure our conversions to `usize` + // later are valid + if !sequence.iter().all(|n| n.is_positive()) { + return TestResult::discard() + } + + // We can't just use `required_pages(Iterator::sum())` here because it ends up + // underestimating the pages due to the ceil rounding at each step + let pages_required = sequence + .iter() + .fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); + let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); + + // We want to explicitly test for the case where a series of allocations + // eventually runs out of pages of memory + if pages_required <= max_pages { + return TestResult::discard() + } + + let mut results = vec![]; + for alloc in sequence { + let layout = Layout::from_size_align(alloc as usize, size_of::()); + let layout = match layout { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + results.push(inner.alloc(layout)); + } + + // Ensure that at least one of the allocations ends up overflowing our + // calculations. + assert!( + results.iter().any(|r| r.is_none()), + "Expected an allocation to overflow our heap, but this didn't happen." + ); + + TestResult::passed() + } +} diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 097be5cc02e04..8b04e5ca53736 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -1,3 +1,6 @@ +#![cfg_attr(not(feature = "std"), no_std)] + + // Copyright (C) Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +22,28 @@ use core::marker::PhantomData; use codec::Encode; +#[cfg(not(feature = "std"))] +#[allow(unused_variables)] +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + // This code gets removed in release builds where the macro will expand into nothing. + debug_print!("{}\n", info); + + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + core::arch::wasm32::unreachable(); + } else if #[cfg(target_arch = "riscv32")] { + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); + } + } else { + core::compile_error!("ink! only supports wasm32 and riscv32"); + } + } +} + cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { mod wasm32; @@ -256,3 +281,61 @@ fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { let tmp = core::mem::take(output); *output = &mut tmp[..new_len]; } + +// is not recognizing its allocator and panic handler definitions. +#[cfg(not(any(feature = "std", feature = "no-allocator")))] +mod allocator; + +cfg_if::cfg_if! { + if #[cfg(any(feature = "ink-debug", feature = "std"))] { + /// Required by the `debug_print*` macros below, because there is no guarantee that + /// contracts will have a direct `ink_prelude` dependency. In the future we could introduce + /// an "umbrella" crate containing all the `ink!` crates which could also host these macros. + #[doc(hidden)] + pub use ink_prelude::format; + + /// Appends a formatted string to the `debug_message` buffer if message recording is + /// enabled in the contracts pallet and if the call is performed via RPC (**not** via an + /// extrinsic). The `debug_message` buffer will be: + /// - Returned to the RPC caller. + /// - Logged as a `debug!` message on the Substrate node, which will be printed to the + /// node console's `stdout` when the log level is set to `-lruntime::contracts=debug`. + /// + /// # Note + /// + /// This depends on the `debug_message` interface which requires the + /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. + #[macro_export] + macro_rules! debug_print { + ($($arg:tt)*) => ($crate::debug_message(&$crate::format!($($arg)*))); + } + + /// Appends a formatted string to the `debug_message` buffer, as per [`debug_print`] but + /// with a newline appended. + /// + /// # Note + /// + /// This depends on the `debug_message` interface which requires the + /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. + #[macro_export] + macro_rules! debug_println { + () => ($crate::debug_print!("\n")); + ($($arg:tt)*) => ( + $crate::debug_print!("{}\n", $crate::format!($($arg)*)); + ) + } + } else { + #[macro_export] + /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. + macro_rules! debug_print { + ($($arg:tt)*) => (); + } + + #[macro_export] + /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. + macro_rules! debug_println { + () => (); + ($($arg:tt)*) => (); + } + } +} From 2fbc2c36d5adcc4d9285b8134a1bb7d3a6a50de3 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 15 Nov 2023 17:36:52 +0100 Subject: [PATCH 03/81] Add prelude --- substrate/frame/contracts/uapi/src/lib.rs | 4 ++- substrate/frame/contracts/uapi/src/prelude.rs | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 substrate/frame/contracts/uapi/src/prelude.rs diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 8b04e5ca53736..22696aa0fe4a1 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -286,13 +286,15 @@ fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { #[cfg(not(any(feature = "std", feature = "no-allocator")))] mod allocator; +mod prelude; + cfg_if::cfg_if! { if #[cfg(any(feature = "ink-debug", feature = "std"))] { /// Required by the `debug_print*` macros below, because there is no guarantee that /// contracts will have a direct `ink_prelude` dependency. In the future we could introduce /// an "umbrella" crate containing all the `ink!` crates which could also host these macros. #[doc(hidden)] - pub use ink_prelude::format; + pub use prelude::format; /// Appends a formatted string to the `debug_message` buffer if message recording is /// enabled in the contracts pallet and if the call is performed via RPC (**not** via an diff --git a/substrate/frame/contracts/uapi/src/prelude.rs b/substrate/frame/contracts/uapi/src/prelude.rs new file mode 100644 index 0000000000000..48399108d3740 --- /dev/null +++ b/substrate/frame/contracts/uapi/src/prelude.rs @@ -0,0 +1,33 @@ +// 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. + +//! Data structures to operate on contract memory during contract execution. +//! +//! These definitions are useful since we are operating in a `no_std` environment +//! and should be used by all ink! crates instead of directly using `std` or `alloc` +//! crates. If needed we shall instead enhance the exposed types here. + +cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + pub use std::{ + format, + }; + } else { + extern crate alloc; + pub use alloc::{ + format, + }; + } +} + From c2712f21c494eb68ce8769d0da9680e7d0277742 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 13:30:25 +0100 Subject: [PATCH 04/81] Add build.rs file --- Cargo.lock | 69 ++- Cargo.toml | 1 - substrate/frame/contracts/fixtures/Cargo.toml | 10 +- substrate/frame/contracts/fixtures/build.rs | 201 +++++++ .../src => fixtures/contracts}/dummy.rs | 0 substrate/frame/contracts/fixtures/src/lib.rs | 36 +- .../contracts/test-contracts/.cargo/config | 15 - .../frame/contracts/test-contracts/Cargo.toml | 21 - substrate/frame/contracts/uapi/Cargo.toml | 4 +- .../contracts/uapi/src/allocator/bump.rs | 17 +- substrate/frame/contracts/uapi/src/lib.rs | 380 +++++++------ substrate/frame/contracts/uapi/src/riscv32.rs | 502 +++++++++++------- substrate/frame/contracts/uapi/src/wasm32.rs | 104 +++- 13 files changed, 909 insertions(+), 451 deletions(-) create mode 100644 substrate/frame/contracts/fixtures/build.rs rename substrate/frame/contracts/{test-contracts/src => fixtures/contracts}/dummy.rs (100%) delete mode 100755 substrate/frame/contracts/test-contracts/.cargo/config delete mode 100644 substrate/frame/contracts/test-contracts/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index a1d6953b565f3..e457f1f9ef15c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8800,7 +8800,7 @@ dependencies = [ "itertools 0.10.5", "tar", "tempfile", - "toml_edit", + "toml_edit 0.19.14", ] [[package]] @@ -9732,10 +9732,15 @@ dependencies = [ [[package]] name = "pallet-contracts-fixtures" -version = "1.0.0" +version = "1.0.2" dependencies = [ + "anyhow", "frame-system", + "parity-wasm", "sp-runtime", + "tempfile", + "toml 0.8.8", + "twox-hash", "wat", ] @@ -9764,7 +9769,13 @@ dependencies = [ name = "pallet-contracts-test-contracts" version = "1.0.0" dependencies = [ - "pallet-contracts-uapi", + "anyhow", + "frame-system", + "parity-wasm", + "sp-runtime", + "tempfile", + "toml 0.8.8", + "twox-hash", ] [[package]] @@ -13691,7 +13702,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.14", ] [[package]] @@ -14154,6 +14165,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -16662,9 +16682,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -19040,13 +19060,13 @@ checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand 2.0.0", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix 0.38.21", "windows-sys 0.48.0", ] @@ -19496,14 +19516,26 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.14", +] + +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.21.0", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -19521,6 +19553,19 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 56b6751a33576..ae0ede005182c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -295,7 +295,6 @@ members = [ "substrate/frame/contracts", "substrate/frame/contracts/fixtures", "substrate/frame/contracts/uapi", - "substrate/frame/contracts/test-contracts", "substrate/frame/contracts/primitives", "substrate/frame/contracts/proc-macro", "substrate/frame/conviction-voting", diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index b44f36f2a5fe7..0fd221df31ca9 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-contracts-fixtures" publish = false -version = "1.0.0" +version = "1.0.3" authors.workspace = true edition.workspace = true license.workspace = true @@ -11,6 +11,14 @@ description = "Fixtures for testing contracts pallet." wat = "1" frame-system = { path = "../../system", default-features = false} sp-runtime = { path = "../../../primitives/runtime", default-features = false} +anyhow = "1.0.0" + +[build-dependencies] +parity-wasm = { version = "0.45.0" } +tempfile = "3.8.1" +toml = "0.8.8" +twox-hash = "1.6.3" +anyhow = "1.0.0" [features] default = [ "std" ] diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs new file mode 100644 index 0000000000000..0e540b3fc4abf --- /dev/null +++ b/substrate/frame/contracts/fixtures/build.rs @@ -0,0 +1,201 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use anyhow::Result; +use parity_wasm::elements::{deserialize_file, serialize_to_file, Internal}; +use std::{ + env, fs, + hash::Hasher, + path::{Path, PathBuf}, + process::Command, +}; +use twox_hash::XxHash32; + +const VERSION: &'static str = env!("CARGO_PKG_VERSION"); + +/// Read the file at `path` and return its hash as a hex string. +fn file_hash(path: &PathBuf) -> String { + let data = fs::read(path).expect("file exists; qed"); + let mut hasher = XxHash32::default(); + hasher.write(&data); + hasher.write(VERSION.as_bytes()); + let hash = hasher.finish(); + format!("{:x}", hash) +} + +/// A contract entry. +struct Entry { + /// The path to the contract source file. + path: PathBuf, + /// The hash of the contract source file. + hash: String, +} + +impl Entry { + /// Create a new contract entry from the given path. + fn new(path: PathBuf) -> Self { + let hash = file_hash(&path); + Self { path, hash } + } + + /// Return the path to the contract source file. + fn path(&self) -> &str { + self.path.to_str().expect("path is valid unicode; qed") + } + + /// Return the name of the contract. + fn name(&self) -> &str { + self.path + .file_stem() + .expect("file exits; qed") + .to_str() + .expect("name is valid unicode; qed") + } + + /// Return the name of the output wasm file. + fn out_wasm_filename(&self) -> String { + format!("{}.wasm", self.name()) + } +} + +/// Collect all contract entries from the given source directory. +/// Entries are filtered by checking if the output wasm file already exists. +fn collect_entries(src_dir: &Path, out_dir: &Path) -> Vec { + fs::read_dir(&src_dir) + .expect("src dir exists; qed") + .filter_map(|file| { + let entry = Entry::new(file.expect("file exists; qed").path()); + if out_dir.join(&entry.hash).exists() { + None + } else { + Some(entry) + } + }) + .collect::>() +} + +/// Create a `Cargo.toml` to compile the given contract entries. +fn create_cargo_toml<'a>( + input_dir: &Path, + entries: impl Iterator, + output_dir: &Path, +) -> Result<()> { + let uapi_path = input_dir.join("../uapi").canonicalize()?; + let mut cargo_toml: toml::Value = toml::from_str(&format!( + " +[package] +name = 'contracts' +version = '0.1.0' + +[[bin]] + +[dependencies] +uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}, default-features = false}} + +[features] +# default = [ 'uapi/ink-debug' ] + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 +" + ))?; + + let binaries = entries + .map(|entry| { + let name = entry.name(); + let path = entry.path(); + toml::Value::Table(toml::toml! { + name = name + path = path + }) + }) + .collect::>(); + + cargo_toml["bin"] = toml::Value::Array(binaries); + let cargo_toml = toml::to_string_pretty(&cargo_toml)?; + let cargo_toml_path = output_dir.join("Cargo.toml"); + fs::write(cargo_toml_path, cargo_toml).map_err(Into::into) +} + +/// Invoke `cargo build` to compile the contracts. +fn invoke_build(current_dir: &Path) -> Result<()> { + let build_res = Command::new("cargo") + .current_dir(current_dir) + .env( + "RUSTFLAGS", + "-C link-arg=-zstack-size=65536 -C link-arg=--import-memory -Clinker-plugin-lto -C target-cpu=mvp", + ) + .arg("build") + .arg("--release") + .arg("--target=wasm32-unknown-unknown") // todo pass risc-v target here as well + .output() + .unwrap(); + + if build_res.status.success() { + return Ok(()) + } + + let stderr = String::from_utf8_lossy(&build_res.stderr); + anyhow::bail!("Failed to build contracts: {:?}", stderr); +} + +/// Post-process the compiled wasm contracts. +fn post_process_wasm(input_path: &Path, output_path: &Path) -> Result<()> { + let mut module = deserialize_file(input_path)?; + if let Some(section) = module.export_section_mut() { + section.entries_mut().retain(|entry| { + matches!(entry.internal(), Internal::Function(_)) && + (entry.field() == "call" || entry.field() == "deploy") + }); + } + + serialize_to_file(output_path, module).map_err(Into::into) +} + +/// Write the compiled contracts to the given output directory. +fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result<()> { + for entry in entries { + let wasm_output = entry.out_wasm_filename(); + post_process_wasm( + &build_dir.join("target/wasm32-unknown-unknown/release").join(&wasm_output), + &out_dir.join(&wasm_output), + )?; + fs::write(out_dir.join(&entry.hash), "")?; + } + + Ok(()) +} + +fn main() -> Result<()> { + let input_dir: PathBuf = env::var("INPUT_DIR").unwrap_or(".".to_string()).into(); + let out_dir: PathBuf = env::var("OUT_DIR")?.into(); + + let entries = collect_entries(&input_dir.join("contracts").canonicalize()?, &out_dir); + if entries.is_empty() { + return Ok(()); + } + + let tmp_dir = tempfile::tempdir()?; + create_cargo_toml(&input_dir, entries.iter(), tmp_dir.path())?; + invoke_build(tmp_dir.path())?; + write_output(tmp_dir.path(), &out_dir, entries)?; + + println!("cargo:rerun-if-changed=contracts"); + Ok(()) +} diff --git a/substrate/frame/contracts/test-contracts/src/dummy.rs b/substrate/frame/contracts/fixtures/contracts/dummy.rs similarity index 100% rename from substrate/frame/contracts/test-contracts/src/dummy.rs rename to substrate/frame/contracts/fixtures/contracts/dummy.rs diff --git a/substrate/frame/contracts/fixtures/src/lib.rs b/substrate/frame/contracts/fixtures/src/lib.rs index 32f4023e64461..ad9b19213d48c 100644 --- a/substrate/frame/contracts/fixtures/src/lib.rs +++ b/substrate/frame/contracts/fixtures/src/lib.rs @@ -16,9 +16,9 @@ // limitations under the License. use sp_runtime::traits::Hash; -use std::{env::var, path::PathBuf}; +use std::{fs, env::var, path::PathBuf}; -fn fixtures_root_dir() -> PathBuf { +fn wat_root_dir() -> PathBuf { match (var("CARGO_MANIFEST_DIR"), var("CARGO_PKG_NAME")) { // When `CARGO_MANIFEST_DIR` is not set, Rust resolves relative paths from the root folder (Err(_), _) => "substrate/frame/contracts/fixtures/data".into(), @@ -31,12 +31,40 @@ fn fixtures_root_dir() -> PathBuf { /// with it's hash. /// /// The fixture files are located under the `fixtures/` directory. -pub fn compile_module(fixture_name: &str) -> wat::Result<(Vec, ::Output)> +pub fn legacy_compile_module(fixture_name: &str) -> anyhow::Result<(Vec, ::Output)> where T: frame_system::Config, { - let fixture_path = fixtures_root_dir().join(format!("{fixture_name}.wat")); + let fixture_path = wat_root_dir().join(format!("{fixture_name}.wat")); let wasm_binary = wat::parse_file(fixture_path)?; let code_hash = T::Hashing::hash(&wasm_binary); Ok((wasm_binary, code_hash)) } + +/// Load a given wasm module and returns a wasm binary contents along with it's hash. +/// Use the legacy compile_module as fallback, if the rust fixture does not exist yet. +pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, ::Output)> +where + T: frame_system::Config, +{ + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let fixture_path = out_dir.join(format!("{fixture_name}.wasm")); + match fs::read(fixture_path) { + Ok(wasm_binary) => { + let code_hash = T::Hashing::hash(&wasm_binary); + Ok((wasm_binary, code_hash)) + }, + Err(_) => legacy_compile_module::(fixture_name), + } +} + +#[cfg(test)] +mod test { + #[test] + fn dummy_mock_should_be_compiled() { + let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); + let dummy_wasm = out_dir.join("dummy.wasm"); + assert!(dummy_wasm.exists()); + } +} + diff --git a/substrate/frame/contracts/test-contracts/.cargo/config b/substrate/frame/contracts/test-contracts/.cargo/config deleted file mode 100755 index ff2419b7de6f1..0000000000000 --- a/substrate/frame/contracts/test-contracts/.cargo/config +++ /dev/null @@ -1,15 +0,0 @@ -[build] -target="wasm32-unknown-unknown" - -# see cargo-contract/crates/build/src/args.rs -rustflags = [ - "-C", "link-arg=-zstack-size=65536", - "-C", "link-arg=--import-memory", - "-C", "target-cpu=mvp", -] - -# does not seem to work for now... -# fails with duplicate lang item in crate `core` (which `pallet_contracts_uapi` depends on): -[unstable] -build-std=["core,alloc"] -build-std-features=["panic_immediate_abort"] diff --git a/substrate/frame/contracts/test-contracts/Cargo.toml b/substrate/frame/contracts/test-contracts/Cargo.toml deleted file mode 100644 index c6922d2a6a2e9..0000000000000 --- a/substrate/frame/contracts/test-contracts/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "pallet-contracts-test-contracts" -publish = false -version = "1.0.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -description = "Fixtures for testing contracts pallet." - -[[bin]] -name = "dummy" -path = "src/dummy.rs" - -[dependencies] -uapi = { package = "pallet-contracts-uapi", path = "../uapi", default-features = false} - -[features] -std = [ "uapi/std" ] -ink-debug = [ "uapi/ink-debug" ] - - diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index ce0849615b78c..242dba6959dd4 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -9,7 +9,7 @@ repository.workspace = true description = "This crates exposes all the host functions that a contract can import" [dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ +scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", "max-encoded-len", ] } @@ -18,7 +18,7 @@ paste = { version = "1.0", default-features = false } [features] default = [ "std" ] -std = [ "codec/std" ] +std = [ "scale/std" ] # Enable contract debug messages via `debug_print!` and `debug_println!`. ink-debug = [] diff --git a/substrate/frame/contracts/uapi/src/allocator/bump.rs b/substrate/frame/contracts/uapi/src/allocator/bump.rs index a713cc5d04809..f0b3109597d7d 100644 --- a/substrate/frame/contracts/uapi/src/allocator/bump.rs +++ b/substrate/frame/contracts/uapi/src/allocator/bump.rs @@ -31,6 +31,9 @@ const PAGE_SIZE: usize = 64 * 1024; static mut INNER: Option = None; +#[cfg(target_arch = "riscv32")] +static mut RISCV_HEAP: [u8; 1024 * 1024] = [0; 1024 * 1024]; + /// A bump allocator suitable for use in a Wasm environment. pub struct BumpAllocator; @@ -148,15 +151,14 @@ impl InnerAlloc { Some(prev_page * PAGE_SIZE) } } else if #[cfg(target_arch = "riscv32")] { - const fn heap_start() -> usize { - // Placeholder value until we specified our riscv VM - 0x7000_0000 + fn heap_start() -> usize { + unsafe { + RISCV_HEAP.as_mut_ptr() as usize + } } - const fn heap_end() -> usize { - // Placeholder value until we specified our riscv VM - // Let's just assume a cool megabyte of mem for now - 0x7000_0400 + fn heap_end() -> usize { + Self::heap_start() + unsafe { RISCV_HEAP.len() } } fn request_pages(&mut self, _pages: usize) -> Option { @@ -552,3 +554,4 @@ mod fuzz_tests { TestResult::passed() } } + diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 22696aa0fe4a1..aacd7cd003cbf 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -1,6 +1,4 @@ #![cfg_attr(not(feature = "std"), no_std)] - - // Copyright (C) Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,38 +18,38 @@ //! Refer to substrate FRAME contract module for more documentation. use core::marker::PhantomData; -use codec::Encode; +use scale::Encode; #[cfg(not(feature = "std"))] #[allow(unused_variables)] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - // This code gets removed in release builds where the macro will expand into nothing. - debug_print!("{}\n", info); - - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - core::arch::wasm32::unreachable(); - } else if #[cfg(target_arch = "riscv32")] { - // Safety: The unimp instruction is guaranteed to trap - unsafe { - core::arch::asm!("unimp"); - core::hint::unreachable_unchecked(); - } - } else { - core::compile_error!("ink! only supports wasm32 and riscv32"); - } - } + // This code gets removed in release builds where the macro will expand into nothing. + debug_print!("{}\n", info); + + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + core::arch::wasm32::unreachable(); + } else if #[cfg(target_arch = "riscv32")] { + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); + } + } else { + core::compile_error!("ink! only supports wasm32 and riscv32"); + } + } } 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::*; - } + 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 { @@ -86,64 +84,65 @@ macro_rules! define_error_codes { } }; } + 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, + /// 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, } /// The flags to indicate further information about the end of a contract execution. #[derive(Default)] pub struct ReturnFlags { - value: u32, + 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 - } + /// 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 + } } /// Thin-wrapper around a `u32` representing a pointer for Wasm32. @@ -159,34 +158,31 @@ impl ReturnFlags { #[repr(transparent)] pub struct Ptr32<'a, T> where - T: ?Sized, + 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>, + /// 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, + T: ?Sized, { - /// Creates a new Wasm32 pointer for the given raw pointer value. - fn new(value: u32) -> Self { - Self { - _value: value, - marker: Default::default(), - } - } + /// 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) - } + /// 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. @@ -202,45 +198,42 @@ impl<'a, T> Ptr32<'a, [T]> { #[repr(transparent)] pub struct Ptr32Mut<'a, T> where - T: ?Sized, + 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>, + /// 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, + T: ?Sized, { - /// Creates a new Wasm32 pointer for the given raw pointer value. - fn new(value: u32) -> Self { - Self { - _value: value, - marker: Default::default(), - } - } + /// 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) - } + /// 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, + 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) - } + /// 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. @@ -248,38 +241,39 @@ where 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) - } + 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) - } + /// 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>; +#[cfg(not(feature = "std"))] #[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]; + debug_assert!(new_len <= output.len()); + let tmp = core::mem::take(output); + *output = &mut tmp[..new_len]; } // is not recognizing its allocator and panic handler definitions. @@ -289,55 +283,55 @@ mod allocator; mod prelude; cfg_if::cfg_if! { - if #[cfg(any(feature = "ink-debug", feature = "std"))] { - /// Required by the `debug_print*` macros below, because there is no guarantee that - /// contracts will have a direct `ink_prelude` dependency. In the future we could introduce - /// an "umbrella" crate containing all the `ink!` crates which could also host these macros. - #[doc(hidden)] - pub use prelude::format; - - /// Appends a formatted string to the `debug_message` buffer if message recording is - /// enabled in the contracts pallet and if the call is performed via RPC (**not** via an - /// extrinsic). The `debug_message` buffer will be: - /// - Returned to the RPC caller. - /// - Logged as a `debug!` message on the Substrate node, which will be printed to the - /// node console's `stdout` when the log level is set to `-lruntime::contracts=debug`. - /// - /// # Note - /// - /// This depends on the `debug_message` interface which requires the - /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. - #[macro_export] - macro_rules! debug_print { - ($($arg:tt)*) => ($crate::debug_message(&$crate::format!($($arg)*))); - } - - /// Appends a formatted string to the `debug_message` buffer, as per [`debug_print`] but - /// with a newline appended. - /// - /// # Note - /// - /// This depends on the `debug_message` interface which requires the - /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. - #[macro_export] - macro_rules! debug_println { - () => ($crate::debug_print!("\n")); - ($($arg:tt)*) => ( - $crate::debug_print!("{}\n", $crate::format!($($arg)*)); - ) - } - } else { - #[macro_export] - /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. - macro_rules! debug_print { - ($($arg:tt)*) => (); - } - - #[macro_export] - /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. - macro_rules! debug_println { - () => (); - ($($arg:tt)*) => (); - } - } + if #[cfg(any(feature = "ink-debug", feature = "std"))] { + /// Required by the `debug_print*` macros below, because there is no guarantee that + /// contracts will have a direct `ink_prelude` dependency. In the future we could introduce + /// an "umbrella" crate containing all the `ink!` crates which could also host these macros. + #[doc(hidden)] + pub use prelude::format; + + /// Appends a formatted string to the `debug_message` buffer if message recording is + /// enabled in the contracts pallet and if the call is performed via RPC (**not** via an + /// extrinsic). The `debug_message` buffer will be: + /// - Returned to the RPC caller. + /// - Logged as a `debug!` message on the Substrate node, which will be printed to the + /// node console's `stdout` when the log level is set to `-lruntime::contracts=debug`. + /// + /// # Note + /// + /// This depends on the `debug_message` interface which requires the + /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. + #[macro_export] + macro_rules! debug_print { + ($($arg:tt)*) => ($crate::debug_message(&$crate::format!($($arg)*))); + } + + /// Appends a formatted string to the `debug_message` buffer, as per [`debug_print`] but + /// with a newline appended. + /// + /// # Note + /// + /// This depends on the `debug_message` interface which requires the + /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. + #[macro_export] + macro_rules! debug_println { + () => ($crate::debug_print!("\n")); + ($($arg:tt)*) => ( + $crate::debug_print!("{}\n", $crate::format!($($arg)*)); + ) + } + } else { + #[macro_export] + /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. + macro_rules! debug_print { + ($($arg:tt)*) => (); + } + + #[macro_export] + /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. + macro_rules! debug_println { + () => (); + ($($arg:tt)*) => (); + } + } } diff --git a/substrate/frame/contracts/uapi/src/riscv32.rs b/substrate/frame/contracts/uapi/src/riscv32.rs index 90809da9c1aaf..72efd17caa72b 100644 --- a/substrate/frame/contracts/uapi/src/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/riscv32.rs @@ -14,51 +14,177 @@ use super::{ extract_from_slice, - Ptr32, - Ptr32Mut, Result, ReturnCode, }; -use crate::ReturnFlags; +use crate::{ + engine::on_chain::EncodeScope, + 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 - } + #[polkavm_derive::polkavm_import] + extern "C" { + #[polkavm_import(index = 1)] + pub fn set_storage( + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> u32; + + #[polkavm_import(index = 2)] + pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> u32; + + #[polkavm_import(index = 3)] + pub fn get_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; + + #[polkavm_import(index = 4)] + pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> u32; + + #[polkavm_import(index = 5)] + pub fn take_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; + + #[polkavm_import(index = 48)] + pub fn transfer(account_ptr: *const u8, value_ptr: *const u8) -> u32; + + #[polkavm_import(index = 7)] + pub fn call(ptr: *const u8) -> u32; + + #[polkavm_import(index = 9)] + pub fn delegate_call( + flags: u32, + code_hash_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; + + #[polkavm_import(index = 10)] + pub fn instantiate(ptr: *const u8) -> u32; + + #[polkavm_import(index = 12)] + pub fn terminate(beneficiary_ptr: *const u8); + + #[polkavm_import(index = 13)] + pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 14)] + pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); + + #[polkavm_import(index = 15)] + pub fn caller(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 16)] + pub fn is_contract(account_ptr: *const u8) -> u32; + + #[polkavm_import(index = 17)] + pub fn code_hash( + account_ptr: *const u8, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; + + #[polkavm_import(index = 18)] + pub fn own_code_hash(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 19)] + pub fn caller_is_origin() -> u32; + + #[polkavm_import(index = 20)] + pub fn caller_is_root() -> u32; + + #[polkavm_import(index = 21)] + pub fn address(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 22)] + pub fn weight_to_fee(gas: u64, out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 24)] + pub fn gas_left(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 26)] + pub fn balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 27)] + pub fn value_transferred(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 28)] + pub fn now(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 29)] + pub fn minimum_balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 30)] + pub fn deposit_event( + topics_ptr: *const u8, + topics_len: u32, + data_ptr: *const u8, + data_len: u32, + ); + + #[polkavm_import(index = 31)] + pub fn block_number(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 32)] + pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + + #[polkavm_import(index = 33)] + pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - fn ecall0(mut a0: u32) -> u32 { - unsafe { - asm!( - "ecall", - inout("a0") a0, - ); - } - a0 - } + #[polkavm_import(index = 34)] + pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - pub fn call(func_id: u32, in_ptr: Ptr32<[u8]>) -> ReturnCode { - ReturnCode(ecall(func_id, in_ptr._value)) - } + #[polkavm_import(index = 35)] + pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + + #[polkavm_import(index = 36)] + pub fn call_chain_extension( + id: u32, + input_ptr: *const u8, + input_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; - pub fn call0(func_id: u32) -> ReturnCode { - ReturnCode(ecall0(func_id)) + #[polkavm_import(index = 37)] + pub fn debug_message(str_ptr: *const u8, str_len: u32) -> u32; + + #[polkavm_import(index = 38)] + pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> u32; + + #[polkavm_import(index = 39)] + pub fn ecdsa_recover( + signature_ptr: *const u8, + message_hash_ptr: *const u8, + out_ptr: *mut u8, + ) -> u32; + + #[polkavm_import(index = 40)] + pub fn sr25519_verify( + signature_ptr: *const u8, + pub_key_ptr: *const u8, + message_len: u32, + message_ptr: *const u8, + ) -> u32; + + #[polkavm_import(index = 41)] + pub fn set_code_hash(code_hash_ptr: *const u8) -> u32; + + #[polkavm_import(index = 42)] + pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> u32; } } @@ -73,23 +199,26 @@ pub fn instantiate( ) -> 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), + let mut in_data = [0u8; 64]; + #[allow(trivial_casts)] + ( + code_hash.as_ptr() as u32, gas_limit, - Ptr32::from_slice(endowment), - Ptr32::from_slice(input), + endowment.as_ptr() as u32, + input.as_ptr() as u32, 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), + out_address.as_mut_ptr() as u32, + &mut address_len as *mut _ as u32, + out_return_value.as_mut_ptr() as u32, + &mut return_value_len as *mut _ as u32, + salt.as_ptr() as u32, salt.len() as u32, ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); + .encode_to(&mut EncodeScope::from(in_data.as_mut())); + let ret_val = unsafe { sys::instantiate(in_data.as_ptr()) }; extract_from_slice(out_address, address_len as usize); extract_from_slice(out_return_value, return_value_len as usize); - ret_code.into() + ReturnCode(ret_val).into() } pub fn call( @@ -101,19 +230,22 @@ pub fn call( output: &mut &mut [u8], ) -> Result { let mut output_len = output.len() as u32; - let ret_code = ( + let mut in_data = [0u8; 64]; + #[allow(trivial_casts)] + ( flags, - Ptr32::from_slice(callee), + callee.as_ptr() as u32, gas_limit, - Ptr32::from_slice(value), - Ptr32::from_slice(input), + value.as_ptr() as u32, + input.as_ptr() as u32, input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr() as u32, + &mut output_len as *mut _ as u32, ) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); + .encode_to(&mut EncodeScope::from(in_data.as_mut())); + let ret_val = unsafe { sys::call(in_data.as_ptr()) }; extract_from_slice(output, output_len as usize); - ret_code.into() + ReturnCode(ret_val).into() } pub fn delegate_call( @@ -123,149 +255,142 @@ pub fn delegate_call( 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))); + let ret_val = unsafe { + sys::delegate_call( + flags, + code_hash.as_ptr(), + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; extract_from_slice(output, output_len as usize); - ret_code.into() + ReturnCode(ret_val).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() + let ret_val = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; + ReturnCode(ret_val).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))); + unsafe { + sys::deposit_event( + topics.as_ptr(), + topics.len() as u32, + data.as_ptr(), + data.len() as u32, + ) + } } 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() + let ret_val = unsafe { + sys::set_storage( + key.as_ptr(), + key.len() as u32, + encoded_value.as_ptr(), + encoded_value.len() as u32, + ) + }; + ReturnCode(ret_val).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() + let ret_val = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; + ret_val.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))); + let ret_val = unsafe { + sys::get_storage( + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; extract_from_slice(output, output_len as usize); - ret_code.into() + ReturnCode(ret_val).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))); + let ret_val = unsafe { + sys::take_storage( + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; extract_from_slice(output, output_len as usize); - ret_code.into() + ReturnCode(ret_val).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() + let ret_val = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; + ReturnCode(ret_val).into() } pub fn terminate(beneficiary: &[u8]) -> ! { - (Ptr32::from_slice(beneficiary)) - .using_encoded(|in_data| sys::call(FUNC_ID, Ptr32::from_slice(in_data))); unsafe { + sys::terminate(beneficiary.as_ptr()); 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))); + let ret_val = unsafe { + sys::call_chain_extension( + func_id, + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; extract_from_slice(output, output_len as usize); - ret_code.into_u32() + ret_val } 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))); + unsafe { sys::input(output.as_mut_ptr(), &mut output_len) } 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 { + sys::seal_return( + flags.into_u32(), + return_value.as_ptr(), + return_value.len() as u32, + ); 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() + let ret_val = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; + ReturnCode(ret_val).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))); + unsafe { + sys::$name( + output.as_mut_ptr(), + &mut output_len, + ) + } + extract_from_slice(output, output_len as usize) } )* } @@ -283,14 +408,7 @@ impl_wrapper_for! { 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))); - } + unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &mut output_len) } extract_from_slice(output, output_len as usize); } @@ -318,9 +436,11 @@ pub fn debug_message(message: &str) { // 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)) { + let ret_val = unsafe { sys::debug_message(bytes.as_ptr(), bytes.len() as u32) }; + if !matches!( + ReturnCode(ret_val).into(), + Err(super::Error::LoggingDisabled) + ) { // SAFETY: safe because executing in a single threaded context unsafe { DEBUG_ENABLED = true } } @@ -337,11 +457,13 @@ 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))); + unsafe { + sys::[]( + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + ) + } } } }; @@ -356,19 +478,20 @@ pub fn ecdsa_recover( 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() + let ret_val = unsafe { + sys::ecdsa_recover( + signature.as_ptr(), + message_hash.as_ptr(), + output.as_mut_ptr(), + ) + }; + ReturnCode(ret_val).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() + let ret_val = + unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; + ReturnCode(ret_val).into() } /// **WARNING**: this function is from the [unstable interface](https://github.com/paritytech/substrate/tree/master/frame/contracts#unstable-interfaces), @@ -378,47 +501,42 @@ pub fn sr25519_verify( 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() + let ret_val = unsafe { + sys::sr25519_verify( + signature.as_ptr(), + pub_key.as_ptr(), + message.len() as u32, + message.as_ptr(), + ) + }; + ReturnCode(ret_val).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() + let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; + ReturnCode(ret_val).into_bool() } pub fn caller_is_origin() -> bool { - let ret_val = sys::call0(FUNC_ID); - ret_val.into_bool() + let ret_val = unsafe { sys::caller_is_origin() }; + ReturnCode(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() + let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; + ReturnCode(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() + let ret_val = unsafe { + sys::code_hash(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) + }; + ReturnCode(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))); + unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } } + diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 906912dc65c05..b8c45012d0348 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -28,12 +28,109 @@ use super::{ extract_from_slice, - Ptr32, - Ptr32Mut, Result, ReturnCode, - ReturnFlags, }; +use crate::ReturnFlags; +use core::marker::PhantomData; +use scale::Encode; + +/// 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. + 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. + 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. + fn from_ref(a_ref: &'a mut T) -> Self { + let a_ptr: *mut T = a_ref; + Self::new(a_ptr as u32) + } +} mod sys { use super::{ @@ -656,3 +753,4 @@ pub fn own_code_hash(output: &mut [u8]) { ) } } + From b6dbd528a89250f7fad53fcbd77b83e47d906358 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 14:09:43 +0100 Subject: [PATCH 05/81] fmt --- Cargo.lock | 15 +- substrate/frame/contracts/fixtures/Cargo.toml | 2 +- substrate/frame/contracts/fixtures/build.rs | 10 +- substrate/frame/contracts/fixtures/src/lib.rs | 14 +- .../frame/contracts/uapi/src/allocator.rs | 1 - .../contracts/uapi/src/allocator/bump.rs | 900 +++++++-------- substrate/frame/contracts/uapi/src/prelude.rs | 19 +- substrate/frame/contracts/uapi/src/riscv32.rs | 745 ++++++------ substrate/frame/contracts/uapi/src/wasm32.rs | 1023 ++++++++--------- 9 files changed, 1289 insertions(+), 1440 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e457f1f9ef15c..9b15233ebe627 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9732,7 +9732,7 @@ dependencies = [ [[package]] name = "pallet-contracts-fixtures" -version = "1.0.2" +version = "1.0.5" dependencies = [ "anyhow", "frame-system", @@ -9765,19 +9765,6 @@ dependencies = [ "syn 2.0.38", ] -[[package]] -name = "pallet-contracts-test-contracts" -version = "1.0.0" -dependencies = [ - "anyhow", - "frame-system", - "parity-wasm", - "sp-runtime", - "tempfile", - "toml 0.8.8", - "twox-hash", -] - [[package]] name = "pallet-contracts-uapi" version = "4.0.0-dev" diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 0fd221df31ca9..2833855d5a817 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-contracts-fixtures" publish = false -version = "1.0.3" +version = "1.0.5" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 0e540b3fc4abf..094dbc5f81a92 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -106,9 +106,6 @@ version = '0.1.0' [dependencies] uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}, default-features = false}} -[features] -# default = [ 'uapi/ink-debug' ] - [profile.release] opt-level = 3 lto = true @@ -135,15 +132,18 @@ codegen-units = 1 /// Invoke `cargo build` to compile the contracts. fn invoke_build(current_dir: &Path) -> Result<()> { - let build_res = Command::new("cargo") + let path = env::var("PATH").unwrap_or_default(); + let build_res = Command::new(env::var("CARGO")?) .current_dir(current_dir) + .env_clear() + .env("PATH", path) .env( "RUSTFLAGS", "-C link-arg=-zstack-size=65536 -C link-arg=--import-memory -Clinker-plugin-lto -C target-cpu=mvp", ) .arg("build") .arg("--release") - .arg("--target=wasm32-unknown-unknown") // todo pass risc-v target here as well + .arg("--target=wasm32-unknown-unknown") // TODO pass risc-v target here as well .output() .unwrap(); diff --git a/substrate/frame/contracts/fixtures/src/lib.rs b/substrate/frame/contracts/fixtures/src/lib.rs index ad9b19213d48c..6143c7ffbc608 100644 --- a/substrate/frame/contracts/fixtures/src/lib.rs +++ b/substrate/frame/contracts/fixtures/src/lib.rs @@ -16,7 +16,7 @@ // limitations under the License. use sp_runtime::traits::Hash; -use std::{fs, env::var, path::PathBuf}; +use std::{env::var, fs, path::PathBuf}; fn wat_root_dir() -> PathBuf { match (var("CARGO_MANIFEST_DIR"), var("CARGO_PKG_NAME")) { @@ -31,7 +31,9 @@ fn wat_root_dir() -> PathBuf { /// with it's hash. /// /// The fixture files are located under the `fixtures/` directory. -pub fn legacy_compile_module(fixture_name: &str) -> anyhow::Result<(Vec, ::Output)> +pub fn legacy_compile_module( + fixture_name: &str, +) -> anyhow::Result<(Vec, ::Output)> where T: frame_system::Config, { @@ -43,7 +45,9 @@ where /// Load a given wasm module and returns a wasm binary contents along with it's hash. /// Use the legacy compile_module as fallback, if the rust fixture does not exist yet. -pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, ::Output)> +pub fn compile_module( + fixture_name: &str, +) -> anyhow::Result<(Vec, ::Output)> where T: frame_system::Config, { @@ -61,10 +65,10 @@ where #[cfg(test)] mod test { #[test] - fn dummy_mock_should_be_compiled() { + fn out_dir_should_have_compiled_mocks() { let out_dir: std::path::PathBuf = env!("OUT_DIR").into(); let dummy_wasm = out_dir.join("dummy.wasm"); + println!("dummy_wasm: {:?}", dummy_wasm); assert!(dummy_wasm.exists()); } } - diff --git a/substrate/frame/contracts/uapi/src/allocator.rs b/substrate/frame/contracts/uapi/src/allocator.rs index 2c9e5bf6dbbe3..76814363badf7 100644 --- a/substrate/frame/contracts/uapi/src/allocator.rs +++ b/substrate/frame/contracts/uapi/src/allocator.rs @@ -3,4 +3,3 @@ mod bump; #[cfg(not(any(feature = "std", feature = "no-allocator")))] #[global_allocator] static mut ALLOC: bump::BumpAllocator = bump::BumpAllocator {}; - diff --git a/substrate/frame/contracts/uapi/src/allocator/bump.rs b/substrate/frame/contracts/uapi/src/allocator/bump.rs index f0b3109597d7d..7f924b9b0cc31 100644 --- a/substrate/frame/contracts/uapi/src/allocator/bump.rs +++ b/substrate/frame/contracts/uapi/src/allocator/bump.rs @@ -21,10 +21,7 @@ //! is `64KiB`). We will request new pages of memory as needed until we run out of memory, //! at which point we will crash with an `OOM` error instead of freeing any memory. -use core::alloc::{ - GlobalAlloc, - Layout, -}; +use core::alloc::{GlobalAlloc, Layout}; /// A page in Wasm is `64KiB` const PAGE_SIZE: usize = 64 * 1024; @@ -38,164 +35,157 @@ static mut RISCV_HEAP: [u8; 1024 * 1024] = [0; 1024 * 1024]; pub struct BumpAllocator; unsafe impl GlobalAlloc for BumpAllocator { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if INNER.is_none() { - INNER = Some(InnerAlloc::new()); - }; - match INNER - .as_mut() - .expect("We just set the value above; qed") - .alloc(layout) - { - Some(start) => start as *mut u8, - None => core::ptr::null_mut(), - } - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // A new page in Wasm is guaranteed to already be zero initialized, so we can just - // use our regular `alloc` call here and save a bit of work. - // - // See: https://webassembly.github.io/spec/core/exec/modules.html#growing-memories - self.alloc(layout) - } - - #[inline] - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if INNER.is_none() { + INNER = Some(InnerAlloc::new()); + }; + match INNER.as_mut().expect("We just set the value above; qed").alloc(layout) { + Some(start) => start as *mut u8, + None => core::ptr::null_mut(), + } + } + + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // A new page in Wasm is guaranteed to already be zero initialized, so we can just + // use our regular `alloc` call here and save a bit of work. + // + // See: https://webassembly.github.io/spec/core/exec/modules.html#growing-memories + self.alloc(layout) + } + + #[inline] + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} } #[cfg_attr(feature = "std", derive(Debug, Copy, Clone))] struct InnerAlloc { - /// Points to the start of the next available allocation. - next: usize, + /// Points to the start of the next available allocation. + next: usize, - /// The address of the upper limit of our heap. - upper_limit: usize, + /// The address of the upper limit of our heap. + upper_limit: usize, } impl InnerAlloc { - fn new() -> Self { - Self { - next: Self::heap_start(), - upper_limit: Self::heap_end(), - } - } - - cfg_if::cfg_if! { - if #[cfg(test)] { - fn heap_start() -> usize { - 0 - } - - fn heap_end() -> usize { - 0 - } - - /// Request a `pages` number of page sized sections of Wasm memory. Each page is `64KiB` in size. - /// - /// Returns `None` if a page is not available. - /// - /// This implementation is only meant to be used for testing, since we cannot (easily) - /// test the `wasm32` implementation. - fn request_pages(&mut self, _pages: usize) -> Option { - Some(self.upper_limit) - } - } else if #[cfg(feature = "std")] { - fn heap_start() -> usize { - 0 - } - - fn heap_end() -> usize { - 0 - } - - fn request_pages(&mut self, _pages: usize) -> Option { - unreachable!( - "This branch is only used to keep the compiler happy when building tests, and - should never actually be called outside of a test run." - ) - } - } else if #[cfg(target_arch = "wasm32")] { - fn heap_start() -> usize { - extern "C" { - static __heap_base: usize; - } - // # SAFETY - // - // The `__heap_base` symbol is defined by the wasm linker and is guaranteed - // to point to the start of the heap. - let heap_start = unsafe { &__heap_base as *const usize as usize }; - // if the symbol isn't found it will resolve to 0 - // for that to happen the rust compiler or linker need to break or change - assert_ne!(heap_start, 0, "Can't find `__heap_base` symbol."); - heap_start - } - - fn heap_end() -> usize { - // Cannot overflow on this architecture - core::arch::wasm32::memory_size(0) * PAGE_SIZE - } - - /// Request a `pages` number of pages of Wasm memory. Each page is `64KiB` in size. - /// - /// Returns `None` if a page is not available. - fn request_pages(&mut self, pages: usize) -> Option { - let prev_page = core::arch::wasm32::memory_grow(0, pages); - if prev_page == usize::MAX { - return None; - } - - // Cannot overflow on this architecture - Some(prev_page * PAGE_SIZE) - } - } else if #[cfg(target_arch = "riscv32")] { - fn heap_start() -> usize { - unsafe { - RISCV_HEAP.as_mut_ptr() as usize - } - } - - fn heap_end() -> usize { - Self::heap_start() + unsafe { RISCV_HEAP.len() } - } - - fn request_pages(&mut self, _pages: usize) -> Option { - // On riscv the memory can't be grown - None - } - } else { - core::compile_error!("ink! only supports wasm32 and riscv32"); - } - } - - /// Tries to allocate enough memory on the heap for the given `Layout`. If there is - /// not enough room on the heap it'll try and grow it by a page. - /// - /// Note: This implementation results in internal fragmentation when allocating across - /// pages. - fn alloc(&mut self, layout: Layout) -> Option { - let alloc_start = self.next; - - let aligned_size = layout.pad_to_align().size(); - let alloc_end = alloc_start.checked_add(aligned_size)?; - - if alloc_end > self.upper_limit { - let required_pages = required_pages(aligned_size)?; - let page_start = self.request_pages(required_pages)?; - - self.upper_limit = required_pages - .checked_mul(PAGE_SIZE) - .and_then(|pages| page_start.checked_add(pages))?; - self.next = page_start.checked_add(aligned_size)?; - - Some(page_start) - } else { - self.next = alloc_end; - Some(alloc_start) - } - } + fn new() -> Self { + Self { next: Self::heap_start(), upper_limit: Self::heap_end() } + } + + cfg_if::cfg_if! { + if #[cfg(test)] { + fn heap_start() -> usize { + 0 + } + + fn heap_end() -> usize { + 0 + } + + /// Request a `pages` number of page sized sections of Wasm memory. Each page is `64KiB` in size. + /// + /// Returns `None` if a page is not available. + /// + /// This implementation is only meant to be used for testing, since we cannot (easily) + /// test the `wasm32` implementation. + fn request_pages(&mut self, _pages: usize) -> Option { + Some(self.upper_limit) + } + } else if #[cfg(feature = "std")] { + fn heap_start() -> usize { + 0 + } + + fn heap_end() -> usize { + 0 + } + + fn request_pages(&mut self, _pages: usize) -> Option { + unreachable!( + "This branch is only used to keep the compiler happy when building tests, and + should never actually be called outside of a test run." + ) + } + } else if #[cfg(target_arch = "wasm32")] { + fn heap_start() -> usize { + extern "C" { + static __heap_base: usize; + } + // # SAFETY + // + // The `__heap_base` symbol is defined by the wasm linker and is guaranteed + // to point to the start of the heap. + let heap_start = unsafe { &__heap_base as *const usize as usize }; + // if the symbol isn't found it will resolve to 0 + // for that to happen the rust compiler or linker need to break or change + assert_ne!(heap_start, 0, "Can't find `__heap_base` symbol."); + heap_start + } + + fn heap_end() -> usize { + // Cannot overflow on this architecture + core::arch::wasm32::memory_size(0) * PAGE_SIZE + } + + /// Request a `pages` number of pages of Wasm memory. Each page is `64KiB` in size. + /// + /// Returns `None` if a page is not available. + fn request_pages(&mut self, pages: usize) -> Option { + let prev_page = core::arch::wasm32::memory_grow(0, pages); + if prev_page == usize::MAX { + return None; + } + + // Cannot overflow on this architecture + Some(prev_page * PAGE_SIZE) + } + } else if #[cfg(target_arch = "riscv32")] { + fn heap_start() -> usize { + unsafe { + RISCV_HEAP.as_mut_ptr() as usize + } + } + + fn heap_end() -> usize { + Self::heap_start() + unsafe { RISCV_HEAP.len() } + } + + fn request_pages(&mut self, _pages: usize) -> Option { + // On riscv the memory can't be grown + None + } + } else { + core::compile_error!("ink! only supports wasm32 and riscv32"); + } + } + + /// Tries to allocate enough memory on the heap for the given `Layout`. If there is + /// not enough room on the heap it'll try and grow it by a page. + /// + /// Note: This implementation results in internal fragmentation when allocating across + /// pages. + fn alloc(&mut self, layout: Layout) -> Option { + let alloc_start = self.next; + + let aligned_size = layout.pad_to_align().size(); + let alloc_end = alloc_start.checked_add(aligned_size)?; + + if alloc_end > self.upper_limit { + let required_pages = required_pages(aligned_size)?; + let page_start = self.request_pages(required_pages)?; + + self.upper_limit = required_pages + .checked_mul(PAGE_SIZE) + .and_then(|pages| page_start.checked_add(pages))?; + self.next = page_start.checked_add(aligned_size)?; + + Some(page_start) + } else { + self.next = alloc_end; + Some(alloc_start) + } + } } /// Calculates the number of pages of memory needed for an allocation of `size` bytes. @@ -205,353 +195,333 @@ impl InnerAlloc { /// satisfy the allocation. #[inline] fn required_pages(size: usize) -> Option { - size.checked_add(PAGE_SIZE - 1) - .and_then(|num| num.checked_div(PAGE_SIZE)) + size.checked_add(PAGE_SIZE - 1).and_then(|num| num.checked_div(PAGE_SIZE)) } #[cfg(test)] mod tests { - use super::*; - use std::mem::size_of; + use super::*; + use std::mem::size_of; - #[test] - fn can_alloc_no_bytes() { - let mut inner = InnerAlloc::new(); + #[test] + fn can_alloc_no_bytes() { + let mut inner = InnerAlloc::new(); - let layout = Layout::new::<()>(); - assert_eq!(inner.alloc(layout), Some(0)); + let layout = Layout::new::<()>(); + assert_eq!(inner.alloc(layout), Some(0)); - let expected_limit = - PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); + let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); - let expected_alloc_start = size_of::<()>(); - assert_eq!(inner.next, expected_alloc_start); - } + let expected_alloc_start = size_of::<()>(); + assert_eq!(inner.next, expected_alloc_start); + } - #[test] - fn can_alloc_a_byte() { - let mut inner = InnerAlloc::new(); + #[test] + fn can_alloc_a_byte() { + let mut inner = InnerAlloc::new(); - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(0)); + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(0)); - let expected_limit = - PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); + let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); - let expected_alloc_start = size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } + let expected_alloc_start = size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } - #[test] - fn can_alloc_a_foobarbaz() { - let mut inner = InnerAlloc::new(); + #[test] + fn can_alloc_a_foobarbaz() { + let mut inner = InnerAlloc::new(); - struct FooBarBaz { - _foo: u32, - _bar: u128, - _baz: (u16, bool), - } + struct FooBarBaz { + _foo: u32, + _bar: u128, + _baz: (u16, bool), + } - let layout = Layout::new::(); - let mut total_size = 0; + let layout = Layout::new::(); + let mut total_size = 0; - let allocations = 3; - for _ in 0..allocations { - assert!(inner.alloc(layout).is_some()); - total_size += layout.pad_to_align().size(); - } + let allocations = 3; + for _ in 0..allocations { + assert!(inner.alloc(layout).is_some()); + total_size += layout.pad_to_align().size(); + } - let expected_limit = PAGE_SIZE * required_pages(total_size).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); + let expected_limit = PAGE_SIZE * required_pages(total_size).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); - let expected_alloc_start = allocations * size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } + let expected_alloc_start = allocations * size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } - #[test] - fn can_alloc_across_pages() { - let mut inner = InnerAlloc::new(); + #[test] + fn can_alloc_across_pages() { + let mut inner = InnerAlloc::new(); - struct Foo { - _foo: [u8; PAGE_SIZE - 1], - } + struct Foo { + _foo: [u8; PAGE_SIZE - 1], + } - // First, let's allocate a struct which is _almost_ a full page - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(0)); + // First, let's allocate a struct which is _almost_ a full page + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(0)); - let expected_limit = - PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); + let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); - let expected_alloc_start = size_of::(); - assert_eq!(inner.next, expected_alloc_start); + let expected_alloc_start = size_of::(); + assert_eq!(inner.next, expected_alloc_start); - // Now we'll allocate two bytes which will push us over to the next page - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(PAGE_SIZE)); + // Now we'll allocate two bytes which will push us over to the next page + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(PAGE_SIZE)); - let expected_limit = 2 * PAGE_SIZE; - assert_eq!(inner.upper_limit, expected_limit); + let expected_limit = 2 * PAGE_SIZE; + assert_eq!(inner.upper_limit, expected_limit); - // Notice that we start the allocation on the second page, instead of making use - // of the remaining byte on the first page - let expected_alloc_start = PAGE_SIZE + size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } + // Notice that we start the allocation on the second page, instead of making use + // of the remaining byte on the first page + let expected_alloc_start = PAGE_SIZE + size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } - #[test] - fn can_alloc_multiple_pages() { - let mut inner = InnerAlloc::new(); + #[test] + fn can_alloc_multiple_pages() { + let mut inner = InnerAlloc::new(); - struct Foo { - _foo: [u8; 2 * PAGE_SIZE], - } + struct Foo { + _foo: [u8; 2 * PAGE_SIZE], + } - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(0)); + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(0)); - let expected_limit = - PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); + let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); + assert_eq!(inner.upper_limit, expected_limit); - let expected_alloc_start = size_of::(); - assert_eq!(inner.next, expected_alloc_start); + let expected_alloc_start = size_of::(); + assert_eq!(inner.next, expected_alloc_start); - // Now we want to make sure that the state of our allocator is correct for any - // subsequent allocations - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(2 * PAGE_SIZE)); + // Now we want to make sure that the state of our allocator is correct for any + // subsequent allocations + let layout = Layout::new::(); + assert_eq!(inner.alloc(layout), Some(2 * PAGE_SIZE)); - let expected_limit = 3 * PAGE_SIZE; - assert_eq!(inner.upper_limit, expected_limit); + let expected_limit = 3 * PAGE_SIZE; + assert_eq!(inner.upper_limit, expected_limit); - let expected_alloc_start = 2 * PAGE_SIZE + size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } + let expected_alloc_start = 2 * PAGE_SIZE + size_of::(); + assert_eq!(inner.next, expected_alloc_start); + } } #[cfg(all(test, feature = "ink-fuzz-tests"))] mod fuzz_tests { - use super::*; - use quickcheck::{ - quickcheck, - TestResult, - }; - use std::mem::size_of; - - #[quickcheck] - fn should_allocate_arbitrary_sized_bytes(n: usize) -> TestResult { - let mut inner = InnerAlloc::new(); - - // If we're going to end up creating an invalid `Layout` we don't want to use - // these test inputs. - let layout = match Layout::from_size_align(n, size_of::()) { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - let size = layout.pad_to_align().size(); - assert_eq!( - inner.alloc(layout), - Some(0), - "The given pointer for the allocation doesn't match." - ); - - let expected_alloc_start = size; - assert_eq!( - inner.next, expected_alloc_start, - "Our next allocation doesn't match where it should start." - ); - - let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); - assert_eq!( - inner.upper_limit, expected_limit, - "The upper bound of our heap doesn't match." - ); - - TestResult::passed() - } - - #[quickcheck] - fn should_allocate_regardless_of_alignment_size( - n: usize, - align: usize, - ) -> TestResult { - let aligns = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]; - let align = aligns[align % aligns.len()]; - - let mut inner = InnerAlloc::new(); - - // If we're going to end up creating an invalid `Layout` we don't want to use - // these test inputs. - let layout = match Layout::from_size_align(n, align) { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - let size = layout.pad_to_align().size(); - assert_eq!( - inner.alloc(layout), - Some(0), - "The given pointer for the allocation doesn't match." - ); - - let expected_alloc_start = size; - assert_eq!( - inner.next, expected_alloc_start, - "Our next allocation doesn't match where it should start." - ); - - let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); - assert_eq!( - inner.upper_limit, expected_limit, - "The upper bound of our heap doesn't match." - ); - - TestResult::passed() - } - - /// The idea behind this fuzz test is to check a series of allocation sequences. For - /// example, we maybe have back to back runs as follows: - /// - /// 1. `vec![1, 2, 3]` - /// 2. `vec![4, 5, 6, 7]` - /// 3. `vec![8]` - /// - /// Each of the vectors represents one sequence of allocations. Within each sequence - /// the individual size of allocations will be randomly selected by `quickcheck`. - #[quickcheck] - fn should_allocate_arbitrary_byte_sequences(sequence: Vec) -> TestResult { - let mut inner = InnerAlloc::new(); - - if sequence.is_empty() { - return TestResult::discard() - } - - // We don't want any negative numbers so we can be sure our conversions to `usize` - // later are valid - if !sequence.iter().all(|n| n.is_positive()) { - return TestResult::discard() - } - - // We can't just use `required_pages(Iterator::sum())` here because it ends up - // underestimating the pages due to the ceil rounding at each step - let pages_required = sequence - .iter() - .fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); - let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); - - // We know this is going to end up overflowing, we'll check this case in a - // different test - if pages_required > max_pages { - return TestResult::discard() - } - - let mut expected_alloc_start = 0; - let mut total_bytes_requested = 0; - let mut total_bytes_fragmented = 0; - - for alloc in sequence { - let layout = Layout::from_size_align(alloc as usize, size_of::()); - let layout = match layout { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - let size = layout.pad_to_align().size(); - - let current_page_limit = PAGE_SIZE * required_pages(inner.next).unwrap(); - let is_too_big_for_current_page = inner.next + size > current_page_limit; - - if is_too_big_for_current_page { - let fragmented_in_current_page = current_page_limit - inner.next; - total_bytes_fragmented += fragmented_in_current_page; - - // We expect our next allocation to be aligned to the start of the next - // page boundary - expected_alloc_start = inner.upper_limit; - } - - assert_eq!( - inner.alloc(layout), - Some(expected_alloc_start), - "The given pointer for the allocation doesn't match." - ); - total_bytes_requested += size; - - expected_alloc_start = total_bytes_requested + total_bytes_fragmented; - assert_eq!( - inner.next, expected_alloc_start, - "Our next allocation doesn't match where it should start." - ); - - let pages_required = required_pages(expected_alloc_start).unwrap(); - let expected_limit = PAGE_SIZE * pages_required; - assert_eq!( - inner.upper_limit, expected_limit, - "The upper bound of our heap doesn't match." - ); - } - - TestResult::passed() - } - - // For this test we have sequences of allocations which will eventually overflow the - // maximum amount of pages (in practice this means our heap will be OOM). - // - // We don't care about the allocations that succeed (those are checked in other - // tests), we just care that eventually an allocation doesn't success. - #[quickcheck] - fn should_not_allocate_arbitrary_byte_sequences_which_eventually_overflow( - sequence: Vec, - ) -> TestResult { - let mut inner = InnerAlloc::new(); - - if sequence.is_empty() { - return TestResult::discard() - } - - // We don't want any negative numbers so we can be sure our conversions to `usize` - // later are valid - if !sequence.iter().all(|n| n.is_positive()) { - return TestResult::discard() - } - - // We can't just use `required_pages(Iterator::sum())` here because it ends up - // underestimating the pages due to the ceil rounding at each step - let pages_required = sequence - .iter() - .fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); - let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); - - // We want to explicitly test for the case where a series of allocations - // eventually runs out of pages of memory - if pages_required <= max_pages { - return TestResult::discard() - } - - let mut results = vec![]; - for alloc in sequence { - let layout = Layout::from_size_align(alloc as usize, size_of::()); - let layout = match layout { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - results.push(inner.alloc(layout)); - } - - // Ensure that at least one of the allocations ends up overflowing our - // calculations. - assert!( - results.iter().any(|r| r.is_none()), - "Expected an allocation to overflow our heap, but this didn't happen." - ); - - TestResult::passed() - } + use super::*; + use quickcheck::{quickcheck, TestResult}; + use std::mem::size_of; + + #[quickcheck] + fn should_allocate_arbitrary_sized_bytes(n: usize) -> TestResult { + let mut inner = InnerAlloc::new(); + + // If we're going to end up creating an invalid `Layout` we don't want to use + // these test inputs. + let layout = match Layout::from_size_align(n, size_of::()) { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + let size = layout.pad_to_align().size(); + assert_eq!( + inner.alloc(layout), + Some(0), + "The given pointer for the allocation doesn't match." + ); + + let expected_alloc_start = size; + assert_eq!( + inner.next, expected_alloc_start, + "Our next allocation doesn't match where it should start." + ); + + let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); + assert_eq!(inner.upper_limit, expected_limit, "The upper bound of our heap doesn't match."); + + TestResult::passed() + } + + #[quickcheck] + fn should_allocate_regardless_of_alignment_size(n: usize, align: usize) -> TestResult { + let aligns = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]; + let align = aligns[align % aligns.len()]; + + let mut inner = InnerAlloc::new(); + + // If we're going to end up creating an invalid `Layout` we don't want to use + // these test inputs. + let layout = match Layout::from_size_align(n, align) { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + let size = layout.pad_to_align().size(); + assert_eq!( + inner.alloc(layout), + Some(0), + "The given pointer for the allocation doesn't match." + ); + + let expected_alloc_start = size; + assert_eq!( + inner.next, expected_alloc_start, + "Our next allocation doesn't match where it should start." + ); + + let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); + assert_eq!(inner.upper_limit, expected_limit, "The upper bound of our heap doesn't match."); + + TestResult::passed() + } + + /// The idea behind this fuzz test is to check a series of allocation sequences. For + /// example, we maybe have back to back runs as follows: + /// + /// 1. `vec![1, 2, 3]` + /// 2. `vec![4, 5, 6, 7]` + /// 3. `vec![8]` + /// + /// Each of the vectors represents one sequence of allocations. Within each sequence + /// the individual size of allocations will be randomly selected by `quickcheck`. + #[quickcheck] + fn should_allocate_arbitrary_byte_sequences(sequence: Vec) -> TestResult { + let mut inner = InnerAlloc::new(); + + if sequence.is_empty() { + return TestResult::discard() + } + + // We don't want any negative numbers so we can be sure our conversions to `usize` + // later are valid + if !sequence.iter().all(|n| n.is_positive()) { + return TestResult::discard() + } + + // We can't just use `required_pages(Iterator::sum())` here because it ends up + // underestimating the pages due to the ceil rounding at each step + let pages_required = + sequence.iter().fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); + let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); + + // We know this is going to end up overflowing, we'll check this case in a + // different test + if pages_required > max_pages { + return TestResult::discard() + } + + let mut expected_alloc_start = 0; + let mut total_bytes_requested = 0; + let mut total_bytes_fragmented = 0; + + for alloc in sequence { + let layout = Layout::from_size_align(alloc as usize, size_of::()); + let layout = match layout { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + let size = layout.pad_to_align().size(); + + let current_page_limit = PAGE_SIZE * required_pages(inner.next).unwrap(); + let is_too_big_for_current_page = inner.next + size > current_page_limit; + + if is_too_big_for_current_page { + let fragmented_in_current_page = current_page_limit - inner.next; + total_bytes_fragmented += fragmented_in_current_page; + + // We expect our next allocation to be aligned to the start of the next + // page boundary + expected_alloc_start = inner.upper_limit; + } + + assert_eq!( + inner.alloc(layout), + Some(expected_alloc_start), + "The given pointer for the allocation doesn't match." + ); + total_bytes_requested += size; + + expected_alloc_start = total_bytes_requested + total_bytes_fragmented; + assert_eq!( + inner.next, expected_alloc_start, + "Our next allocation doesn't match where it should start." + ); + + let pages_required = required_pages(expected_alloc_start).unwrap(); + let expected_limit = PAGE_SIZE * pages_required; + assert_eq!( + inner.upper_limit, expected_limit, + "The upper bound of our heap doesn't match." + ); + } + + TestResult::passed() + } + + // For this test we have sequences of allocations which will eventually overflow the + // maximum amount of pages (in practice this means our heap will be OOM). + // + // We don't care about the allocations that succeed (those are checked in other + // tests), we just care that eventually an allocation doesn't success. + #[quickcheck] + fn should_not_allocate_arbitrary_byte_sequences_which_eventually_overflow( + sequence: Vec, + ) -> TestResult { + let mut inner = InnerAlloc::new(); + + if sequence.is_empty() { + return TestResult::discard() + } + + // We don't want any negative numbers so we can be sure our conversions to `usize` + // later are valid + if !sequence.iter().all(|n| n.is_positive()) { + return TestResult::discard() + } + + // We can't just use `required_pages(Iterator::sum())` here because it ends up + // underestimating the pages due to the ceil rounding at each step + let pages_required = + sequence.iter().fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); + let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); + + // We want to explicitly test for the case where a series of allocations + // eventually runs out of pages of memory + if pages_required <= max_pages { + return TestResult::discard() + } + + let mut results = vec![]; + for alloc in sequence { + let layout = Layout::from_size_align(alloc as usize, size_of::()); + let layout = match layout { + Ok(l) => l, + Err(_) => return TestResult::discard(), + }; + + results.push(inner.alloc(layout)); + } + + // Ensure that at least one of the allocations ends up overflowing our + // calculations. + assert!( + results.iter().any(|r| r.is_none()), + "Expected an allocation to overflow our heap, but this didn't happen." + ); + + TestResult::passed() + } } - diff --git a/substrate/frame/contracts/uapi/src/prelude.rs b/substrate/frame/contracts/uapi/src/prelude.rs index 48399108d3740..10b1fa576d3d8 100644 --- a/substrate/frame/contracts/uapi/src/prelude.rs +++ b/substrate/frame/contracts/uapi/src/prelude.rs @@ -19,15 +19,14 @@ //! crates. If needed we shall instead enhance the exposed types here. cfg_if::cfg_if! { - if #[cfg(feature = "std")] { - pub use std::{ - format, - }; - } else { + if #[cfg(feature = "std")] { + pub use std::{ + format, + }; + } else { extern crate alloc; - pub use alloc::{ - format, - }; - } + pub use alloc::{ + format, + }; + } } - diff --git a/substrate/frame/contracts/uapi/src/riscv32.rs b/substrate/frame/contracts/uapi/src/riscv32.rs index 72efd17caa72b..fcb955c89d8ce 100644 --- a/substrate/frame/contracts/uapi/src/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/riscv32.rs @@ -12,371 +12,336 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{ - extract_from_slice, - Result, - ReturnCode, -}; -use crate::{ - engine::on_chain::EncodeScope, - ReturnFlags, -}; +use super::{extract_from_slice, Result, ReturnCode}; +use crate::{engine::on_chain::EncodeScope, ReturnFlags}; use scale::Encode; mod sys { - #[polkavm_derive::polkavm_import] - extern "C" { - #[polkavm_import(index = 1)] - pub fn set_storage( - key_ptr: *const u8, - key_len: u32, - value_ptr: *const u8, - value_len: u32, - ) -> u32; - - #[polkavm_import(index = 2)] - pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> u32; - - #[polkavm_import(index = 3)] - pub fn get_storage( - key_ptr: *const u8, - key_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 4)] - pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> u32; - - #[polkavm_import(index = 5)] - pub fn take_storage( - key_ptr: *const u8, - key_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 48)] - pub fn transfer(account_ptr: *const u8, value_ptr: *const u8) -> u32; - - #[polkavm_import(index = 7)] - pub fn call(ptr: *const u8) -> u32; - - #[polkavm_import(index = 9)] - pub fn delegate_call( - flags: u32, - code_hash_ptr: *const u8, - input_data_ptr: *const u8, - input_data_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 10)] - pub fn instantiate(ptr: *const u8) -> u32; - - #[polkavm_import(index = 12)] - pub fn terminate(beneficiary_ptr: *const u8); - - #[polkavm_import(index = 13)] - pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 14)] - pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); - - #[polkavm_import(index = 15)] - pub fn caller(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 16)] - pub fn is_contract(account_ptr: *const u8) -> u32; - - #[polkavm_import(index = 17)] - pub fn code_hash( - account_ptr: *const u8, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 18)] - pub fn own_code_hash(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 19)] - pub fn caller_is_origin() -> u32; - - #[polkavm_import(index = 20)] - pub fn caller_is_root() -> u32; - - #[polkavm_import(index = 21)] - pub fn address(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 22)] - pub fn weight_to_fee(gas: u64, out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 24)] - pub fn gas_left(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 26)] - pub fn balance(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 27)] - pub fn value_transferred(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 28)] - pub fn now(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 29)] - pub fn minimum_balance(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 30)] - pub fn deposit_event( - topics_ptr: *const u8, - topics_len: u32, - data_ptr: *const u8, - data_len: u32, - ); - - #[polkavm_import(index = 31)] - pub fn block_number(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 32)] - pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - - #[polkavm_import(index = 33)] - pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + #[polkavm_derive::polkavm_import] + extern "C" { + #[polkavm_import(index = 1)] + pub fn set_storage( + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> u32; - #[polkavm_import(index = 34)] - pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + #[polkavm_import(index = 2)] + pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> u32; - #[polkavm_import(index = 35)] - pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - - #[polkavm_import(index = 36)] - pub fn call_chain_extension( - id: u32, - input_ptr: *const u8, - input_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; + #[polkavm_import(index = 3)] + pub fn get_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; - #[polkavm_import(index = 37)] - pub fn debug_message(str_ptr: *const u8, str_len: u32) -> u32; + #[polkavm_import(index = 4)] + pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> u32; - #[polkavm_import(index = 38)] - pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> u32; + #[polkavm_import(index = 5)] + pub fn take_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; + + #[polkavm_import(index = 48)] + pub fn transfer(account_ptr: *const u8, value_ptr: *const u8) -> u32; - #[polkavm_import(index = 39)] - pub fn ecdsa_recover( - signature_ptr: *const u8, - message_hash_ptr: *const u8, - out_ptr: *mut u8, - ) -> u32; + #[polkavm_import(index = 7)] + pub fn call(ptr: *const u8) -> u32; - #[polkavm_import(index = 40)] - pub fn sr25519_verify( - signature_ptr: *const u8, - pub_key_ptr: *const u8, - message_len: u32, - message_ptr: *const u8, - ) -> u32; + #[polkavm_import(index = 9)] + pub fn delegate_call( + flags: u32, + code_hash_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; + + #[polkavm_import(index = 10)] + pub fn instantiate(ptr: *const u8) -> u32; + + #[polkavm_import(index = 12)] + pub fn terminate(beneficiary_ptr: *const u8); + + #[polkavm_import(index = 13)] + pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 14)] + pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); + + #[polkavm_import(index = 15)] + pub fn caller(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 16)] + pub fn is_contract(account_ptr: *const u8) -> u32; + + #[polkavm_import(index = 17)] + pub fn code_hash(account_ptr: *const u8, out_ptr: *mut u8, out_len_ptr: *mut u32) -> u32; + + #[polkavm_import(index = 18)] + pub fn own_code_hash(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 19)] + pub fn caller_is_origin() -> u32; + + #[polkavm_import(index = 20)] + pub fn caller_is_root() -> u32; + + #[polkavm_import(index = 21)] + pub fn address(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 22)] + pub fn weight_to_fee(gas: u64, out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 24)] + pub fn gas_left(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 26)] + pub fn balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 27)] + pub fn value_transferred(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 28)] + pub fn now(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 29)] + pub fn minimum_balance(out_ptr: *mut u8, out_len_ptr: *mut u32); + + #[polkavm_import(index = 30)] + pub fn deposit_event( + topics_ptr: *const u8, + topics_len: u32, + data_ptr: *const u8, + data_len: u32, + ); + + #[polkavm_import(index = 31)] + pub fn block_number(out_ptr: *mut u8, out_len_ptr: *mut u32); - #[polkavm_import(index = 41)] - pub fn set_code_hash(code_hash_ptr: *const u8) -> u32; + #[polkavm_import(index = 32)] + pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - #[polkavm_import(index = 42)] - pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> u32; - } + #[polkavm_import(index = 33)] + pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + + #[polkavm_import(index = 34)] + pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + + #[polkavm_import(index = 35)] + pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); + + #[polkavm_import(index = 36)] + pub fn call_chain_extension( + id: u32, + input_ptr: *const u8, + input_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> u32; + + #[polkavm_import(index = 37)] + pub fn debug_message(str_ptr: *const u8, str_len: u32) -> u32; + + #[polkavm_import(index = 38)] + pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> u32; + + #[polkavm_import(index = 39)] + pub fn ecdsa_recover( + signature_ptr: *const u8, + message_hash_ptr: *const u8, + out_ptr: *mut u8, + ) -> u32; + + #[polkavm_import(index = 40)] + pub fn sr25519_verify( + signature_ptr: *const u8, + pub_key_ptr: *const u8, + message_len: u32, + message_ptr: *const u8, + ) -> u32; + + #[polkavm_import(index = 41)] + pub fn set_code_hash(code_hash_ptr: *const u8) -> u32; + + #[polkavm_import(index = 42)] + pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> u32; + } } 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], + 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 mut in_data = [0u8; 64]; - #[allow(trivial_casts)] - ( - code_hash.as_ptr() as u32, - gas_limit, - endowment.as_ptr() as u32, - input.as_ptr() as u32, - input.len() as u32, - out_address.as_mut_ptr() as u32, - &mut address_len as *mut _ as u32, - out_return_value.as_mut_ptr() as u32, - &mut return_value_len as *mut _ as u32, - salt.as_ptr() as u32, - salt.len() as u32, - ) - .encode_to(&mut EncodeScope::from(in_data.as_mut())); - let ret_val = unsafe { sys::instantiate(in_data.as_ptr()) }; - extract_from_slice(out_address, address_len as usize); - extract_from_slice(out_return_value, return_value_len as usize); - ReturnCode(ret_val).into() + let mut address_len = out_address.len() as u32; + let mut return_value_len = out_return_value.len() as u32; + let mut in_data = [0u8; 64]; + #[allow(trivial_casts)] + ( + code_hash.as_ptr() as u32, + gas_limit, + endowment.as_ptr() as u32, + input.as_ptr() as u32, + input.len() as u32, + out_address.as_mut_ptr() as u32, + &mut address_len as *mut _ as u32, + out_return_value.as_mut_ptr() as u32, + &mut return_value_len as *mut _ as u32, + salt.as_ptr() as u32, + salt.len() as u32, + ) + .encode_to(&mut EncodeScope::from(in_data.as_mut())); + let ret_val = unsafe { sys::instantiate(in_data.as_ptr()) }; + extract_from_slice(out_address, address_len as usize); + extract_from_slice(out_return_value, return_value_len as usize); + ReturnCode(ret_val).into() } pub fn call( - flags: u32, - callee: &[u8], - gas_limit: u64, - value: &[u8], - input: &[u8], - output: &mut &mut [u8], + 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 mut in_data = [0u8; 64]; - #[allow(trivial_casts)] - ( - flags, - callee.as_ptr() as u32, - gas_limit, - value.as_ptr() as u32, - input.as_ptr() as u32, - input.len() as u32, - output.as_mut_ptr() as u32, - &mut output_len as *mut _ as u32, - ) - .encode_to(&mut EncodeScope::from(in_data.as_mut())); - let ret_val = unsafe { sys::call(in_data.as_ptr()) }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() + let mut output_len = output.len() as u32; + let mut in_data = [0u8; 64]; + #[allow(trivial_casts)] + ( + flags, + callee.as_ptr() as u32, + gas_limit, + value.as_ptr() as u32, + input.as_ptr() as u32, + input.len() as u32, + output.as_mut_ptr() as u32, + &mut output_len as *mut _ as u32, + ) + .encode_to(&mut EncodeScope::from(in_data.as_mut())); + let ret_val = unsafe { sys::call(in_data.as_ptr()) }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).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_val = unsafe { - sys::delegate_call( - flags, - code_hash.as_ptr(), - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).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_val = unsafe { + sys::delegate_call( + flags, + code_hash.as_ptr(), + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).into() } pub fn transfer(account_id: &[u8], value: &[u8]) -> Result { - let ret_val = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; - ReturnCode(ret_val).into() + let ret_val = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; + ReturnCode(ret_val).into() } pub fn deposit_event(topics: &[u8], data: &[u8]) { - unsafe { - sys::deposit_event( - topics.as_ptr(), - topics.len() as u32, - data.as_ptr(), - data.len() as u32, - ) - } + unsafe { + sys::deposit_event(topics.as_ptr(), topics.len() as u32, data.as_ptr(), data.len() as u32) + } } pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { - let ret_val = unsafe { - sys::set_storage( - key.as_ptr(), - key.len() as u32, - encoded_value.as_ptr(), - encoded_value.len() as u32, - ) - }; - ReturnCode(ret_val).into() + let ret_val = unsafe { + sys::set_storage( + key.as_ptr(), + key.len() as u32, + encoded_value.as_ptr(), + encoded_value.len() as u32, + ) + }; + ReturnCode(ret_val).into() } pub fn clear_storage(key: &[u8]) -> Option { - let ret_val = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; - ret_val.into() + let ret_val = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; + ret_val.into() } pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::get_storage( - key.as_ptr(), - key.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() + let mut output_len = output.len() as u32; + let ret_val = unsafe { + sys::get_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) + }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).into() } pub fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::take_storage( - key.as_ptr(), - key.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() + let mut output_len = output.len() as u32; + let ret_val = unsafe { + sys::take_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) + }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).into() } pub fn storage_contains(key: &[u8]) -> Option { - let ret_val = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; - ReturnCode(ret_val).into() + let ret_val = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; + ReturnCode(ret_val).into() } pub fn terminate(beneficiary: &[u8]) -> ! { - unsafe { - sys::terminate(beneficiary.as_ptr()); - core::hint::unreachable_unchecked(); - } + unsafe { + sys::terminate(beneficiary.as_ptr()); + 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_val = unsafe { - sys::call_chain_extension( - func_id, - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - }; - extract_from_slice(output, output_len as usize); - ret_val + let mut output_len = output.len() as u32; + let ret_val = unsafe { + sys::call_chain_extension( + func_id, + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; + extract_from_slice(output, output_len as usize); + ret_val } pub fn input(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::input(output.as_mut_ptr(), &mut output_len) } - extract_from_slice(output, output_len as usize); + let mut output_len = output.len() as u32; + unsafe { sys::input(output.as_mut_ptr(), &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(), - return_value.as_ptr(), - return_value.len() as u32, - ); - core::hint::unreachable_unchecked(); - } + unsafe { + sys::seal_return(flags.into_u32(), return_value.as_ptr(), return_value.len() as u32); + core::hint::unreachable_unchecked(); + } } pub fn call_runtime(call: &[u8]) -> Result { - let ret_val = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; - ReturnCode(ret_val).into() + let ret_val = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; + ReturnCode(ret_val).into() } macro_rules! impl_wrapper_for { @@ -396,20 +361,20 @@ macro_rules! impl_wrapper_for { } } impl_wrapper_for! { - caller, - block_number, - address, - balance, - gas_left, - value_transferred, - now, - minimum_balance, + 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; - unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &mut output_len) } - extract_from_slice(output, output_len as usize); + let mut output_len = output.len() as u32; + unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &mut output_len) } + extract_from_slice(output, output_len as usize); } #[cfg(feature = "ink-debug")] @@ -424,29 +389,26 @@ pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { /// 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_val = unsafe { sys::debug_message(bytes.as_ptr(), bytes.len() as u32) }; - if !matches!( - ReturnCode(ret_val).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 } - } + 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_val = unsafe { sys::debug_message(bytes.as_ptr(), bytes.len() as u32) }; + if !matches!(ReturnCode(ret_val).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"))] @@ -454,19 +416,19 @@ pub fn debug_message(message: &str) { 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::[]( - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - ) - } - } - } - }; + ( $name:ident, $bytes_result:literal ) => { + paste::item! { + pub fn [](input: &[u8], output: &mut [u8; $bytes_result]) { + unsafe { + sys::[]( + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + ) + } + } + } + }; } impl_hash_fn!(sha2_256, 32); impl_hash_fn!(keccak_256, 32); @@ -474,69 +436,58 @@ 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], + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], ) -> Result { - let ret_val = unsafe { - sys::ecdsa_recover( - signature.as_ptr(), - message_hash.as_ptr(), - output.as_mut_ptr(), - ) - }; - ReturnCode(ret_val).into() + let ret_val = unsafe { + sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) + }; + ReturnCode(ret_val).into() } pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { - let ret_val = - unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; - ReturnCode(ret_val).into() + let ret_val = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; + ReturnCode(ret_val).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_val = unsafe { - sys::sr25519_verify( - signature.as_ptr(), - pub_key.as_ptr(), - message.len() as u32, - message.as_ptr(), - ) - }; - ReturnCode(ret_val).into() +pub fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { + let ret_val = unsafe { + sys::sr25519_verify( + signature.as_ptr(), + pub_key.as_ptr(), + message.len() as u32, + message.as_ptr(), + ) + }; + ReturnCode(ret_val).into() } pub fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; - ReturnCode(ret_val).into_bool() + let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; + ReturnCode(ret_val).into_bool() } pub fn caller_is_origin() -> bool { - let ret_val = unsafe { sys::caller_is_origin() }; - ReturnCode(ret_val).into_bool() + let ret_val = unsafe { sys::caller_is_origin() }; + ReturnCode(ret_val).into_bool() } pub fn set_code_hash(code_hash: &[u8]) -> Result { - let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; - ReturnCode(ret_val).into() + let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; + ReturnCode(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(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) - }; - ReturnCode(ret_val).into() + let mut output_len = output.len() as u32; + let ret_val = + unsafe { sys::code_hash(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; + ReturnCode(ret_val).into() } pub fn own_code_hash(output: &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } + let mut output_len = output.len() as u32; + unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } } - diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index b8c45012d0348..fdf92bb2629b1 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -26,11 +26,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{ - extract_from_slice, - Result, - ReturnCode, -}; +use super::{extract_from_slice, Result, ReturnCode}; use crate::ReturnFlags; use core::marker::PhantomData; use scale::Encode; @@ -48,34 +44,31 @@ use scale::Encode; #[repr(transparent)] pub struct Ptr32<'a, T> where - T: ?Sized, + 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>, + /// 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, + T: ?Sized, { - /// Creates a new Wasm32 pointer for the given raw pointer value. - fn new(value: u32) -> Self { - Self { - _value: value, - marker: Default::default(), - } - } + /// 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. - fn from_slice(slice: &'a [T]) -> Self { - Self::new(slice.as_ptr() as u32) - } + /// Creates a new Wasm32 pointer from the given shared slice. + fn from_slice(slice: &'a [T]) -> Self { + Self::new(slice.as_ptr() as u32) + } } /// Thin-wrapper around a `u32` representing a pointer for Wasm32. @@ -91,489 +84,446 @@ impl<'a, T> Ptr32<'a, [T]> { #[repr(transparent)] pub struct Ptr32Mut<'a, T> where - T: ?Sized, + 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>, + /// 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, + T: ?Sized, { - /// Creates a new Wasm32 pointer for the given raw pointer value. - fn new(value: u32) -> Self { - Self { - _value: value, - marker: Default::default(), - } - } + /// 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. - fn from_slice(slice: &'a mut [T]) -> Self { - Self::new(slice.as_ptr() as u32) - } + /// Creates a new Wasm32 pointer from the given exclusive slice. + fn from_slice(slice: &'a mut [T]) -> Self { + Self::new(slice.as_ptr() as u32) + } } impl<'a, T> Ptr32Mut<'a, T> where - T: Sized, + T: Sized, { - /// Creates a new Wasm32 pointer from the given exclusive reference. - fn from_ref(a_ref: &'a mut T) -> Self { - let a_ptr: *mut T = a_ref; - Self::new(a_ptr as u32) - } + /// Creates a new Wasm32 pointer from the given exclusive reference. + fn from_ref(a_ref: &'a mut T) -> Self { + let a_ptr: *mut T = a_ref; + Self::new(a_ptr as u32) + } } 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; - } + 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], + 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() + 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], + 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() + 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 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() + 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, - ) - } + 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() + 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() + 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() + 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() + 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() + 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)) } + 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() + 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); + 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, - ) - } + 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() + let ret_code = unsafe { sys::call_runtime(Ptr32::from_slice(call), call.len() as u32) }; + ret_code.into() } macro_rules! impl_wrapper_for { @@ -595,29 +545,29 @@ macro_rules! impl_wrapper_for { } } impl_wrapper_for! { - caller, - block_number, - address, - balance, - gas_left, - value_transferred, - now, - minimum_balance, + 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); + 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")] @@ -632,27 +582,26 @@ pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { /// 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 } - } + 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"))] @@ -660,19 +609,19 @@ pub fn debug_message(message: &str) { 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), - ) - } - } - } - }; + ( $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); @@ -680,77 +629,67 @@ 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], + 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() + 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() + 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 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() + 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() + 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() + 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() + 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), - ) - } + let mut output_len = output.len() as u32; + unsafe { sys::own_code_hash(Ptr32Mut::from_slice(output), Ptr32Mut::from_ref(&mut output_len)) } } - From 28bc1e2f3ff7646feba5a5e99d9de393b7f0014f Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 14:20:21 +0100 Subject: [PATCH 06/81] nit --- Cargo.lock | 2 +- substrate/frame/contracts/fixtures/Cargo.toml | 2 +- substrate/frame/contracts/fixtures/build.rs | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b15233ebe627..f6cd0a5038ae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9732,7 +9732,7 @@ dependencies = [ [[package]] name = "pallet-contracts-fixtures" -version = "1.0.5" +version = "1.0.0" dependencies = [ "anyhow", "frame-system", diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 2833855d5a817..1cd58ef02088b 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pallet-contracts-fixtures" publish = false -version = "1.0.5" +version = "1.0.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 094dbc5f81a92..5f7eaeb81bce3 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -25,14 +25,15 @@ use std::{ }; use twox_hash::XxHash32; -const VERSION: &'static str = env!("CARGO_PKG_VERSION"); +/// Salt used for hashing the contract source files. +const SALT: &[u8]= &[0u8]; /// Read the file at `path` and return its hash as a hex string. -fn file_hash(path: &PathBuf) -> String { +fn file_hash(path: &Path) -> String { let data = fs::read(path).expect("file exists; qed"); let mut hasher = XxHash32::default(); hasher.write(&data); - hasher.write(VERSION.as_bytes()); + hasher.write(SALT); let hash = hasher.finish(); format!("{:x}", hash) } @@ -73,7 +74,7 @@ impl Entry { } /// Collect all contract entries from the given source directory. -/// Entries are filtered by checking if the output wasm file already exists. +/// Contracts that have already been compiled are filtered out. fn collect_entries(src_dir: &Path, out_dir: &Path) -> Vec { fs::read_dir(&src_dir) .expect("src dir exists; qed") From c396af7de3e50faa69daf33ec2bd327bfa436cdc Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 14:23:03 +0100 Subject: [PATCH 07/81] fix --- substrate/frame/contracts/fixtures/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 5f7eaeb81bce3..f065262eb918a 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -184,7 +184,7 @@ fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result } fn main() -> Result<()> { - let input_dir: PathBuf = env::var("INPUT_DIR").unwrap_or(".".to_string()).into(); + let input_dir: PathBuf = ".".into(); let out_dir: PathBuf = env::var("OUT_DIR")?.into(); let entries = collect_entries(&input_dir.join("contracts").canonicalize()?, &out_dir); From 974f33ce3f01b7f19804d564660d93e0a4cab63a Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 14:27:11 +0100 Subject: [PATCH 08/81] fixes --- substrate/frame/contracts/fixtures/build.rs | 2 +- .../frame/contracts/fixtures/contracts/dummy.rs | 16 ++++++++++++++++ substrate/frame/contracts/uapi/src/allocator.rs | 16 ++++++++++++++++ .../frame/contracts/uapi/src/allocator/bump.rs | 7 +++++-- substrate/frame/contracts/uapi/src/lib.rs | 2 +- substrate/frame/contracts/uapi/src/wasm32.rs | 14 -------------- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index f065262eb918a..75619a5466489 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -26,7 +26,7 @@ use std::{ use twox_hash::XxHash32; /// Salt used for hashing the contract source files. -const SALT: &[u8]= &[0u8]; +const SALT: &[u8] = &[0u8]; /// Read the file at `path` and return its hash as a hex string. fn file_hash(path: &Path) -> String { diff --git a/substrate/frame/contracts/fixtures/contracts/dummy.rs b/substrate/frame/contracts/fixtures/contracts/dummy.rs index f82c959d52c1a..1bafa51053fa6 100644 --- a/substrate/frame/contracts/fixtures/contracts/dummy.rs +++ b/substrate/frame/contracts/fixtures/contracts/dummy.rs @@ -1,3 +1,19 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #![cfg_attr(not(feature = "std"), no_std, no_main)] #[no_mangle] diff --git a/substrate/frame/contracts/uapi/src/allocator.rs b/substrate/frame/contracts/uapi/src/allocator.rs index 76814363badf7..6ed747766e19d 100644 --- a/substrate/frame/contracts/uapi/src/allocator.rs +++ b/substrate/frame/contracts/uapi/src/allocator.rs @@ -1,3 +1,19 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. mod bump; #[cfg(not(any(feature = "std", feature = "no-allocator")))] diff --git a/substrate/frame/contracts/uapi/src/allocator/bump.rs b/substrate/frame/contracts/uapi/src/allocator/bump.rs index 7f924b9b0cc31..d541beebbcef9 100644 --- a/substrate/frame/contracts/uapi/src/allocator/bump.rs +++ b/substrate/frame/contracts/uapi/src/allocator/bump.rs @@ -1,10 +1,13 @@ +// This file is part of Substrate. + // Copyright (C) Parity Technologies (UK) Ltd. -// +// SPDX-License-Identifier: Apache-2.0 + // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// 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, diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index aacd7cd003cbf..1c688d5c7b51c 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(not(feature = "std"), no_std)] // Copyright (C) Parity Technologies (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,6 +16,7 @@ //! //! Refer to substrate FRAME contract module for more documentation. +#![cfg_attr(not(feature = "std"), no_std)] use core::marker::PhantomData; use scale::Encode; diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index fdf92bb2629b1..7a446368aa4ff 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -12,20 +12,6 @@ // 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, Result, ReturnCode}; use crate::ReturnFlags; use core::marker::PhantomData; From 03910c7fd226de4b37bfd73fb29fcf418b428d3c Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 17 Nov 2023 14:29:58 +0100 Subject: [PATCH 09/81] Update substrate/frame/contracts/fixtures/build.rs --- substrate/frame/contracts/fixtures/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 75619a5466489..9bf29cbe15cfb 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -25,7 +25,7 @@ use std::{ }; use twox_hash::XxHash32; -/// Salt used for hashing the contract source files. +/// Salt used for hashing contract source files. const SALT: &[u8] = &[0u8]; /// Read the file at `path` and return its hash as a hex string. From c4a8bb65c63a0d6c7850a00bd764de7762af15d4 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 14:33:06 +0100 Subject: [PATCH 10/81] nit --- substrate/frame/contracts/fixtures/build.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 9bf29cbe15cfb..22dbf0cfb4166 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -127,8 +127,7 @@ codegen-units = 1 cargo_toml["bin"] = toml::Value::Array(binaries); let cargo_toml = toml::to_string_pretty(&cargo_toml)?; - let cargo_toml_path = output_dir.join("Cargo.toml"); - fs::write(cargo_toml_path, cargo_toml).map_err(Into::into) + fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into) } /// Invoke `cargo build` to compile the contracts. From 4f895ece19e3fd304505539b754a87732d5838e3 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 14:56:02 +0100 Subject: [PATCH 11/81] rm dummy.wat --- substrate/frame/contracts/fixtures/data/dummy.wat | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 substrate/frame/contracts/fixtures/data/dummy.wat diff --git a/substrate/frame/contracts/fixtures/data/dummy.wat b/substrate/frame/contracts/fixtures/data/dummy.wat deleted file mode 100644 index a6435e49df222..0000000000000 --- a/substrate/frame/contracts/fixtures/data/dummy.wat +++ /dev/null @@ -1,6 +0,0 @@ -;; A valid contract which does nothing at all -(module - (import "env" "memory" (memory 1 1)) - (func (export "deploy")) - (func (export "call")) -) From ce4f1edc848c852de9baa9fed73f5128da657bc0 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 15:08:28 +0100 Subject: [PATCH 12/81] fixes --- substrate/frame/contracts/fixtures/build.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 22dbf0cfb4166..a0b716dc38cbd 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -26,7 +26,7 @@ use std::{ use twox_hash::XxHash32; /// Salt used for hashing contract source files. -const SALT: &[u8] = &[0u8]; +const SALT: &[u8] = &[1u8]; /// Read the file at `path` and return its hash as a hex string. fn file_hash(path: &Path) -> String { @@ -132,11 +132,11 @@ codegen-units = 1 /// Invoke `cargo build` to compile the contracts. fn invoke_build(current_dir: &Path) -> Result<()> { - let path = env::var("PATH").unwrap_or_default(); let build_res = Command::new(env::var("CARGO")?) .current_dir(current_dir) .env_clear() - .env("PATH", path) + .env("PATH", env::var("PATH").unwrap_or_default()) + .env("HOME", env::var("HOME").unwrap_or_default()) .env( "RUSTFLAGS", "-C link-arg=-zstack-size=65536 -C link-arg=--import-memory -Clinker-plugin-lto -C target-cpu=mvp", From ccbd83ec45186825cf91a2d96b264db8034bb43a Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 15:13:27 +0100 Subject: [PATCH 13/81] fix --- substrate/frame/contracts/fixtures/build.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index a0b716dc38cbd..a162c1b6b201a 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -26,7 +26,7 @@ use std::{ use twox_hash::XxHash32; /// Salt used for hashing contract source files. -const SALT: &[u8] = &[1u8]; +const SALT: &[u8] = &[2u8]; /// Read the file at `path` and return its hash as a hex string. fn file_hash(path: &Path) -> String { @@ -132,11 +132,22 @@ codegen-units = 1 /// Invoke `cargo build` to compile the contracts. fn invoke_build(current_dir: &Path) -> Result<()> { + + + // panic and print all env vars + eprintln!("env vars:"); + eprintln!("{:#?}", env::vars()); + + if let Ok(_) = env::var("PATH") { + panic!("done printing env vars"); + } + + let build_res = Command::new(env::var("CARGO")?) .current_dir(current_dir) .env_clear() .env("PATH", env::var("PATH").unwrap_or_default()) - .env("HOME", env::var("HOME").unwrap_or_default()) + .env("RUSTC", env::var("RUSTC").unwrap_or_default()) .env( "RUSTFLAGS", "-C link-arg=-zstack-size=65536 -C link-arg=--import-memory -Clinker-plugin-lto -C target-cpu=mvp", From 9016b6316e08b73951f7ed69f1e271d8ef5d2d77 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 15:14:03 +0100 Subject: [PATCH 14/81] fix --- substrate/frame/contracts/fixtures/build.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index a162c1b6b201a..99e237f513aa6 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -132,17 +132,6 @@ codegen-units = 1 /// Invoke `cargo build` to compile the contracts. fn invoke_build(current_dir: &Path) -> Result<()> { - - - // panic and print all env vars - eprintln!("env vars:"); - eprintln!("{:#?}", env::vars()); - - if let Ok(_) = env::var("PATH") { - panic!("done printing env vars"); - } - - let build_res = Command::new(env::var("CARGO")?) .current_dir(current_dir) .env_clear() From f02512e8c6ae81770cee9345a2b4e34cb912faa4 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 15:53:53 +0100 Subject: [PATCH 15/81] test --- substrate/frame/contracts/fixtures/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 99e237f513aa6..836959ec0b074 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -136,6 +136,7 @@ fn invoke_build(current_dir: &Path) -> Result<()> { .current_dir(current_dir) .env_clear() .env("PATH", env::var("PATH").unwrap_or_default()) + .env("HOME", env::var("HOME").unwrap_or_default()) .env("RUSTC", env::var("RUSTC").unwrap_or_default()) .env( "RUSTFLAGS", From 11990ce8294c2cd4c4807e7cf977b86b1543805d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 16:12:40 +0100 Subject: [PATCH 16/81] use encoded rust_flags --- substrate/frame/contracts/fixtures/build.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 836959ec0b074..b4d3e379ba6a7 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -134,13 +134,9 @@ codegen-units = 1 fn invoke_build(current_dir: &Path) -> Result<()> { let build_res = Command::new(env::var("CARGO")?) .current_dir(current_dir) - .env_clear() - .env("PATH", env::var("PATH").unwrap_or_default()) - .env("HOME", env::var("HOME").unwrap_or_default()) - .env("RUSTC", env::var("RUSTC").unwrap_or_default()) .env( - "RUSTFLAGS", - "-C link-arg=-zstack-size=65536 -C link-arg=--import-memory -Clinker-plugin-lto -C target-cpu=mvp", + "CARGO_ENCODED_RUSTFLAGS", + "-C\x1flink-arg=-zstack-size=65536\x1f-C\x1flink-arg=--import-memory\x1f-Clinker-plugin-lto\x1f-C\x1ftarget-cpu=mvp", ) .arg("build") .arg("--release") From 46c4844bb42ec2cdeb0092e43f9cc9257b5fefe5 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 17 Nov 2023 20:15:38 +0100 Subject: [PATCH 17/81] Update substrate/frame/contracts/fixtures/contracts/dummy.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen --- substrate/frame/contracts/fixtures/contracts/dummy.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/contracts/dummy.rs b/substrate/frame/contracts/fixtures/contracts/dummy.rs index 1bafa51053fa6..d1b2c02eb0541 100644 --- a/substrate/frame/contracts/fixtures/contracts/dummy.rs +++ b/substrate/frame/contracts/fixtures/contracts/dummy.rs @@ -14,7 +14,8 @@ // 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. -#![cfg_attr(not(feature = "std"), no_std, no_main)] +#![no_std] +#![no_main] #[no_mangle] pub fn deploy() { From d0e18a1a69d9138d96c1ab29e14da05fe4ce2770 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Fri, 17 Nov 2023 20:15:49 +0100 Subject: [PATCH 18/81] Update substrate/frame/contracts/fixtures/Cargo.toml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen --- substrate/frame/contracts/fixtures/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 1cd58ef02088b..d629cf835915b 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -14,7 +14,7 @@ sp-runtime = { path = "../../../primitives/runtime", default-features = false} anyhow = "1.0.0" [build-dependencies] -parity-wasm = { version = "0.45.0" } +parity-wasm = "0.45.0" tempfile = "3.8.1" toml = "0.8.8" twox-hash = "1.6.3" From 0cd6b90e15feed670f983c5e1ee824a0a3d623b2 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 20:53:24 +0100 Subject: [PATCH 19/81] Review part 1 --- Cargo.lock | 1 + substrate/frame/contracts/fixtures/Cargo.toml | 3 +- substrate/frame/contracts/fixtures/build.rs | 3 +- .../contracts/fixtures/contracts/dummy.rs | 20 +- substrate/frame/contracts/uapi/Cargo.toml | 4 - .../frame/contracts/uapi/src/allocator.rs | 21 - .../contracts/uapi/src/allocator/bump.rs | 530 ------------------ substrate/frame/contracts/uapi/src/lib.rs | 80 --- substrate/frame/contracts/uapi/src/prelude.rs | 32 -- 9 files changed, 23 insertions(+), 671 deletions(-) delete mode 100644 substrate/frame/contracts/uapi/src/allocator.rs delete mode 100644 substrate/frame/contracts/uapi/src/allocator/bump.rs delete mode 100644 substrate/frame/contracts/uapi/src/prelude.rs diff --git a/Cargo.lock b/Cargo.lock index f6cd0a5038ae2..810135ba1362b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9735,6 +9735,7 @@ name = "pallet-contracts-fixtures" version = "1.0.0" dependencies = [ "anyhow", + "cfg-if", "frame-system", "parity-wasm", "sp-runtime", diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index d629cf835915b..6a6bed02056d0 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -14,11 +14,12 @@ sp-runtime = { path = "../../../primitives/runtime", default-features = false} anyhow = "1.0.0" [build-dependencies] -parity-wasm = "0.45.0" +parity-wasm = "0.45.0" tempfile = "3.8.1" toml = "0.8.8" twox-hash = "1.6.3" anyhow = "1.0.0" +cfg-if = { version = "1.0", default-features = false } [features] default = [ "std" ] diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index b4d3e379ba6a7..f01ff3edd7e39 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -26,7 +26,7 @@ use std::{ use twox_hash::XxHash32; /// Salt used for hashing contract source files. -const SALT: &[u8] = &[2u8]; +const SALT: &[u8] = &[10u8]; /// Read the file at `path` and return its hash as a hex string. fn file_hash(path: &Path) -> String { @@ -106,6 +106,7 @@ version = '0.1.0' [dependencies] uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}, default-features = false}} +cfg-if = {{ version = '1.0', default-features = false }} [profile.release] opt-level = 3 diff --git a/substrate/frame/contracts/fixtures/contracts/dummy.rs b/substrate/frame/contracts/fixtures/contracts/dummy.rs index d1b2c02eb0541..42561115fba62 100644 --- a/substrate/frame/contracts/fixtures/contracts/dummy.rs +++ b/substrate/frame/contracts/fixtures/contracts/dummy.rs @@ -17,12 +17,28 @@ #![no_std] #![no_main] +// TODO move shared boiler plate outside of fixtures +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + core::arch::wasm32::unreachable(); + } else if #[cfg(target_arch = "riscv32")] { + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); + } + } else { + core::compile_error!("only supports wasm32 and riscv32"); + } + } +} + #[no_mangle] pub fn deploy() { - uapi::debug_message("contract deployed"); } #[no_mangle] pub fn call() { - uapi::debug_message("contract called"); } diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index 242dba6959dd4..b969a42f429d7 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -19,7 +19,3 @@ paste = { version = "1.0", default-features = false } [features] default = [ "std" ] std = [ "scale/std" ] - -# Enable contract debug messages via `debug_print!` and `debug_println!`. -ink-debug = [] - diff --git a/substrate/frame/contracts/uapi/src/allocator.rs b/substrate/frame/contracts/uapi/src/allocator.rs deleted file mode 100644 index 6ed747766e19d..0000000000000 --- a/substrate/frame/contracts/uapi/src/allocator.rs +++ /dev/null @@ -1,21 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -mod bump; - -#[cfg(not(any(feature = "std", feature = "no-allocator")))] -#[global_allocator] -static mut ALLOC: bump::BumpAllocator = bump::BumpAllocator {}; diff --git a/substrate/frame/contracts/uapi/src/allocator/bump.rs b/substrate/frame/contracts/uapi/src/allocator/bump.rs deleted file mode 100644 index d541beebbcef9..0000000000000 --- a/substrate/frame/contracts/uapi/src/allocator/bump.rs +++ /dev/null @@ -1,530 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! A simple bump allocator. -//! -//! Its goal to have a much smaller footprint than the admittedly more full-featured -//! `wee_alloc` allocator which is currently being used by ink! smart contracts. -//! -//! The heap which is used by this allocator is built from pages of Wasm memory (each page -//! is `64KiB`). We will request new pages of memory as needed until we run out of memory, -//! at which point we will crash with an `OOM` error instead of freeing any memory. - -use core::alloc::{GlobalAlloc, Layout}; - -/// A page in Wasm is `64KiB` -const PAGE_SIZE: usize = 64 * 1024; - -static mut INNER: Option = None; - -#[cfg(target_arch = "riscv32")] -static mut RISCV_HEAP: [u8; 1024 * 1024] = [0; 1024 * 1024]; - -/// A bump allocator suitable for use in a Wasm environment. -pub struct BumpAllocator; - -unsafe impl GlobalAlloc for BumpAllocator { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if INNER.is_none() { - INNER = Some(InnerAlloc::new()); - }; - match INNER.as_mut().expect("We just set the value above; qed").alloc(layout) { - Some(start) => start as *mut u8, - None => core::ptr::null_mut(), - } - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // A new page in Wasm is guaranteed to already be zero initialized, so we can just - // use our regular `alloc` call here and save a bit of work. - // - // See: https://webassembly.github.io/spec/core/exec/modules.html#growing-memories - self.alloc(layout) - } - - #[inline] - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} -} - -#[cfg_attr(feature = "std", derive(Debug, Copy, Clone))] -struct InnerAlloc { - /// Points to the start of the next available allocation. - next: usize, - - /// The address of the upper limit of our heap. - upper_limit: usize, -} - -impl InnerAlloc { - fn new() -> Self { - Self { next: Self::heap_start(), upper_limit: Self::heap_end() } - } - - cfg_if::cfg_if! { - if #[cfg(test)] { - fn heap_start() -> usize { - 0 - } - - fn heap_end() -> usize { - 0 - } - - /// Request a `pages` number of page sized sections of Wasm memory. Each page is `64KiB` in size. - /// - /// Returns `None` if a page is not available. - /// - /// This implementation is only meant to be used for testing, since we cannot (easily) - /// test the `wasm32` implementation. - fn request_pages(&mut self, _pages: usize) -> Option { - Some(self.upper_limit) - } - } else if #[cfg(feature = "std")] { - fn heap_start() -> usize { - 0 - } - - fn heap_end() -> usize { - 0 - } - - fn request_pages(&mut self, _pages: usize) -> Option { - unreachable!( - "This branch is only used to keep the compiler happy when building tests, and - should never actually be called outside of a test run." - ) - } - } else if #[cfg(target_arch = "wasm32")] { - fn heap_start() -> usize { - extern "C" { - static __heap_base: usize; - } - // # SAFETY - // - // The `__heap_base` symbol is defined by the wasm linker and is guaranteed - // to point to the start of the heap. - let heap_start = unsafe { &__heap_base as *const usize as usize }; - // if the symbol isn't found it will resolve to 0 - // for that to happen the rust compiler or linker need to break or change - assert_ne!(heap_start, 0, "Can't find `__heap_base` symbol."); - heap_start - } - - fn heap_end() -> usize { - // Cannot overflow on this architecture - core::arch::wasm32::memory_size(0) * PAGE_SIZE - } - - /// Request a `pages` number of pages of Wasm memory. Each page is `64KiB` in size. - /// - /// Returns `None` if a page is not available. - fn request_pages(&mut self, pages: usize) -> Option { - let prev_page = core::arch::wasm32::memory_grow(0, pages); - if prev_page == usize::MAX { - return None; - } - - // Cannot overflow on this architecture - Some(prev_page * PAGE_SIZE) - } - } else if #[cfg(target_arch = "riscv32")] { - fn heap_start() -> usize { - unsafe { - RISCV_HEAP.as_mut_ptr() as usize - } - } - - fn heap_end() -> usize { - Self::heap_start() + unsafe { RISCV_HEAP.len() } - } - - fn request_pages(&mut self, _pages: usize) -> Option { - // On riscv the memory can't be grown - None - } - } else { - core::compile_error!("ink! only supports wasm32 and riscv32"); - } - } - - /// Tries to allocate enough memory on the heap for the given `Layout`. If there is - /// not enough room on the heap it'll try and grow it by a page. - /// - /// Note: This implementation results in internal fragmentation when allocating across - /// pages. - fn alloc(&mut self, layout: Layout) -> Option { - let alloc_start = self.next; - - let aligned_size = layout.pad_to_align().size(); - let alloc_end = alloc_start.checked_add(aligned_size)?; - - if alloc_end > self.upper_limit { - let required_pages = required_pages(aligned_size)?; - let page_start = self.request_pages(required_pages)?; - - self.upper_limit = required_pages - .checked_mul(PAGE_SIZE) - .and_then(|pages| page_start.checked_add(pages))?; - self.next = page_start.checked_add(aligned_size)?; - - Some(page_start) - } else { - self.next = alloc_end; - Some(alloc_start) - } - } -} - -/// Calculates the number of pages of memory needed for an allocation of `size` bytes. -/// -/// This function rounds up to the next page. For example, if we have an allocation of -/// `size = PAGE_SIZE / 2` this function will indicate that one page is required to -/// satisfy the allocation. -#[inline] -fn required_pages(size: usize) -> Option { - size.checked_add(PAGE_SIZE - 1).and_then(|num| num.checked_div(PAGE_SIZE)) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::mem::size_of; - - #[test] - fn can_alloc_no_bytes() { - let mut inner = InnerAlloc::new(); - - let layout = Layout::new::<()>(); - assert_eq!(inner.alloc(layout), Some(0)); - - let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); - - let expected_alloc_start = size_of::<()>(); - assert_eq!(inner.next, expected_alloc_start); - } - - #[test] - fn can_alloc_a_byte() { - let mut inner = InnerAlloc::new(); - - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(0)); - - let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); - - let expected_alloc_start = size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } - - #[test] - fn can_alloc_a_foobarbaz() { - let mut inner = InnerAlloc::new(); - - struct FooBarBaz { - _foo: u32, - _bar: u128, - _baz: (u16, bool), - } - - let layout = Layout::new::(); - let mut total_size = 0; - - let allocations = 3; - for _ in 0..allocations { - assert!(inner.alloc(layout).is_some()); - total_size += layout.pad_to_align().size(); - } - - let expected_limit = PAGE_SIZE * required_pages(total_size).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); - - let expected_alloc_start = allocations * size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } - - #[test] - fn can_alloc_across_pages() { - let mut inner = InnerAlloc::new(); - - struct Foo { - _foo: [u8; PAGE_SIZE - 1], - } - - // First, let's allocate a struct which is _almost_ a full page - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(0)); - - let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); - - let expected_alloc_start = size_of::(); - assert_eq!(inner.next, expected_alloc_start); - - // Now we'll allocate two bytes which will push us over to the next page - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(PAGE_SIZE)); - - let expected_limit = 2 * PAGE_SIZE; - assert_eq!(inner.upper_limit, expected_limit); - - // Notice that we start the allocation on the second page, instead of making use - // of the remaining byte on the first page - let expected_alloc_start = PAGE_SIZE + size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } - - #[test] - fn can_alloc_multiple_pages() { - let mut inner = InnerAlloc::new(); - - struct Foo { - _foo: [u8; 2 * PAGE_SIZE], - } - - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(0)); - - let expected_limit = PAGE_SIZE * required_pages(layout.pad_to_align().size()).unwrap(); - assert_eq!(inner.upper_limit, expected_limit); - - let expected_alloc_start = size_of::(); - assert_eq!(inner.next, expected_alloc_start); - - // Now we want to make sure that the state of our allocator is correct for any - // subsequent allocations - let layout = Layout::new::(); - assert_eq!(inner.alloc(layout), Some(2 * PAGE_SIZE)); - - let expected_limit = 3 * PAGE_SIZE; - assert_eq!(inner.upper_limit, expected_limit); - - let expected_alloc_start = 2 * PAGE_SIZE + size_of::(); - assert_eq!(inner.next, expected_alloc_start); - } -} - -#[cfg(all(test, feature = "ink-fuzz-tests"))] -mod fuzz_tests { - use super::*; - use quickcheck::{quickcheck, TestResult}; - use std::mem::size_of; - - #[quickcheck] - fn should_allocate_arbitrary_sized_bytes(n: usize) -> TestResult { - let mut inner = InnerAlloc::new(); - - // If we're going to end up creating an invalid `Layout` we don't want to use - // these test inputs. - let layout = match Layout::from_size_align(n, size_of::()) { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - let size = layout.pad_to_align().size(); - assert_eq!( - inner.alloc(layout), - Some(0), - "The given pointer for the allocation doesn't match." - ); - - let expected_alloc_start = size; - assert_eq!( - inner.next, expected_alloc_start, - "Our next allocation doesn't match where it should start." - ); - - let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); - assert_eq!(inner.upper_limit, expected_limit, "The upper bound of our heap doesn't match."); - - TestResult::passed() - } - - #[quickcheck] - fn should_allocate_regardless_of_alignment_size(n: usize, align: usize) -> TestResult { - let aligns = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]; - let align = aligns[align % aligns.len()]; - - let mut inner = InnerAlloc::new(); - - // If we're going to end up creating an invalid `Layout` we don't want to use - // these test inputs. - let layout = match Layout::from_size_align(n, align) { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - let size = layout.pad_to_align().size(); - assert_eq!( - inner.alloc(layout), - Some(0), - "The given pointer for the allocation doesn't match." - ); - - let expected_alloc_start = size; - assert_eq!( - inner.next, expected_alloc_start, - "Our next allocation doesn't match where it should start." - ); - - let expected_limit = PAGE_SIZE * required_pages(size).unwrap(); - assert_eq!(inner.upper_limit, expected_limit, "The upper bound of our heap doesn't match."); - - TestResult::passed() - } - - /// The idea behind this fuzz test is to check a series of allocation sequences. For - /// example, we maybe have back to back runs as follows: - /// - /// 1. `vec![1, 2, 3]` - /// 2. `vec![4, 5, 6, 7]` - /// 3. `vec![8]` - /// - /// Each of the vectors represents one sequence of allocations. Within each sequence - /// the individual size of allocations will be randomly selected by `quickcheck`. - #[quickcheck] - fn should_allocate_arbitrary_byte_sequences(sequence: Vec) -> TestResult { - let mut inner = InnerAlloc::new(); - - if sequence.is_empty() { - return TestResult::discard() - } - - // We don't want any negative numbers so we can be sure our conversions to `usize` - // later are valid - if !sequence.iter().all(|n| n.is_positive()) { - return TestResult::discard() - } - - // We can't just use `required_pages(Iterator::sum())` here because it ends up - // underestimating the pages due to the ceil rounding at each step - let pages_required = - sequence.iter().fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); - let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); - - // We know this is going to end up overflowing, we'll check this case in a - // different test - if pages_required > max_pages { - return TestResult::discard() - } - - let mut expected_alloc_start = 0; - let mut total_bytes_requested = 0; - let mut total_bytes_fragmented = 0; - - for alloc in sequence { - let layout = Layout::from_size_align(alloc as usize, size_of::()); - let layout = match layout { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - let size = layout.pad_to_align().size(); - - let current_page_limit = PAGE_SIZE * required_pages(inner.next).unwrap(); - let is_too_big_for_current_page = inner.next + size > current_page_limit; - - if is_too_big_for_current_page { - let fragmented_in_current_page = current_page_limit - inner.next; - total_bytes_fragmented += fragmented_in_current_page; - - // We expect our next allocation to be aligned to the start of the next - // page boundary - expected_alloc_start = inner.upper_limit; - } - - assert_eq!( - inner.alloc(layout), - Some(expected_alloc_start), - "The given pointer for the allocation doesn't match." - ); - total_bytes_requested += size; - - expected_alloc_start = total_bytes_requested + total_bytes_fragmented; - assert_eq!( - inner.next, expected_alloc_start, - "Our next allocation doesn't match where it should start." - ); - - let pages_required = required_pages(expected_alloc_start).unwrap(); - let expected_limit = PAGE_SIZE * pages_required; - assert_eq!( - inner.upper_limit, expected_limit, - "The upper bound of our heap doesn't match." - ); - } - - TestResult::passed() - } - - // For this test we have sequences of allocations which will eventually overflow the - // maximum amount of pages (in practice this means our heap will be OOM). - // - // We don't care about the allocations that succeed (those are checked in other - // tests), we just care that eventually an allocation doesn't success. - #[quickcheck] - fn should_not_allocate_arbitrary_byte_sequences_which_eventually_overflow( - sequence: Vec, - ) -> TestResult { - let mut inner = InnerAlloc::new(); - - if sequence.is_empty() { - return TestResult::discard() - } - - // We don't want any negative numbers so we can be sure our conversions to `usize` - // later are valid - if !sequence.iter().all(|n| n.is_positive()) { - return TestResult::discard() - } - - // We can't just use `required_pages(Iterator::sum())` here because it ends up - // underestimating the pages due to the ceil rounding at each step - let pages_required = - sequence.iter().fold(0, |acc, &x| acc + required_pages(x as usize).unwrap()); - let max_pages = required_pages(usize::MAX - PAGE_SIZE + 1).unwrap(); - - // We want to explicitly test for the case where a series of allocations - // eventually runs out of pages of memory - if pages_required <= max_pages { - return TestResult::discard() - } - - let mut results = vec![]; - for alloc in sequence { - let layout = Layout::from_size_align(alloc as usize, size_of::()); - let layout = match layout { - Ok(l) => l, - Err(_) => return TestResult::discard(), - }; - - results.push(inner.alloc(layout)); - } - - // Ensure that at least one of the allocations ends up overflowing our - // calculations. - assert!( - results.iter().any(|r| r.is_none()), - "Expected an allocation to overflow our heap, but this didn't happen." - ); - - TestResult::passed() - } -} diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 1c688d5c7b51c..abff7f08c9841 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -20,27 +20,6 @@ use core::marker::PhantomData; use scale::Encode; -#[cfg(not(feature = "std"))] -#[allow(unused_variables)] -#[panic_handler] -fn panic(info: &core::panic::PanicInfo) -> ! { - // This code gets removed in release builds where the macro will expand into nothing. - debug_print!("{}\n", info); - - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - core::arch::wasm32::unreachable(); - } else if #[cfg(target_arch = "riscv32")] { - // Safety: The unimp instruction is guaranteed to trap - unsafe { - core::arch::asm!("unimp"); - core::hint::unreachable_unchecked(); - } - } else { - core::compile_error!("ink! only supports wasm32 and riscv32"); - } - } -} cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { @@ -276,62 +255,3 @@ fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { *output = &mut tmp[..new_len]; } -// is not recognizing its allocator and panic handler definitions. -#[cfg(not(any(feature = "std", feature = "no-allocator")))] -mod allocator; - -mod prelude; - -cfg_if::cfg_if! { - if #[cfg(any(feature = "ink-debug", feature = "std"))] { - /// Required by the `debug_print*` macros below, because there is no guarantee that - /// contracts will have a direct `ink_prelude` dependency. In the future we could introduce - /// an "umbrella" crate containing all the `ink!` crates which could also host these macros. - #[doc(hidden)] - pub use prelude::format; - - /// Appends a formatted string to the `debug_message` buffer if message recording is - /// enabled in the contracts pallet and if the call is performed via RPC (**not** via an - /// extrinsic). The `debug_message` buffer will be: - /// - Returned to the RPC caller. - /// - Logged as a `debug!` message on the Substrate node, which will be printed to the - /// node console's `stdout` when the log level is set to `-lruntime::contracts=debug`. - /// - /// # Note - /// - /// This depends on the `debug_message` interface which requires the - /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. - #[macro_export] - macro_rules! debug_print { - ($($arg:tt)*) => ($crate::debug_message(&$crate::format!($($arg)*))); - } - - /// Appends a formatted string to the `debug_message` buffer, as per [`debug_print`] but - /// with a newline appended. - /// - /// # Note - /// - /// This depends on the `debug_message` interface which requires the - /// `"pallet-contracts/unstable-interface"` feature to be enabled in the target runtime. - #[macro_export] - macro_rules! debug_println { - () => ($crate::debug_print!("\n")); - ($($arg:tt)*) => ( - $crate::debug_print!("{}\n", $crate::format!($($arg)*)); - ) - } - } else { - #[macro_export] - /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. - macro_rules! debug_print { - ($($arg:tt)*) => (); - } - - #[macro_export] - /// Debug messages disabled. Enable the `ink-debug` feature for contract debugging. - macro_rules! debug_println { - () => (); - ($($arg:tt)*) => (); - } - } -} diff --git a/substrate/frame/contracts/uapi/src/prelude.rs b/substrate/frame/contracts/uapi/src/prelude.rs deleted file mode 100644 index 10b1fa576d3d8..0000000000000 --- a/substrate/frame/contracts/uapi/src/prelude.rs +++ /dev/null @@ -1,32 +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. - -//! Data structures to operate on contract memory during contract execution. -//! -//! These definitions are useful since we are operating in a `no_std` environment -//! and should be used by all ink! crates instead of directly using `std` or `alloc` -//! crates. If needed we shall instead enhance the exposed types here. - -cfg_if::cfg_if! { - if #[cfg(feature = "std")] { - pub use std::{ - format, - }; - } else { - extern crate alloc; - pub use alloc::{ - format, - }; - } -} From 01550ba261e63d9d7c4e10491ce13a5432dc8347 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 21:30:20 +0100 Subject: [PATCH 20/81] Clean up Ptr stuff --- substrate/frame/contracts/fixtures/build.rs | 2 +- substrate/frame/contracts/uapi/src/lib.rs | 93 ----- substrate/frame/contracts/uapi/src/riscv32.rs | 38 -- substrate/frame/contracts/uapi/src/wasm32.rs | 341 +++++++----------- 4 files changed, 126 insertions(+), 348 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index f01ff3edd7e39..6fb302061970b 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -26,7 +26,7 @@ use std::{ use twox_hash::XxHash32; /// Salt used for hashing contract source files. -const SALT: &[u8] = &[10u8]; +const SALT: &[u8] = &[11u8]; /// Read the file at `path` and return its hash as a hex string. fn file_hash(path: &Path) -> String { diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index abff7f08c9841..6615fb0c7e89a 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -20,7 +20,6 @@ use core::marker::PhantomData; use scale::Encode; - cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { mod wasm32; @@ -124,97 +123,6 @@ impl ReturnFlags { } } -/// 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); @@ -254,4 +162,3 @@ fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { let tmp = core::mem::take(output); *output = &mut tmp[..new_len]; } - diff --git a/substrate/frame/contracts/uapi/src/riscv32.rs b/substrate/frame/contracts/uapi/src/riscv32.rs index fcb955c89d8ce..594db2f03c86a 100644 --- a/substrate/frame/contracts/uapi/src/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/riscv32.rs @@ -377,44 +377,6 @@ pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { 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_val = unsafe { sys::debug_message(bytes.as_ptr(), bytes.len() as u32) }; - if !matches!(ReturnCode(ret_val).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! { diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 7a446368aa4ff..a8e73b0d35c66 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -17,227 +17,136 @@ use crate::ReturnFlags; use core::marker::PhantomData; use scale::Encode; -/// 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. - 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. - 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. - fn from_ref(a_ref: &'a mut T) -> Self { - let a_ptr: *mut T = a_ref; - Self::new(a_ptr as u32) - } -} - mod sys { - use super::{Ptr32, Ptr32Mut, ReturnCode}; + use super::ReturnCode; #[link(wasm_import_module = "seal0")] extern "C" { pub fn transfer( - account_id_ptr: Ptr32<[u8]>, + account_id_ptr: *const u8, account_id_len: u32, - transferred_value_ptr: Ptr32<[u8]>, + transferred_value_ptr: *const u8, transferred_value_len: u32, ) -> ReturnCode; pub fn deposit_event( - topics_ptr: Ptr32<[u8]>, + topics_ptr: *const u8, topics_len: u32, - data_ptr: Ptr32<[u8]>, + data_ptr: *const u8, data_len: u32, ); pub fn call_chain_extension( func_id: u32, - input_ptr: Ptr32<[u8]>, + input_ptr: *const u8, input_len: u32, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, + output_ptr: *mut u8, + output_len_ptr: *mut u32, ) -> 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 input(buf_ptr: *mut u8, buf_len_ptr: *mut u32); + pub fn seal_return(flags: u32, data_ptr: *const 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 caller(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn block_number(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn address(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn balance(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn weight_to_fee(gas: u64, output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn value_transferred(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn now(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn minimum_balance(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn hash_keccak_256(input_ptr: Ptr32<[u8]>, input_len: u32, output_ptr: Ptr32Mut<[u8]>); - pub fn hash_blake2_256(input_ptr: Ptr32<[u8]>, input_len: u32, output_ptr: Ptr32Mut<[u8]>); - pub fn hash_blake2_128(input_ptr: Ptr32<[u8]>, input_len: u32, output_ptr: Ptr32Mut<[u8]>); - pub fn hash_sha2_256(input_ptr: Ptr32<[u8]>, input_len: u32, output_ptr: Ptr32Mut<[u8]>); + pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); + pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); + pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); + pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); - pub fn is_contract(account_id_ptr: Ptr32<[u8]>) -> ReturnCode; + pub fn is_contract(account_id_ptr: *const u8) -> ReturnCode; pub fn caller_is_origin() -> ReturnCode; - pub fn set_code_hash(code_hash_ptr: Ptr32<[u8]>) -> ReturnCode; + pub fn set_code_hash(code_hash_ptr: *const u8) -> ReturnCode; pub fn code_hash( - account_id_ptr: Ptr32<[u8]>, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, + account_id_ptr: *const u8, + output_ptr: *mut u8, + output_len_ptr: *mut u32, ) -> ReturnCode; - pub fn own_code_hash(output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut); + pub fn own_code_hash(output_ptr: *mut u8, output_len_ptr: *mut u32); #[cfg(feature = "ink-debug")] - pub fn debug_message(str_ptr: Ptr32<[u8]>, str_len: u32) -> ReturnCode; + pub fn debug_message(str_ptr: *const u8, str_len: u32) -> ReturnCode; pub fn delegate_call( flags: u32, - code_hash_ptr: Ptr32<[u8]>, - input_data_ptr: Ptr32<[u8]>, + code_hash_ptr: *const u8, + input_data_ptr: *const u8, input_data_len: u32, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, + output_ptr: *mut u8, + output_len_ptr: *mut u32, ) -> ReturnCode; pub fn ecdsa_recover( // 65 bytes of ecdsa signature - signature_ptr: Ptr32<[u8]>, + signature_ptr: *const u8, // 32 bytes hash of the message - message_hash_ptr: Ptr32<[u8]>, - output_ptr: Ptr32Mut<[u8]>, + message_hash_ptr: *const u8, + output_ptr: *mut u8, ) -> ReturnCode; pub fn ecdsa_to_eth_address( - public_key_ptr: Ptr32<[u8]>, - output_ptr: Ptr32Mut<[u8]>, + public_key_ptr: *const u8, + output_ptr: *mut 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]>, + signature_ptr: *const u8, + public_key_ptr: *const u8, message_len: u32, - message_ptr: Ptr32<[u8]>, + message_ptr: *const u8, ) -> ReturnCode; pub fn take_storage( - key_ptr: Ptr32<[u8]>, + key_ptr: *const u8, key_len: u32, - out_ptr: Ptr32Mut<[u8]>, - out_len_ptr: Ptr32Mut, + out_ptr: *mut u8, + out_len_ptr: *mut u32, ) -> ReturnCode; - pub fn call_runtime(call_ptr: Ptr32<[u8]>, call_len: u32) -> ReturnCode; + pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode; } #[link(wasm_import_module = "seal1")] extern "C" { pub fn instantiate( - init_code_ptr: Ptr32<[u8]>, + init_code_ptr: *const u8, gas: u64, - endowment_ptr: Ptr32<[u8]>, - input_ptr: Ptr32<[u8]>, + endowment_ptr: *const u8, + input_ptr: *const u8, input_len: u32, - address_ptr: Ptr32Mut<[u8]>, - address_len_ptr: Ptr32Mut, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - salt_ptr: Ptr32<[u8]>, + address_ptr: *mut u8, + address_len_ptr: *mut u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + salt_ptr: *const u8, salt_len: u32, ) -> ReturnCode; - pub fn terminate(beneficiary_ptr: Ptr32<[u8]>) -> !; + pub fn terminate(beneficiary_ptr: *const u8) -> !; pub fn call( flags: u32, - callee_ptr: Ptr32<[u8]>, + callee_ptr: *const u8, gas: u64, - transferred_value_ptr: Ptr32<[u8]>, - input_data_ptr: Ptr32<[u8]>, + transferred_value_ptr: *const u8, + input_data_ptr: *const u8, input_data_len: u32, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, + output_ptr: *mut u8, + output_len_ptr: *mut u32, ) -> ReturnCode; // # Parameters @@ -249,7 +158,7 @@ mod sys { // // 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; + pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; // # Parameters // @@ -261,7 +170,7 @@ mod sys { // // 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; + pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; // # Parameters // @@ -276,10 +185,10 @@ mod sys { // // `ReturnCode::KeyNotFound` pub fn get_storage( - key_ptr: Ptr32<[u8]>, + key_ptr: *const u8, key_len: u32, - out_ptr: Ptr32Mut<[u8]>, - out_len_ptr: Ptr32Mut, + out_ptr: *mut u8, + out_len_ptr: *mut u32, ) -> ReturnCode; } @@ -298,9 +207,9 @@ mod sys { // 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_ptr: *const u8, key_len: u32, - value_ptr: Ptr32<[u8]>, + value_ptr: *const u8, value_len: u32, ) -> ReturnCode; } @@ -321,16 +230,16 @@ pub fn instantiate( let ret_code = { unsafe { sys::instantiate( - Ptr32::from_slice(code_hash), + code_hash.as_ptr(), gas_limit, - Ptr32::from_slice(endowment), - Ptr32::from_slice(input), + endowment.as_ptr(), + input.as_ptr(), 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), + out_address.as_mut_ptr(), + &mut address_len, + out_return_value.as_mut_ptr(), + &mut return_value_len, + salt.as_ptr(), salt.len() as u32, ) } @@ -354,13 +263,13 @@ pub fn call( unsafe { sys::call( flags, - Ptr32::from_slice(callee), + callee.as_ptr(), gas_limit, - Ptr32::from_slice(value), - Ptr32::from_slice(input), + value.as_ptr(), + input.as_ptr(), input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr(), + &mut output_len, ) } }; @@ -375,11 +284,11 @@ pub fn delegate_call(flags: u32, code_hash: &[u8], input: &[u8], output: &mut &m unsafe { sys::delegate_call( flags, - Ptr32::from_slice(code_hash), - Ptr32::from_slice(input), + code_hash.as_ptr(), + input.as_ptr(), input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr(), + &mut output_len, ) } }; @@ -390,9 +299,9 @@ pub fn delegate_call(flags: u32, code_hash: &[u8], input: &[u8], output: &mut &m pub fn transfer(account_id: &[u8], value: &[u8]) -> Result { let ret_code = unsafe { sys::transfer( - Ptr32::from_slice(account_id), + account_id.as_ptr(), account_id.len() as u32, - Ptr32::from_slice(value), + value.as_ptr(), value.len() as u32, ) }; @@ -402,9 +311,9 @@ pub fn transfer(account_id: &[u8], value: &[u8]) -> Result { pub fn deposit_event(topics: &[u8], data: &[u8]) { unsafe { sys::deposit_event( - Ptr32::from_slice(topics), + topics.as_ptr(), topics.len() as u32, - Ptr32::from_slice(data), + data.as_ptr(), data.len() as u32, ) } @@ -413,9 +322,9 @@ pub fn deposit_event(topics: &[u8], data: &[u8]) { pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { let ret_code = unsafe { sys::set_storage( - Ptr32::from_slice(key), + key.as_ptr(), key.len() as u32, - Ptr32::from_slice(encoded_value), + encoded_value.as_ptr(), encoded_value.len() as u32, ) }; @@ -423,7 +332,7 @@ pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { } pub fn clear_storage(key: &[u8]) -> Option { - let ret_code = unsafe { sys::clear_storage(Ptr32::from_slice(key), key.len() as u32) }; + let ret_code = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; ret_code.into() } @@ -433,10 +342,10 @@ pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { let ret_code = { unsafe { sys::get_storage( - Ptr32::from_slice(key), + key.as_ptr(), key.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr(), + &mut output_len, ) } }; @@ -450,10 +359,10 @@ pub fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { let ret_code = { unsafe { sys::take_storage( - Ptr32::from_slice(key), + key.as_ptr(), key.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr(), + &mut output_len, ) } }; @@ -462,12 +371,12 @@ pub fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { } pub fn storage_contains(key: &[u8]) -> Option { - let ret_code = unsafe { sys::contains_storage(Ptr32::from_slice(key), key.len() as u32) }; + let ret_code = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; ret_code.into() } pub fn terminate(beneficiary: &[u8]) -> ! { - unsafe { sys::terminate(Ptr32::from_slice(beneficiary)) } + unsafe { sys::terminate(beneficiary.as_ptr()) } } #[inline(always)] @@ -477,10 +386,10 @@ pub fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) unsafe { sys::call_chain_extension( func_id, - Ptr32::from_slice(input), + input.as_ptr(), input.len() as u32, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr(), + &mut output_len, ) } }; @@ -492,7 +401,7 @@ pub fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) 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)) }; + unsafe { sys::input(output.as_mut_ptr(), &mut output_len) }; } extract_from_slice(output, output_len as usize); } @@ -501,14 +410,14 @@ pub fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { unsafe { sys::seal_return( flags.into_u32(), - Ptr32::from_slice(return_value), + return_value.as_ptr(), 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) }; + let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; ret_code.into() } @@ -521,8 +430,8 @@ macro_rules! impl_wrapper_for { { unsafe { sys::$name( - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr(), + &mut output_len, ) }; } @@ -548,8 +457,8 @@ pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { unsafe { sys::weight_to_fee( gas, - Ptr32Mut::from_slice(output), - Ptr32Mut::from_ref(&mut output_len), + output.as_mut_ptr(), + &mut output_len, ) }; } @@ -580,7 +489,7 @@ pub fn debug_message(message: &str) { // 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) }; + let ret_code = unsafe { sys::debug_message(bytes.as_ptr(), 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 } @@ -600,9 +509,9 @@ macro_rules! impl_hash_fn { pub fn [](input: &[u8], output: &mut [u8; $bytes_result]) { unsafe { sys::[]( - Ptr32::from_slice(input), + input.as_ptr(), input.len() as u32, - Ptr32Mut::from_slice(output), + output.as_mut_ptr(), ) } } @@ -621,9 +530,9 @@ pub fn ecdsa_recover( ) -> Result { let ret_code = unsafe { sys::ecdsa_recover( - Ptr32::from_slice(signature), - Ptr32::from_slice(message_hash), - Ptr32Mut::from_slice(output), + signature.as_ptr(), + message_hash.as_ptr(), + output.as_mut_ptr(), ) }; ret_code.into() @@ -631,7 +540,7 @@ pub fn ecdsa_recover( 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)) + sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; ret_code.into() } @@ -639,17 +548,17 @@ pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result 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), + signature.as_ptr(), + pub_key.as_ptr(), message.len() as u32, - Ptr32::from_slice(message), + message.as_ptr(), ) }; ret_code.into() } pub fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(Ptr32::from_slice(account_id)) }; + let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; ret_val.into_bool() } @@ -659,7 +568,7 @@ pub fn caller_is_origin() -> bool { } pub fn set_code_hash(code_hash: &[u8]) -> Result { - let ret_val = unsafe { sys::set_code_hash(Ptr32::from_slice(code_hash)) }; + let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; ret_val.into() } @@ -667,9 +576,9 @@ 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), + account_id.as_ptr(), + output.as_mut_ptr(), + &mut output_len, ) }; ret_val.into() @@ -677,5 +586,5 @@ pub fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result { 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)) } + unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } } From eb61dd7ec9e25dd6d10510eb8d559d327f96b515 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 21:40:27 +0100 Subject: [PATCH 21/81] smarter hashing --- substrate/frame/contracts/fixtures/build.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 6fb302061970b..654cb79f34fed 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -25,15 +25,12 @@ use std::{ }; use twox_hash::XxHash32; -/// Salt used for hashing contract source files. -const SALT: &[u8] = &[11u8]; - /// Read the file at `path` and return its hash as a hex string. fn file_hash(path: &Path) -> String { let data = fs::read(path).expect("file exists; qed"); let mut hasher = XxHash32::default(); hasher.write(&data); - hasher.write(SALT); + hasher.write(include_bytes!("build.rs")); let hash = hasher.finish(); format!("{:x}", hash) } From a539a878467fb2d50be6cd359f3ffc4cba883288 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 21:50:24 +0100 Subject: [PATCH 22/81] cleanup warnings --- substrate/frame/contracts/fixtures/build.rs | 2 +- substrate/frame/contracts/uapi/Cargo.toml | 3 - substrate/frame/contracts/uapi/src/lib.rs | 5 +- substrate/frame/contracts/uapi/src/wasm32.rs | 63 ++++---------------- 4 files changed, 12 insertions(+), 61 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 654cb79f34fed..972f8d498f12b 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -102,7 +102,7 @@ version = '0.1.0' [[bin]] [dependencies] -uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}, default-features = false}} +uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}}} cfg-if = {{ version = '1.0', default-features = false }} [profile.release] diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index b969a42f429d7..6265a5807f79d 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -16,6 +16,3 @@ scale = { package = "parity-scale-codec", version = "3.6.1", default-features = cfg-if = { version = "1.0", default-features = false } paste = { version = "1.0", default-features = false } -[features] -default = [ "std" ] -std = [ "scale/std" ] diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 6615fb0c7e89a..f7bc22b123b2c 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -16,9 +16,7 @@ //! //! Refer to substrate FRAME contract module for more documentation. -#![cfg_attr(not(feature = "std"), no_std)] -use core::marker::PhantomData; -use scale::Encode; +#![no_std] cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { @@ -155,7 +153,6 @@ impl ReturnCode { type Result = core::result::Result<(), Error>; -#[cfg(not(feature = "std"))] #[inline(always)] fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { debug_assert!(new_len <= output.len()); diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index a8e73b0d35c66..195f31e1ca83f 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -14,8 +14,6 @@ use super::{extract_from_slice, Result, ReturnCode}; use crate::ReturnFlags; -use core::marker::PhantomData; -use scale::Encode; mod sys { use super::ReturnCode; @@ -96,10 +94,7 @@ mod sys { output_ptr: *mut u8, ) -> ReturnCode; - pub fn ecdsa_to_eth_address( - public_key_ptr: *const u8, - output_ptr: *mut u8, - ) -> ReturnCode; + pub fn ecdsa_to_eth_address(public_key_ptr: *const u8, output_ptr: *mut 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. @@ -310,12 +305,7 @@ pub fn transfer(account_id: &[u8], value: &[u8]) -> Result { pub fn deposit_event(topics: &[u8], data: &[u8]) { unsafe { - sys::deposit_event( - topics.as_ptr(), - topics.len() as u32, - data.as_ptr(), - data.len() as u32, - ) + sys::deposit_event(topics.as_ptr(), topics.len() as u32, data.as_ptr(), data.len() as u32) } } @@ -341,12 +331,7 @@ 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( - key.as_ptr(), - key.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) + sys::get_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) } }; extract_from_slice(output, output_len as usize); @@ -358,12 +343,7 @@ 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( - key.as_ptr(), - key.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) + sys::take_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) } }; extract_from_slice(output, output_len as usize); @@ -407,13 +387,7 @@ pub fn input(output: &mut &mut [u8]) { } pub fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - unsafe { - sys::seal_return( - flags.into_u32(), - return_value.as_ptr(), - return_value.len() as u32, - ) - } + unsafe { sys::seal_return(flags.into_u32(), return_value.as_ptr(), return_value.len() as u32) } } pub fn call_runtime(call: &[u8]) -> Result { @@ -454,13 +428,7 @@ impl_wrapper_for! { 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, - output.as_mut_ptr(), - &mut output_len, - ) - }; + unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &mut output_len) }; } extract_from_slice(output, output_len as usize); } @@ -529,19 +497,13 @@ pub fn ecdsa_recover( output: &mut [u8; 33], ) -> Result { let ret_code = unsafe { - sys::ecdsa_recover( - signature.as_ptr(), - message_hash.as_ptr(), - output.as_mut_ptr(), - ) + sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) }; 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(pubkey.as_ptr(), output.as_mut_ptr()) - }; + let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; ret_code.into() } @@ -574,13 +536,8 @@ pub fn set_code_hash(code_hash: &[u8]) -> Result { 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( - account_id.as_ptr(), - output.as_mut_ptr(), - &mut output_len, - ) - }; + let ret_val = + unsafe { sys::code_hash(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; ret_val.into() } From 2d321fdf88dcf027c6d94489b246c2c3324ee0be Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 17 Nov 2023 22:20:57 +0100 Subject: [PATCH 23/81] add common --- Cargo.lock | 7 ++++ Cargo.toml | 1 + substrate/frame/contracts/fixtures/build.rs | 2 ++ .../contracts/fixtures/common/Cargo.toml | 12 +++++++ .../contracts/fixtures/common/src/lib.rs | 34 +++++++++++++++++++ .../contracts/fixtures/contracts/dummy.rs | 18 +--------- 6 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 substrate/frame/contracts/fixtures/common/Cargo.toml create mode 100644 substrate/frame/contracts/fixtures/common/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 810135ba1362b..7c18e0fec715f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9745,6 +9745,13 @@ dependencies = [ "wat", ] +[[package]] +name = "pallet-contracts-fixtures-common" +version = "1.0.0" +dependencies = [ + "cfg-if", +] + [[package]] name = "pallet-contracts-primitives" version = "24.0.0" diff --git a/Cargo.toml b/Cargo.toml index ae0ede005182c..914afdf8f9e71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -294,6 +294,7 @@ members = [ "substrate/frame/collective", "substrate/frame/contracts", "substrate/frame/contracts/fixtures", + "substrate/frame/contracts/fixtures/common", "substrate/frame/contracts/uapi", "substrate/frame/contracts/primitives", "substrate/frame/contracts/proc-macro", diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 972f8d498f12b..b25a446cfea30 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -93,6 +93,7 @@ fn create_cargo_toml<'a>( output_dir: &Path, ) -> Result<()> { let uapi_path = input_dir.join("../uapi").canonicalize()?; + let common_path = input_dir.join("./common").canonicalize()?; let mut cargo_toml: toml::Value = toml::from_str(&format!( " [package] @@ -103,6 +104,7 @@ version = '0.1.0' [dependencies] uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}}} +common = {{ package = 'pallet-contracts-fixtures-common', path = {common_path:?}}} cfg-if = {{ version = '1.0', default-features = false }} [profile.release] diff --git a/substrate/frame/contracts/fixtures/common/Cargo.toml b/substrate/frame/contracts/fixtures/common/Cargo.toml new file mode 100644 index 0000000000000..d326b860c433c --- /dev/null +++ b/substrate/frame/contracts/fixtures/common/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pallet-contracts-fixtures-common" +publish = false +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +description = "Common utilities for pallet-contracts-fixtures." + +[dependencies] +cfg-if = { version = "1.0", default-features = false } + diff --git a/substrate/frame/contracts/fixtures/common/src/lib.rs b/substrate/frame/contracts/fixtures/common/src/lib.rs new file mode 100644 index 0000000000000..560b75798c820 --- /dev/null +++ b/substrate/frame/contracts/fixtures/common/src/lib.rs @@ -0,0 +1,34 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#![no_std] + +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + core::arch::wasm32::unreachable(); + } else if #[cfg(target_arch = "riscv32")] { + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); + } + } else { + core::compile_error!("only supports wasm32 and riscv32"); + } + } +} diff --git a/substrate/frame/contracts/fixtures/contracts/dummy.rs b/substrate/frame/contracts/fixtures/contracts/dummy.rs index 42561115fba62..6270b5d9b380d 100644 --- a/substrate/frame/contracts/fixtures/contracts/dummy.rs +++ b/substrate/frame/contracts/fixtures/contracts/dummy.rs @@ -17,23 +17,7 @@ #![no_std] #![no_main] -// TODO move shared boiler plate outside of fixtures -#[panic_handler] -fn panic(info: &core::panic::PanicInfo) -> ! { - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - core::arch::wasm32::unreachable(); - } else if #[cfg(target_arch = "riscv32")] { - // Safety: The unimp instruction is guaranteed to trap - unsafe { - core::arch::asm!("unimp"); - core::hint::unreachable_unchecked(); - } - } else { - core::compile_error!("only supports wasm32 and riscv32"); - } - } -} +extern crate common; #[no_mangle] pub fn deploy() { From 697f7e101fb9313cc6a2e54520676b13ab82b9b2 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Sat, 18 Nov 2023 11:49:52 +0100 Subject: [PATCH 24/81] clippy fix --- substrate/frame/contracts/uapi/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index f7bc22b123b2c..b4e97c1a80cf8 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -115,7 +115,7 @@ impl ReturnFlags { } /// Returns the underlying `u32` representation. - #[cfg(not(feature = "std"))] + #[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] pub(crate) fn into_u32(self) -> u32 { self.value } @@ -154,6 +154,7 @@ impl ReturnCode { type Result = core::result::Result<(), Error>; #[inline(always)] +#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { debug_assert!(new_len <= output.len()); let tmp = core::mem::take(output); From 8da304366c95f189bca811f358e99e408560d85f Mon Sep 17 00:00:00 2001 From: pgherveou Date: Sat, 18 Nov 2023 12:07:53 +0100 Subject: [PATCH 25/81] fix clippy 2 --- Cargo.lock | 3 --- .../contracts/fixtures/common/Cargo.toml | 3 --- .../contracts/fixtures/common/src/lib.rs | 21 ++++++++----------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4276d8c965f21..cd2bee6a9cb46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9814,9 +9814,6 @@ dependencies = [ [[package]] name = "pallet-contracts-fixtures-common" version = "1.0.0" -dependencies = [ - "cfg-if", -] [[package]] name = "pallet-contracts-mock-network" diff --git a/substrate/frame/contracts/fixtures/common/Cargo.toml b/substrate/frame/contracts/fixtures/common/Cargo.toml index d326b860c433c..b70db7cde45f2 100644 --- a/substrate/frame/contracts/fixtures/common/Cargo.toml +++ b/substrate/frame/contracts/fixtures/common/Cargo.toml @@ -7,6 +7,3 @@ edition.workspace = true license.workspace = true description = "Common utilities for pallet-contracts-fixtures." -[dependencies] -cfg-if = { version = "1.0", default-features = false } - diff --git a/substrate/frame/contracts/fixtures/common/src/lib.rs b/substrate/frame/contracts/fixtures/common/src/lib.rs index 560b75798c820..43aa8f189026a 100644 --- a/substrate/frame/contracts/fixtures/common/src/lib.rs +++ b/substrate/frame/contracts/fixtures/common/src/lib.rs @@ -17,18 +17,15 @@ #![no_std] #[panic_handler] +#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] fn panic(_info: &core::panic::PanicInfo) -> ! { - cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - core::arch::wasm32::unreachable(); - } else if #[cfg(target_arch = "riscv32")] { - // Safety: The unimp instruction is guaranteed to trap - unsafe { - core::arch::asm!("unimp"); - core::hint::unreachable_unchecked(); - } - } else { - core::compile_error!("only supports wasm32 and riscv32"); - } + #[cfg(target_arch = "wasm32")] + core::arch::wasm32::unreachable(); + + #[cfg(target_arch = "riscv32")] + // Safety: The unimp instruction is guaranteed to trap + unsafe { + core::arch::asm!("unimp"); + core::hint::unreachable_unchecked(); } } From d9889e1805030807ecc1f85b90eeece2fcf34a2d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Sat, 18 Nov 2023 16:06:50 +0100 Subject: [PATCH 26/81] rm missing ink! stuff --- substrate/frame/contracts/uapi/src/wasm32.rs | 56 -------------------- 1 file changed, 56 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 195f31e1ca83f..91263cd86a34d 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -74,9 +74,6 @@ mod sys { pub fn own_code_hash(output_ptr: *mut u8, output_len_ptr: *mut u32); - #[cfg(feature = "ink-debug")] - pub fn debug_message(str_ptr: *const u8, str_len: u32) -> ReturnCode; - pub fn delegate_call( flags: u32, code_hash_ptr: *const u8, @@ -433,59 +430,6 @@ pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { 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(bytes.as_ptr(), 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::[]( - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - ) - } - } - } - }; -} impl_hash_fn!(sha2_256, 32); impl_hash_fn!(keccak_256, 32); impl_hash_fn!(blake2_256, 32); From fe32a966140eb3d3b86dbeb41059b90aa01b9c5d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 20 Nov 2023 14:23:14 +0100 Subject: [PATCH 27/81] add back rm code --- substrate/frame/contracts/uapi/src/wasm32.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 91263cd86a34d..a94430c7ea0f7 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -410,6 +410,23 @@ macro_rules! impl_wrapper_for { )* } } + +macro_rules! impl_hash_fn { + ( $name:ident, $bytes_result:literal ) => { + paste::item! { + pub fn [](input: &[u8], output: &mut [u8; $bytes_result]) { + unsafe { + sys::[]( + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + ) + } + } + } + }; +} + impl_wrapper_for! { caller, block_number, From a4451fcbc68af9f28d9e156ba5b1fd9eb74f192d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 20 Nov 2023 16:59:19 +0100 Subject: [PATCH 28/81] Add call.wat --- .../contracts/fixtures/contracts/call.rs | 44 +++++++++++++++++++ .../frame/contracts/fixtures/data/call.wat | 39 ---------------- 2 files changed, 44 insertions(+), 39 deletions(-) create mode 100644 substrate/frame/contracts/fixtures/contracts/call.rs delete mode 100644 substrate/frame/contracts/fixtures/data/call.wat diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs new file mode 100644 index 0000000000000..83bffc60cc7d2 --- /dev/null +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! This calls another contract as passed as its account id. +#![no_std] +#![no_main] + +extern crate common; + +#[no_mangle] +pub fn deploy() {} + +#[no_mangle] +pub fn call() { + let mut buffer = [0u8; 36]; // 4 bytes for the callee input data, 32 bytes for the callee address. + let mut out = [0u8; 0]; // No output data. + + // Read the input data. + uapi::input(&mut &mut buffer[..]); + + /// Call the callee. + uapi::call( + 0u32, // No flags. + &buffer[4..36], // callee address. + 0u64, // How much gas to devote for the execution. 0 = all. + &buffer[36..], // Pointer to value to transfer. + &buffer[0..4], // Pointer to input data buffer address. + &mut &mut out[..], // Pointer to output data buffer address. + ); +} diff --git a/substrate/frame/contracts/fixtures/data/call.wat b/substrate/frame/contracts/fixtures/data/call.wat deleted file mode 100644 index 4558b2c6409b9..0000000000000 --- a/substrate/frame/contracts/fixtures/data/call.wat +++ /dev/null @@ -1,39 +0,0 @@ -;; This calls another contract as passed as its account id. -(module - (import "seal0" "seal_input" (func $seal_input (param i32 i32))) - (import "seal1" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32) (result i32))) - (import "env" "memory" (memory 1 1)) - - (func $assert (param i32) - (block $ok - (br_if $ok - (get_local 0) - ) - (unreachable) - ) - ) - - (func (export "deploy")) - - (func (export "call") - ;; Store length of input buffer. - (i32.store (i32.const 0) (i32.const 512)) - - ;; Copy input at address 4. - (call $seal_input (i32.const 4) (i32.const 0)) - - ;; Call passed contract. - (call $assert (i32.eqz - (call $seal_call - (i32.const 0) ;; No flags - (i32.const 8) ;; Pointer to "callee" address. - (i64.const 0) ;; How much gas to devote for the execution. 0 = all. - (i32.const 512) ;; Pointer to the buffer with value to transfer - (i32.const 4) ;; Pointer to input data buffer address - (i32.const 4) ;; Length of input data buffer - (i32.const 4294967295) ;; u32 max value is the sentinel value: do not copy output - (i32.const 0) ;; Length is ignored in this case - ) - )) - ) -) From db39e91b251d57a7e7603e357b3f8814d065ec62 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 21 Nov 2023 13:13:24 +0100 Subject: [PATCH 29/81] Adds Api trait --- substrate/frame/contracts/fixtures/build.rs | 3 +- .../contracts/fixtures/contracts/call.rs | 8 +- substrate/frame/contracts/uapi/src/api.rs | 471 +++++++++++++++ substrate/frame/contracts/uapi/src/lib.rs | 13 +- substrate/frame/contracts/uapi/src/riscv32.rs | 498 ++++++++-------- substrate/frame/contracts/uapi/src/wasm32.rs | 545 +++++++++--------- 6 files changed, 1026 insertions(+), 512 deletions(-) create mode 100644 substrate/frame/contracts/uapi/src/api.rs diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index b25a446cfea30..3c39903dab161 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -149,7 +149,8 @@ fn invoke_build(current_dir: &Path) -> Result<()> { } let stderr = String::from_utf8_lossy(&build_res.stderr); - anyhow::bail!("Failed to build contracts: {:?}", stderr); + eprintln!("{}", stderr); + anyhow::bail!("Failed to build contracts"); } /// Post-process the compiled wasm contracts. diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index 83bffc60cc7d2..f7b59d8e5bfd3 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -20,6 +20,8 @@ #![no_main] extern crate common; +extern crate uapi; +use uapi::{Api, ApiImpl as api}; #[no_mangle] pub fn deploy() {} @@ -30,10 +32,10 @@ pub fn call() { let mut out = [0u8; 0]; // No output data. // Read the input data. - uapi::input(&mut &mut buffer[..]); + api::input(&mut &mut buffer[..]); - /// Call the callee. - uapi::call( + // Call the callee. + api::call( 0u32, // No flags. &buffer[4..36], // callee address. 0u64, // How much gas to devote for the execution. 0 = all. diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs new file mode 100644 index 0000000000000..36b7f3edbdce8 --- /dev/null +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -0,0 +1,471 @@ +// 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. + +// TODO: +// - remove duplicated doc in pallet_contracts +// - bring in CallFlags, ReturnFlags +// - check doc references are ok +// - Should we replace pallet_contracts::ReturnValue by uapi::Error +// - storage_contains defines in uapi but not in pallet_contracts +// - document return behavior for call_chain_extension + +use crate::{Result, ReturnFlags}; +use paste::paste; + +macro_rules! hash_fn { + ( $name:ident, $bytes:literal ) => { + paste! { + #[doc = "Computes the " $name " " $bytes "-bit hash on the given input buffer."] + #[doc = "\n# Parameters\n"] + #[doc = "- `input`: The input data buffer."] + #[doc = "- `output`: The output buffer to write the hash result to."] + fn [](input: &[u8], output: &mut [u8; $bytes]); + } + }; +} + +pub trait Api { + /// Instantiate a contract from the given code. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the code to be instantiated. + /// - `gas_limit`: How much gas to devote for the execution. + /// - `endowment`: The value to transfer into the contract. + /// - `input`: The input data buffer. + /// - `out_address`: A reference to the address buffer to write the address of the contract. + /// - `out_return_value`: A reference to the return value buffer to write the return value. + /// - `salt`: The salt bytes to use for this instantiation. + /// + /// # Errors + /// + /// Please consult the [Error][`crate::Error`] enum declaration for more information on those + /// errors. Here we only note things specific to this function. + /// + /// An error means that the account wasn't created and no address or output buffer + /// is returned unless stated otherwise. + /// + /// - [CalleeReverted][crate::Error::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][crate::Error::CalleeTrapped] + /// - [TransferFailed][crate::Error::TransferFailed] + /// - [CodeNotFound][crate::Error::CodeNotFound] + 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; + + /// Call (possibly transferring some amount of funds) into the specified account. + /// + /// # Parameters + /// + /// - `flags`: See `pallet_contracts::wasm::runtime::CallFlags` for a documentation of the supported flags. + /// - `callee`: The address of the callee. + /// - `gas_limit`: How much gas to devote for the execution. + /// - `value`: The value to transfer into the contract. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the output data. + /// + /// # Errors + /// + /// An error means that the call wasn't successful output buffer is returned unless + /// stated otherwise. + /// + /// - [CalleeReverted][crate::Error::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][crate::Error::CalleeTrapped] + /// - [TransferFailed][crate::Error::TransferFailed] + /// - [NotCallable][crate::Error::NotCallable] + fn call( + flags: u32, + callee: &[u8], + gas_limit: u64, + value: &[u8], + input: &[u8], + output: &mut &mut [u8], + ) -> Result; + + /// 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 `pallet_contracts::wasm::runtime::CallFlags` for a documentation of the supported flags. + /// - `code_hash`: The hash of the code to be executed. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the output data. + /// + /// # Errors + /// + /// An error means that the call wasn't successful and no output buffer is returned unless + /// stated otherwise. + /// + /// - [CalleeReverted][crate::Error::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][crate::Error::CalleeTrapped] + /// - [CodeNotFound][crate::Error::CodeNotFound] + fn delegate_call(flags: u32, code_hash: &[u8], input: &[u8], output: &mut &mut [u8]) -> Result; + + /// Transfer some amount of funds into the specified account. + /// + /// # Parameters + /// + /// - `account_id`: The address of the account to transfer funds to. + /// - `value`: The value to transfer. + /// + /// # Errors + /// + /// - [TransferFailed][crate::Error::TransferFailed] + fn transfer(account_id: &[u8], value: &[u8]) -> Result; + + /// Deposit an event with the given topics. + /// + /// There should not be any duplicates in `topics`. + /// + /// # Parameters + /// + /// - `topics`: The encoded `Vec` of `pallet_contracts::TopicOf` topic to deposit. + fn deposit_event(topics: &[u8], data: &[u8]); + + /// Set the storage entry by the given key to the specified value. If `value` is `None` then + /// the storage entry is deleted. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// - `encoded_value`: The storage value. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. Otherwise + /// `SENTINEL` is returned as a sentinel value. + fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option; + + /// Clear the value at the given key in the contract storage. + /// + /// # Parameters + /// + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. Otherwise + /// `SENTINEL` is returned as a sentinel value. + fn clear_storage(key: &[u8]) -> Option; + + /// Reads the storage entry of the executing account by the given `key`. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][crate::Error::KeyNotFound] + fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Retrieve and remove the value under the given key from storage. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. + /// + /// # Errors + /// + /// [KeyNotFound][crate::Error::KeyNotFound] + fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Checks whether there is a value stored under the given key. + /// + /// # Parameters + /// - `key`: The storage key. + /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn storage_contains(key: &[u8]) -> Option; + + /// Remove the calling account and transfer remaining **free** balance. + /// + /// This function never returns. Either the termination was successful and the + /// execution of the destroyed contract is halted. Or it failed during the termination + /// which is considered fatal and results in a trap + rollback. + /// + /// # Parameters + /// + /// - `beneficiary`: The address of the beneficiary account. + /// + /// # Traps + /// + /// - The contract is live i.e is already on the call stack. + /// - Failed to send the balance to the beneficiary. + /// - The deletion queue is full. + fn terminate(beneficiary: &[u8]) -> !; + + /// 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. + /// + /// # Parameters + /// + /// - `func_id`: The function id of the chain extension. + /// - `input`: The input data buffer. + /// - `output`: A reference to the output data buffer to write the output data. + /// + /// # Return + /// TODO + fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32; + + /// Stores the input passed by the caller into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the input data. + fn input(output: &mut &mut [u8]); + + /// Cease contract execution and save a data buffer as a result of the execution. + /// + /// # Parameters + /// + /// - `flags`: See `pallet_contracts::wasm::runtime::ReturnFlags` for a documentation of the supported flags. + /// - `return_value`: The return value buffer. + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; + + /// Call some dispatchable of the runtime. + /// + /// # Parameters + /// + /// - `call`: The call data. + /// + /// # Return + /// + /// Returns `Error::Success` when the dispatchable was successfully executed and + /// returned `Ok`. When the dispatchable was executed but returned an error + /// `Error::CallRuntimeFailed` 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 generally easier it cannot be + /// used in all 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. + fn call_runtime(call: &[u8]) -> Result; + + /// Stores the address of the caller into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the caller address. + fn caller(output: &mut &mut [u8]); + + /// Stores the current block number of the current contract into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the block number. + fn block_number(output: &mut &mut [u8]); + + /// Stores the address of the current contract into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the address. + fn address(output: &mut &mut [u8]); + + /// Stores the *free* balance of the current account into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the balance. + fn balance(output: &mut &mut [u8]); + + /// Stores the amount of weight left into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the weight left. + fn gas_left(output: &mut &mut [u8]); + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the transferred value. + fn value_transferred(output: &mut &mut [u8]); + + + /// Load the latest block timestamp into the supplied buffer + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the timestamp. + fn now(output: &mut &mut [u8]); + + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the minimum balance. + fn minimum_balance(output: &mut &mut [u8]); + + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// + /// # Parameters + /// + /// - `gas`: The amount of gas to query the price for. + /// - `output`: A reference to the output data buffer to write the price. + fn weight_to_fee(gas: u64, output: &mut &mut [u8]); + + hash_fn!(sha2_256, 32); + hash_fn!(keccak_256, 32); + hash_fn!(blake2_256, 32); + hash_fn!(blake2_128, 16); + + /// 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`: The signature bytes. + /// - `message_hash`: The message hash bytes. + /// - `output`: A reference to the output data buffer to write the public key. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][crate::Error::EcdsaRecoveryFailed] + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result; + + /// Calculates Ethereum address from the ECDSA compressed public key and stores + /// it into the supplied buffer. + /// + /// # Parameters + /// + /// - `pubkey`: The public key bytes. + /// - `output`: A reference to the output data buffer to write the address. + /// + /// # Errors + /// + /// - [EcdsaRecoveryFailed][crate::Error::EcdsaRecoveryFailed] + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; + + /// Verify a sr25519 signature + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message`: The message bytes. + /// + /// # Errors + /// + /// - [r25519VerifyFailed ][crate::Error::Sr25519VerifyFailed] + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; + + /// Checks whether a specified address belongs to a contract. + /// + /// # Parameters + /// + /// - `account_id`: The address to check. + /// + /// # Return + /// + /// Returns `true` if the address belongs to a contract. + fn is_contract(account_id: &[u8]) -> bool; + + /// Checks whether the caller of the current contract is the origin of the whole call stack. + /// + /// Prefer this over [`is_contract()`][`Self::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. + /// + /// # Return + /// + /// 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. + fn caller_is_origin() -> bool; + + /// Replace the contract code at the specified address with new code. + /// + /// # Note + /// + /// There are a couple of important considerations which must be taken into account when + /// using this API: + /// + /// 1. The storage at the code address will remain untouched. This means that contract + /// developers must ensure that the storage layout of the new code is compatible with that of + /// the old code. + /// + /// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another + /// way, when using this API you lose the guarantee that an address always identifies a specific + /// code hash. + /// + /// 3. If a contract calls into itself after changing its code the new call would use + /// the new code. However, if the original caller panics after returning from the sub call it + /// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next + /// caller would use the old code. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the new code. + /// + /// # Errors + /// + /// - [CodeNotFound ][crate::Error::CodeNotFound] + fn set_code_hash(code_hash: &[u8]) -> Result; + + /// Retrieve the code hash for a specified contract address. + /// + /// # Parameters + /// + /// - `account_id`: The address of the contract. + /// - `output`: A reference to the output data buffer to write the code hash. + /// + /// + /// # Errors + /// + /// - [CodeNotFound ][crate::Error::CodeNotFound] + fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; + + /// Retrieve the code hash of the currently executing contract. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the code hash. + fn own_code_hash(output: &mut [u8]); +} diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index b4e97c1a80cf8..5a476f8737cfc 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -18,13 +18,16 @@ #![no_std] +mod api; +pub use api::Api; + cfg_if::cfg_if! { if #[cfg(target_arch = "wasm32")] { mod wasm32; - pub use wasm32::*; + pub use wasm32::ApiImpl; } else if #[cfg(target_arch = "riscv32")] { mod riscv32; - pub use riscv32::*; + pub use riscv32::ApiImpl; } } @@ -90,6 +93,12 @@ define_error_codes! { CallRuntimeFailed = 10, /// ECDSA public key recovery failed. Most probably wrong recovery id or signature. EcdsaRecoveryFailed = 11, + /// sr25519 signature verification failed. + Sr25519VerifyFailed = 12, + /// The `xcm_execute` call failed. + XcmExecutionFailed = 13, + /// The `xcm_send` call failed. + XcmSendFailed = 14, } /// The flags to indicate further information about the end of a contract execution. diff --git a/substrate/frame/contracts/uapi/src/riscv32.rs b/substrate/frame/contracts/uapi/src/riscv32.rs index 594db2f03c86a..f727f4a4d071f 100644 --- a/substrate/frame/contracts/uapi/src/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/riscv32.rs @@ -177,279 +177,289 @@ mod sys { } } -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 mut in_data = [0u8; 64]; - #[allow(trivial_casts)] - ( - code_hash.as_ptr() as u32, - gas_limit, - endowment.as_ptr() as u32, - input.as_ptr() as u32, - input.len() as u32, - out_address.as_mut_ptr() as u32, - &mut address_len as *mut _ as u32, - out_return_value.as_mut_ptr() as u32, - &mut return_value_len as *mut _ as u32, - salt.as_ptr() as u32, - salt.len() as u32, - ) - .encode_to(&mut EncodeScope::from(in_data.as_mut())); - let ret_val = unsafe { sys::instantiate(in_data.as_ptr()) }; - extract_from_slice(out_address, address_len as usize); - extract_from_slice(out_return_value, return_value_len as usize); - ReturnCode(ret_val).into() +macro_rules! impl_wrapper_for { + ( $( $name:ident, )* ) => { + $( + fn $name(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { + sys::$name( + output.as_mut_ptr(), + &mut output_len, + ) + } + extract_from_slice(output, output_len as usize) + } + )* + } } - -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 mut in_data = [0u8; 64]; - #[allow(trivial_casts)] - ( - flags, - callee.as_ptr() as u32, - gas_limit, - value.as_ptr() as u32, - input.as_ptr() as u32, - input.len() as u32, - output.as_mut_ptr() as u32, - &mut output_len as *mut _ as u32, - ) - .encode_to(&mut EncodeScope::from(in_data.as_mut())); - let ret_val = unsafe { sys::call(in_data.as_ptr()) }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() +macro_rules! impl_hash_fn { + ( $name:ident, $bytes_result:literal ) => { + paste::item! { + fn [](input: &[u8], output: &mut [u8; $bytes_result]) { + unsafe { + sys::[]( + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + ) + } + } + } + }; } -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_val = unsafe { - sys::delegate_call( +pub enum ApiImpl {} + +impl super::Api for ApiImpl { + 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 mut in_data = [0u8; 64]; + #[allow(trivial_casts)] + ( + code_hash.as_ptr() as u32, + gas_limit, + endowment.as_ptr() as u32, + input.as_ptr() as u32, + input.len() as u32, + out_address.as_mut_ptr() as u32, + &mut address_len as *mut _ as u32, + out_return_value.as_mut_ptr() as u32, + &mut return_value_len as *mut _ as u32, + salt.as_ptr() as u32, + salt.len() as u32, + ) + .encode_to(&mut EncodeScope::from(in_data.as_mut())); + let ret_val = unsafe { sys::instantiate(in_data.as_ptr()) }; + extract_from_slice(out_address, address_len as usize); + extract_from_slice(out_return_value, return_value_len as usize); + ReturnCode(ret_val).into() + } + + 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 mut in_data = [0u8; 64]; + #[allow(trivial_casts)] + ( flags, - code_hash.as_ptr(), - input.as_ptr(), + callee.as_ptr() as u32, + gas_limit, + value.as_ptr() as u32, + input.as_ptr() as u32, input.len() as u32, - output.as_mut_ptr(), - &mut output_len, + output.as_mut_ptr() as u32, + &mut output_len as *mut _ as u32, ) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() -} + .encode_to(&mut EncodeScope::from(in_data.as_mut())); + let ret_val = unsafe { sys::call(in_data.as_ptr()) }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).into() + } -pub fn transfer(account_id: &[u8], value: &[u8]) -> Result { - let ret_val = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; - ReturnCode(ret_val).into() -} + 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_val = unsafe { + sys::delegate_call( + flags, + code_hash.as_ptr(), + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).into() + } -pub fn deposit_event(topics: &[u8], data: &[u8]) { - unsafe { - sys::deposit_event(topics.as_ptr(), topics.len() as u32, data.as_ptr(), data.len() as u32) + fn transfer(account_id: &[u8], value: &[u8]) -> Result { + let ret_val = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; + ReturnCode(ret_val).into() } -} -pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { - let ret_val = unsafe { - sys::set_storage( - key.as_ptr(), - key.len() as u32, - encoded_value.as_ptr(), - encoded_value.len() as u32, - ) - }; - ReturnCode(ret_val).into() -} + fn deposit_event(topics: &[u8], data: &[u8]) { + unsafe { + sys::deposit_event( + topics.as_ptr(), + topics.len() as u32, + data.as_ptr(), + data.len() as u32, + ) + } + } -pub fn clear_storage(key: &[u8]) -> Option { - let ret_val = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; - ret_val.into() -} + fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { + let ret_val = unsafe { + sys::set_storage( + key.as_ptr(), + key.len() as u32, + encoded_value.as_ptr(), + encoded_value.len() as u32, + ) + }; + ReturnCode(ret_val).into() + } -pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::get_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() -} + fn clear_storage(key: &[u8]) -> Option { + let ret_val = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; + ret_val.into() + } -pub fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::take_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() -} + fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_val = unsafe { + sys::get_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) + }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).into() + } -pub fn storage_contains(key: &[u8]) -> Option { - let ret_val = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; - ReturnCode(ret_val).into() -} + fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_val = unsafe { + sys::take_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) + }; + extract_from_slice(output, output_len as usize); + ReturnCode(ret_val).into() + } -pub fn terminate(beneficiary: &[u8]) -> ! { - unsafe { - sys::terminate(beneficiary.as_ptr()); - core::hint::unreachable_unchecked(); + fn storage_contains(key: &[u8]) -> Option { + let ret_val = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; + ReturnCode(ret_val).into() } -} -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_val = unsafe { - sys::call_chain_extension( - func_id, - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - }; - extract_from_slice(output, output_len as usize); - ret_val -} + fn terminate(beneficiary: &[u8]) -> ! { + unsafe { + sys::terminate(beneficiary.as_ptr()); + core::hint::unreachable_unchecked(); + } + } -pub fn input(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::input(output.as_mut_ptr(), &mut output_len) } - extract_from_slice(output, output_len as usize); -} + fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 { + let mut output_len = output.len() as u32; + let ret_val = unsafe { + sys::call_chain_extension( + func_id, + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + }; + extract_from_slice(output, output_len as usize); + ret_val + } -pub fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - unsafe { - sys::seal_return(flags.into_u32(), return_value.as_ptr(), return_value.len() as u32); - core::hint::unreachable_unchecked(); + fn input(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { sys::input(output.as_mut_ptr(), &mut output_len) } + extract_from_slice(output, output_len as usize); } -} -pub fn call_runtime(call: &[u8]) -> Result { - let ret_val = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; - ReturnCode(ret_val).into() -} + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { + unsafe { + sys::seal_return(flags.into_u32(), return_value.as_ptr(), return_value.len() as u32); + core::hint::unreachable_unchecked(); + } + } -macro_rules! impl_wrapper_for { - ( $( $name:ident, )* ) => { - $( - pub fn $name(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { - sys::$name( - output.as_mut_ptr(), - &mut output_len, - ) - } - extract_from_slice(output, output_len as usize) - } - )* - } -} -impl_wrapper_for! { - caller, - block_number, - address, - balance, - gas_left, - value_transferred, - now, - minimum_balance, -} + fn call_runtime(call: &[u8]) -> Result { + let ret_val = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; + ReturnCode(ret_val).into() + } -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, output.as_mut_ptr(), &mut output_len) } - extract_from_slice(output, output_len as usize); -} + impl_wrapper_for! { + caller, + block_number, + address, + balance, + gas_left, + value_transferred, + now, + minimum_balance, + } -macro_rules! impl_hash_fn { - ( $name:ident, $bytes_result:literal ) => { - paste::item! { - pub fn [](input: &[u8], output: &mut [u8; $bytes_result]) { - unsafe { - sys::[]( - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - ) - } - } - } - }; -} -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_val = unsafe { - sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) - }; - ReturnCode(ret_val).into() -} + fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &mut output_len) } + extract_from_slice(output, output_len as usize); + } -pub fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { - let ret_val = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; - ReturnCode(ret_val).into() -} + impl_hash_fn!(sha2_256, 32); + impl_hash_fn!(keccak_256, 32); + impl_hash_fn!(blake2_256, 32); + impl_hash_fn!(blake2_128, 16); + + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result { + let ret_val = unsafe { + sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) + }; + ReturnCode(ret_val).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_val = unsafe { - sys::sr25519_verify( - signature.as_ptr(), - pub_key.as_ptr(), - message.len() as u32, - message.as_ptr(), - ) - }; - ReturnCode(ret_val).into() -} + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { + let ret_val = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; + ReturnCode(ret_val).into() + } -pub fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; - ReturnCode(ret_val).into_bool() -} + /// **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. + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { + let ret_val = unsafe { + sys::sr25519_verify( + signature.as_ptr(), + pub_key.as_ptr(), + message.len() as u32, + message.as_ptr(), + ) + }; + ReturnCode(ret_val).into() + } -pub fn caller_is_origin() -> bool { - let ret_val = unsafe { sys::caller_is_origin() }; - ReturnCode(ret_val).into_bool() -} + fn is_contract(account_id: &[u8]) -> bool { + let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; + ReturnCode(ret_val).into_bool() + } -pub fn set_code_hash(code_hash: &[u8]) -> Result { - let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; - ReturnCode(ret_val).into() -} + fn caller_is_origin() -> bool { + let ret_val = unsafe { sys::caller_is_origin() }; + ReturnCode(ret_val).into_bool() + } -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(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; - ReturnCode(ret_val).into() -} + fn set_code_hash(code_hash: &[u8]) -> Result { + let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; + ReturnCode(ret_val).into() + } -pub fn own_code_hash(output: &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } + 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(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; + ReturnCode(ret_val).into() + } + + fn own_code_hash(output: &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } + } } diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index a94430c7ea0f7..f612230388d01 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -207,302 +207,323 @@ mod sys { } } -#[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( - code_hash.as_ptr(), - gas_limit, - endowment.as_ptr(), - input.as_ptr(), - input.len() as u32, - out_address.as_mut_ptr(), - &mut address_len, - out_return_value.as_mut_ptr(), - &mut return_value_len, - salt.as_ptr(), - salt.len() as u32, - ) +macro_rules! impl_wrapper_for { + ( $( $name:ident, )* ) => { + $( + #[inline(always)] + fn $name(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { + sys::$name( + output.as_mut_ptr(), + &mut output_len, + ) + }; + } + } + )* + } +} + +macro_rules! impl_hash_fn { + ( $name:ident, $bytes_result:literal ) => { + paste::item! { + fn [](input: &[u8], output: &mut [u8; $bytes_result]) { + unsafe { + sys::[]( + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + ) + } + } } }; - 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, - callee.as_ptr(), - gas_limit, +pub enum ApiImpl {} + +impl super::Api for ApiImpl { + #[inline(always)] + 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( + code_hash.as_ptr(), + gas_limit, + endowment.as_ptr(), + input.as_ptr(), + input.len() as u32, + out_address.as_mut_ptr(), + &mut address_len, + out_return_value.as_mut_ptr(), + &mut return_value_len, + salt.as_ptr(), + 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)] + 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, + callee.as_ptr(), + gas_limit, + value.as_ptr(), + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } + + #[inline(always)] + 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, + code_hash.as_ptr(), + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } + + fn transfer(account_id: &[u8], value: &[u8]) -> Result { + let ret_code = unsafe { + sys::transfer( + account_id.as_ptr(), + account_id.len() as u32, value.as_ptr(), - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, + value.len() as u32, ) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into() -} + }; + 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 = { + fn deposit_event(topics: &[u8], data: &[u8]) { unsafe { - sys::delegate_call( - flags, - code_hash.as_ptr(), - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, + sys::deposit_event( + topics.as_ptr(), + topics.len() as u32, + data.as_ptr(), + data.len() as u32, ) } - }; - 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( - account_id.as_ptr(), - account_id.len() as u32, - value.as_ptr(), - value.len() as u32, - ) - }; - ret_code.into() -} + fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { + let ret_code = unsafe { + sys::set_storage( + key.as_ptr(), + key.len() as u32, + encoded_value.as_ptr(), + encoded_value.len() as u32, + ) + }; + ret_code.into() + } -pub fn deposit_event(topics: &[u8], data: &[u8]) { - unsafe { - sys::deposit_event(topics.as_ptr(), topics.len() as u32, data.as_ptr(), data.len() as u32) + fn clear_storage(key: &[u8]) -> Option { + let ret_code = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; + ret_code.into() } -} -pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { - let ret_code = unsafe { - sys::set_storage( - key.as_ptr(), - key.len() as u32, - encoded_value.as_ptr(), - encoded_value.len() as u32, - ) - }; - ret_code.into() -} + #[inline(always)] + 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( + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } -pub fn clear_storage(key: &[u8]) -> Option { - let ret_code = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; - ret_code.into() -} + #[inline(always)] + 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( + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } -#[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(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into() -} + fn storage_contains(key: &[u8]) -> Option { + let ret_code = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; + 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(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) - } - }; - extract_from_slice(output, output_len as usize); - ret_code.into() -} + fn terminate(beneficiary: &[u8]) -> ! { + unsafe { sys::terminate(beneficiary.as_ptr()) } + } -pub fn storage_contains(key: &[u8]) -> Option { - let ret_code = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; - ret_code.into() -} + #[inline(always)] + 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, + input.as_ptr(), + input.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into_u32() + } -pub fn terminate(beneficiary: &[u8]) -> ! { - unsafe { sys::terminate(beneficiary.as_ptr()) } -} + #[inline(always)] + fn input(output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { sys::input(output.as_mut_ptr(), &mut output_len) }; + } + extract_from_slice(output, output_len as usize); + } -#[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 = { + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { unsafe { - sys::call_chain_extension( - func_id, - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) + sys::seal_return(flags.into_u32(), return_value.as_ptr(), return_value.len() as u32) } - }; - 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(output.as_mut_ptr(), &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(), return_value.as_ptr(), return_value.len() as u32) } -} -pub fn call_runtime(call: &[u8]) -> Result { - let ret_code = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; - ret_code.into() -} + fn call_runtime(call: &[u8]) -> Result { + let ret_code = unsafe { sys::call_runtime(call.as_ptr(), 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( - output.as_mut_ptr(), - &mut output_len, - ) - }; - } - } - )* - } -} + impl_wrapper_for! { + caller, + block_number, + address, + balance, + gas_left, + value_transferred, + now, + minimum_balance, + } -macro_rules! impl_hash_fn { - ( $name:ident, $bytes_result:literal ) => { - paste::item! { - pub fn [](input: &[u8], output: &mut [u8; $bytes_result]) { - unsafe { - sys::[]( - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - ) - } - } + #[inline(always)] + fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &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, output.as_mut_ptr(), &mut output_len) }; + extract_from_slice(output, output_len as usize); } - extract_from_slice(output, output_len as usize); -} -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(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) - }; - ret_code.into() -} + impl_hash_fn!(sha2_256, 32); + impl_hash_fn!(keccak_256, 32); + impl_hash_fn!(blake2_256, 32); + impl_hash_fn!(blake2_128, 16); + + fn ecdsa_recover( + signature: &[u8; 65], + message_hash: &[u8; 32], + output: &mut [u8; 33], + ) -> Result { + let ret_code = unsafe { + sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) + }; + 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(pubkey.as_ptr(), output.as_mut_ptr()) }; - ret_code.into() -} + fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { + let ret_code = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; + ret_code.into() + } -pub fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { - let ret_code = unsafe { - sys::sr25519_verify( - signature.as_ptr(), - pub_key.as_ptr(), - message.len() as u32, - message.as_ptr(), - ) - }; - ret_code.into() -} + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { + let ret_code = unsafe { + sys::sr25519_verify( + signature.as_ptr(), + pub_key.as_ptr(), + message.len() as u32, + message.as_ptr(), + ) + }; + ret_code.into() + } -pub fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; - ret_val.into_bool() -} + fn is_contract(account_id: &[u8]) -> bool { + let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; + ret_val.into_bool() + } -pub fn caller_is_origin() -> bool { - let ret_val = unsafe { sys::caller_is_origin() }; - ret_val.into_bool() -} + 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(code_hash.as_ptr()) }; - ret_val.into() -} + fn set_code_hash(code_hash: &[u8]) -> Result { + let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; + 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(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; - ret_val.into() -} + 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(account_id.as_ptr(), output.as_mut_ptr(), &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(output.as_mut_ptr(), &mut output_len) } + fn own_code_hash(output: &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } + } } From 68430f9de14ed6cd7f18d1e43bc97a4489b7f9d4 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 21 Nov 2023 22:05:09 +0100 Subject: [PATCH 30/81] Refactoring primitives & uapi --- Cargo.lock | 22 ++---- Cargo.toml | 1 - .../contracts/contracts-rococo/Cargo.toml | 3 +- substrate/bin/node/runtime/Cargo.toml | 3 +- substrate/frame/contracts/Cargo.toml | 3 +- substrate/frame/contracts/fixtures/build.rs | 3 +- .../contracts/fixtures/contracts/call.rs | 16 ++-- .../frame/contracts/mock-network/Cargo.toml | 3 +- .../frame/contracts/mock-network/src/tests.rs | 2 +- .../frame/contracts/primitives/Cargo.toml | 33 --------- .../frame/contracts/primitives/README.md | 3 - .../frame/contracts/src/chain_extension.rs | 2 +- substrate/frame/contracts/src/debug.rs | 2 +- substrate/frame/contracts/src/exec.rs | 4 +- substrate/frame/contracts/src/lib.rs | 3 +- .../src/lib.rs => src/primitives.rs} | 13 +--- substrate/frame/contracts/src/tests.rs | 2 +- .../frame/contracts/src/tests/test_debug.rs | 2 +- substrate/frame/contracts/src/wasm/mod.rs | 7 +- substrate/frame/contracts/src/wasm/runtime.rs | 50 +------------ substrate/frame/contracts/uapi/Cargo.toml | 8 +- substrate/frame/contracts/uapi/src/api.rs | 50 +++++++++---- substrate/frame/contracts/uapi/src/flags.rs | 73 +++++++++++++++++++ substrate/frame/contracts/uapi/src/lib.rs | 59 ++++++--------- substrate/frame/contracts/uapi/src/wasm32.rs | 71 +++++++++++------- 25 files changed, 220 insertions(+), 218 deletions(-) delete mode 100644 substrate/frame/contracts/primitives/Cargo.toml delete mode 100644 substrate/frame/contracts/primitives/README.md rename substrate/frame/contracts/{primitives/src/lib.rs => src/primitives.rs} (96%) create mode 100644 substrate/frame/contracts/uapi/src/flags.rs diff --git a/Cargo.lock b/Cargo.lock index cd2bee6a9cb46..556ef4ea51abb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3183,7 +3183,7 @@ dependencies = [ "pallet-balances", "pallet-collator-selection", "pallet-contracts", - "pallet-contracts-primitives", + "pallet-contracts-uapi", "pallet-insecure-randomness-collective-flip", "pallet-message-queue", "pallet-multisig", @@ -7216,7 +7216,7 @@ dependencies = [ "pallet-child-bounties", "pallet-collective", "pallet-contracts", - "pallet-contracts-primitives", + "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-democracy", @@ -9768,8 +9768,8 @@ dependencies = [ "pallet-assets", "pallet-balances", "pallet-contracts-fixtures", - "pallet-contracts-primitives", "pallet-contracts-proc-macro", + "pallet-contracts-uapi", "pallet-insecure-randomness-collective-flip", "pallet-message-queue", "pallet-proxy", @@ -9826,8 +9826,8 @@ dependencies = [ "pallet-balances", "pallet-contracts", "pallet-contracts-fixtures", - "pallet-contracts-primitives", "pallet-contracts-proc-macro", + "pallet-contracts-uapi", "pallet-insecure-randomness-collective-flip", "pallet-message-queue", "pallet-proxy", @@ -9853,18 +9853,6 @@ dependencies = [ "xcm-simulator", ] -[[package]] -name = "pallet-contracts-primitives" -version = "24.0.0" -dependencies = [ - "bitflags 1.3.2", - "parity-scale-codec", - "scale-info", - "sp-runtime", - "sp-std 8.0.0", - "sp-weights", -] - [[package]] name = "pallet-contracts-proc-macro" version = "4.0.0-dev" @@ -9878,9 +9866,11 @@ dependencies = [ name = "pallet-contracts-uapi" version = "4.0.0-dev" dependencies = [ + "bitflags 1.3.2", "cfg-if", "parity-scale-codec", "paste", + "scale-info", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index c05dcda5be5d0..64139224c2d3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -290,7 +290,6 @@ members = [ "substrate/frame/contracts/fixtures", "substrate/frame/contracts/fixtures/common", "substrate/frame/contracts/uapi", - "substrate/frame/contracts/primitives", "substrate/frame/contracts/proc-macro", "substrate/frame/contracts/mock-network", "substrate/frame/conviction-voting", diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index eded360436b1d..b85b654bd7585 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -52,7 +52,7 @@ pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/ pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false} pallet-sudo = { path = "../../../../../substrate/frame/sudo", default-features = false} pallet-contracts = { path = "../../../../../substrate/frame/contracts", default-features = false} -pallet-contracts-primitives = { path = "../../../../../substrate/frame/contracts/primitives", default-features = false} +pallet-contracts-uapi = { path = "../../../../../substrate/frame/contracts/uapi", default-features = false} # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } @@ -102,7 +102,6 @@ std = [ "pallet-authorship/std", "pallet-balances/std", "pallet-collator-selection/std", - "pallet-contracts-primitives/std", "pallet-contracts/std", "pallet-insecure-randomness-collective-flip/std", "pallet-message-queue/std", diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 2414358b60b43..697cf9fe12e03 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -74,7 +74,7 @@ pallet-broker = { path = "../../../frame/broker", default-features = false} pallet-child-bounties = { path = "../../../frame/child-bounties", default-features = false} pallet-collective = { path = "../../../frame/collective", default-features = false} pallet-contracts = { path = "../../../frame/contracts", default-features = false} -pallet-contracts-primitives = { path = "../../../frame/contracts/primitives", default-features = false} +pallet-contracts-uapi = { path = "../../../frame/contracts/uapi", default-features = false} pallet-conviction-voting = { path = "../../../frame/conviction-voting", default-features = false} pallet-core-fellowship = { path = "../../../frame/core-fellowship", default-features = false} pallet-democracy = { path = "../../../frame/democracy", default-features = false} @@ -171,7 +171,6 @@ std = [ "pallet-broker/std", "pallet-child-bounties/std", "pallet-collective/std", - "pallet-contracts-primitives/std", "pallet-contracts/std", "pallet-conviction-voting/std", "pallet-core-fellowship/std", diff --git a/substrate/frame/contracts/Cargo.toml b/substrate/frame/contracts/Cargo.toml index 239b0865e0f0b..451d99bca8d52 100644 --- a/substrate/frame/contracts/Cargo.toml +++ b/substrate/frame/contracts/Cargo.toml @@ -40,7 +40,7 @@ frame-benchmarking = { path = "../benchmarking", default-features = false, optio frame-support = { path = "../support", default-features = false} frame-system = { path = "../system", default-features = false} pallet-balances = { path = "../balances", default-features = false , optional = true} -pallet-contracts-primitives = { path = "primitives", default-features = false} +pallet-contracts-uapi = { path = "uapi" } pallet-contracts-proc-macro = { path = "proc-macro" } sp-api = { path = "../../primitives/api", default-features = false} sp-core = { path = "../../primitives/core", default-features = false} @@ -84,7 +84,6 @@ std = [ "log/std", "pallet-balances?/std", "pallet-contracts-fixtures/std", - "pallet-contracts-primitives/std", "pallet-contracts-proc-macro/full", "pallet-insecure-randomness-collective-flip/std", "pallet-proxy/std", diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 3c39903dab161..5c42f4a7353ed 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -100,10 +100,11 @@ fn create_cargo_toml<'a>( name = 'contracts' version = '0.1.0' +# Binary targets are injected below. [[bin]] [dependencies] -uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}}} +uapi = {{ package = 'pallet-contracts-uapi', default-features = false, path = {uapi_path:?}}} common = {{ package = 'pallet-contracts-fixtures-common', path = {common_path:?}}} cfg-if = {{ version = '1.0', default-features = false }} diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index f7b59d8e5bfd3..b8a978299e6e9 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -21,7 +21,7 @@ extern crate common; extern crate uapi; -use uapi::{Api, ApiImpl as api}; +use uapi::{Api, CallFlags, ApiImpl as api}; #[no_mangle] pub fn deploy() {} @@ -34,13 +34,13 @@ pub fn call() { // Read the input data. api::input(&mut &mut buffer[..]); - // Call the callee. + // xx Call the callee. api::call( - 0u32, // No flags. - &buffer[4..36], // callee address. - 0u64, // How much gas to devote for the execution. 0 = all. - &buffer[36..], // Pointer to value to transfer. - &buffer[0..4], // Pointer to input data buffer address. - &mut &mut out[..], // Pointer to output data buffer address. + CallFlags::empty(), + &buffer[4..36], // callee address. + 0u64, // How much gas to devote for the execution. 0 = all. + &buffer[36..], // Pointer to value to transfer. + &buffer[0..4], // Pointer to input data buffer address. + Some(&mut out[..]), // Pointer to output data buffer address. ); } diff --git a/substrate/frame/contracts/mock-network/Cargo.toml b/substrate/frame/contracts/mock-network/Cargo.toml index 9d5fe1aaf4ec6..28e24dd15e45d 100644 --- a/substrate/frame/contracts/mock-network/Cargo.toml +++ b/substrate/frame/contracts/mock-network/Cargo.toml @@ -16,7 +16,7 @@ frame-system = { path = "../../system", default-features = false} pallet-assets = { path = "../../assets" } pallet-balances = { path = "../../balances" } pallet-contracts = { path = ".." } -pallet-contracts-primitives = { path = "../primitives", default-features = false} +pallet-contracts-uapi = { path = "../uapi", default-features = false} pallet-contracts-proc-macro = { path = "../proc-macro" } pallet-insecure-randomness-collective-flip = { path = "../../insecure-randomness-collective-flip" } pallet-message-queue = { path = "../../message-queue" } @@ -52,7 +52,6 @@ std = [ "frame-support/std", "frame-system/std", "pallet-balances/std", - "pallet-contracts-primitives/std", "pallet-contracts-proc-macro/full", "pallet-contracts/std", "pallet-insecure-randomness-collective-flip/std", diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index 5193f65705516..1a5a2634d05fd 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -31,7 +31,7 @@ use frame_support::{ use pallet_balances::{BalanceLock, Reasons}; use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; use pallet_contracts_fixtures::compile_module; -use pallet_contracts_primitives::Code; +use crate::primitives::Code; use xcm::{v3::prelude::*, VersionedMultiLocation, VersionedXcm}; use xcm_simulator::TestExt; diff --git a/substrate/frame/contracts/primitives/Cargo.toml b/substrate/frame/contracts/primitives/Cargo.toml deleted file mode 100644 index 0394841aa1f41..0000000000000 --- a/substrate/frame/contracts/primitives/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "pallet-contracts-primitives" -version = "24.0.0" -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" -homepage = "https://substrate.io" -repository.workspace = true -description = "A crate that hosts a common definitions that are relevant for the pallet-contracts." -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -bitflags = "1.0" -scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } - -# Substrate Dependencies (This crate should not rely on frame) -sp-std = { path = "../../../primitives/std", default-features = false} -sp-runtime = { path = "../../../primitives/runtime", default-features = false} -sp-weights = { path = "../../../primitives/weights", default-features = false} - -[features] -default = [ "std" ] -std = [ - "codec/std", - "scale-info/std", - "sp-runtime/std", - "sp-std/std", - "sp-weights/std", -] diff --git a/substrate/frame/contracts/primitives/README.md b/substrate/frame/contracts/primitives/README.md deleted file mode 100644 index c84cfbfe1a87b..0000000000000 --- a/substrate/frame/contracts/primitives/README.md +++ /dev/null @@ -1,3 +0,0 @@ -A crate that hosts a common definitions that are relevant for the pallet-contracts. - -License: Apache-2.0 diff --git a/substrate/frame/contracts/src/chain_extension.rs b/substrate/frame/contracts/src/chain_extension.rs index 664504d207f3a..8a7243d6bb371 100644 --- a/substrate/frame/contracts/src/chain_extension.rs +++ b/substrate/frame/contracts/src/chain_extension.rs @@ -81,7 +81,7 @@ use sp_std::{marker::PhantomData, vec::Vec}; pub use crate::{exec::Ext, gas::ChargedAmount, storage::meter::Diff, Config}; pub use frame_system::Config as SysConfig; -pub use pallet_contracts_primitives::ReturnFlags; +pub use pallet_contracts_uapi::ReturnFlags; /// Result that returns a [`DispatchError`] on error. pub type Result = sp_std::result::Result; diff --git a/substrate/frame/contracts/src/debug.rs b/substrate/frame/contracts/src/debug.rs index e22a841e6fb7f..032c2c076af2e 100644 --- a/substrate/frame/contracts/src/debug.rs +++ b/substrate/frame/contracts/src/debug.rs @@ -16,8 +16,8 @@ // limitations under the License. pub use crate::exec::{ExecResult, ExportedFunction}; +pub use crate::primitives::ExecReturnValue; use crate::{Config, LOG_TARGET}; -pub use pallet_contracts_primitives::ExecReturnValue; /// Umbrella trait for all interfaces that serves for debugging. pub trait Debugger: Tracing + CallInterceptor {} diff --git a/substrate/frame/contracts/src/exec.rs b/substrate/frame/contracts/src/exec.rs index c26d82f7f110c..2183d6b96cc5d 100644 --- a/substrate/frame/contracts/src/exec.rs +++ b/substrate/frame/contracts/src/exec.rs @@ -18,6 +18,7 @@ use crate::{ debug::{CallInterceptor, CallSpan, Tracing}, gas::GasMeter, + primitives::{ExecReturnValue, StorageDeposit}, storage::{self, meter::Diff, WriteOutcome}, BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DebugBufferVec, Determinism, Error, Event, Nonce, Origin, Pallet as Contracts, Schedule, @@ -37,7 +38,6 @@ use frame_support::{ Blake2_128Concat, BoundedVec, StorageHasher, }; use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; -use pallet_contracts_primitives::{ExecReturnValue, StorageDeposit}; use smallvec::{Array, SmallVec}; use sp_core::{ ecdsa::Public as ECDSAPublic, @@ -1618,7 +1618,7 @@ mod tests { use codec::{Decode, Encode}; use frame_support::{assert_err, assert_ok, parameter_types}; use frame_system::{EventRecord, Phase}; - use pallet_contracts_primitives::ReturnFlags; + use pallet_contracts_uapi::ReturnFlags; use pretty_assertions::assert_eq; use sp_runtime::{traits::Hash, DispatchError}; use std::{cell::RefCell, collections::hash_map::HashMap, rc::Rc}; diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 188679dbf4902..6f4bb2ef42869 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -94,6 +94,7 @@ mod gas; mod schedule; mod storage; mod wasm; +mod primitives; pub mod chain_extension; pub mod debug; @@ -128,7 +129,7 @@ use frame_system::{ pallet_prelude::{BlockNumberFor, OriginFor}, EventRecord, Pallet as System, }; -use pallet_contracts_primitives::{ +use crate::primitives::{ Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult, ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult, InstantiateReturnValue, StorageDeposit, diff --git a/substrate/frame/contracts/primitives/src/lib.rs b/substrate/frame/contracts/src/primitives.rs similarity index 96% rename from substrate/frame/contracts/primitives/src/lib.rs rename to substrate/frame/contracts/src/primitives.rs index c33149285004b..f5d2aeb1f1798 100644 --- a/substrate/frame/contracts/primitives/src/lib.rs +++ b/substrate/frame/contracts/src/primitives.rs @@ -19,7 +19,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bitflags::bitflags; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ @@ -27,7 +26,8 @@ use sp_runtime::{ DispatchError, RuntimeDebug, }; use sp_std::prelude::*; -use sp_weights::Weight; +use frame_support::weights::Weight; +use pallet_contracts_uapi::ReturnFlags; /// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and /// `ContractsApi::instantiate`. @@ -109,15 +109,6 @@ pub enum ContractAccessError { MigrationInProgress, } -bitflags! { - /// Flags used by a contract to customize exit behaviour. - #[derive(Encode, Decode, TypeInfo)] - pub struct ReturnFlags: u32 { - /// If this bit is set all changes made by the contract execution are rolled back. - const REVERT = 0x0000_0001; - } -} - /// Output of a contract call or instantiation which ran to completion. #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ExecReturnValue { diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index 76fd012852a90..b4aafe44c1e4f 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -54,7 +54,7 @@ use frame_support::{ }; use frame_system::{EventRecord, Phase}; use pallet_contracts_fixtures::compile_module; -use pallet_contracts_primitives::CodeUploadReturnValue; +use crate::primitives::CodeUploadReturnValue; use pretty_assertions::{assert_eq, assert_ne}; use sp_core::ByteArray; use sp_io::hashing::blake2_256; diff --git a/substrate/frame/contracts/src/tests/test_debug.rs b/substrate/frame/contracts/src/tests/test_debug.rs index 2d7ed47436575..c6a812b25a89b 100644 --- a/substrate/frame/contracts/src/tests/test_debug.rs +++ b/substrate/frame/contracts/src/tests/test_debug.rs @@ -19,9 +19,9 @@ use super::*; use crate::{ debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing}, AccountIdOf, + primitives::ExecReturnValue }; use frame_support::traits::Currency; -use pallet_contracts_primitives::ExecReturnValue; use pretty_assertions::assert_eq; use std::cell::RefCell; diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs index 77e94b16777b0..16c96e34fc74c 100644 --- a/substrate/frame/contracts/src/wasm/mod.rs +++ b/substrate/frame/contracts/src/wasm/mod.rs @@ -26,9 +26,9 @@ pub use crate::wasm::runtime::api_doc; #[cfg(test)] pub use tests::MockExt; - +pub use pallet_contracts_uapi::ReturnFlags; pub use crate::wasm::runtime::{ - AllowDeprecatedInterface, AllowUnstableInterface, CallFlags, Environment, ReturnCode, Runtime, + AllowDeprecatedInterface, AllowUnstableInterface, Environment, ReturnCode, Runtime, RuntimeCosts, }; @@ -445,7 +445,8 @@ mod tests { assert_err, assert_ok, dispatch::DispatchResultWithPostInfo, weights::Weight, }; use frame_system::pallet_prelude::BlockNumberFor; - use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags}; + use crate::primitives::ExecReturnValue; + use pallet_contracts_uapi::ReturnFlags; use pretty_assertions::assert_eq; use sp_core::H256; use sp_runtime::DispatchError; diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index b3013adb790e2..0b4df2ed41940 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -22,8 +22,10 @@ use crate::{ gas::{ChargedAmount, Token}, schedule::HostFnWeights, BalanceOf, CodeHash, Config, DebugBufferVec, Error, SENTINEL, + primitives::{ExecReturnValue, }, + }; -use bitflags::bitflags; +use pallet_contracts_uapi::{CallFlags, ReturnFlags}; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use frame_support::{ dispatch::DispatchInfo, @@ -33,7 +35,6 @@ use frame_support::{ traits::Get, weights::Weight, }; -use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags}; use pallet_contracts_proc_macro::define_env; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; use sp_runtime::{ @@ -411,51 +412,6 @@ impl Token for RuntimeToken { } } -bitflags! { - /// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`. - pub struct CallFlags: u32 { - /// Forward the input of current function to the callee. - /// - /// Supplied input pointers are ignored when set. - /// - /// # Note - /// - /// A forwarding call will consume the current contracts input. Any attempt to - /// access the input after this call returns will lead to [`Error::InputForwarded`]. - /// It does not matter if this is due to calling `seal_input` or trying another - /// forwarding call. Consider using [`Self::CLONE_INPUT`] in order to preserve - /// the input. - const FORWARD_INPUT = 0b0000_0001; - /// Identical to [`Self::FORWARD_INPUT`] but without consuming the input. - /// - /// This adds some additional weight costs to the call. - /// - /// # Note - /// - /// This implies [`Self::FORWARD_INPUT`] and takes precedence when both are set. - const CLONE_INPUT = 0b0000_0010; - /// Do not return from the call but rather return the result of the callee to the - /// callers caller. - /// - /// # Note - /// - /// This makes the current contract completely transparent to its caller by replacing - /// this contracts potential output by the callee ones. Any code after `seal_call` - /// can be safely considered unreachable. - const TAIL_CALL = 0b0000_0100; - /// Allow the callee to reenter into the current contract. - /// - /// Without this flag any reentrancy into the current contract that originates from - /// the callee (or any of its callees) is denied. This includes the first callee: - /// You cannot call into yourself with this flag set. - /// - /// # Note - /// - /// For `seal_delegate_call` should be always unset, otherwise - /// [`Error::InvalidCallFlags`] is returned. - const ALLOW_REENTRY = 0b0000_1000; - } -} /// The kind of call that should be performed. enum CallType { diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index 6265a5807f79d..87955c0a9a9f1 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -9,10 +9,16 @@ repository.workspace = true description = "This crates exposes all the host functions that a contract can import" [dependencies] +scale-info = { version = "2.10.0", default-features = false, features = ["derive"], optional = true } scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ "derive", "max-encoded-len", -] } +], optional = true } cfg-if = { version = "1.0", default-features = false } paste = { version = "1.0", default-features = false } +bitflags = "1.0" + +[features] +default = [ "scale" ] +scale = ["scale-info", "dep:scale"] diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 36b7f3edbdce8..1bdb00f6f1502 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -14,19 +14,18 @@ // TODO: // - remove duplicated doc in pallet_contracts -// - bring in CallFlags, ReturnFlags -// - check doc references are ok +// - Move TopicOf to primitives // - Should we replace pallet_contracts::ReturnValue by uapi::Error // - storage_contains defines in uapi but not in pallet_contracts // - document return behavior for call_chain_extension -use crate::{Result, ReturnFlags}; +use crate::{CallFlags, Result, ReturnFlags}; use paste::paste; macro_rules! hash_fn { ( $name:ident, $bytes:literal ) => { paste! { - #[doc = "Computes the " $name " " $bytes "-bit hash on the given input buffer."] + #[doc = "Computes the " $name " " $bytes "-bit hash on the given input buffer."] #[doc = "\n# Parameters\n"] #[doc = "- `input`: The input data buffer."] #[doc = "- `output`: The output buffer to write the hash result to."] @@ -38,6 +37,14 @@ macro_rules! hash_fn { pub trait Api { /// Instantiate a contract from the given code. /// + /// This function creates an account and executes the constructor defined in the code specified + /// by the code hash. + /// + /// # Note + /// + /// The copy of the output buffer and address can be skipped by providing `None` for the + /// `out_address` and `out_return_value` parameters. + /// /// # Parameters /// /// - `code_hash`: The hash of the code to be instantiated. @@ -65,8 +72,8 @@ pub trait Api { gas_limit: u64, endowment: &[u8], input: &[u8], - out_address: &mut &mut [u8], - out_return_value: &mut &mut [u8], + out_address: Option<&mut [u8]>, + out_return_value: Option<&mut [u8]>, salt: &[u8], ) -> Result; @@ -74,13 +81,18 @@ pub trait Api { /// /// # Parameters /// - /// - `flags`: See `pallet_contracts::wasm::runtime::CallFlags` for a documentation of the supported flags. + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. /// - `callee`: The address of the callee. /// - `gas_limit`: How much gas to devote for the execution. /// - `value`: The value to transfer into the contract. /// - `input`: The input data buffer used to call the contract. /// - `output`: A reference to the output data buffer to write the output data. /// + /// # Note + /// + /// The copy of the output buffer can be skipped by providing `None` for the + /// `output` parameter. + /// /// # Errors /// /// An error means that the call wasn't successful output buffer is returned unless @@ -91,12 +103,12 @@ pub trait Api { /// - [TransferFailed][crate::Error::TransferFailed] /// - [NotCallable][crate::Error::NotCallable] fn call( - flags: u32, + flags: CallFlags, callee: &[u8], gas_limit: u64, value: &[u8], input: &[u8], - output: &mut &mut [u8], + output: Option<&mut [u8]>, ) -> Result; /// Execute code in the context (storage, caller, value) of the current contract. @@ -107,11 +119,16 @@ pub trait Api { /// /// # Parameters /// - /// - `flags`: See `pallet_contracts::wasm::runtime::CallFlags` for a documentation of the supported flags. + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. /// - `code_hash`: The hash of the code to be executed. /// - `input`: The input data buffer used to call the contract. /// - `output`: A reference to the output data buffer to write the output data. /// + /// # Note + /// + /// The copy of the output buffer can be skipped by providing `None` for the + /// `output` parameter. + /// /// # Errors /// /// An error means that the call wasn't successful and no output buffer is returned unless @@ -120,7 +137,12 @@ pub trait Api { /// - [CalleeReverted][crate::Error::CalleeReverted]: Output buffer is returned. /// - [CalleeTrapped][crate::Error::CalleeTrapped] /// - [CodeNotFound][crate::Error::CodeNotFound] - fn delegate_call(flags: u32, code_hash: &[u8], input: &[u8], output: &mut &mut [u8]) -> Result; + fn delegate_call( + flags: CallFlags, + code_hash: &[u8], + input: &[u8], + output: Option<&mut [u8]>, + ) -> Result; /// Transfer some amount of funds into the specified account. /// @@ -251,7 +273,7 @@ pub trait Api { /// /// # Parameters /// - /// - `flags`: See `pallet_contracts::wasm::runtime::ReturnFlags` for a documentation of the supported flags. + /// - `flags`: See [`ReturnFlags`] for a documentation of the supported flags. /// - `return_value`: The return value buffer. fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; @@ -323,7 +345,6 @@ pub trait Api { /// - `output`: A reference to the output data buffer to write the transferred value. fn value_transferred(output: &mut &mut [u8]); - /// Load the latest block timestamp into the supplied buffer /// /// # Parameters @@ -338,7 +359,6 @@ pub trait Api { /// - `output`: A reference to the output data buffer to write the minimum balance. fn minimum_balance(output: &mut &mut [u8]); - /// Stores the price for the specified amount of gas into the supplied buffer. /// /// # Parameters @@ -388,7 +408,7 @@ pub trait Api { /// Verify a sr25519 signature /// /// # Parameters - /// + /// /// - `signature`: The signature bytes. /// - `message`: The message bytes. /// diff --git a/substrate/frame/contracts/uapi/src/flags.rs b/substrate/frame/contracts/uapi/src/flags.rs new file mode 100644 index 0000000000000..32553817fb7ae --- /dev/null +++ b/substrate/frame/contracts/uapi/src/flags.rs @@ -0,0 +1,73 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bitflags::bitflags; + +bitflags! { + /// Flags used by a contract to customize exit behaviour. + #[cfg_attr(feature = "scale", derive(scale::Encode, scale::Decode, scale_info::TypeInfo))] + pub struct ReturnFlags: u32 { + /// If this bit is set all changes made by the contract execution are rolled back. + const REVERT = 0x0000_0001; + } +} + +bitflags! { + /// Flags used to change the behaviour of `seal_call` and `seal_delegate_call`. + pub struct CallFlags: u32 { + /// Forward the input of current function to the callee. + /// + /// Supplied input pointers are ignored when set. + /// + /// # Note + /// + /// A forwarding call will consume the current contracts input. Any attempt to + /// access the input after this call returns will lead to [`Error::InputForwarded`]. + /// It does not matter if this is due to calling `seal_input` or trying another + /// forwarding call. Consider using [`Self::CLONE_INPUT`] in order to preserve + /// the input. + const FORWARD_INPUT = 0b0000_0001; + /// Identical to [`Self::FORWARD_INPUT`] but without consuming the input. + /// + /// This adds some additional weight costs to the call. + /// + /// # Note + /// + /// This implies [`Self::FORWARD_INPUT`] and takes precedence when both are set. + const CLONE_INPUT = 0b0000_0010; + /// Do not return from the call but rather return the result of the callee to the + /// callers caller. + /// + /// # Note + /// + /// This makes the current contract completely transparent to its caller by replacing + /// this contracts potential output by the callee ones. Any code after `seal_call` + /// can be safely considered unreachable. + const TAIL_CALL = 0b0000_0100; + /// Allow the callee to reenter into the current contract. + /// + /// Without this flag any reentrancy into the current contract that originates from + /// the callee (or any of its callees) is denied. This includes the first callee: + /// You cannot call into yourself with this flag set. + /// + /// # Note + /// + /// For `seal_delegate_call` should be always unset, otherwise + /// [`Error::InvalidCallFlags`] is returned. + const ALLOW_REENTRY = 0b0000_1000; + } +} diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 5a476f8737cfc..d09ee7c784f52 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -18,6 +18,9 @@ #![no_std] +mod flags; +pub use flags::*; + mod api; pub use api::Api; @@ -101,50 +104,21 @@ define_error_codes! { XcmSendFailed = 14, } -/// 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(any(target_arch = "wasm32", target_arch = "riscv32"))] - pub(crate) fn into_u32(self) -> u32 { - self.value - } -} - /// The raw return code returned by the host side. #[repr(transparent)] pub struct ReturnCode(u32); +/// Used as a sentinel value when reading and writing contract memory. +/// +/// We use this value to signal `None` to a contract when only a primitive is +/// allowed and we don't want to go through encoding a full Rust type. +/// Using `u32::Max` is a safe sentinel because contracts are never +/// allowed to use such a large amount of resources. So this value doesn't +/// make sense for a memory location or length. +pub(crate) const SENTINEL: u32 = u32::MAX; + 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) } } @@ -169,3 +143,12 @@ fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { let tmp = core::mem::take(output); *output = &mut tmp[..new_len]; } + +#[inline(always)] +#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +fn ptr_and_len_from_slice(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { + match data { + Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), + None => (SENTINEL as _, 0), + } +} diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index f612230388d01..285e4ccdde483 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::{extract_from_slice, Result, ReturnCode}; -use crate::ReturnFlags; +use super::{ + extract_from_slice, ptr_and_len_from_slice, CallFlags, Result, ReturnCode, ReturnFlags, +}; mod sys { use super::ReturnCode; @@ -251,12 +252,14 @@ impl super::Api for ApiImpl { gas_limit: u64, endowment: &[u8], input: &[u8], - out_address: &mut &mut [u8], - out_return_value: &mut &mut [u8], + mut out_address: Option<&mut [u8]>, + mut out_return_value: Option<&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 (out_addr_ptr, mut out_addr_len) = ptr_and_len_from_slice(&mut out_address); + let (return_value_ptr, mut return_value_len) = + ptr_and_len_from_slice(&mut out_return_value); + let ret_code = { unsafe { sys::instantiate( @@ -265,64 +268,84 @@ impl super::Api for ApiImpl { endowment.as_ptr(), input.as_ptr(), input.len() as u32, - out_address.as_mut_ptr(), - &mut address_len, - out_return_value.as_mut_ptr(), + out_addr_ptr, + &mut out_addr_len, + return_value_ptr, &mut return_value_len, salt.as_ptr(), salt.len() as u32, ) } }; - extract_from_slice(out_address, address_len as usize); - extract_from_slice(out_return_value, return_value_len as usize); + + if let Some(ref mut out_address) = out_address { + extract_from_slice(out_address, out_addr_len as usize); + } + + if let Some(ref mut out_return_value) = out_return_value { + extract_from_slice(out_return_value, return_value_len as usize); + } + ret_code.into() } #[inline(always)] fn call( - flags: u32, + flags: CallFlags, callee: &[u8], gas_limit: u64, value: &[u8], input: &[u8], - output: &mut &mut [u8], + mut output: Option<&mut [u8]>, ) -> Result { - let mut output_len = output.len() as u32; + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); let ret_code = { unsafe { sys::call( - flags, + flags.bits(), callee.as_ptr(), gas_limit, value.as_ptr(), input.as_ptr(), input.len() as u32, - output.as_mut_ptr(), + output_ptr, &mut output_len, ) } }; - extract_from_slice(output, output_len as usize); + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into() } #[inline(always)] - fn delegate_call(flags: u32, code_hash: &[u8], input: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; + fn delegate_call( + flags: CallFlags, + code_hash: &[u8], + input: &[u8], + mut output: Option<&mut [u8]>, + ) -> Result { + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); let ret_code = { unsafe { sys::delegate_call( - flags, + flags.bits(), code_hash.as_ptr(), input.as_ptr(), input.len() as u32, - output.as_mut_ptr(), + output_ptr, &mut output_len, ) } }; - extract_from_slice(output, output_len as usize); + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into() } @@ -437,9 +460,7 @@ impl super::Api for ApiImpl { } fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - unsafe { - sys::seal_return(flags.into_u32(), return_value.as_ptr(), return_value.len() as u32) - } + unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) } } fn call_runtime(call: &[u8]) -> Result { From dad8fecd37e818eef9f93b2dfa0c22c186fa412e Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 22 Nov 2023 11:59:37 +0100 Subject: [PATCH 31/81] Move types to uapi and add docs --- substrate/frame/contracts/fixtures/build.rs | 1 + .../contracts/fixtures/contracts/call.rs | 24 +- .../frame/contracts/proc-macro/src/lib.rs | 4 +- substrate/frame/contracts/src/tests.rs | 4 +- substrate/frame/contracts/src/wasm/mod.rs | 22 +- substrate/frame/contracts/src/wasm/runtime.rs | 723 +++--------------- substrate/frame/contracts/uapi/src/api.rs | 174 +++-- substrate/frame/contracts/uapi/src/lib.rs | 19 +- 8 files changed, 242 insertions(+), 729 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 5c42f4a7353ed..500f56bbcab8e 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -15,6 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! Compile contracts to wasm and RISC-V binaries. use anyhow::Result; use parity_wasm::elements::{deserialize_file, serialize_to_file, Internal}; use std::{ diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index b8a978299e6e9..9caa810afef0a 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -21,7 +21,7 @@ extern crate common; extern crate uapi; -use uapi::{Api, CallFlags, ApiImpl as api}; +use uapi::{Api, ApiImpl as api, CallFlags}; #[no_mangle] pub fn deploy() {} @@ -32,15 +32,15 @@ pub fn call() { let mut out = [0u8; 0]; // No output data. // Read the input data. - api::input(&mut &mut buffer[..]); - - // xx Call the callee. - api::call( - CallFlags::empty(), - &buffer[4..36], // callee address. - 0u64, // How much gas to devote for the execution. 0 = all. - &buffer[36..], // Pointer to value to transfer. - &buffer[0..4], // Pointer to input data buffer address. - Some(&mut out[..]), // Pointer to output data buffer address. - ); + api::input(&mut &mut buffer[..]); + + // Call the callee + api::call( + CallFlags::empty(), + &buffer[4..36], // callee address. + 0u64, // How much gas to devote for the execution. 0 = all. + &buffer[36..], // Pointer to value to transfer. + &buffer[0..4], // Pointer to input data buffer address. + Some(&mut out[..]), // Pointer to output data buffer address. + ); } diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs index ad9cd2dadecf9..875a78fde8c68 100644 --- a/substrate/frame/contracts/proc-macro/src/lib.rs +++ b/substrate/frame/contracts/proc-macro/src/lib.rs @@ -271,7 +271,7 @@ impl HostFn { // process return type let msg = r#"Should return one of the following: - Result<(), TrapReason>, - - Result, + - Result, - Result, - Result"#; let ret_ty = match item.clone().sig.output { @@ -336,7 +336,7 @@ impl HostFn { "()" => Ok(HostFnReturn::Unit), "u32" => Ok(HostFnReturn::U32), "u64" => Ok(HostFnReturn::U64), - "ReturnCode" => Ok(HostFnReturn::ReturnCode), + "ReturnErrorCode" => Ok(HostFnReturn::ReturnCode), _ => Err(err(arg1.span(), &msg)), }?; diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index b4aafe44c1e4f..6e0e4f7a5b5db 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -30,9 +30,10 @@ use crate::{ }, exec::{Frame, Key}, migration::codegen::LATEST_MIGRATION_VERSION, + primitives::CodeUploadReturnValue, storage::DeletionQueueManager, tests::test_utils::{get_contract, get_contract_checked}, - wasm::{Determinism, ReturnCode as RuntimeReturnCode}, + wasm::{Determinism, ReturnErrorCode as RuntimeReturnCode}, weights::WeightInfo, BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, @@ -54,7 +55,6 @@ use frame_support::{ }; use frame_system::{EventRecord, Phase}; use pallet_contracts_fixtures::compile_module; -use crate::primitives::CodeUploadReturnValue; use pretty_assertions::{assert_eq, assert_ne}; use sp_core::ByteArray; use sp_io::hashing::blake2_256; diff --git a/substrate/frame/contracts/src/wasm/mod.rs b/substrate/frame/contracts/src/wasm/mod.rs index 16c96e34fc74c..325dd44b26cd3 100644 --- a/substrate/frame/contracts/src/wasm/mod.rs +++ b/substrate/frame/contracts/src/wasm/mod.rs @@ -24,13 +24,13 @@ mod runtime; #[cfg(doc)] pub use crate::wasm::runtime::api_doc; -#[cfg(test)] -pub use tests::MockExt; -pub use pallet_contracts_uapi::ReturnFlags; pub use crate::wasm::runtime::{ - AllowDeprecatedInterface, AllowUnstableInterface, Environment, ReturnCode, Runtime, + AllowDeprecatedInterface, AllowUnstableInterface, Environment, ReturnErrorCode, Runtime, RuntimeCosts, }; +pub use pallet_contracts_uapi::ReturnFlags; +#[cfg(test)] +pub use tests::MockExt; use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, @@ -436,6 +436,7 @@ mod tests { use crate::{ exec::{AccountIdOf, ErrorOrigin, ExecError, Executable, Ext, Key, SeedOf}, gas::GasMeter, + primitives::ExecReturnValue, storage::WriteOutcome, tests::{RuntimeCall, Test, ALICE, BOB}, BalanceOf, CodeHash, Error, Origin, Pallet as Contracts, @@ -445,7 +446,6 @@ mod tests { assert_err, assert_ok, dispatch::DispatchResultWithPostInfo, weights::Weight, }; use frame_system::pallet_prelude::BlockNumberFor; - use crate::primitives::ExecReturnValue; use pallet_contracts_uapi::ReturnFlags; use pretty_assertions::assert_eq; use sp_core::H256; @@ -2740,7 +2740,7 @@ mod tests { let result = execute(CODE, input, &mut ext).unwrap(); assert_eq!( u32::from_le_bytes(result.data[0..4].try_into().unwrap()), - ReturnCode::KeyNotFound as u32 + ReturnErrorCode::KeyNotFound as u32 ); // value exists @@ -2748,7 +2748,7 @@ mod tests { let result = execute(CODE, input, &mut ext).unwrap(); assert_eq!( u32::from_le_bytes(result.data[0..4].try_into().unwrap()), - ReturnCode::Success as u32 + ReturnErrorCode::Success as u32 ); assert_eq!(ext.storage.get(&[1u8; 64].to_vec()).unwrap(), &[42u8]); assert_eq!(&result.data[4..], &[42u8]); @@ -2758,7 +2758,7 @@ mod tests { let result = execute(CODE, input, &mut ext).unwrap(); assert_eq!( u32::from_le_bytes(result.data[0..4].try_into().unwrap()), - ReturnCode::Success as u32 + ReturnErrorCode::Success as u32 ); assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), Some(&vec![])); assert_eq!(&result.data[4..], &([] as [u8; 0])); @@ -2921,7 +2921,7 @@ mod tests { let result = execute(CODE, input, &mut ext).unwrap(); assert_eq!( u32::from_le_bytes(result.data[0..4].try_into().unwrap()), - ReturnCode::KeyNotFound as u32 + ReturnErrorCode::KeyNotFound as u32 ); // value did exist -> value returned @@ -2929,7 +2929,7 @@ mod tests { let result = execute(CODE, input, &mut ext).unwrap(); assert_eq!( u32::from_le_bytes(result.data[0..4].try_into().unwrap()), - ReturnCode::Success as u32 + ReturnErrorCode::Success as u32 ); assert_eq!(ext.storage.get(&[1u8; 64].to_vec()), None); assert_eq!(&result.data[4..], &[42u8]); @@ -2939,7 +2939,7 @@ mod tests { let result = execute(CODE, input, &mut ext).unwrap(); assert_eq!( u32::from_le_bytes(result.data[0..4].try_into().unwrap()), - ReturnCode::Success as u32 + ReturnErrorCode::Success as u32 ); assert_eq!(ext.storage.get(&[2u8; 19].to_vec()), None); assert_eq!(&result.data[4..], &[0u8; 0]); diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 0b4df2ed41940..11e5e92519b28 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -20,12 +20,10 @@ use crate::{ exec::{ExecError, ExecResult, Ext, Key, TopicOf}, gas::{ChargedAmount, Token}, + primitives::ExecReturnValue, schedule::HostFnWeights, BalanceOf, CodeHash, Config, DebugBufferVec, Error, SENTINEL, - primitives::{ExecReturnValue, }, - }; -use pallet_contracts_uapi::{CallFlags, ReturnFlags}; use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use frame_support::{ dispatch::DispatchInfo, @@ -36,6 +34,7 @@ use frame_support::{ weights::Weight, }; use pallet_contracts_proc_macro::define_env; +use pallet_contracts_uapi::{CallFlags, ReturnFlags}; use sp_io::hashing::{blake2_128, blake2_256, keccak_256, sha2_256}; use sp_runtime::{ traits::{Bounded, Zero}, @@ -88,55 +87,16 @@ enum KeyType { Var(u32), } -/// Every error that can be returned to a contract when it calls any of the host functions. -/// -/// # Note -/// -/// This enum can be extended in the future: New codes can be added but existing codes -/// will not be changed or removed. This means that any contract **must not** exhaustively -/// match return codes. Instead, contracts should prepare for unknown variants and deal with -/// those errors gracefully in order to be forward compatible. -#[derive(Debug)] -#[repr(u32)] -pub enum ReturnCode { - /// API call successful. - Success = 0, - /// The called function trapped and has its state changes reverted. - /// In this case no output buffer is returned. - CalleeTrapped = 1, - /// The called function ran to completion but decided to revert its state. - /// An output buffer is returned when one was supplied. - CalleeReverted = 2, - /// The passed key does not exist in storage. - KeyNotFound = 3, - /// See [`Error::TransferFailed`]. - TransferFailed = 5, - /// No code could be found at the supplied code hash. - CodeNotFound = 7, - /// The contract that was called is no contract (a plain account). - NotCallable = 8, - /// The call dispatched by `seal_call_runtime` was executed but returned an error. - CallRuntimeFailed = 10, - /// ECDSA pubkey recovery failed (most probably wrong recovery id or signature), or - /// ECDSA compressed pubkey conversion into Ethereum address failed (most probably - /// wrong pubkey provided). - EcdsaRecoverFailed = 11, - /// sr25519 signature verification failed. - Sr25519VerifyFailed = 12, - /// The `xcm_execute` call failed. - XcmExecutionFailed = 13, - /// The `xcm_send` call failed. - XcmSendFailed = 14, -} +pub use pallet_contracts_uapi::ReturnErrorCode; parameter_types! { /// Getter types used by [`crate::api_doc::Current::call_runtime`] - const CallRuntimeFailed: ReturnCode = ReturnCode::CallRuntimeFailed; + const CallRuntimeFailed: ReturnErrorCode = ReturnErrorCode::CallRuntimeFailed; /// Getter types used by [`crate::api_doc::Current::xcm_execute`] - const XcmExecutionFailed: ReturnCode = ReturnCode::XcmExecutionFailed; + const XcmExecutionFailed: ReturnErrorCode = ReturnErrorCode::XcmExecutionFailed; } -impl From for ReturnCode { +impl From for ReturnErrorCode { fn from(from: ExecReturnValue) -> Self { if from.flags.contains(ReturnFlags::REVERT) { Self::CalleeReverted @@ -146,12 +106,6 @@ impl From for ReturnCode { } } -impl From for u32 { - fn from(code: ReturnCode) -> u32 { - code as u32 - } -} - /// The data passed through when a contract uses `seal_return`. #[derive(RuntimeDebug)] pub struct ReturnData { @@ -412,7 +366,6 @@ impl Token for RuntimeToken { } } - /// The kind of call that should be performed. enum CallType { /// Execute another instantiated contract @@ -559,20 +512,20 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { /// Charge, Run and adjust gas, for executing the given dispatchable. fn call_dispatchable< - ErrorReturnCode: Get, + ErrorReturnCode: Get, F: FnOnce(&mut Self) -> DispatchResultWithPostInfo, >( &mut self, dispatch_info: DispatchInfo, run: F, - ) -> Result { + ) -> Result { use frame_support::dispatch::extract_actual_weight; let charged = self.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?; let result = run(self); let actual_weight = extract_actual_weight(&result, &dispatch_info); self.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); match result { - Ok(_) => Ok(ReturnCode::Success), + Ok(_) => Ok(ReturnErrorCode::Success), Err(e) => { if self.ext.append_debug_buffer("") { self.ext.append_debug_buffer("call failed with: "); @@ -764,8 +717,8 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { } /// Fallible conversion of `DispatchError` to `ReturnCode`. - fn err_into_return_code(from: DispatchError) -> Result { - use ReturnCode::*; + fn err_into_return_code(from: DispatchError) -> Result { + use ReturnErrorCode::*; let transfer_failed = Error::::TransferFailed.into(); let no_code = Error::::CodeNotFound.into(); @@ -780,7 +733,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { } /// Fallible conversion of a `ExecResult` to `ReturnCode`. - fn exec_into_return_code(from: ExecResult) -> Result { + fn exec_into_return_code(from: ExecResult) -> Result { use crate::exec::ErrorOrigin::Callee; let ExecError { error, origin } = match from { @@ -789,7 +742,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { }; match (error, origin) { - (_, Callee) => Ok(ReturnCode::CalleeTrapped), + (_, Callee) => Ok(ReturnErrorCode::CalleeTrapped), (err, _) => Self::err_into_return_code(err), } } @@ -863,7 +816,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { key_ptr: u32, out_ptr: u32, out_len_ptr: u32, - ) -> Result { + ) -> Result { let charged = self.charge_gas(RuntimeCosts::GetStorage(self.ext.max_value_size()))?; let key = self.decode_key(memory, key_type, key_ptr)?; let outcome = self.ext.get_storage(&key); @@ -878,10 +831,10 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { false, already_charged, )?; - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) } else { self.adjust_gas(charged, RuntimeCosts::GetStorage(0)); - Ok(ReturnCode::KeyNotFound) + Ok(ReturnErrorCode::KeyNotFound) } } @@ -908,7 +861,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> Result { + ) -> Result { self.charge_gas(call_type.cost())?; let input_data = if flags.contains(CallFlags::CLONE_INPUT) { let input = self.input_data.as_ref().ok_or(Error::::InputForwarded)?; @@ -992,7 +945,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { output_len_ptr: u32, salt_ptr: u32, salt_len: u32, - ) -> Result { + ) -> Result { self.charge_gas(RuntimeCosts::InstantiateBase { input_data_len, salt_len })?; let deposit_limit: BalanceOf<::T> = if deposit_ptr == SENTINEL { BalanceOf::<::T>::zero() @@ -1071,20 +1024,6 @@ pub mod env { /// /// 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. #[version(1)] #[prefixed_alias] fn set_storage( @@ -1098,21 +1037,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::set_storage`] #[version(2)] #[prefixed_alias] fn set_storage( @@ -1137,16 +1062,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::clear_storage`] #[version(1)] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { @@ -1157,17 +1073,6 @@ pub mod env { /// /// 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` #[prefixed_alias] fn get_storage( ctx: _, @@ -1175,28 +1080,12 @@ pub mod env { key_ptr: u32, out_ptr: u32, out_len_ptr: u32, - ) -> Result { + ) -> Result { ctx.get_storage(memory, 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` + /// See [`pallet_contracts_uapi::Api::get_storage`] #[version(1)] #[prefixed_alias] fn get_storage( @@ -1206,7 +1095,7 @@ pub mod env { key_len: u32, out_ptr: u32, out_len_ptr: u32, - ) -> Result { + ) -> Result { ctx.get_storage(memory, KeyType::Var(key_len), key_ptr, out_ptr, out_len_ptr) } @@ -1214,33 +1103,13 @@ pub mod env { /// /// 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. #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32) -> Result { ctx.contains_storage(memory, 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. + /// See [`pallet_contracts_uapi::Api::storage_contains`] #[version(1)] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { @@ -1248,18 +1117,7 @@ pub mod env { } /// 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` + /// See [`pallet_contracts_uapi::Api::take_storage`] #[prefixed_alias] fn take_storage( ctx: _, @@ -1268,7 +1126,7 @@ pub mod env { key_len: u32, out_ptr: u32, out_len_ptr: u32, - ) -> Result { + ) -> Result { let charged = ctx.charge_gas(RuntimeCosts::TakeStorage(ctx.ext.max_value_size()))?; ensure!( key_len <= <::T as Config>::MaxStorageKeyLen::get(), @@ -1282,27 +1140,15 @@ pub mod env { )? { ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(value.len() as u32)); ctx.write_sandbox_output(memory, out_ptr, out_len_ptr, &value, false, already_charged)?; - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) } else { ctx.adjust_gas(charged, RuntimeCosts::TakeStorage(0)); - Ok(ReturnCode::KeyNotFound) + Ok(ReturnErrorCode::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` + /// See [`pallet_contracts_uapi::Api::transfer`]. #[prefixed_alias] fn transfer( ctx: _, @@ -1311,14 +1157,14 @@ pub mod env { _account_len: u32, value_ptr: u32, _value_len: u32, - ) -> Result { + ) -> Result { ctx.charge_gas(RuntimeCosts::Transfer)?; let callee: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(memory, account_ptr)?; let value: BalanceOf<::T> = ctx.read_sandbox_memory_as(memory, value_ptr)?; let result = ctx.ext.transfer(&callee, value); match result { - Ok(()) => Ok(ReturnCode::Success), + Ok(()) => Ok(ReturnErrorCode::Success), Err(err) => { let code = Runtime::::err_into_return_code(err)?; Ok(code) @@ -1352,7 +1198,7 @@ pub mod env { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> Result { + ) -> Result { ctx.call( memory, CallFlags::ALLOW_REENTRY, @@ -1387,7 +1233,7 @@ pub mod env { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> Result { + ) -> Result { ctx.call( memory, CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, @@ -1405,39 +1251,7 @@ pub mod env { } /// 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 `crate::wasm::runtime::CallFlags` for a documentation of the supported flags. - /// - `callee_ptr`: a pointer to the address of the callee contract. Should be decodable as an - /// `T::AccountId`. Traps otherwise. - /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. - /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit_ptr`: a pointer to the buffer with value of the storage deposit limit for the - /// call. Should be decodable as a `T::Balance`. Traps otherwise. Passing `SENTINEL` means - /// setting no specific limit for the call, which implies storage usage up to the limit of the - /// parent call. - /// - `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` + /// See [`pallet_contracts_uapi::Api::call`]. #[version(2)] #[unstable] fn call( @@ -1453,7 +1267,7 @@ pub mod env { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> Result { + ) -> Result { ctx.call( memory, CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, @@ -1471,29 +1285,7 @@ pub mod env { } /// 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 `crate::wasm::runtime::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` + /// See [`pallet_contracts_uapi::Api::delegate_call`]. #[prefixed_alias] fn delegate_call( ctx: _, @@ -1504,7 +1296,7 @@ pub mod env { input_data_len: u32, output_ptr: u32, output_len_ptr: u32, - ) -> Result { + ) -> Result { ctx.call( memory, CallFlags::from_bits(flags).ok_or(Error::::InvalidCallFlags)?, @@ -1545,7 +1337,7 @@ pub mod env { output_len_ptr: u32, salt_ptr: u32, salt_len: u32, - ) -> Result { + ) -> Result { ctx.instantiate( memory, code_hash_ptr, @@ -1584,7 +1376,7 @@ pub mod env { output_len_ptr: u32, salt_ptr: u32, salt_len: u32, - ) -> Result { + ) -> Result { ctx.instantiate( memory, code_hash_ptr, @@ -1603,48 +1395,7 @@ pub mod env { } /// 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`. - /// - /// # Parameters - /// - /// - `code_hash_ptr`: a pointer to the buffer that contains the initializer code. - /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. - /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit_ptr`: a pointer to the buffer with value of the storage deposit limit for - /// instantiation. Should be decodable as a `T::Balance`. Traps otherwise. Passing `SENTINEL` - /// means setting no specific limit for the call, which implies storage usage up to the limit - /// of the parent call. - /// - `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. `SENTINEL` means - /// not to copy. - /// - `address_len_ptr`: pointer to where put the length of the address. - /// - `output_ptr`: a pointer where the output buffer is copied to. `SENTINEL` means not to - /// copy. - /// - `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` + /// See [`pallet_contracts_uapi::Api::instantiate`]. #[version(2)] #[unstable] fn instantiate( @@ -1663,7 +1414,7 @@ pub mod env { output_len_ptr: u32, salt_ptr: u32, salt_len: u32, - ) -> Result { + ) -> Result { ctx.instantiate( memory, code_hash_ptr, @@ -1704,20 +1455,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::terminate`]. #[version(1)] #[prefixed_alias] fn terminate(ctx: _, memory: _, beneficiary_ptr: u32) -> Result<(), TrapReason> { @@ -1725,15 +1463,7 @@ pub mod env { } /// 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 [`call()`][`Self::call()`]. + /// See [`pallet_contracts_uapi::Api::input`]. #[prefixed_alias] fn input(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::InputBase)?; @@ -1749,22 +1479,7 @@ pub mod env { } /// Cease contract execution and save a data buffer as a result of the execution. - /// - /// This function never returns as it stops execution of the caller. - /// This is the only way to return a data buffer to the caller. Returning from - /// execution without calling this function is equivalent to calling: - /// ```nocompile - /// 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. + /// See [`pallet_contracts_uapi::Api::return_value`]. fn seal_return( ctx: _, memory: _, @@ -1780,18 +1495,7 @@ pub mod env { } /// 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. - /// - /// If there is no address associated with the caller (e.g. because the caller is root) then - /// it traps with `BadOrigin`. + /// See [`pallet_contracts_uapi::Api::caller`]. #[prefixed_alias] fn caller(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Caller)?; @@ -1807,13 +1511,7 @@ pub mod env { } /// 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). + /// See [`pallet_contracts_uapi::Api::is_contract`]. #[prefixed_alias] fn is_contract(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::IsContract)?; @@ -1824,18 +1522,7 @@ pub mod env { } /// 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` + /// See [`pallet_contracts_uapi::Api::code_hash`]. #[prefixed_alias] fn code_hash( ctx: _, @@ -1843,7 +1530,7 @@ pub mod env { account_ptr: u32, out_ptr: u32, out_len_ptr: u32, - ) -> Result { + ) -> Result { ctx.charge_gas(RuntimeCosts::CodeHash)?; let address: <::T as frame_system::Config>::AccountId = ctx.read_sandbox_memory_as(memory, account_ptr)?; @@ -1856,19 +1543,14 @@ pub mod env { false, already_charged, )?; - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) } else { - Ok(ReturnCode::KeyNotFound) + Ok(ReturnErrorCode::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. + /// See [`pallet_contracts_uapi::Api::own_code_hash`]. #[prefixed_alias] fn own_code_hash(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; @@ -1884,15 +1566,7 @@ pub mod env { } /// Checks whether the caller of the current contract is the origin of the whole call stack. - /// - /// Prefer this over [`is_contract()`][`Self::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`). + /// See [`pallet_contracts_uapi::Api::caller_is_origin`]. #[prefixed_alias] fn caller_is_origin(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; @@ -1915,11 +1589,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::address`]. #[prefixed_alias] fn address(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Address)?; @@ -1959,21 +1629,7 @@ pub mod env { } /// Stores the price for the specified amount of weight into the supplied buffer. - /// - /// # Parameters - /// - /// - `out_ptr`: pointer to the linear memory where the returning value is written to. If the - /// available space at `out_ptr` is less than the size of the value a trap is triggered. - /// - `out_len_ptr`: in-out pointer into linear memory where the buffer length is read from and - /// the value length is written to. - /// - /// The data is encoded as `T::Balance`. - /// - /// # Note - /// - /// It is recommended to avoid specifying very small values for `ref_time_limit` and - /// `proof_size_limit` as the prices for a single gas can be smaller than the basic balance - /// unit. + /// See [`pallet_contracts_uapi::Api::weight_to_fee`]. #[version(1)] #[unstable] fn weight_to_fee( @@ -2016,13 +1672,7 @@ pub mod env { } /// Stores the amount of weight 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 Weight. + /// See [`pallet_contracts_uapi::Api::gas_left`]. #[version(1)] #[unstable] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { @@ -2039,13 +1689,7 @@ pub mod env { } /// 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`. + /// See [`pallet_contracts_uapi::Api::balance`]. #[prefixed_alias] fn balance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; @@ -2060,13 +1704,7 @@ pub mod env { } /// 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`. + /// See [`pallet_contracts_uapi::Api::value_transferred`]. #[prefixed_alias] fn value_transferred( ctx: _, @@ -2166,11 +1804,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::now`]. #[prefixed_alias] fn now(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Now)?; @@ -2185,8 +1819,7 @@ pub mod env { } /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// - /// The data is encoded as `T::Balance`. + /// See [`pallet_contracts_uapi::Api::minimum_balance`]. #[prefixed_alias] fn minimum_balance( ctx: _, @@ -2334,15 +1967,8 @@ pub mod env { )?) } - /// 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. + /// Deposit a contract event with the data buffer and optional list of topics. + /// See [pallet_contracts_uapi::Api::deposit_event] #[prefixed_alias] fn deposit_event( ctx: _, @@ -2378,11 +2004,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::block_number`]. #[prefixed_alias] fn block_number(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::BlockNumber)?; @@ -2397,22 +2019,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::hash_sha2_256`]. #[prefixed_alias] fn hash_sha2_256( ctx: _, @@ -2428,22 +2035,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::hash_keccak_256`]. #[prefixed_alias] fn hash_keccak_256( ctx: _, @@ -2459,22 +2051,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::hash_blake2_256`]. #[prefixed_alias] fn hash_blake2_256( ctx: _, @@ -2490,22 +2067,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::hash_blake2_128`]. #[prefixed_alias] fn hash_blake2_128( ctx: _, @@ -2521,16 +2083,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::call_chain_extension`]. #[prefixed_alias] fn call_chain_extension( ctx: _, @@ -2583,7 +2136,7 @@ pub mod env { memory: _, str_ptr: u32, str_len: u32, - ) -> Result { + ) -> Result { let str_len = str_len.min(DebugBufferVec::::bound() as u32); ctx.charge_gas(RuntimeCosts::DebugMessage(str_len))?; if ctx.ext.append_debug_buffer("") { @@ -2592,47 +2145,17 @@ pub mod env { ctx.ext.append_debug_buffer(msg); } } - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) } /// 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 - /// - /// - `call_ptr`: the pointer into the linear memory where the input data is placed. - /// - `call_len`: the length of the input data in bytes. - /// - /// # Return Value - /// - /// Returns `ReturnCode::Success` when the dispatchable was successfully executed and - /// returned `Ok`. When the dispatchable was executed but returned an error - /// `ReturnCode::CallRuntimeFailed` 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 generally easier it cannot be - /// used in all 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. + /// See [`frame_support::traits::call_runtime`]. fn call_runtime( ctx: _, memory: _, call_ptr: u32, call_len: u32, - ) -> Result { + ) -> Result { use frame_support::dispatch::GetDispatchInfo; ctx.charge_gas(RuntimeCosts::CopyFromContract(call_len))?; let call: ::RuntimeCall = @@ -2665,7 +2188,7 @@ pub mod env { msg_ptr: u32, msg_len: u32, output_ptr: u32, - ) -> Result { + ) -> Result { use frame_support::dispatch::DispatchInfo; use xcm::VersionedXcm; use xcm_builder::{ExecuteController, ExecuteControllerWeightInfo}; @@ -2721,7 +2244,7 @@ pub mod env { msg_ptr: u32, msg_len: u32, output_ptr: u32, - ) -> Result { + ) -> Result { use xcm::{VersionedMultiLocation, VersionedXcm}; use xcm_builder::{SendController, SendControllerWeightInfo}; @@ -2737,35 +2260,20 @@ pub mod env { match <::Xcm>::send(origin, dest.into(), message.into()) { Ok(message_id) => { ctx.write_sandbox_memory(memory, output_ptr, &message_id.encode())?; - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) }, Err(e) => { if ctx.ext.append_debug_buffer("") { ctx.ext.append_debug_buffer("seal0::xcm_send failed with: "); ctx.ext.append_debug_buffer(e.into()); }; - Ok(ReturnCode::XcmSendFailed) + Ok(ReturnErrorCode::XcmSendFailed) }, } } /// 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` + /// See [`pallet_contracts_uapi::Api::ecdsa_recover`]. #[prefixed_alias] fn ecdsa_recover( ctx: _, @@ -2773,7 +2281,7 @@ pub mod env { signature_ptr: u32, message_hash_ptr: u32, output_ptr: u32, - ) -> Result { + ) -> Result { ctx.charge_gas(RuntimeCosts::EcdsaRecovery)?; let mut signature: [u8; 65] = [0; 65]; @@ -2789,26 +2297,14 @@ pub mod env { // buffer. ctx.write_sandbox_memory(memory, output_ptr, pub_key.as_ref())?; - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) }, - Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), + Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), } } /// Verify a sr25519 signature - /// - /// # Parameters - /// - /// - `signature_ptr`: the pointer into the linear memory where the signature is placed. Should - /// be a value of 64 bytes. - /// - `pub_key_ptr`: the pointer into the linear memory where the public key is placed. Should - /// be a value of 32 bytes. - /// - `message_len`: the length of the message payload. - /// - `message_ptr`: the pointer into the linear memory where the message is placed. - /// - /// # Errors - /// - /// - `ReturnCode::Sr25519VerifyFailed + /// See [`pallet_contracts_uapi::Api::sr25519_verify`]. #[unstable] fn sr25519_verify( ctx: _, @@ -2817,7 +2313,7 @@ pub mod env { pub_key_ptr: u32, message_len: u32, message_ptr: u32, - ) -> Result { + ) -> Result { ctx.charge_gas(RuntimeCosts::Sr25519Verify(message_len))?; let mut signature: [u8; 64] = [0; 64]; @@ -2829,41 +2325,16 @@ pub mod env { let message: Vec = ctx.read_sandbox_memory(memory, message_ptr, message_len)?; if ctx.ext.sr25519_verify(&signature, &message, &pub_key) { - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) } else { - Ok(ReturnCode::Sr25519VerifyFailed) + Ok(ReturnErrorCode::Sr25519VerifyFailed) } } /// Replace the contract code at the specified address with new code. - /// - /// # Note - /// - /// There are a couple of important considerations which must be taken into account when - /// using this API: - /// - /// 1. The storage at the code address will remain untouched. This means that contract - /// developers must ensure that the storage layout of the new code is compatible with that of - /// the old code. - /// - /// 2. Contracts using this API can't be assumed as having deterministic addresses. Said another - /// way, when using this API you lose the guarantee that an address always identifies a specific - /// code hash. - /// - /// 3. If a contract calls into itself after changing its code the new call would use - /// the new code. However, if the original caller panics after returning from the sub call it - /// would revert the changes made by [`set_code_hash()`][`Self::set_code_hash`] and the next - /// caller would use the old code. - /// - /// # Parameters - /// - /// - `code_hash_ptr`: A pointer to the buffer that contains the new code hash. - /// - /// # Errors - /// - /// - `ReturnCode::CodeNotFound` + /// See [`pallet_contracts_uapi::Api::set_code_hash`]. #[prefixed_alias] - fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result { + fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::SetCodeHash)?; let code_hash: CodeHash<::T> = ctx.read_sandbox_memory_as(memory, code_hash_ptr)?; @@ -2872,33 +2343,19 @@ pub mod env { let code = Runtime::::err_into_return_code(err)?; Ok(code) }, - Ok(()) => Ok(ReturnCode::Success), + Ok(()) => Ok(ReturnErrorCode::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` + /// See [`pallet_contracts_uapi::Api::ecdsa_to_eth_address`]. #[prefixed_alias] fn ecdsa_to_eth_address( ctx: _, memory: _, key_ptr: u32, out_ptr: u32, - ) -> Result { + ) -> Result { ctx.charge_gas(RuntimeCosts::EcdsaToEthAddress)?; let mut compressed_key: [u8; 33] = [0; 33]; ctx.read_sandbox_memory_into_buf(memory, key_ptr, &mut compressed_key)?; @@ -2906,9 +2363,9 @@ pub mod env { match result { Ok(eth_address) => { ctx.write_sandbox_memory(memory, out_ptr, eth_address.as_ref())?; - Ok(ReturnCode::Success) + Ok(ReturnErrorCode::Success) }, - Err(_) => Ok(ReturnCode::EcdsaRecoverFailed), + Err(_) => Ok(ReturnErrorCode::EcdsaRecoveryFailed), } } diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 1bdb00f6f1502..493657109ab6f 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -13,11 +13,7 @@ // limitations under the License. // TODO: -// - remove duplicated doc in pallet_contracts -// - Move TopicOf to primitives -// - Should we replace pallet_contracts::ReturnValue by uapi::Error -// - storage_contains defines in uapi but not in pallet_contracts -// - document return behavior for call_chain_extension +// - Add missing unstable methods use crate::{CallFlags, Result, ReturnFlags}; use paste::paste; @@ -26,6 +22,10 @@ macro_rules! hash_fn { ( $name:ident, $bytes:literal ) => { paste! { #[doc = "Computes the " $name " " $bytes "-bit hash on the given input buffer."] + #[doc = "\n# Notes\n"] + #[doc = "- The `input` and `output` buffer may overlap."] + #[doc = "- The output buffer is expected to hold at least " $bytes " bits."] + #[doc = "- It is the callers responsibility to provide an output buffer that is large enough to hold the expected amount of bytes returned by the hash function."] #[doc = "\n# Parameters\n"] #[doc = "- `input`: The input data buffer."] #[doc = "- `output`: The output buffer to write the hash result to."] @@ -35,38 +35,35 @@ macro_rules! hash_fn { } pub trait Api { - /// Instantiate a contract from the given code. + /// 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. /// - /// # Note - /// - /// The copy of the output buffer and address can be skipped by providing `None` for the - /// `out_address` and `out_return_value` parameters. - /// /// # Parameters /// /// - `code_hash`: The hash of the code to be instantiated. /// - `gas_limit`: How much gas to devote for the execution. /// - `endowment`: The value to transfer into the contract. /// - `input`: The input data buffer. - /// - `out_address`: A reference to the address buffer to write the address of the contract. - /// - `out_return_value`: A reference to the return value buffer to write the return value. + /// - `out_address`: A reference to the address buffer to write the address of the contract. If + /// `None` is provided then the output buffer is not copied. + /// - `out_return_value`: A reference to the return value buffer to write the constructor output + /// buffer. If `None` is provided then the output buffer is not copied. /// - `salt`: The salt bytes to use for this instantiation. /// /// # Errors /// - /// Please consult the [Error][`crate::Error`] enum declaration for more information on those + /// Please consult the [`ReturnErrorCode`] enum declaration for more information on those /// errors. Here we only note things specific to this function. /// /// An error means that the account wasn't created and no address or output buffer /// is returned unless stated otherwise. /// - /// - [CalleeReverted][crate::Error::CalleeReverted]: Output buffer is returned. - /// - [CalleeTrapped][crate::Error::CalleeTrapped] - /// - [TransferFailed][crate::Error::TransferFailed] - /// - [CodeNotFound][crate::Error::CodeNotFound] + /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. + /// - [`ReturnErrorCode::CalleeTrapped`] + /// - [`ReturnErrorCode::TransferFailed`] + /// - [`ReturnErrorCode::CodeNotFound`] fn instantiate( code_hash: &[u8], gas_limit: u64, @@ -82,26 +79,24 @@ pub trait Api { /// # Parameters /// /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. - /// - `callee`: The address of the callee. + /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps + /// otherwise. /// - `gas_limit`: How much gas to devote for the execution. - /// - `value`: The value to transfer into the contract. + /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. + /// Traps otherwise. /// - `input`: The input data buffer used to call the contract. - /// - `output`: A reference to the output data buffer to write the output data. - /// - /// # Note - /// - /// The copy of the output buffer can be skipped by providing `None` for the - /// `output` parameter. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. /// /// # Errors /// /// An error means that the call wasn't successful output buffer is returned unless /// stated otherwise. /// - /// - [CalleeReverted][crate::Error::CalleeReverted]: Output buffer is returned. - /// - [CalleeTrapped][crate::Error::CalleeTrapped] - /// - [TransferFailed][crate::Error::TransferFailed] - /// - [NotCallable][crate::Error::NotCallable] + /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. + /// - [`ReturnErrorCode::CalleeTrapped`] + /// - [`ReturnErrorCode::TransferFailed`] + /// - [`ReturnErrorCode::NotCallable`] fn call( flags: CallFlags, callee: &[u8], @@ -122,21 +117,17 @@ pub trait Api { /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. /// - `code_hash`: The hash of the code to be executed. /// - `input`: The input data buffer used to call the contract. - /// - `output`: A reference to the output data buffer to write the output data. - /// - /// # Note - /// - /// The copy of the output buffer can be skipped by providing `None` for the - /// `output` parameter. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. /// /// # Errors /// /// An error means that the call wasn't successful and no output buffer is returned unless /// stated otherwise. /// - /// - [CalleeReverted][crate::Error::CalleeReverted]: Output buffer is returned. - /// - [CalleeTrapped][crate::Error::CalleeTrapped] - /// - [CodeNotFound][crate::Error::CodeNotFound] + /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. + /// - [`ReturnErrorCode::CalleeTrapped`] + /// - [`ReturnErrorCode::CodeNotFound`] fn delegate_call( flags: CallFlags, code_hash: &[u8], @@ -148,25 +139,29 @@ pub trait Api { /// /// # Parameters /// - /// - `account_id`: The address of the account to transfer funds to. - /// - `value`: The value to transfer. + /// - `account_id`: The address of the account to transfer funds to. Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. /// /// # Errors /// - /// - [TransferFailed][crate::Error::TransferFailed] + /// - [`ReturnErrorCode::TransferFailed`] fn transfer(account_id: &[u8], value: &[u8]) -> Result; - /// Deposit an event with the given topics. + /// Deposit a contract event with the data buffer and optional list of topics. There is a limit + /// on the maximum number of topics specified by `event_topics`. /// /// There should not be any duplicates in `topics`. /// /// # Parameters /// - /// - `topics`: The encoded `Vec` of `pallet_contracts::TopicOf` topic to deposit. + /// - `topics`: The topics list encoded as `Vec`. It can't contain duplicates. fn deposit_event(topics: &[u8], data: &[u8]); - /// Set the storage entry by the given key to the specified value. If `value` is `None` then - /// the storage entry is deleted. + /// 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. /// /// # Parameters /// @@ -175,9 +170,8 @@ pub trait Api { /// /// # Return /// - /// Returns the size of the pre-existing value at the specified key if any. Otherwise - /// `SENTINEL` is returned as a sentinel value. - fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option; + /// Returns the size of the pre-existing value at the specified key if any. + fn set_storage(key: &[u8], value: &[u8]) -> Option; /// Clear the value at the given key in the contract storage. /// @@ -187,11 +181,12 @@ pub trait Api { /// /// # Return /// - /// Returns the size of the pre-existing value at the specified key if any. Otherwise - /// `SENTINEL` is returned as a sentinel value. + /// Returns the size of the pre-existing value at the specified key if any. fn clear_storage(key: &[u8]) -> Option; - /// Reads the storage entry of the executing account by the given `key`. + /// Retrieve the value under the given key from storage. + /// + /// The key length must not exceed the maximum defined by the contracts module parameter. /// /// # Parameters /// - `key`: The storage key. @@ -199,7 +194,7 @@ pub trait Api { /// /// # Errors /// - /// [KeyNotFound][crate::Error::KeyNotFound] + /// [`ReturnErrorCode::KeyNotFound`] fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result; /// Retrieve and remove the value under the given key from storage. @@ -210,11 +205,13 @@ pub trait Api { /// /// # Errors /// - /// [KeyNotFound][crate::Error::KeyNotFound] + /// [`ReturnErrorCode::KeyNotFound`] fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result; /// 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`: The storage key. /// @@ -231,7 +228,8 @@ pub trait Api { /// /// # Parameters /// - /// - `beneficiary`: The address of the beneficiary account. + /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an + /// `T::AccountId`. /// /// # Traps /// @@ -259,11 +257,18 @@ pub trait Api { /// - `output`: A reference to the output data buffer to write the output data. /// /// # Return - /// TODO + /// + /// The chain extension returned value, if executed successfully. fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32; /// Stores the input passed by the caller into the supplied buffer. /// + /// # Note + /// + /// This function traps if: + /// - the input is larger than the available space. + /// - the input was previously forwarded by a [`call()`][`Self::call()`]. + /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the input data. @@ -271,9 +276,19 @@ pub trait Api { /// Cease contract execution and save a data buffer as a result of the execution. /// + /// This function never returns as it stops execution of the caller. + /// This is the only way to return a data buffer to the caller. Returning from + /// execution without calling this function is equivalent to calling: + /// ```nocompile + /// return_value(ReturnFlags::empty(), &[]) + /// ``` + /// + /// Using an unnamed non empty `ReturnFlags` triggers a trap. + /// /// # Parameters /// - /// - `flags`: See [`ReturnFlags`] for a documentation of the supported flags. + /// - `flags`: Flag used to signal special return conditions to the supervisor. See + /// [`ReturnFlags`] for a documentation of the supported flags. /// - `return_value`: The return value buffer. fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; @@ -305,6 +320,15 @@ pub trait Api { /// Stores the address of the caller into the supplied buffer. /// + /// If the available space in `output` 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. + /// + /// If there is no address associated with the caller (e.g. because the caller is root) then + /// it traps with `BadOrigin`. + /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the caller address. @@ -312,6 +336,8 @@ pub trait Api { /// Stores the current block number of the current contract into the supplied buffer. /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the block number. @@ -319,6 +345,8 @@ pub trait Api { /// Stores the address of the current contract into the supplied buffer. /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the address. @@ -326,12 +354,17 @@ pub trait Api { /// Stores the *free* balance of the current account into the supplied buffer. /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the balance. fn balance(output: &mut &mut [u8]); /// Stores the amount of weight left into the supplied buffer. + /// The data is encoded as Weight. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// @@ -339,6 +372,9 @@ pub trait Api { fn gas_left(output: &mut &mut [u8]); /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// @@ -347,12 +383,17 @@ pub trait Api { /// Load the latest block timestamp into the supplied buffer /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// /// # Parameters /// /// - `output`: A reference to the output data buffer to write the timestamp. fn now(output: &mut &mut [u8]); /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// @@ -360,6 +401,9 @@ pub trait Api { fn minimum_balance(output: &mut &mut [u8]); /// Stores the price for the specified amount of gas into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// @@ -385,7 +429,7 @@ pub trait Api { /// /// # Errors /// - /// - [EcdsaRecoveryFailed][crate::Error::EcdsaRecoveryFailed] + /// - [`ReturnErrorCode::EcdsaRecoveryFailed`] fn ecdsa_recover( signature: &[u8; 65], message_hash: &[u8; 32], @@ -402,7 +446,7 @@ pub trait Api { /// /// # Errors /// - /// - [EcdsaRecoveryFailed][crate::Error::EcdsaRecoveryFailed] + /// - [`ReturnErrorCode::EcdsaRecoveryFailed`] fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; /// Verify a sr25519 signature @@ -414,14 +458,15 @@ pub trait Api { /// /// # Errors /// - /// - [r25519VerifyFailed ][crate::Error::Sr25519VerifyFailed] + /// - [`ReturnErrorCode::Sr25519VerifyFailed`] fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; /// Checks whether a specified address belongs to a contract. /// /// # Parameters /// - /// - `account_id`: The address to check. + /// - `account_id`: The address to check. Should be decodable as an `T::AccountId`. Traps + /// otherwise. /// /// # Return /// @@ -466,20 +511,21 @@ pub trait Api { /// /// # Errors /// - /// - [CodeNotFound ][crate::Error::CodeNotFound] + /// - [`ReturnErrorCode::CodeNotFound`] fn set_code_hash(code_hash: &[u8]) -> Result; /// Retrieve the code hash for a specified contract address. /// /// # Parameters /// - /// - `account_id`: The address of the contract. + /// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps + /// otherwise. /// - `output`: A reference to the output data buffer to write the code hash. /// /// /// # Errors /// - /// - [CodeNotFound ][crate::Error::CodeNotFound] + /// - [`ReturnErrorCode::CodeNotFound`] fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; /// Retrieve the code hash of the currently executing contract. diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index d09ee7c784f52..c784c4f525b6c 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -42,8 +42,11 @@ macro_rules! define_error_codes { )* ) => { /// Every error that can be returned to a contract when it calls any of the host functions. + #[derive(Debug)] #[repr(u32)] - pub enum Error { + pub enum ReturnErrorCode { + /// API call successful. + Success = 0, $( $( #[$attr] )* $name = $discr, @@ -58,15 +61,21 @@ macro_rules! define_error_codes { match return_code.0 { 0 => Ok(()), $( - $discr => Err(Error::$name), + $discr => Err(ReturnErrorCode::$name), )* - _ => Err(Error::Unknown), + _ => Err(ReturnErrorCode::Unknown), } } } }; } +impl From for u32 { + fn from(code: ReturnErrorCode) -> u32 { + code as u32 + } +} + define_error_codes! { /// The called function trapped and has its state changes reverted. /// In this case no output buffer is returned. @@ -115,7 +124,7 @@ pub struct ReturnCode(u32); /// 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. -pub(crate) const SENTINEL: u32 = u32::MAX; +const SENTINEL: u32 = u32::MAX; impl From for Option { fn from(code: ReturnCode) -> Self { @@ -134,7 +143,7 @@ impl ReturnCode { } } -type Result = core::result::Result<(), Error>; +type Result = core::result::Result<(), ReturnErrorCode>; #[inline(always)] #[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] From cf476096be9b525e3f70e901b8efd2729f97cd78 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 22 Nov 2023 12:33:45 +0100 Subject: [PATCH 32/81] update call.rs --- .../frame/contracts/fixtures/contracts/call.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index 9caa810afef0a..31d40913399b1 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -28,8 +28,10 @@ pub fn deploy() {} #[no_mangle] pub fn call() { - let mut buffer = [0u8; 36]; // 4 bytes for the callee input data, 32 bytes for the callee address. - let mut out = [0u8; 0]; // No output data. + let mut buffer = [0u8; 40]; + let callee_input = 0..4; + let callee_addr = 4..36; + let value = 36..40; // Read the input data. api::input(&mut &mut buffer[..]); @@ -37,10 +39,10 @@ pub fn call() { // Call the callee api::call( CallFlags::empty(), - &buffer[4..36], // callee address. - 0u64, // How much gas to devote for the execution. 0 = all. - &buffer[36..], // Pointer to value to transfer. - &buffer[0..4], // Pointer to input data buffer address. - Some(&mut out[..]), // Pointer to output data buffer address. + &buffer[callee_addr], + 0u64, // How much gas to devote for the execution. 0 = all. + &buffer[value], + &buffer[callee_input], + None, ); } From a98e7aff943d5033a459f3413cdab0539b133f56 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 22 Nov 2023 12:59:23 +0100 Subject: [PATCH 33/81] fix lints --- substrate/frame/contracts/mock-network/src/tests.rs | 3 +-- substrate/frame/contracts/src/debug.rs | 6 ++++-- substrate/frame/contracts/src/lib.rs | 12 ++++++------ substrate/frame/contracts/src/primitives.rs | 4 ++-- substrate/frame/contracts/src/tests/test_debug.rs | 2 +- substrate/frame/contracts/uapi/Cargo.toml | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index 1a5a2634d05fd..320a0edf827c5 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -18,7 +18,7 @@ use crate::{ parachain::{self, Runtime}, parachain_account_sovereign_account_id, - primitives::{AccountId, CENTS}, + primitives::{AccountId, Code, CENTS}, relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, }; use assert_matches::assert_matches; @@ -31,7 +31,6 @@ use frame_support::{ use pallet_balances::{BalanceLock, Reasons}; use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; use pallet_contracts_fixtures::compile_module; -use crate::primitives::Code; use xcm::{v3::prelude::*, VersionedMultiLocation, VersionedXcm}; use xcm_simulator::TestExt; diff --git a/substrate/frame/contracts/src/debug.rs b/substrate/frame/contracts/src/debug.rs index 032c2c076af2e..6cdca7aa4c76d 100644 --- a/substrate/frame/contracts/src/debug.rs +++ b/substrate/frame/contracts/src/debug.rs @@ -15,8 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub use crate::exec::{ExecResult, ExportedFunction}; -pub use crate::primitives::ExecReturnValue; +pub use crate::{ + exec::{ExecResult, ExportedFunction}, + primitives::ExecReturnValue, +}; use crate::{Config, LOG_TARGET}; /// Umbrella trait for all interfaces that serves for debugging. diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index 6f4bb2ef42869..cfd3c1c470454 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -91,10 +91,10 @@ mod address; mod benchmarking; mod exec; mod gas; +mod primitives; mod schedule; mod storage; mod wasm; -mod primitives; pub mod chain_extension; pub mod debug; @@ -108,6 +108,11 @@ use crate::{ AccountIdOf, ErrorOrigin, ExecError, Executable, Ext, Key, MomentOf, Stack as ExecStack, }, gas::GasMeter, + primitives::{ + Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult, + ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult, + InstantiateReturnValue, StorageDeposit, + }, storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, wasm::{CodeInfo, WasmBlob}, }; @@ -129,11 +134,6 @@ use frame_system::{ pallet_prelude::{BlockNumberFor, OriginFor}, EventRecord, Pallet as System, }; -use crate::primitives::{ - Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult, - ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult, - InstantiateReturnValue, StorageDeposit, -}; use scale_info::TypeInfo; use smallvec::Array; use sp_runtime::{ diff --git a/substrate/frame/contracts/src/primitives.rs b/substrate/frame/contracts/src/primitives.rs index f5d2aeb1f1798..7ba4aa231d16a 100644 --- a/substrate/frame/contracts/src/primitives.rs +++ b/substrate/frame/contracts/src/primitives.rs @@ -20,14 +20,14 @@ #![cfg_attr(not(feature = "std"), no_std)] use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::weights::Weight; +use pallet_contracts_uapi::ReturnFlags; use scale_info::TypeInfo; use sp_runtime::{ traits::{Saturating, Zero}, DispatchError, RuntimeDebug, }; use sp_std::prelude::*; -use frame_support::weights::Weight; -use pallet_contracts_uapi::ReturnFlags; /// Result type of a `bare_call` or `bare_instantiate` call as well as `ContractsApi::call` and /// `ContractsApi::instantiate`. diff --git a/substrate/frame/contracts/src/tests/test_debug.rs b/substrate/frame/contracts/src/tests/test_debug.rs index c6a812b25a89b..c9b6557bbb978 100644 --- a/substrate/frame/contracts/src/tests/test_debug.rs +++ b/substrate/frame/contracts/src/tests/test_debug.rs @@ -18,8 +18,8 @@ use super::*; use crate::{ debug::{CallInterceptor, CallSpan, ExecResult, ExportedFunction, Tracing}, + primitives::ExecReturnValue, AccountIdOf, - primitives::ExecReturnValue }; use frame_support::traits::Currency; use pretty_assertions::assert_eq; diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index 87955c0a9a9f1..da1e985ec49a2 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -20,5 +20,5 @@ bitflags = "1.0" [features] default = [ "scale" ] -scale = ["scale-info", "dep:scale"] +scale = [ "dep:scale", "scale-info" ] From 213a01f28f3db0d9e76d706399e10d4253fe15f0 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 11:57:52 +0100 Subject: [PATCH 34/81] fix sys funcs --- .../frame/contracts/proc-macro/src/lib.rs | 2 +- .../frame/contracts/src/benchmarking/mod.rs | 1 + substrate/frame/contracts/src/wasm/runtime.rs | 162 +--- substrate/frame/contracts/uapi/src/api.rs | 740 +++++++++++------- .../frame/contracts/uapi/src/api/unstable.rs | 13 + substrate/frame/contracts/uapi/src/lib.rs | 9 + substrate/frame/contracts/uapi/src/wasm32.rs | 571 ++++++++++---- 7 files changed, 935 insertions(+), 563 deletions(-) create mode 100644 substrate/frame/contracts/uapi/src/api/unstable.rs diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs index 875a78fde8c68..1f336faaa26e3 100644 --- a/substrate/frame/contracts/proc-macro/src/lib.rs +++ b/substrate/frame/contracts/proc-macro/src/lib.rs @@ -811,7 +811,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) /// /// Only following return types are allowed for the host functions defined with the macro: /// - `Result<(), TrapReason>`, -/// - `Result`, +/// - `Result`, /// - `Result`. /// /// The macro expands to `pub struct Env` declaration, with the following traits implementations: diff --git a/substrate/frame/contracts/src/benchmarking/mod.rs b/substrate/frame/contracts/src/benchmarking/mod.rs index ac5787e234041..82ac82e71fd01 100644 --- a/substrate/frame/contracts/src/benchmarking/mod.rs +++ b/substrate/frame/contracts/src/benchmarking/mod.rs @@ -220,6 +220,7 @@ benchmarks! { #[pov_mode = Measured] on_initialize_per_trie_key { let k in 0..1024; + println!("here"); let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; instance.info()?.queue_trie_for_deletion(); }: { diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 11e5e92519b28..b2aa1e7635e33 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -1005,10 +1005,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { pub mod env { /// Set the value at the given key in the contract storage. - /// - /// Equivalent to the newer [`seal1`][`super::api_doc::Version1::set_storage`] version with the - /// exception of the return type. Still a valid thing to call when not interested in the return - /// value. + /// See [`pallet_contracts_uapi::Api::set_storage`] #[prefixed_alias] fn set_storage( ctx: _, @@ -1021,9 +1018,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::set_storage_v1`] #[version(1)] #[prefixed_alias] fn set_storage( @@ -1037,7 +1032,7 @@ pub mod env { } /// Set the value at the given key in the contract storage. - /// See [`pallet_contracts_uapi::Api::set_storage`] + /// See [`pallet_contracts_uapi::Api::set_storage_v2`] #[version(2)] #[prefixed_alias] fn set_storage( @@ -1052,17 +1047,14 @@ pub mod env { } /// Clear the value at the given key in the contract storage. - /// - /// Equivalent to the newer [`seal1`][`super::api_doc::Version1::clear_storage`] version with - /// the exception of the return type. Still a valid thing to call when not interested in the - /// return value. + /// See [`pallet_contracts_uapi::Api::clear_storage`] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32) -> Result<(), TrapReason> { ctx.clear_storage(memory, KeyType::Fix, key_ptr).map(|_| ()) } /// Clear the value at the given key in the contract storage. - /// See [`pallet_contracts_uapi::Api::clear_storage`] + /// See [`pallet_contracts_uapi::Api::clear_storage_v1`] #[version(1)] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { @@ -1070,9 +1062,7 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::get_storage`] #[prefixed_alias] fn get_storage( ctx: _, @@ -1085,7 +1075,7 @@ pub mod env { } /// Retrieve the value under the given key from storage. - /// See [`pallet_contracts_uapi::Api::get_storage`] + /// See [`pallet_contracts_uapi::Api::get_storage_v1`] #[version(1)] #[prefixed_alias] fn get_storage( @@ -1100,16 +1090,14 @@ pub mod env { } /// 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. + /// See [`pallet_contracts_uapi::Api::contains_storage`] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32) -> Result { ctx.contains_storage(memory, KeyType::Fix, key_ptr) } /// Checks whether there is a value stored under the given key. - /// See [`pallet_contracts_uapi::Api::storage_contains`] + /// See [`pallet_contracts_uapi::Api::contains_storage_v1`] #[version(1)] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { @@ -1173,18 +1161,7 @@ pub mod env { } /// Make a call to another contract. - /// - /// # New version available - /// - /// 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 - /// [`codec::MaxEncodedLen`]. The fields exist - /// for backwards compatibility. Consider switching to the newest version of this function. + /// See [`pallet_contracts_uapi::Api::call`]. #[prefixed_alias] fn call( ctx: _, @@ -1216,10 +1193,7 @@ pub mod env { } /// Make a call to another contract. - /// - /// Equivalent to the newer [`seal2`][`super::api_doc::Version2::call`] version but works with - /// *ref_time* Weight only. It is recommended to switch to the latest version, once it's - /// stabilized. + /// See [`pallet_contracts_uapi::Api::call_v1`]. #[version(1)] #[prefixed_alias] fn call( @@ -1251,7 +1225,7 @@ pub mod env { } /// Make a call to another contract. - /// See [`pallet_contracts_uapi::Api::call`]. + /// See [`pallet_contracts_uapi::Api::call_v2`]. #[version(2)] #[unstable] fn call( @@ -1309,17 +1283,7 @@ pub mod env { } /// Instantiate a contract with the specified code hash. - /// - /// # New version available - /// - /// 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 [`codec::MaxEncodedLen`]. The fields exist - /// for backwards compatibility. Consider switching to the newest version of this function. + /// See [`pallet_contracts_uapi::Api::instantiate`]. #[prefixed_alias] fn instantiate( ctx: _, @@ -1356,10 +1320,7 @@ pub mod env { } /// Instantiate a contract with the specified code hash. - /// - /// Equivalent to the newer [`seal2`][`super::api_doc::Version2::instantiate`] version but works - /// with *ref_time* Weight only. It is recommended to switch to the latest version, once it's - /// stabilized. + /// See [`pallet_contracts_uapi::Api::instantiate_v1`]. #[version(1)] #[prefixed_alias] fn instantiate( @@ -1395,7 +1356,7 @@ pub mod env { } /// Instantiate a contract with the specified code hash. - /// See [`pallet_contracts_uapi::Api::instantiate`]. + /// See [`pallet_contracts_uapi::Api::instantiate_v2`]. #[version(2)] #[unstable] fn instantiate( @@ -1433,11 +1394,7 @@ pub mod env { } /// Remove the calling account and transfer remaining balance. - /// - /// # New version available - /// - /// This is equivalent to calling the newer version of this function. The newer version - /// drops the now unnecessary length fields. + /// See [`pallet_contracts_uapi::Api::terminate`]. /// /// # Note /// @@ -1455,7 +1412,7 @@ pub mod env { } /// Remove the calling account and transfer remaining **free** balance. - /// See [`pallet_contracts_uapi::Api::terminate`]. + /// See [`pallet_contracts_uapi::Api::terminate_v1`]. #[version(1)] #[prefixed_alias] fn terminate(ctx: _, memory: _, beneficiary_ptr: u32) -> Result<(), TrapReason> { @@ -1574,14 +1531,7 @@ pub mod env { } /// Checks whether the caller of the current contract is root. - /// - /// Note that only the origin of the call stack can be root. Hence this function returning - /// `true` implies that the contract is being called by the origin. - /// - /// A return value of `true` indicates that this contract is being called by a root origin, - /// and `false` indicates that the caller is a signed origin. - /// - /// Returned value is a `u32`-encoded boolean: (`0 = false`, `1 = true`). + /// See [`pallet_contracts_uapi::Api::caller_is_root`]. #[unstable] fn caller_is_root(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsRoot)?; @@ -1604,10 +1554,7 @@ pub mod env { } /// Stores the price for the specified amount of gas into the supplied buffer. - /// - /// Equivalent to the newer [`seal1`][`super::api_doc::Version2::weight_to_fee`] version but - /// works with *ref_time* Weight only. It is recommended to switch to the latest version, once - /// it's stabilized. + /// See [`pallet_contracts_uapi::Api::weight_to_fee`]. #[prefixed_alias] fn weight_to_fee( ctx: _, @@ -1629,7 +1576,7 @@ pub mod env { } /// Stores the price for the specified amount of weight into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::weight_to_fee`]. + /// See [`pallet_contracts_uapi::Api::weight_to_fee_v1`]. #[version(1)] #[unstable] fn weight_to_fee( @@ -1653,10 +1600,7 @@ pub mod env { } /// Stores the weight left into the supplied buffer. - /// - /// Equivalent to the newer [`seal1`][`super::api_doc::Version2::gas_left`] version but - /// works with *ref_time* Weight only. It is recommended to switch to the latest version, once - /// it's stabilized. + /// See [`pallet_contracts_uapi::Api::gas_left`]. #[prefixed_alias] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::GasLeft)?; @@ -1672,7 +1616,7 @@ pub mod env { } /// Stores the amount of weight left into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::gas_left`]. + /// See [`pallet_contracts_uapi::Api::gas_left_v1`]. #[version(1)] #[unstable] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { @@ -2166,21 +2110,7 @@ pub mod env { } /// Execute an XCM program locally, using the contract's address as the origin. - /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that - /// the function is called directly instead of being dispatched. - /// - /// # Parameters - /// - /// - `msg_ptr`: the pointer into the linear memory where the [`xcm::prelude::VersionedXcm`] is - /// placed. - /// - `msg_len`: the length of the message in bytes. - /// - `output_ptr`: the pointer into the linear memory where the [`xcm::prelude::Outcome`] - /// message id is placed. - /// - /// # Return Value - /// - /// Returns `ReturnCode::Success` when the XCM was successfully executed. When the XCM - /// execution fails, `ReturnCode::XcmExecutionFailed` is returned. + /// See [`pallet_contracts_uapi::Api::execute_xcm`]. #[unstable] fn xcm_execute( ctx: _, @@ -2219,23 +2149,7 @@ pub mod env { } /// Send an XCM program from the contract to the specified destination. - /// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that - /// the function is called directly instead of being dispatched. - /// - /// # Parameters - /// - /// - `dest_ptr`: the pointer into the linear memory where the - /// [`xcm::prelude::VersionedMultiLocation`] is placed. - /// - `msg_ptr`: the pointer into the linear memory where the [`xcm::prelude::VersionedXcm`] is - /// placed. - /// - `msg_len`: the length of the message in bytes. - /// - `output_ptr`: the pointer into the linear memory where the [`xcm::v3::XcmHash`] message id - /// is placed. - /// - /// # Return Value - /// - /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM - /// execution fails, `ReturnCode::CallRuntimeFailed` is returned. + /// See [`pallet_contracts_uapi::Api::send_xcm`]. #[unstable] fn xcm_send( ctx: _, @@ -2371,10 +2285,7 @@ pub mod env { /// Returns the number of times the currently executing contract exists on the call stack in /// addition to the calling instance. - /// - /// # Return Value - /// - /// Returns `0` when there is no reentrancy. + /// See [`pallet_contracts_uapi::Api::reentrance_count`]. #[unstable] fn reentrance_count(ctx: _, memory: _) -> Result { ctx.charge_gas(RuntimeCosts::ReentrantCount)?; @@ -2383,14 +2294,7 @@ pub mod env { /// Returns the number of times specified contract exists on the call stack. Delegated calls are /// not counted as separate calls. - /// - /// # Parameters - /// - /// - `account_ptr`: a pointer to the contract address. - /// - /// # Return Value - /// - /// Returns `0` when the contract does not exist on the call stack. + /// See [`pallet_contracts_uapi::Api::account_reentrance_count`]. #[unstable] fn account_reentrance_count(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::AccountEntranceCount)?; @@ -2400,19 +2304,14 @@ pub mod env { } /// Returns a nonce that is unique per contract instantiation. - /// - /// The nonce is incremented for each successful contract instantiation. This is a - /// sensible default salt for contract instantiations. + /// See [`pallet_contracts_uapi::Api::instantiation_nonce`]. fn instantiation_nonce(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::InstantationNonce)?; Ok(ctx.ext.nonce()) } /// Adds a new delegate dependency to the contract. - /// - /// # Parameters - /// - /// - `code_hash_ptr`: A pointer to the code hash of the dependency. + /// See [`pallet_contracts_uapi::Api::add_delegate_dependency`]. #[unstable] fn add_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::AddDelegateDependency)?; @@ -2422,10 +2321,7 @@ pub mod env { } /// Removes the delegate dependency from the contract. - /// - /// # Parameters - /// - /// - `code_hash_ptr`: A pointer to the code hash of the dependency. + /// see [`pallet_contracts_uapi::Api::remove_delegate_dependency`]. #[unstable] fn remove_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::RemoveDelegateDependency)?; diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 493657109ab6f..77f0ac93e78da 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -15,6 +15,7 @@ // TODO: // - Add missing unstable methods +pub mod unstable; use crate::{CallFlags, Result, ReturnFlags}; use paste::paste; @@ -34,210 +35,122 @@ macro_rules! hash_fn { }; } +/// Defines all the user apis implemented by both wasm and RISC-V vms. pub trait Api { - /// 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. + /// Returns the number of times specified contract exists on the call stack. Delegated calls are + /// not counted as separate calls. /// /// # Parameters /// - /// - `code_hash`: The hash of the code to be instantiated. - /// - `gas_limit`: How much gas to devote for the execution. - /// - `endowment`: The value to transfer into the contract. - /// - `input`: The input data buffer. - /// - `out_address`: A reference to the address buffer to write the address of the contract. If - /// `None` is provided then the output buffer is not copied. - /// - `out_return_value`: A reference to the return value buffer to write the constructor output - /// buffer. If `None` is provided then the output buffer is not copied. - /// - `salt`: The salt bytes to use for this instantiation. + /// - `account`: The contract address. Should be decodable as an `T::AccountId`. Traps otherwise. /// - /// # Errors + /// # Return /// - /// Please consult the [`ReturnErrorCode`] enum declaration for more information on those - /// errors. Here we only note things specific to this function. + /// Returns the number of times specified contract exists on the call stack. + #[deprecated(note = "Unstable")] + fn account_reentrance_count(account: &[u8]) -> u32; + + /// Stores the address of the current contract into the supplied buffer. /// - /// An error means that the account wasn't created and no address or output buffer - /// is returned unless stated otherwise. + /// If the available space in `output` is less than the size of the value a trap is triggered. /// - /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. - /// - [`ReturnErrorCode::CalleeTrapped`] - /// - [`ReturnErrorCode::TransferFailed`] - /// - [`ReturnErrorCode::CodeNotFound`] - fn instantiate( - code_hash: &[u8], - gas_limit: u64, - endowment: &[u8], - input: &[u8], - out_address: Option<&mut [u8]>, - out_return_value: Option<&mut [u8]>, - salt: &[u8], - ) -> Result; + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the address. + fn address(output: &mut &mut [u8]); - /// Call (possibly transferring some amount of funds) into the specified account. + /// Adds a new delegate dependency to the contract. + /// + /// Traps if the maximum number of delegate_dependencies is reached or if + /// the delegate dependency already exists. /// /// # Parameters /// - /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. - /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps - /// otherwise. - /// - `gas_limit`: How much gas to devote for the execution. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. - /// Traps otherwise. - /// - `input`: The input data buffer used to call the contract. - /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` - /// is provided then the output buffer is not copied. + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps otherwise. + #[deprecated(note = "Unstable")] + fn add_delegate_dependency(code_hash: &[u8]); + + /// Stores the *free* balance of the current account into the supplied buffer. /// - /// # Errors + /// If the available space in `output` is less than the size of the value a trap is triggered. /// - /// An error means that the call wasn't successful output buffer is returned unless - /// stated otherwise. + /// # Parameters /// - /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. - /// - [`ReturnErrorCode::CalleeTrapped`] - /// - [`ReturnErrorCode::TransferFailed`] - /// - [`ReturnErrorCode::NotCallable`] + /// - `output`: A reference to the output data buffer to write the balance. + fn balance(output: &mut &mut [u8]); + + /// Stores the current block number of the current contract into the supplied buffer. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the block number. + fn block_number(output: &mut &mut [u8]); + + /// Make a call to another contract. + /// + /// This is equivalent to calling the newer version of this function with + /// `flags` set to `ALLOW_REENTRY`. See the newer version for documentation. + #[deprecated(note = "Deprecated, use newer version instead")] fn call( - flags: CallFlags, callee: &[u8], - gas_limit: u64, + gas: u64, value: &[u8], - input: &[u8], + input_data: &[u8], output: Option<&mut [u8]>, ) -> Result; - /// Execute code in the context (storage, caller, value) of the current contract. + /// Make a call to another 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. + /// Equivalent to the newer [`seal2`][`Api::call_v2`] version but works with + /// *ref_time* Weight only + fn call_v1( + flags: CallFlags, + callee: &[u8], + gas: u64, + value: &[u8], + input_data: &[u8], + output: Option<&mut [u8]>, + ) -> Result; + + /// Call (possibly transferring some amount of funds) into the specified account. /// /// # Parameters /// /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. - /// - `code_hash`: The hash of the code to be executed. + /// - `callee`: The address of the callee. Should be decodable as an `T::AccountId`. Traps + /// otherwise. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a `Option`. Traps otherwise. Passing `None` means setting no specific limit for the call, which implies storage usage up to the limit of the parent call. + /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. + /// Traps otherwise. /// - `input`: The input data buffer used to call the contract. /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` /// is provided then the output buffer is not copied. /// /// # Errors /// - /// An error means that the call wasn't successful and no output buffer is returned unless + /// An error means that the call wasn't successful output buffer is returned unless /// stated otherwise. /// /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. /// - [`ReturnErrorCode::CalleeTrapped`] - /// - [`ReturnErrorCode::CodeNotFound`] - fn delegate_call( + /// - [`ReturnErrorCode::TransferFailed`] + /// - [`ReturnErrorCode::NotCallable`] + #[deprecated(note = "Unstable, use `call_v1` instead")] + fn call_v2( flags: CallFlags, - code_hash: &[u8], - input: &[u8], + callee: &[u8], + ref_time_limit: u64, + proof_time_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], + input_data: &[u8], output: Option<&mut [u8]>, ) -> Result; - /// Transfer some amount of funds into the specified account. - /// - /// # Parameters - /// - /// - `account_id`: The address of the account to transfer funds to. Should be decodable as an - /// `T::AccountId`. Traps otherwise. - /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. - /// - /// # Errors - /// - /// - [`ReturnErrorCode::TransferFailed`] - fn transfer(account_id: &[u8], value: &[u8]) -> Result; - - /// Deposit a contract event with the data buffer and optional list of topics. There is a limit - /// on the maximum number of topics specified by `event_topics`. - /// - /// There should not be any duplicates in `topics`. - /// - /// # Parameters - /// - /// - `topics`: The topics list encoded as `Vec`. It can't contain duplicates. - fn deposit_event(topics: &[u8], data: &[u8]); - - /// Set the value at the given key in the contract storage. - /// - /// The key and value lengths must not exceed the maximums defined by the contracts module - /// parameters. - /// - /// # Parameters - /// - /// - `key`: The storage key. - /// - `encoded_value`: The storage value. - /// - /// # Return - /// - /// Returns the size of the pre-existing value at the specified key if any. - fn set_storage(key: &[u8], value: &[u8]) -> Option; - - /// Clear the value at the given key in the contract storage. - /// - /// # Parameters - /// - /// - `key`: The storage key. - /// - /// # Return - /// - /// Returns the size of the pre-existing value at the specified key if any. - fn clear_storage(key: &[u8]) -> Option; - - /// Retrieve the value under the given key from storage. - /// - /// The key length must not exceed the maximum defined by the contracts module parameter. - /// - /// # Parameters - /// - `key`: The storage key. - /// - `output`: A reference to the output data buffer to write the storage entry. - /// - /// # Errors - /// - /// [`ReturnErrorCode::KeyNotFound`] - fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result; - - /// Retrieve and remove the value under the given key from storage. - /// - /// # Parameters - /// - `key`: The storage key. - /// - `output`: A reference to the output data buffer to write the storage entry. - /// - /// # Errors - /// - /// [`ReturnErrorCode::KeyNotFound`] - fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result; - - /// 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`: The storage key. - /// - /// # Return - /// - /// Returns the size of the pre-existing value at the specified key if any. - fn storage_contains(key: &[u8]) -> Option; - - /// Remove the calling account and transfer remaining **free** balance. - /// - /// This function never returns. Either the termination was successful and the - /// execution of the destroyed contract is halted. Or it failed during the termination - /// which is considered fatal and results in a trap + rollback. - /// - /// # Parameters - /// - /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an - /// `T::AccountId`. - /// - /// # 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. - fn terminate(beneficiary: &[u8]) -> !; - /// 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 @@ -261,37 +174,6 @@ pub trait Api { /// The chain extension returned value, if executed successfully. fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32; - /// Stores the input passed by the caller into the supplied buffer. - /// - /// # Note - /// - /// This function traps if: - /// - the input is larger than the available space. - /// - the input was previously forwarded by a [`call()`][`Self::call()`]. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the input data. - fn input(output: &mut &mut [u8]); - - /// Cease contract execution and save a data buffer as a result of the execution. - /// - /// This function never returns as it stops execution of the caller. - /// This is the only way to return a data buffer to the caller. Returning from - /// execution without calling this function is equivalent to calling: - /// ```nocompile - /// return_value(ReturnFlags::empty(), &[]) - /// ``` - /// - /// Using an unnamed non empty `ReturnFlags` triggers a trap. - /// - /// # Parameters - /// - /// - `flags`: Flag used to signal special return conditions to the supervisor. See - /// [`ReturnFlags`] for a documentation of the supported flags. - /// - `return_value`: The return value buffer. - fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; - /// Call some dispatchable of the runtime. /// /// # Parameters @@ -334,87 +216,116 @@ pub trait Api { /// - `output`: A reference to the output data buffer to write the caller address. fn caller(output: &mut &mut [u8]); - /// Stores the current block number of the current contract into the supplied buffer. + /// Checks whether the caller of the current contract is the origin of the whole call stack. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// Prefer this over [`is_contract()`][`Self::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. /// - /// # Parameters + /// # Return /// - /// - `output`: A reference to the output data buffer to write the block number. - fn block_number(output: &mut &mut [u8]); + /// 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. + fn caller_is_origin() -> bool; - /// Stores the address of the current contract into the supplied buffer. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// Checks whether the caller of the current contract is root. /// - /// # Parameters + /// Note that only the origin of the call stack can be root. Hence this function returning + /// `true` implies that the contract is being called by the origin. /// - /// - `output`: A reference to the output data buffer to write the address. - fn address(output: &mut &mut [u8]); + /// A return value of `true` indicates that this contract is being called by a root origin, + /// and `false` indicates that the caller is a signed origin. + #[deprecated(note = "Unstable")] + fn caller_is_root() -> u32; - /// Stores the *free* balance of the current account into the supplied buffer. + /// Clear the value at the given key in the contract storage. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// Equivalent to the newer [`Self::clear_storage_v1`] version with + /// the exception of the return type. Still a valid thing to call when not interested in the + /// return value. + fn clear_storage(key: &[u8]); + + /// Clear the value at the given key in the contract storage. /// /// # Parameters /// - /// - `output`: A reference to the output data buffer to write the balance. - fn balance(output: &mut &mut [u8]); - - /// Stores the amount of weight left into the supplied buffer. - /// The data is encoded as Weight. + /// - `key`: The storage key. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn clear_storage_v1(key: &[u8]) -> Option; + + /// Retrieve the code hash for a specified contract address. /// /// # Parameters /// - /// - `output`: A reference to the output data buffer to write the weight left. - fn gas_left(output: &mut &mut [u8]); - - /// Stores the value transferred along with this call/instantiate into the supplied buffer. - /// The data is encoded as `T::Balance`. + /// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps + /// otherwise. + /// - `output`: A reference to the output data buffer to write the code hash. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. /// - /// # Parameters + /// # Errors /// - /// - `output`: A reference to the output data buffer to write the transferred value. - fn value_transferred(output: &mut &mut [u8]); + /// - [`ReturnErrorCode::CodeNotFound`] + fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; - /// Load the latest block timestamp into the supplied buffer + /// Checks whether there is a value stored under the given key. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// 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. + fn contains_storage(key: &[u8]) -> Option; + + /// 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`: The storage key. /// - /// - `output`: A reference to the output data buffer to write the timestamp. - fn now(output: &mut &mut [u8]); + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn contains_storage_v1(key: &[u8]) -> Option; - /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// The data is encoded as `T::Balance`. + /// Execute code in the context (storage, caller, value) of the current contract. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// 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 /// - /// - `output`: A reference to the output data buffer to write the minimum balance. - fn minimum_balance(output: &mut &mut [u8]); + /// - `flags`: See [`CallFlags`] for a documentation of the supported flags. + /// - `code_hash`: The hash of the code to be executed. + /// - `input`: The input data buffer used to call the contract. + /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` + /// is provided then the output buffer is not copied. + /// + /// # Errors + /// + /// An error means that the call wasn't successful and no output buffer is returned unless + /// stated otherwise. + /// + /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. + /// - [`ReturnErrorCode::CalleeTrapped`] + /// - [`ReturnErrorCode::CodeNotFound`] + fn delegate_call( + flags: CallFlags, + code_hash: &[u8], + input_data: &[u8], + output: Option<&mut [u8]>, + ) -> Result; - /// Stores the price for the specified amount of gas into the supplied buffer. - /// The data is encoded as `T::Balance`. + /// 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`. /// - /// If the available space in `output` is less than the size of the value a trap is triggered. + /// There should not be any duplicates in `topics`. /// /// # Parameters /// - /// - `gas`: The amount of gas to query the price for. - /// - `output`: A reference to the output data buffer to write the price. - fn weight_to_fee(gas: u64, output: &mut &mut [u8]); - - hash_fn!(sha2_256, 32); - hash_fn!(keccak_256, 32); - hash_fn!(blake2_256, 32); - hash_fn!(blake2_128, 16); + /// - `topics`: The topics list encoded as `Vec`. It can't contain duplicates. + fn deposit_event(topics: &[u8], data: &[u8]); /// Recovers the ECDSA public key from the given message hash and signature. /// @@ -449,17 +360,135 @@ pub trait Api { /// - [`ReturnErrorCode::EcdsaRecoveryFailed`] fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; - /// Verify a sr25519 signature + /// Stores the weight left into the supplied buffer. + /// + /// Equivalent to the newer [`Self::gas_left_v1`] version but + /// works with *ref_time* Weight only. + fn gas_left(out: &mut &mut [u8]); + + /// Stores the amount of weight left into the supplied buffer. + /// The data is encoded as Weight. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. /// /// # Parameters /// - /// - `signature`: The signature bytes. - /// - `message`: The message bytes. + /// - `output`: A reference to the output data buffer to write the weight left. + #[deprecated(note = "Unstable, use `gas_left` instead")] + fn gas_left_v1(output: &mut &mut [u8]); + + /// 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. + fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Retrieve the value under the given key from storage. + /// + /// The key length must not exceed the maximum defined by the contracts module parameter. + /// + /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. /// /// # Errors /// - /// - [`ReturnErrorCode::Sr25519VerifyFailed`] - fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; + /// [`ReturnErrorCode::KeyNotFound`] + fn get_storage_v1(key: &[u8], output: &mut &mut [u8]) -> Result; + + hash_fn!(sha2_256, 32); + hash_fn!(keccak_256, 32); + hash_fn!(blake2_256, 32); + hash_fn!(blake2_128, 16); + + /// Stores the input passed by the caller into the supplied buffer. + /// + /// # Note + /// + /// This function traps if: + /// - the input is larger than the available space. + /// - the input was previously forwarded by a [`call()`][`Self::call()`]. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the input data. + fn input(output: &mut &mut [u8]); + + /// Instantiate a contract with the specified code hash. + #[deprecated(note = "Deprecated, use newer version instead")] + fn instantiate( + code_hash: &[u8], + gas: u64, + value: &[u8], + input: &[u8], + address: Option<&mut [u8]>, + output: Option<&mut [u8]>, + salt: &[u8], + ) -> Result; + + /// Instantiate a contract with the specified code hash. + /// + /// Equivalent to the newer [`Self::instantiate_v2`] version but works + /// with *ref_time* Weight only. + fn instantiate_v1( + code_hash: &[u8], + gas: u64, + value: &[u8], + input: &[u8], + address: Option<&mut [u8]>, + output: Option<&mut [u8]>, + salt: &[u8], + ) -> Result; + + /// Instantiate a contract with the specified code hash. + /// + /// This function creates an account and executes the constructor defined in the code specified + /// by the code hash. + /// + /// # Parameters + /// + /// - `code_hash`: The hash of the code to be instantiated. + /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. + /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a `Option`. Traps otherwise. Passing `None` means setting no specific limit for the call, which implies storage usage up to the limit of the parent call. + /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. Traps otherwise. + /// - `input`: The input data buffer. + /// - `address`: A reference to the address buffer to write the address of the contract. If + /// `None` is provided then the output buffer is not copied. + /// - `output`: A reference to the return value buffer to write the constructor output + /// buffer. If `None` is provided then the output buffer is not copied. + /// - `salt`: The salt bytes to use for this instantiation. + /// + /// # Errors + /// + /// Please consult the [`ReturnErrorCode`] 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. + /// + /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. + /// - [`ReturnErrorCode::CalleeTrapped`] + /// - [`ReturnErrorCode::TransferFailed`] + /// - [`ReturnErrorCode::CodeNotFound`] + #[deprecated(note = "Unstable, use `instantiate_v1` instead")] + fn instantiate_v2( + code_hash: &[u8], + ref_time_limit: u64, + proof_size_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], + input: &[u8], + address: Option<&mut [u8]>, + output: Option<&mut [u8]>, + salt: &[u8], + ) -> Result; + + /// Returns a nonce that is unique per contract instantiation. + /// + /// The nonce is incremented for each successful contract instantiation. This is a + /// sensible default salt for contract instantiations. + fn instantiation_nonce() -> u64; /// Checks whether a specified address belongs to a contract. /// @@ -473,17 +502,68 @@ pub trait Api { /// Returns `true` if the address belongs to a contract. fn is_contract(account_id: &[u8]) -> bool; - /// Checks whether the caller of the current contract is the origin of the whole call stack. + /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. + /// The data is encoded as `T::Balance`. /// - /// Prefer this over [`is_contract()`][`Self::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. + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the minimum balance. + fn minimum_balance(output: &mut &mut [u8]); + + /// Retrieve the code hash of the currently executing contract. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the code hash. + fn own_code_hash(output: &mut [u8]); + + /// Load the latest block timestamp into the supplied buffer + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the timestamp. + fn now(output: &mut &mut [u8]); + + /// Returns the number of times the currently executing contract exists on the call stack in + /// addition to the calling instance. /// /// # Return /// - /// 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. - fn caller_is_origin() -> bool; + /// Returns `0` when there is no reentrancy. + #[deprecated(note = "Unstable")] + fn reentrance_count() -> u32; + + /// Removes the delegate dependency from the contract. + /// + /// Traps if the delegate dependency does not exist. + /// + /// # Parameters + /// + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps otherwise. + #[deprecated(note = "Unstable")] + fn remove_delegate_dependency(code_hash: &[u8]); + + /// Cease contract execution and save a data buffer as a result of the execution. + /// + /// This function never returns as it stops execution of the caller. + /// This is the only way to return a data buffer to the caller. Returning from + /// execution without calling this function is equivalent to calling: + /// ```nocompile + /// return_value(ReturnFlags::empty(), &[]) + /// ``` + /// + /// Using an unnamed non empty `ReturnFlags` triggers a trap. + /// + /// # Parameters + /// + /// - `flags`: Flag used to signal special return conditions to the supervisor. See + /// [`ReturnFlags`] for a documentation of the supported flags. + /// - `return_value`: The return value buffer. + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> !; /// Replace the contract code at the specified address with new code. /// @@ -507,31 +587,161 @@ pub trait Api { /// /// # Parameters /// - /// - `code_hash`: The hash of the new code. + /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps otherwise. /// /// # Errors /// /// - [`ReturnErrorCode::CodeNotFound`] fn set_code_hash(code_hash: &[u8]) -> Result; - /// Retrieve the code hash for a specified contract address. + /// Set the value at the given key in the contract storage. + /// + /// Equivalent to [`Self::set_storage_1`] version with the + /// exception of the return type. Still a valid thing to call for fixed sized storage key, when not interested in the return + /// value. + fn set_storage(key: &[u8], value: &[u8]); + + /// 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. + fn set_storage_v1(key: &[u8], value: &[u8]) -> Option; + + /// 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. /// /// # Parameters /// - /// - `account_id`: The address of the contract.Should be decodable as an `T::AccountId`. Traps - /// otherwise. - /// - `output`: A reference to the output data buffer to write the code hash. + /// - `key`: The storage key. + /// - `encoded_value`: The storage value. /// + /// # Return + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn set_storage_v2(key: &[u8], value: &[u8]) -> Option; + + /// Verify a sr25519 signature + /// + /// # Parameters + /// + /// - `signature`: The signature bytes. + /// - `message`: The message bytes. /// /// # Errors /// - /// - [`ReturnErrorCode::CodeNotFound`] - fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; + /// - [`ReturnErrorCode::Sr25519VerifyFailed`] + fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; - /// Retrieve the code hash of the currently executing contract. + /// Retrieve and remove the value under the given key from storage. /// /// # Parameters + /// - `key`: The storage key. + /// - `output`: A reference to the output data buffer to write the storage entry. /// - /// - `output`: A reference to the output data buffer to write the code hash. - fn own_code_hash(output: &mut [u8]); + /// # Errors + /// + /// [`ReturnErrorCode::KeyNotFound`] + fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result; + + /// Transfer some amount of funds into the specified account. + /// + /// # Parameters + /// + /// - `account_id`: The address of the account to transfer funds to. Should be decodable as an + /// `T::AccountId`. Traps otherwise. + /// - `value`: The value to transfer. Should be decodable as a `T::Balance`. Traps otherwise. + /// + /// # Errors + /// + /// - [`ReturnErrorCode::TransferFailed`] + fn transfer(account_id: &[u8], value: &[u8]) -> Result; + + /// Remove the calling account and transfer remaining balance. + /// + /// This is equivalent to calling the newer version of this function + #[deprecated(note = "Deprecated, use newer version instead")] + fn terminate(beneficiary: &[u8]) -> !; + + /// Remove the calling account and transfer remaining **free** balance. + /// + /// This function never returns. Either the termination was successful and the + /// execution of the destroyed contract is halted. Or it failed during the termination + /// which is considered fatal and results in a trap + rollback. + /// + /// # Parameters + /// + /// - `beneficiary`: The address of the beneficiary account, Should be decodable as an + /// `T::AccountId`. + /// + /// # 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. + fn terminate_v1(beneficiary: &[u8]) -> !; + + /// Stores the value transferred along with this call/instantiate into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `output`: A reference to the output data buffer to write the transferred value. + fn value_transferred(output: &mut &mut [u8]); + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// + /// Equivalent to the newer [`Self::weight_to_fee_v1`] version but + /// works with *ref_time* Weight only. It is recommended to switch to the latest version, once + /// it's stabilized. + fn weight_to_fee(gas: u64, output: &mut &mut [u8]); + + /// Stores the price for the specified amount of gas into the supplied buffer. + /// The data is encoded as `T::Balance`. + /// + /// If the available space in `output` is less than the size of the value a trap is triggered. + /// + /// # Parameters + /// + /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. + /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. + /// - `output`: A reference to the output data buffer to write the price. + #[deprecated(note = "Unstable, use `weight_to_fee` instead")] + fn weight_to_fee_v1( ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); + + /// Execute an XCM program locally, using the contract's address as the origin. + /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that + /// the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps otherwise. + /// - `output`: A reference to the output data buffer to write the [`xcm::prelude::Outcome`] + /// + /// # Return + /// + /// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM + /// execution fails, `ReturnCode::XcmExecutionFailed` is returned + #[deprecated(note = "Unstable")] + fn xcm_execute( msg: &[u8], output: &mut &mut [u8]) -> Result; + + /// Send an XCM program from the contract to the specified destination. + /// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that + /// the function is called directly instead of being dispatched. + /// + /// # Parameters + /// + /// - `dest`: The XCM destination, should be decodable as [`xcm::prelude::VersionedMultiLocation`], traps otherwise. + /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps otherwise. + /// - `output`: A reference to the output data buffer to write the [`xcm::v3::XcmHash`] + /// + /// # Return + /// + /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM + /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. + #[deprecated(note = "Unstable")] + fn xcm_send( dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result; } diff --git a/substrate/frame/contracts/uapi/src/api/unstable.rs b/substrate/frame/contracts/uapi/src/api/unstable.rs new file mode 100644 index 0000000000000..8807a292de105 --- /dev/null +++ b/substrate/frame/contracts/uapi/src/api/unstable.rs @@ -0,0 +1,13 @@ +// 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. diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index c784c4f525b6c..e34d969cbdc80 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -161,3 +161,12 @@ fn ptr_and_len_from_slice(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { None => (SENTINEL as _, 0), } } + +#[inline(always)] +#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +fn ptr_from_slice(data: &Option<&[u8]>) -> *const u8 { + match data { + Some(ref data) => data.as_ptr(), + None => SENTINEL as _ + } +} diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 285e4ccdde483..ac0d4606ef556 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -11,9 +11,9 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - +#![allow(unused_variables)] use super::{ - extract_from_slice, ptr_and_len_from_slice, CallFlags, Result, ReturnCode, ReturnFlags, + extract_from_slice, ptr_from_slice, ptr_and_len_from_slice, Api, CallFlags, Result, ReturnCode, ReturnFlags, }; mod sys { @@ -21,6 +21,48 @@ mod sys { #[link(wasm_import_module = "seal0")] extern "C" { + pub fn terminate(beneficiary_ptr: *const u8) -> !; + + pub fn get_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; + + pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; + pub fn call( + callee_ptr: *const u8, + gas: u64, + transferred_value_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + ) -> ReturnCode; + + pub fn set_storage( + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> ReturnCode; + + pub fn instantiate( + code_hash_ptr: *const u8, + gas: u64, + value_ptr: *const u8, + input_ptr: *const u8, + input_len: u32, + address_ptr: *mut u8, + address_len_ptr: *mut u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + salt_ptr: *const u8, + salt_len: u32, + ) -> ReturnCode; + pub fn transfer( account_id_ptr: *const u8, account_id_len: u32, @@ -35,6 +77,8 @@ mod sys { data_len: u32, ); + pub fn caller_is_root() -> ReturnCode; + pub fn call_chain_extension( func_id: u32, input_ptr: *const u8, @@ -85,16 +129,13 @@ mod sys { ) -> ReturnCode; pub fn ecdsa_recover( - // 65 bytes of ecdsa signature signature_ptr: *const u8, - // 32 bytes hash of the message message_hash_ptr: *const u8, output_ptr: *mut u8, ) -> ReturnCode; pub fn ecdsa_to_eth_address(public_key_ptr: *const u8, output_ptr: *mut 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: *const u8, @@ -113,118 +154,128 @@ mod sys { pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode; } - #[link(wasm_import_module = "seal1")] - extern "C" { - pub fn instantiate( - init_code_ptr: *const u8, - gas: u64, - endowment_ptr: *const u8, - input_ptr: *const u8, - input_len: u32, - address_ptr: *mut u8, - address_len_ptr: *mut u32, - output_ptr: *mut u8, - output_len_ptr: *mut u32, - salt_ptr: *const u8, - salt_len: u32, - ) -> ReturnCode; - - pub fn terminate(beneficiary_ptr: *const u8) -> !; - - pub fn call( - flags: u32, - callee_ptr: *const u8, - gas: u64, - transferred_value_ptr: *const u8, - input_data_ptr: *const u8, - input_data_len: u32, - output_ptr: *mut u8, - output_len_ptr: *mut u32, - ) -> 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: *const 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: *const 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: *const u8, - key_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> ReturnCode; + pub mod v1 { + use crate::ReturnCode; + + #[link(wasm_import_module = "seal1")] + extern "C" { + pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn instantiate( + code_hash_ptr: *const u8, + gas: u64, + value_ptr: *const u8, + input_ptr: *const u8, + input_len: u32, + address_ptr: *mut u8, + address_len_ptr: *mut u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + salt_ptr: *const u8, + salt_len: u32, + ) -> ReturnCode; + + pub fn set_storage( + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> ReturnCode; + + pub fn terminate(beneficiary_ptr: *const u8) -> !; + + pub fn call( + flags: u32, + callee_ptr: *const u8, + gas: u64, + transferred_value_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + ) -> ReturnCode; + + pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; + + pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; + + pub fn get_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> 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: *const u8, - key_len: u32, - value_ptr: *const u8, - value_len: u32, - ) -> ReturnCode; + pub mod v2 { + use crate::ReturnCode; + + #[link(wasm_import_module = "seal2")] + extern "C" { + pub fn call( + flags: u32, + callee_ptr: *const u8, + ref_time_limit: u64, + proof_time_limit: u64, + deposit_ptr: *const u8, + transferred_value_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + ) -> ReturnCode; + + pub fn instantiate( + code_hash_ptr: *const u8, + ref_time_limit: u64, + proof_time_limit: u64, + deposit_ptr: *const u8, + value_ptr: *const u8, + input_ptr: *const u8, + input_len: u32, + address_ptr: *mut u8, + address_len_ptr: *mut u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + salt_ptr: *const u8, + salt_len: u32, + ) -> ReturnCode; + + pub fn set_storage( + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> ReturnCode; + } } } macro_rules! impl_wrapper_for { - ( $( $name:ident, )* ) => { - $( + (@impl_fn $( $mod:ident )::*, $suffix:literal, $name:ident) => { + paste::paste! { #[inline(always)] - fn $name(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - { - unsafe { - sys::$name( - output.as_mut_ptr(), - &mut output_len, - ) - }; - } - } - )* - } + fn [<$name $suffix>](output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + unsafe { + $( $mod )::*::$name(output.as_mut_ptr(), &mut output_len); + } + } + } + }; + + () => {}; + + (($mod:ident, $suffix:literal) => [$( $name:ident),*], $($tail:tt)*) => { + $(impl_wrapper_for!(@impl_fn sys::$mod, $suffix, $name);)* + impl_wrapper_for!($($tail)*); + }; + + (() => [$( $name:ident),*], $($tail:tt)*) => { + $(impl_wrapper_for!(@impl_fn sys, "", $name);)* + impl_wrapper_for!($($tail)*); + }; } macro_rules! impl_hash_fn { @@ -243,71 +294,209 @@ macro_rules! impl_hash_fn { }; } +macro_rules! impl_legacy_instantiate { + ($fn_name:ident, $sys_fn:path) => { + #[inline(always)] + fn $fn_name( + code_hash: &[u8], + gas: u64, + value: &[u8], + input: &[u8], + mut address: Option<&mut [u8]>, + mut output: Option<&mut [u8]>, + salt: &[u8], + ) -> Result { + let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let ret_code = unsafe { + $sys_fn( + code_hash.as_ptr(), + gas, + value.as_ptr(), + input.as_ptr(), + input.len() as u32, + address_ptr, + &mut address_len, + output_ptr, + &mut output_len, + salt.as_ptr(), + salt.len() as u32, + ) + }; + + if let Some(ref mut address) = address { + extract_from_slice(address, address_len as usize); + } + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into() + } + }; +} + +macro_rules! impl_get_storage { + ($fn_name:ident, $sys_get_storage:path) => { + #[inline(always)] + fn $fn_name(key: &[u8], output: &mut &mut [u8]) -> Result { + let mut output_len = output.len() as u32; + let ret_code = { + unsafe { + $sys_get_storage( + key.as_ptr(), + key.len() as u32, + output.as_mut_ptr(), + &mut output_len, + ) + } + }; + extract_from_slice(output, output_len as usize); + ret_code.into() + } + } +} + + pub enum ApiImpl {} -impl super::Api for ApiImpl { +impl Api for ApiImpl { + + impl_legacy_instantiate!(instantiate, sys::instantiate); + + impl_legacy_instantiate!(instantiate_v1, sys::v1::instantiate); + #[inline(always)] - fn instantiate( + fn instantiate_v2( code_hash: &[u8], - gas_limit: u64, - endowment: &[u8], + ref_time_limit: u64, + proof_size_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], input: &[u8], - mut out_address: Option<&mut [u8]>, - mut out_return_value: Option<&mut [u8]>, + mut address: Option<&mut [u8]>, + mut output: Option<&mut [u8]>, salt: &[u8], ) -> Result { - let (out_addr_ptr, mut out_addr_len) = ptr_and_len_from_slice(&mut out_address); - let (return_value_ptr, mut return_value_len) = - ptr_and_len_from_slice(&mut out_return_value); + let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let deposit_ptr = ptr_from_slice(&deposit); let ret_code = { unsafe { - sys::instantiate( + sys::v2::instantiate( code_hash.as_ptr(), - gas_limit, - endowment.as_ptr(), + ref_time_limit, + proof_size_limit, + deposit_ptr, + value.as_ptr(), input.as_ptr(), input.len() as u32, - out_addr_ptr, - &mut out_addr_len, - return_value_ptr, - &mut return_value_len, + address_ptr, + &mut address_len, + output_ptr, + &mut output_len, salt.as_ptr(), salt.len() as u32, ) } }; - if let Some(ref mut out_address) = out_address { - extract_from_slice(out_address, out_addr_len as usize); + if let Some(ref mut address) = address { + extract_from_slice(address, address_len as usize); } - if let Some(ref mut out_return_value) = out_return_value { - extract_from_slice(out_return_value, return_value_len as usize); + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); } ret_code.into() } - #[inline(always)] + fn call( - flags: CallFlags, callee: &[u8], - gas_limit: u64, + gas: u64, value: &[u8], - input: &[u8], + input_data: &[u8], mut output: Option<&mut [u8]>, ) -> Result { let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); let ret_code = { unsafe { sys::call( + callee.as_ptr(), + gas, + value.as_ptr(), + input_data.as_ptr(), + input_data.len() as u32, + output_ptr, + &mut output_len, + ) + } + }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + #[inline(always)] + fn call_v1( + flags: CallFlags, + callee: &[u8], + gas: u64, + value: &[u8], + input_data: &[u8], + mut output: Option<&mut [u8]>, + ) -> Result { + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let ret_code = { + unsafe { + sys::v1::call( flags.bits(), callee.as_ptr(), - gas_limit, + gas, value.as_ptr(), - input.as_ptr(), - input.len() as u32, + input_data.as_ptr(), + input_data.len() as u32, + output_ptr, + &mut output_len, + ) + } + }; + + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + + ret_code.into() + } + + fn call_v2( + flags: CallFlags, + callee: &[u8], + ref_time_limit: u64, + proof_time_limit: u64, + deposit: Option<&[u8]>, + value: &[u8], + input_data: &[u8], + mut output: Option<&mut [u8]>, + ) -> Result { + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let deposit_ptr = ptr_from_slice(&deposit); + let ret_code = { + unsafe { + sys::v2::call( + flags.bits(), + callee.as_ptr(), + ref_time_limit, + proof_time_limit, + deposit_ptr, + value.as_ptr(), + input_data.as_ptr(), + input_data.len() as u32, output_ptr, &mut output_len, ) @@ -319,6 +508,10 @@ impl super::Api for ApiImpl { } ret_code.into() + } + + fn caller_is_root() -> u32 { + unsafe { sys::caller_is_root() }.into_u32() } #[inline(always)] @@ -372,9 +565,20 @@ impl super::Api for ApiImpl { } } - fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { - let ret_code = unsafe { + fn set_storage(key: &[u8], value: &[u8]) { + unsafe { sys::set_storage( + key.as_ptr(), + key.len() as u32, + value.as_ptr(), + value.len() as u32, + ) + }; + } + + fn set_storage_v1(key: &[u8], encoded_value: &[u8]) -> Option { + let ret_code = unsafe { + sys::v1::set_storage( key.as_ptr(), key.len() as u32, encoded_value.as_ptr(), @@ -384,28 +588,32 @@ impl super::Api for ApiImpl { ret_code.into() } - fn clear_storage(key: &[u8]) -> Option { - let ret_code = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; + fn set_storage_v2(key: &[u8], encoded_value: &[u8]) -> Option { + let ret_code = unsafe { + sys::v2::set_storage( + key.as_ptr(), + key.len() as u32, + encoded_value.as_ptr(), + encoded_value.len() as u32, + ) + }; ret_code.into() } - #[inline(always)] - 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( - key.as_ptr(), - key.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - } - }; - extract_from_slice(output, output_len as usize); + + fn clear_storage(key: &[u8]) { + unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; + } + + fn clear_storage_v1(key: &[u8]) -> Option { + let ret_code = unsafe { sys::v1::clear_storage(key.as_ptr(), key.len() as u32) }; ret_code.into() } + impl_get_storage!(get_storage, sys::get_storage); + + impl_get_storage!(get_storage_v1, sys::v1::get_storage); + #[inline(always)] fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { let mut output_len = output.len() as u32; @@ -423,15 +631,24 @@ impl super::Api for ApiImpl { ret_code.into() } - fn storage_contains(key: &[u8]) -> Option { + fn contains_storage(key: &[u8]) -> Option { let ret_code = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; ret_code.into() } + fn contains_storage_v1(key: &[u8]) -> Option { + let ret_code = unsafe { sys::v1::contains_storage(key.as_ptr(), key.len() as u32) }; + ret_code.into() + } + fn terminate(beneficiary: &[u8]) -> ! { unsafe { sys::terminate(beneficiary.as_ptr()) } } + fn terminate_v1(beneficiary: &[u8]) -> ! { + unsafe { sys::v1::terminate(beneficiary.as_ptr()) } + } + #[inline(always)] fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 { let mut output_len = output.len() as u32; @@ -469,14 +686,8 @@ impl super::Api for ApiImpl { } impl_wrapper_for! { - caller, - block_number, - address, - balance, - gas_left, - value_transferred, - now, - minimum_balance, + () => [caller, block_number, address, balance, gas_left, value_transferred, now, minimum_balance], + (v1, "_v1") => [gas_left], } #[inline(always)] @@ -488,6 +699,11 @@ impl super::Api for ApiImpl { extract_from_slice(output, output_len as usize); } + fn weight_to_fee_v1( ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { + todo!() + } + + impl_hash_fn!(sha2_256, 32); impl_hash_fn!(keccak_256, 32); impl_hash_fn!(blake2_256, 32); @@ -547,4 +763,31 @@ impl super::Api for ApiImpl { let mut output_len = output.len() as u32; unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } } + + fn account_reentrance_count(account: &[u8]) -> u32 { + todo!() + } + + fn add_delegate_dependency(code_hash: &[u8]) { + todo!() + } + + fn remove_delegate_dependency(code_hash: &[u8]) { + todo!() + } + + fn instantiation_nonce() -> u64 { + todo!() + } + + fn reentrance_count() -> u32 { todo!() } + + fn xcm_execute( msg: &[u8], output: &mut &mut [u8]) -> Result { + todo!() + } + + fn xcm_send( dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result { + todo!() + } + } From eefde89dee6287f4ce7e1a7aafab4e1121ab13cc Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 12:19:04 +0100 Subject: [PATCH 35/81] sort & clean up --- substrate/frame/contracts/uapi/src/api.rs | 46 ++- substrate/frame/contracts/uapi/src/lib.rs | 2 +- substrate/frame/contracts/uapi/src/wasm32.rs | 352 +++++++++++-------- 3 files changed, 229 insertions(+), 171 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 77f0ac93e78da..2f2d4201e82d6 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -42,7 +42,8 @@ pub trait Api { /// /// # Parameters /// - /// - `account`: The contract address. Should be decodable as an `T::AccountId`. Traps otherwise. + /// - `account`: The contract address. Should be decodable as an `T::AccountId`. Traps + /// otherwise. /// /// # Return /// @@ -66,7 +67,8 @@ pub trait Api { /// /// # Parameters /// - /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps otherwise. + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps + /// otherwise. #[deprecated(note = "Unstable")] fn add_delegate_dependency(code_hash: &[u8]); @@ -123,7 +125,9 @@ pub trait Api { /// otherwise. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a `Option`. Traps otherwise. Passing `None` means setting no specific limit for the call, which implies storage usage up to the limit of the parent call. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a + /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for + /// the call, which implies storage usage up to the limit of the parent call. /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. /// Traps otherwise. /// - `input`: The input data buffer used to call the contract. @@ -450,13 +454,16 @@ pub trait Api { /// - `code_hash`: The hash of the code to be instantiated. /// - `ref_time_limit`: how much *ref_time* Weight to devote to the execution. /// - `proof_size_limit`: how much *proof_size* Weight to devote to the execution. - /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a `Option`. Traps otherwise. Passing `None` means setting no specific limit for the call, which implies storage usage up to the limit of the parent call. - /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. Traps otherwise. + /// - `deposit`: The storage deposit limit for instantiation. Should be decodable as a + /// `Option`. Traps otherwise. Passing `None` means setting no specific limit for + /// the call, which implies storage usage up to the limit of the parent call. + /// - `value`: The value to transfer into the contract. Should be decodable as a `T::Balance`. + /// Traps otherwise. /// - `input`: The input data buffer. /// - `address`: A reference to the address buffer to write the address of the contract. If /// `None` is provided then the output buffer is not copied. - /// - `output`: A reference to the return value buffer to write the constructor output - /// buffer. If `None` is provided then the output buffer is not copied. + /// - `output`: A reference to the return value buffer to write the constructor output buffer. + /// If `None` is provided then the output buffer is not copied. /// - `salt`: The salt bytes to use for this instantiation. /// /// # Errors @@ -543,7 +550,8 @@ pub trait Api { /// /// # Parameters /// - /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps otherwise. + /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps + /// otherwise. #[deprecated(note = "Unstable")] fn remove_delegate_dependency(code_hash: &[u8]); @@ -587,7 +595,8 @@ pub trait Api { /// /// # Parameters /// - /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps otherwise. + /// - `code_hash`: The hash of the new code. Should be decodable as an `T::Hash`. Traps + /// otherwise. /// /// # Errors /// @@ -597,8 +606,8 @@ pub trait Api { /// Set the value at the given key in the contract storage. /// /// Equivalent to [`Self::set_storage_1`] version with the - /// exception of the return type. Still a valid thing to call for fixed sized storage key, when not interested in the return - /// value. + /// exception of the return type. Still a valid thing to call for fixed sized storage key, when + /// not interested in the return value. fn set_storage(key: &[u8], value: &[u8]); /// Set the value at the given key in the contract storage. @@ -710,7 +719,7 @@ pub trait Api { /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. /// - `output`: A reference to the output data buffer to write the price. #[deprecated(note = "Unstable, use `weight_to_fee` instead")] - fn weight_to_fee_v1( ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); + fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); /// Execute an XCM program locally, using the contract's address as the origin. /// This is equivalent to dispatching `pallet_xcm::execute` through call_runtime, except that @@ -718,7 +727,8 @@ pub trait Api { /// /// # Parameters /// - /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps otherwise. + /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps + /// otherwise. /// - `output`: A reference to the output data buffer to write the [`xcm::prelude::Outcome`] /// /// # Return @@ -726,7 +736,7 @@ pub trait Api { /// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM /// execution fails, `ReturnCode::XcmExecutionFailed` is returned #[deprecated(note = "Unstable")] - fn xcm_execute( msg: &[u8], output: &mut &mut [u8]) -> Result; + fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result; /// Send an XCM program from the contract to the specified destination. /// This is equivalent to dispatching `pallet_xcm::send` through `call_runtime`, except that @@ -734,8 +744,10 @@ pub trait Api { /// /// # Parameters /// - /// - `dest`: The XCM destination, should be decodable as [`xcm::prelude::VersionedMultiLocation`], traps otherwise. - /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps otherwise. + /// - `dest`: The XCM destination, should be decodable as + /// [`xcm::prelude::VersionedMultiLocation`], traps otherwise. + /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps + /// otherwise. /// - `output`: A reference to the output data buffer to write the [`xcm::v3::XcmHash`] /// /// # Return @@ -743,5 +755,5 @@ pub trait Api { /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. #[deprecated(note = "Unstable")] - fn xcm_send( dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result; + fn xcm_send(dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result; } diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index e34d969cbdc80..add4b90b535e9 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -167,6 +167,6 @@ fn ptr_and_len_from_slice(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { fn ptr_from_slice(data: &Option<&[u8]>) -> *const u8 { match data { Some(ref data) => data.as_ptr(), - None => SENTINEL as _ + None => SENTINEL as _, } } diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index ac0d4606ef556..59156750e24c4 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -11,9 +11,10 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#![allow(unused_variables)] +// #![allow(unused_variables)] use super::{ - extract_from_slice, ptr_from_slice, ptr_and_len_from_slice, Api, CallFlags, Result, ReturnCode, ReturnFlags, + extract_from_slice, ptr_and_len_from_slice, ptr_from_slice, Api, CallFlags, Result, ReturnCode, + ReturnFlags, }; mod sys { @@ -21,17 +22,16 @@ mod sys { #[link(wasm_import_module = "seal0")] extern "C" { - pub fn terminate(beneficiary_ptr: *const u8) -> !; + pub fn account_reentrance_count(account_ptr: *const u8) -> u32; - pub fn get_storage( - key_ptr: *const u8, - key_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> ReturnCode; - pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; + pub fn add_delegate_dependency(code_hash_ptr: *const u8); + + pub fn address(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn balance(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn block_number(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; pub fn call( callee_ptr: *const u8, gas: u64, @@ -42,43 +42,6 @@ mod sys { output_len_ptr: *mut u32, ) -> ReturnCode; - pub fn set_storage( - key_ptr: *const u8, - key_len: u32, - value_ptr: *const u8, - value_len: u32, - ) -> ReturnCode; - - pub fn instantiate( - code_hash_ptr: *const u8, - gas: u64, - value_ptr: *const u8, - input_ptr: *const u8, - input_len: u32, - address_ptr: *mut u8, - address_len_ptr: *mut u32, - output_ptr: *mut u8, - output_len_ptr: *mut u32, - salt_ptr: *const u8, - salt_len: u32, - ) -> ReturnCode; - - pub fn transfer( - account_id_ptr: *const u8, - account_id_len: u32, - transferred_value_ptr: *const u8, - transferred_value_len: u32, - ) -> ReturnCode; - - pub fn deposit_event( - topics_ptr: *const u8, - topics_len: u32, - data_ptr: *const u8, - data_len: u32, - ); - - pub fn caller_is_root() -> ReturnCode; - pub fn call_chain_extension( func_id: u32, input_ptr: *const u8, @@ -87,29 +50,15 @@ mod sys { output_len_ptr: *mut u32, ) -> ReturnCode; - pub fn input(buf_ptr: *mut u8, buf_len_ptr: *mut u32); - pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32) -> !; + pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode; pub fn caller(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn block_number(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn address(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn balance(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn weight_to_fee(gas: u64, output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn value_transferred(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn now(output_ptr: *mut u8, output_len_ptr: *mut u32); - pub fn minimum_balance(output_ptr: *mut u8, output_len_ptr: *mut u32); - - pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); - pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); - pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); - pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); - - pub fn is_contract(account_id_ptr: *const u8) -> ReturnCode; pub fn caller_is_origin() -> ReturnCode; - pub fn set_code_hash(code_hash_ptr: *const u8) -> ReturnCode; + pub fn caller_is_root() -> ReturnCode; + + pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; pub fn code_hash( account_id_ptr: *const u8, @@ -117,7 +66,7 @@ mod sys { output_len_ptr: *mut u32, ) -> ReturnCode; - pub fn own_code_hash(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; pub fn delegate_call( flags: u32, @@ -128,6 +77,13 @@ mod sys { output_len_ptr: *mut u32, ) -> ReturnCode; + pub fn deposit_event( + topics_ptr: *const u8, + topics_len: u32, + data_ptr: *const u8, + data_len: u32, + ); + pub fn ecdsa_recover( signature_ptr: *const u8, message_hash_ptr: *const u8, @@ -136,7 +92,64 @@ mod sys { pub fn ecdsa_to_eth_address(public_key_ptr: *const u8, output_ptr: *mut u8) -> ReturnCode; - /// which is unsafe and normally is not available on production chains. + pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn get_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; + + pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); + + pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); + + pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); + + pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, output_ptr: *mut u8); + + pub fn input(buf_ptr: *mut u8, buf_len_ptr: *mut u32); + + pub fn instantiate( + code_hash_ptr: *const u8, + gas: u64, + value_ptr: *const u8, + input_ptr: *const u8, + input_len: u32, + address_ptr: *mut u8, + address_len_ptr: *mut u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + salt_ptr: *const u8, + salt_len: u32, + ) -> ReturnCode; + + pub fn instantiation_nonce() -> u64; + + pub fn is_contract(account_id_ptr: *const u8) -> ReturnCode; + + pub fn minimum_balance(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn now(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn own_code_hash(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn reentrance_count() -> u32; + + pub fn remove_delegate_dependency(code_hash_ptr: *const u8); + + pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32) -> !; + + pub fn set_code_hash(code_hash_ptr: *const u8) -> ReturnCode; + + pub fn set_storage( + key_ptr: *const u8, + key_len: u32, + value_ptr: *const u8, + value_len: u32, + ) -> ReturnCode; + pub fn sr25519_verify( signature_ptr: *const u8, public_key_ptr: *const u8, @@ -151,7 +164,27 @@ mod sys { out_len_ptr: *mut u32, ) -> ReturnCode; - pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> ReturnCode; + pub fn terminate(beneficiary_ptr: *const u8) -> !; + + pub fn transfer( + account_id_ptr: *const u8, + account_id_len: u32, + transferred_value_ptr: *const u8, + transferred_value_len: u32, + ) -> ReturnCode; + + pub fn value_transferred(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn weight_to_fee(gas: u64, output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn xcm_execute(msg_ptr: *const u8, msg_len: u32, output_ptr: *mut u8) -> ReturnCode; + + pub fn xcm_send( + dest_ptr: *const u8, + msg_ptr: *const u8, + msg_len: u32, + output_ptr: *mut u8, + ) -> ReturnCode; } pub mod v1 { @@ -159,7 +192,29 @@ mod sys { #[link(wasm_import_module = "seal1")] extern "C" { - pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32); + pub fn call( + flags: u32, + callee_ptr: *const u8, + gas: u64, + transferred_value_ptr: *const u8, + input_data_ptr: *const u8, + input_data_len: u32, + output_ptr: *mut u8, + output_len_ptr: *mut u32, + ) -> ReturnCode; + + pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; + + pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; + + pub fn gas_left(output_ptr: *mut u8, output_len_ptr: *mut u32); + + pub fn get_storage( + key_ptr: *const u8, + key_len: u32, + out_ptr: *mut u8, + out_len_ptr: *mut u32, + ) -> ReturnCode; pub fn instantiate( code_hash_ptr: *const u8, @@ -184,27 +239,12 @@ mod sys { pub fn terminate(beneficiary_ptr: *const u8) -> !; - pub fn call( - flags: u32, - callee_ptr: *const u8, - gas: u64, - transferred_value_ptr: *const u8, - input_data_ptr: *const u8, - input_data_len: u32, + pub fn weight_to_fee( + ref_time_limit: u64, + proof_time_limit: u64, output_ptr: *mut u8, output_len_ptr: *mut u32, - ) -> ReturnCode; - - pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; - - pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> ReturnCode; - - pub fn get_storage( - key_ptr: *const u8, - key_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> ReturnCode; + ); } } @@ -295,48 +335,48 @@ macro_rules! impl_hash_fn { } macro_rules! impl_legacy_instantiate { - ($fn_name:ident, $sys_fn:path) => { + ($fn_name:ident, $sys_fn:path) => { #[inline(always)] - fn $fn_name( - code_hash: &[u8], - gas: u64, - value: &[u8], - input: &[u8], - mut address: Option<&mut [u8]>, - mut output: Option<&mut [u8]>, - salt: &[u8], + fn $fn_name( + code_hash: &[u8], + gas: u64, + value: &[u8], + input: &[u8], + mut address: Option<&mut [u8]>, + mut output: Option<&mut [u8]>, + salt: &[u8], ) -> Result { - let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); - let ret_code = unsafe { - $sys_fn( - code_hash.as_ptr(), - gas, - value.as_ptr(), - input.as_ptr(), - input.len() as u32, - address_ptr, - &mut address_len, - output_ptr, - &mut output_len, - salt.as_ptr(), - salt.len() as u32, - ) - }; - - if let Some(ref mut address) = address { - extract_from_slice(address, address_len as usize); - } - if let Some(ref mut output) = output { - extract_from_slice(output, output_len as usize); - } - ret_code.into() - } - }; + let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let ret_code = unsafe { + $sys_fn( + code_hash.as_ptr(), + gas, + value.as_ptr(), + input.as_ptr(), + input.len() as u32, + address_ptr, + &mut address_len, + output_ptr, + &mut output_len, + salt.as_ptr(), + salt.len() as u32, + ) + }; + + if let Some(ref mut address) = address { + extract_from_slice(address, address_len as usize); + } + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into() + } + }; } macro_rules! impl_get_storage { - ($fn_name:ident, $sys_get_storage:path) => { + ($fn_name:ident, $sys_get_storage:path) => { #[inline(always)] fn $fn_name(key: &[u8], output: &mut &mut [u8]) -> Result { let mut output_len = output.len() as u32; @@ -353,16 +393,13 @@ macro_rules! impl_get_storage { extract_from_slice(output, output_len as usize); ret_code.into() } - } + }; } - pub enum ApiImpl {} impl Api for ApiImpl { - impl_legacy_instantiate!(instantiate, sys::instantiate); - impl_legacy_instantiate!(instantiate_v1, sys::v1::instantiate); #[inline(always)] @@ -412,7 +449,6 @@ impl Api for ApiImpl { ret_code.into() } - fn call( callee: &[u8], gas: u64, @@ -508,7 +544,7 @@ impl Api for ApiImpl { } ret_code.into() - } + } fn caller_is_root() -> u32 { unsafe { sys::caller_is_root() }.into_u32() @@ -567,12 +603,7 @@ impl Api for ApiImpl { fn set_storage(key: &[u8], value: &[u8]) { unsafe { - sys::set_storage( - key.as_ptr(), - key.len() as u32, - value.as_ptr(), - value.len() as u32, - ) + sys::set_storage(key.as_ptr(), key.len() as u32, value.as_ptr(), value.len() as u32) }; } @@ -600,7 +631,6 @@ impl Api for ApiImpl { ret_code.into() } - fn clear_storage(key: &[u8]) { unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; } @@ -699,10 +729,20 @@ impl Api for ApiImpl { extract_from_slice(output, output_len as usize); } - fn weight_to_fee_v1( ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { - todo!() - } - + fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]) { + let mut output_len = output.len() as u32; + { + unsafe { + sys::v1::weight_to_fee( + ref_time_limit, + proof_size_limit, + output.as_mut_ptr(), + &mut output_len, + ) + }; + } + extract_from_slice(output, output_len as usize); + } impl_hash_fn!(sha2_256, 32); impl_hash_fn!(keccak_256, 32); @@ -765,29 +805,35 @@ impl Api for ApiImpl { } fn account_reentrance_count(account: &[u8]) -> u32 { - todo!() + unsafe { sys::account_reentrance_count(account.as_ptr()) } } fn add_delegate_dependency(code_hash: &[u8]) { - todo!() + unsafe { sys::add_delegate_dependency(code_hash.as_ptr()) } } fn remove_delegate_dependency(code_hash: &[u8]) { - todo!() + unsafe { sys::remove_delegate_dependency(code_hash.as_ptr()) } } fn instantiation_nonce() -> u64 { - todo!() + unsafe { sys::instantiation_nonce() } } - fn reentrance_count() -> u32 { todo!() } - - fn xcm_execute( msg: &[u8], output: &mut &mut [u8]) -> Result { - todo!() + fn reentrance_count() -> u32 { + unsafe { sys::reentrance_count() } } - fn xcm_send( dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result { - todo!() + fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result { + let ret_code = + unsafe { sys::xcm_execute(msg.as_ptr(), msg.len() as _, output.as_mut_ptr()) }; + ret_code.into() } + fn xcm_send(dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result { + let ret_code = unsafe { + sys::xcm_send(dest.as_ptr(), msg.as_ptr(), msg.len() as _, output.as_mut_ptr()) + }; + ret_code.into() + } } From 89aa067d2e3d8fcedc19813c89644c90ecc92f7c Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 12:22:07 +0100 Subject: [PATCH 36/81] fix fixture --- substrate/frame/contracts/fixtures/contracts/call.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index 31d40913399b1..3709c1075b57f 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -37,7 +37,7 @@ pub fn call() { api::input(&mut &mut buffer[..]); // Call the callee - api::call( + api::call_v1( CallFlags::empty(), &buffer[callee_addr], 0u64, // How much gas to devote for the execution. 0 = all. From 47fba865285405c997974c5f8c3b7b0d28960395 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 12:53:15 +0100 Subject: [PATCH 37/81] Fix doc tests --- substrate/frame/contracts/proc-macro/src/lib.rs | 6 +++--- substrate/frame/contracts/src/wasm/runtime.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/substrate/frame/contracts/proc-macro/src/lib.rs b/substrate/frame/contracts/proc-macro/src/lib.rs index 1f336faaa26e3..4ef02497b8ee2 100644 --- a/substrate/frame/contracts/proc-macro/src/lib.rs +++ b/substrate/frame/contracts/proc-macro/src/lib.rs @@ -550,7 +550,7 @@ fn expand_env(def: &EnvDef, docs: bool) -> TokenStream2 { /// consumed by humans through rustdoc. #[cfg(doc)] pub mod api_doc { - use super::{TrapReason, ReturnCode}; + use super::{TrapReason, ReturnErrorCode}; #docs } } @@ -767,7 +767,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) /// #[define_env] /// pub mod some_env { /// #[version(2)] -/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { /// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) /// } /// @@ -793,7 +793,7 @@ fn expand_functions(def: &EnvDef, expand_blocks: bool, host_state: TokenStream2) /// pub mod some_env { /// #[version(1)] /// #[prefixed_alias] -/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { +/// fn foo(ctx: _, memory: _, key_ptr: u32, value_ptr: u32, value_len: u32) -> Result { /// ctx.some_host_fn(KeyType::Fix, key_ptr, value_ptr, value_len).map(|_| ()) /// } /// diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index b2aa1e7635e33..88f2822eba858 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -716,7 +716,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { Ok(()) } - /// Fallible conversion of `DispatchError` to `ReturnCode`. + /// Fallible conversion of `DispatchError` to `ReturnErrorCode`. fn err_into_return_code(from: DispatchError) -> Result { use ReturnErrorCode::*; @@ -732,7 +732,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { } } - /// Fallible conversion of a `ExecResult` to `ReturnCode`. + /// Fallible conversion of a `ExecResult` to `ReturnErrorCode`. fn exec_into_return_code(from: ExecResult) -> Result { use crate::exec::ErrorOrigin::Callee; From 6cfb4415dbff1562a96a5395c965bc7eee0a64e4 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 13:03:56 +0100 Subject: [PATCH 38/81] fix comments --- substrate/frame/contracts/src/benchmarking/mod.rs | 1 - substrate/frame/contracts/src/wasm/runtime.rs | 12 ++++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/substrate/frame/contracts/src/benchmarking/mod.rs b/substrate/frame/contracts/src/benchmarking/mod.rs index 82ac82e71fd01..ac5787e234041 100644 --- a/substrate/frame/contracts/src/benchmarking/mod.rs +++ b/substrate/frame/contracts/src/benchmarking/mod.rs @@ -220,7 +220,6 @@ benchmarks! { #[pov_mode = Measured] on_initialize_per_trie_key { let k in 0..1024; - println!("here"); let instance = Contract::::with_storage(WasmModule::dummy(), k, T::Schedule::get().limits.payload_len)?; instance.info()?.queue_trie_for_deletion(); }: { diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 88f2822eba858..7886bc41e9ae3 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -1162,6 +1162,12 @@ pub mod env { /// Make a call to another contract. /// See [`pallet_contracts_uapi::Api::call`]. + /// + /// # Note + /// + /// The values `_callee_len` and `_value_len` are ignored because the encoded sizes of those + /// types are fixed through [`codec::MaxEncodedLen`]. The fields exist for backwards + /// compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn call( ctx: _, @@ -1284,6 +1290,12 @@ pub mod env { /// Instantiate a contract with the specified code hash. /// See [`pallet_contracts_uapi::Api::instantiate`]. + /// + /// # Note + /// + /// The values `_code_hash_len` and `_value_len` are ignored because the encoded sizes + /// of those types are fixed through [`codec::MaxEncodedLen`]. The fields exist + /// for backwards compatibility. Consider switching to the newest version of this function. #[prefixed_alias] fn instantiate( ctx: _, From d99968284bac5ff1c17b40c89e23a314d182b731 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 13:21:10 +0100 Subject: [PATCH 39/81] fix cargo doc links --- substrate/frame/contracts/uapi/src/api.rs | 60 +++++++++++------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 2f2d4201e82d6..9e44cc499b2ec 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -139,10 +139,10 @@ pub trait Api { /// An error means that the call wasn't successful output buffer is returned unless /// stated otherwise. /// - /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. - /// - [`ReturnErrorCode::CalleeTrapped`] - /// - [`ReturnErrorCode::TransferFailed`] - /// - [`ReturnErrorCode::NotCallable`] + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] #[deprecated(note = "Unstable, use `call_v1` instead")] fn call_v2( flags: CallFlags, @@ -271,7 +271,7 @@ pub trait Api { /// /// # Errors /// - /// - [`ReturnErrorCode::CodeNotFound`] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] fn code_hash(account_id: &[u8], output: &mut [u8]) -> Result; /// Checks whether there is a value stored under the given key. @@ -311,9 +311,9 @@ pub trait Api { /// An error means that the call wasn't successful and no output buffer is returned unless /// stated otherwise. /// - /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. - /// - [`ReturnErrorCode::CalleeTrapped`] - /// - [`ReturnErrorCode::CodeNotFound`] + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] fn delegate_call( flags: CallFlags, code_hash: &[u8], @@ -344,7 +344,7 @@ pub trait Api { /// /// # Errors /// - /// - [`ReturnErrorCode::EcdsaRecoveryFailed`] + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] fn ecdsa_recover( signature: &[u8; 65], message_hash: &[u8; 32], @@ -361,7 +361,7 @@ pub trait Api { /// /// # Errors /// - /// - [`ReturnErrorCode::EcdsaRecoveryFailed`] + /// - [EcdsaRecoveryFailed][`crate::ReturnErrorCode::EcdsaRecoveryFailed] fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; /// Stores the weight left into the supplied buffer. @@ -397,7 +397,7 @@ pub trait Api { /// /// # Errors /// - /// [`ReturnErrorCode::KeyNotFound`] + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] fn get_storage_v1(key: &[u8], output: &mut &mut [u8]) -> Result; hash_fn!(sha2_256, 32); @@ -468,16 +468,16 @@ pub trait Api { /// /// # Errors /// - /// Please consult the [`ReturnErrorCode`] enum declaration for more information on those - /// errors. Here we only note things specific to this function. + /// Please consult the [ReturnErrorCode][`crate::ReturnErrorCode`] enum declaration for more + /// information on those errors. Here we only note things specific to this function. /// /// An error means that the account wasn't created and no address or output buffer /// is returned unless stated otherwise. /// - /// - [`ReturnErrorCode::CalleeReverted`]: Output buffer is returned. - /// - [`ReturnErrorCode::CalleeTrapped`] - /// - [`ReturnErrorCode::TransferFailed`] - /// - [`ReturnErrorCode::CodeNotFound`] + /// - [CalleeReverted][`crate::ReturnErrorCode::CalleeReverted]: Output buffer is returned. + /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] #[deprecated(note = "Unstable, use `instantiate_v1` instead")] fn instantiate_v2( code_hash: &[u8], @@ -600,12 +600,12 @@ pub trait Api { /// /// # Errors /// - /// - [`ReturnErrorCode::CodeNotFound`] + /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] fn set_code_hash(code_hash: &[u8]) -> Result; /// Set the value at the given key in the contract storage. /// - /// Equivalent to [`Self::set_storage_1`] version with the + /// Equivalent to [`Self::set_storage_v1`] version with the /// exception of the return type. Still a valid thing to call for fixed sized storage key, when /// not interested in the return value. fn set_storage(key: &[u8], value: &[u8]); @@ -640,7 +640,7 @@ pub trait Api { /// /// # Errors /// - /// - [`ReturnErrorCode::Sr25519VerifyFailed`] + /// - [Sr25519VerifyFailed][`crate::ReturnErrorCode::Sr25519VerifyFailed] fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result; /// Retrieve and remove the value under the given key from storage. @@ -651,7 +651,7 @@ pub trait Api { /// /// # Errors /// - /// [`ReturnErrorCode::KeyNotFound`] + /// [KeyNotFound][`crate::ReturnErrorCode::KeyNotFound] fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result; /// Transfer some amount of funds into the specified account. @@ -664,7 +664,7 @@ pub trait Api { /// /// # Errors /// - /// - [`ReturnErrorCode::TransferFailed`] + /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] fn transfer(account_id: &[u8], value: &[u8]) -> Result; /// Remove the calling account and transfer remaining balance. @@ -727,9 +727,9 @@ pub trait Api { /// /// # Parameters /// - /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps - /// otherwise. - /// - `output`: A reference to the output data buffer to write the [`xcm::prelude::Outcome`] + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// - `output`: A reference to the output data buffer to write the [Outcome](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/enum.Outcome.html) /// /// # Return /// @@ -744,11 +744,11 @@ pub trait Api { /// /// # Parameters /// - /// - `dest`: The XCM destination, should be decodable as - /// [`xcm::prelude::VersionedMultiLocation`], traps otherwise. - /// - `msg`: The message, should be decodable as a [`xcm::prelude::VersionedXcm`], traps - /// otherwise. - /// - `output`: A reference to the output data buffer to write the [`xcm::v3::XcmHash`] + /// - `dest`: The XCM destination, should be decodable as [VersionedMultiLocation](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedMultiLocation.html), + /// traps otherwise. + /// - `msg`: The message, should be decodable as a [VersionedXcm](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/enum.VersionedXcm.html), + /// traps otherwise. + /// - `output`: A reference to the output data buffer to write the [XcmHash](https://paritytech.github.io/polkadot-sdk/master/staging_xcm/v3/type.XcmHash.html) /// /// # Return /// From 3336bb60b743b7ee1d7753e61ad5cafaafded6fe Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 13:35:50 +0100 Subject: [PATCH 40/81] Compile with warning flags --- substrate/frame/contracts/fixtures/build.rs | 14 ++++++++++---- .../frame/contracts/fixtures/contracts/call.rs | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 500f56bbcab8e..7ed825a0b8336 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -134,12 +134,18 @@ codegen-units = 1 /// Invoke `cargo build` to compile the contracts. fn invoke_build(current_dir: &Path) -> Result<()> { + let encoded_rustflags = [ + "-Clink-arg=-zstack-size=65536", + "-Clink-arg=--import-memory", + "-Clinker-plugin-lto", + "-Ctarget-cpu=mvp", + "-Dwarnings", + ] + .join("\x1f"); + let build_res = Command::new(env::var("CARGO")?) .current_dir(current_dir) - .env( - "CARGO_ENCODED_RUSTFLAGS", - "-C\x1flink-arg=-zstack-size=65536\x1f-C\x1flink-arg=--import-memory\x1f-Clinker-plugin-lto\x1f-C\x1ftarget-cpu=mvp", - ) + .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) .arg("build") .arg("--release") .arg("--target=wasm32-unknown-unknown") // TODO pass risc-v target here as well diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index 3709c1075b57f..d2a4600436596 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -44,5 +44,5 @@ pub fn call() { &buffer[value], &buffer[callee_input], None, - ); + ).unwrap(); } From ea678198808da811102f454afe180444fdf1f34d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 13:40:56 +0100 Subject: [PATCH 41/81] fix some doc --- substrate/frame/contracts/uapi/src/api.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 9e44cc499b2ec..1a0454004dfbe 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -93,7 +93,7 @@ pub trait Api { /// Make a call to another contract. /// /// This is equivalent to calling the newer version of this function with - /// `flags` set to `ALLOW_REENTRY`. See the newer version for documentation. + /// `flags` set to [`CallFlags::ALLOW_REENTRY`]. See the newer version for documentation. #[deprecated(note = "Deprecated, use newer version instead")] fn call( callee: &[u8], @@ -105,7 +105,7 @@ pub trait Api { /// Make a call to another contract. /// - /// Equivalent to the newer [`seal2`][`Api::call_v2`] version but works with + /// Equivalent to the newer [`Self::call_v2`] version but works with /// *ref_time* Weight only fn call_v1( flags: CallFlags, From b976c17faef3f5f7c7e875f2289dd8cefad02d1b Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 13:48:21 +0100 Subject: [PATCH 42/81] add more docs --- substrate/frame/contracts/uapi/src/riscv32.rs | 1 + substrate/frame/contracts/uapi/src/wasm32.rs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/substrate/frame/contracts/uapi/src/riscv32.rs b/substrate/frame/contracts/uapi/src/riscv32.rs index f727f4a4d071f..52b58056828f4 100644 --- a/substrate/frame/contracts/uapi/src/riscv32.rs +++ b/substrate/frame/contracts/uapi/src/riscv32.rs @@ -11,6 +11,7 @@ // 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. +// TODO: bring up to date with wasm32.rs use super::{extract_from_slice, Result, ReturnCode}; use crate::{engine::on_chain::EncodeScope, ReturnFlags}; diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 59156750e24c4..4008c5a320277 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -292,6 +292,7 @@ mod sys { } } +/// A macro to implement all Api functions with a signature of `fn(&mut &mut [u8])`. macro_rules! impl_wrapper_for { (@impl_fn $( $mod:ident )::*, $suffix:literal, $name:ident) => { paste::paste! { @@ -318,6 +319,7 @@ macro_rules! impl_wrapper_for { }; } +/// A macro to implement all the hash functions Apis. macro_rules! impl_hash_fn { ( $name:ident, $bytes_result:literal ) => { paste::item! { @@ -334,6 +336,7 @@ macro_rules! impl_hash_fn { }; } +/// A macro to implement the legacy instantiate functions. macro_rules! impl_legacy_instantiate { ($fn_name:ident, $sys_fn:path) => { #[inline(always)] @@ -375,6 +378,7 @@ macro_rules! impl_legacy_instantiate { }; } +/// A macro to implement the get_storage functions. macro_rules! impl_get_storage { ($fn_name:ident, $sys_get_storage:path) => { #[inline(always)] @@ -641,7 +645,6 @@ impl Api for ApiImpl { } impl_get_storage!(get_storage, sys::get_storage); - impl_get_storage!(get_storage_v1, sys::v1::get_storage); #[inline(always)] From 09e8a597595173900671caf77aefe7fdf40c7c6a Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 14:14:49 +0100 Subject: [PATCH 43/81] fix runtime-benchmarks build --- substrate/frame/contracts/src/benchmarking/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/src/benchmarking/mod.rs b/substrate/frame/contracts/src/benchmarking/mod.rs index ac5787e234041..c532b354ab640 100644 --- a/substrate/frame/contracts/src/benchmarking/mod.rs +++ b/substrate/frame/contracts/src/benchmarking/mod.rs @@ -33,7 +33,6 @@ use crate::{ migration::{ codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, v15, MigrationStep, }, - wasm::CallFlags, Pallet as Contracts, *, }; use codec::{Encode, MaxEncodedLen}; @@ -46,6 +45,7 @@ use frame_support::{ }; use frame_system::RawOrigin; use pallet_balances; +use pallet_contracts_uapi::CallFlags; use sp_runtime::traits::{Bounded, Hash}; use sp_std::prelude::*; use wasm_instrument::parity_wasm::elements::{BlockType, Instruction, ValueType}; From 84ba07b0c426005b12333b7baf4c496ec5d9e4df Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 14:52:11 +0100 Subject: [PATCH 44/81] Fix substrate-node --- .../runtimes/contracts/contracts-rococo/src/lib.rs | 10 +++++----- substrate/bin/node/runtime/src/lib.rs | 10 +++++----- substrate/frame/contracts/src/lib.rs | 7 ++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs index 9d6a53c5ed342..0b6e92c04a26d 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/src/lib.rs @@ -588,7 +588,7 @@ impl_runtime_apis! { gas_limit: Option, storage_deposit_limit: Option, input_data: Vec, - ) -> pallet_contracts_primitives::ContractExecResult { + ) -> pallet_contracts::ContractExecResult { let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); Contracts::bare_call( origin, @@ -608,10 +608,10 @@ impl_runtime_apis! { value: Balance, gas_limit: Option, storage_deposit_limit: Option, - code: pallet_contracts_primitives::Code, + code: pallet_contracts::Code, data: Vec, salt: Vec, - ) -> pallet_contracts_primitives::ContractInstantiateResult { + ) -> pallet_contracts::ContractInstantiateResult { let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); Contracts::bare_instantiate( origin, @@ -631,7 +631,7 @@ impl_runtime_apis! { code: Vec, storage_deposit_limit: Option, determinism: pallet_contracts::Determinism, - ) -> pallet_contracts_primitives::CodeUploadResult { + ) -> pallet_contracts::CodeUploadResult { Contracts::bare_upload_code( origin, code, @@ -643,7 +643,7 @@ impl_runtime_apis! { fn get_storage( address: AccountId, key: Vec, - ) -> pallet_contracts_primitives::GetStorageResult { + ) -> pallet_contracts::GetStorageResult { Contracts::get_storage(address, key) } } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index d7beb29becf40..e2746b1c35bc1 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2497,7 +2497,7 @@ impl_runtime_apis! { gas_limit: Option, storage_deposit_limit: Option, input_data: Vec, - ) -> pallet_contracts_primitives::ContractExecResult { + ) -> pallet_contracts::ContractExecResult { let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); Contracts::bare_call( origin, @@ -2517,10 +2517,10 @@ impl_runtime_apis! { value: Balance, gas_limit: Option, storage_deposit_limit: Option, - code: pallet_contracts_primitives::Code, + code: pallet_contracts::Code, data: Vec, salt: Vec, - ) -> pallet_contracts_primitives::ContractInstantiateResult + ) -> pallet_contracts::ContractInstantiateResult { let gas_limit = gas_limit.unwrap_or(RuntimeBlockWeights::get().max_block); Contracts::bare_instantiate( @@ -2541,7 +2541,7 @@ impl_runtime_apis! { code: Vec, storage_deposit_limit: Option, determinism: pallet_contracts::Determinism, - ) -> pallet_contracts_primitives::CodeUploadResult + ) -> pallet_contracts::CodeUploadResult { Contracts::bare_upload_code( origin, @@ -2554,7 +2554,7 @@ impl_runtime_apis! { fn get_storage( address: AccountId, key: Vec, - ) -> pallet_contracts_primitives::GetStorageResult { + ) -> pallet_contracts::GetStorageResult { Contracts::get_storage( address, key diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index cfd3c1c470454..a15006e6388c0 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -92,6 +92,8 @@ mod benchmarking; mod exec; mod gas; mod primitives; +pub use primitives::*; + mod schedule; mod storage; mod wasm; @@ -108,11 +110,6 @@ use crate::{ AccountIdOf, ErrorOrigin, ExecError, Executable, Ext, Key, MomentOf, Stack as ExecStack, }, gas::GasMeter, - primitives::{ - Code, CodeUploadResult, CodeUploadReturnValue, ContractAccessError, ContractExecResult, - ContractInstantiateResult, ContractResult, ExecReturnValue, GetStorageResult, - InstantiateReturnValue, StorageDeposit, - }, storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager}, wasm::{CodeInfo, WasmBlob}, }; From 153fe9f9f8398a7038bb8f99f6e247d482b0acbd Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 15:19:11 +0100 Subject: [PATCH 45/81] fix dep --- substrate/frame/contracts/mock-network/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/contracts/mock-network/src/tests.rs b/substrate/frame/contracts/mock-network/src/tests.rs index 320a0edf827c5..a66b2b0801961 100644 --- a/substrate/frame/contracts/mock-network/src/tests.rs +++ b/substrate/frame/contracts/mock-network/src/tests.rs @@ -18,7 +18,7 @@ use crate::{ parachain::{self, Runtime}, parachain_account_sovereign_account_id, - primitives::{AccountId, Code, CENTS}, + primitives::{AccountId, CENTS}, relay_chain, MockNet, ParaA, ParachainBalances, Relay, ALICE, BOB, INITIAL_BALANCE, }; use assert_matches::assert_matches; @@ -29,7 +29,7 @@ use frame_support::{ traits::{fungibles::Mutate, Currency}, }; use pallet_balances::{BalanceLock, Reasons}; -use pallet_contracts::{CollectEvents, DebugInfo, Determinism}; +use pallet_contracts::{Code, CollectEvents, DebugInfo, Determinism}; use pallet_contracts_fixtures::compile_module; use xcm::{v3::prelude::*, VersionedMultiLocation, VersionedXcm}; use xcm_simulator::TestExt; From 56c6f25053360d70e6eb028382e651581a45c968 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Fri, 24 Nov 2023 15:28:26 +0100 Subject: [PATCH 46/81] fix primitives --- substrate/frame/contracts/src/primitives.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/substrate/frame/contracts/src/primitives.rs b/substrate/frame/contracts/src/primitives.rs index 7ba4aa231d16a..ab73b28e8c49f 100644 --- a/substrate/frame/contracts/src/primitives.rs +++ b/substrate/frame/contracts/src/primitives.rs @@ -17,8 +17,6 @@ //! A crate that hosts a common definitions that are relevant for the pallet-contracts. -#![cfg_attr(not(feature = "std"), no_std)] - use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::weights::Weight; use pallet_contracts_uapi::ReturnFlags; From 992e967e0e630a26b2c99e59931ca2e2e4e958b2 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 14:32:39 +0100 Subject: [PATCH 47/81] fix wasm32 legacy --- substrate/frame/contracts/uapi/src/wasm32.rs | 127 ++++++++++++------- 1 file changed, 82 insertions(+), 45 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 4008c5a320277..6153930c688ff 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -34,8 +34,10 @@ mod sys { pub fn call( callee_ptr: *const u8, + callee_len: u32, gas: u64, - transferred_value_ptr: *const u8, + value_ptr: *const u8, + value_len: u32, input_data_ptr: *const u8, input_data_len: u32, output_ptr: *mut u8, @@ -113,8 +115,10 @@ mod sys { pub fn instantiate( code_hash_ptr: *const u8, + code_hash_len: u32, gas: u64, value_ptr: *const u8, + value_len: u32, input_ptr: *const u8, input_len: u32, address_ptr: *mut u8, @@ -336,48 +340,6 @@ macro_rules! impl_hash_fn { }; } -/// A macro to implement the legacy instantiate functions. -macro_rules! impl_legacy_instantiate { - ($fn_name:ident, $sys_fn:path) => { - #[inline(always)] - fn $fn_name( - code_hash: &[u8], - gas: u64, - value: &[u8], - input: &[u8], - mut address: Option<&mut [u8]>, - mut output: Option<&mut [u8]>, - salt: &[u8], - ) -> Result { - let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); - let ret_code = unsafe { - $sys_fn( - code_hash.as_ptr(), - gas, - value.as_ptr(), - input.as_ptr(), - input.len() as u32, - address_ptr, - &mut address_len, - output_ptr, - &mut output_len, - salt.as_ptr(), - salt.len() as u32, - ) - }; - - if let Some(ref mut address) = address { - extract_from_slice(address, address_len as usize); - } - if let Some(ref mut output) = output { - extract_from_slice(output, output_len as usize); - } - ret_code.into() - } - }; -} - /// A macro to implement the get_storage functions. macro_rules! impl_get_storage { ($fn_name:ident, $sys_get_storage:path) => { @@ -403,8 +365,81 @@ macro_rules! impl_get_storage { pub enum ApiImpl {} impl Api for ApiImpl { - impl_legacy_instantiate!(instantiate, sys::instantiate); - impl_legacy_instantiate!(instantiate_v1, sys::v1::instantiate); + #[inline(always)] + fn instantiate( + code_hash: &[u8], + gas: u64, + value: &[u8], + input: &[u8], + mut address: Option<&mut [u8]>, + mut output: Option<&mut [u8]>, + salt: &[u8], + ) -> Result { + let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let ret_code = unsafe { + sys::instantiate( + code_hash.as_ptr(), + code_hash.len() as u32, + gas, + value.as_ptr(), + value.len() as u32, + input.as_ptr(), + input.len() as u32, + address_ptr, + &mut address_len, + output_ptr, + &mut output_len, + salt.as_ptr(), + salt.len() as u32, + ) + }; + + if let Some(ref mut address) = address { + extract_from_slice(address, address_len as usize); + } + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into() + } + + #[inline(always)] + fn instantiate_v1( + code_hash: &[u8], + gas: u64, + value: &[u8], + input: &[u8], + mut address: Option<&mut [u8]>, + mut output: Option<&mut [u8]>, + salt: &[u8], + ) -> Result { + let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); + let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let ret_code = unsafe { + sys::v1::instantiate( + code_hash.as_ptr(), + gas, + value.as_ptr(), + input.as_ptr(), + input.len() as u32, + address_ptr, + &mut address_len, + output_ptr, + &mut output_len, + salt.as_ptr(), + salt.len() as u32, + ) + }; + + if let Some(ref mut address) = address { + extract_from_slice(address, address_len as usize); + } + if let Some(ref mut output) = output { + extract_from_slice(output, output_len as usize); + } + ret_code.into() + } #[inline(always)] fn instantiate_v2( @@ -465,8 +500,10 @@ impl Api for ApiImpl { unsafe { sys::call( callee.as_ptr(), + callee.len() as u32, gas, value.as_ptr(), + value.len() as u32, input_data.as_ptr(), input_data.len() as u32, output_ptr, From 77c23ba4730f630f1202d85cef1d13dd2a621bb0 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 16:44:05 +0100 Subject: [PATCH 48/81] Remove features std from fixtures --- substrate/frame/contracts/Cargo.toml | 1 - substrate/frame/contracts/fixtures/Cargo.toml | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/substrate/frame/contracts/Cargo.toml b/substrate/frame/contracts/Cargo.toml index 451d99bca8d52..417f719d803b7 100644 --- a/substrate/frame/contracts/Cargo.toml +++ b/substrate/frame/contracts/Cargo.toml @@ -83,7 +83,6 @@ std = [ "frame-system/std", "log/std", "pallet-balances?/std", - "pallet-contracts-fixtures/std", "pallet-contracts-proc-macro/full", "pallet-insecure-randomness-collective-flip/std", "pallet-proxy/std", diff --git a/substrate/frame/contracts/fixtures/Cargo.toml b/substrate/frame/contracts/fixtures/Cargo.toml index 6a6bed02056d0..257e01e50fa1f 100644 --- a/substrate/frame/contracts/fixtures/Cargo.toml +++ b/substrate/frame/contracts/fixtures/Cargo.toml @@ -9,8 +9,8 @@ description = "Fixtures for testing contracts pallet." [dependencies] wat = "1" -frame-system = { path = "../../system", default-features = false} -sp-runtime = { path = "../../../primitives/runtime", default-features = false} +frame-system = { path = "../../system" } +sp-runtime = { path = "../../../primitives/runtime" } anyhow = "1.0.0" [build-dependencies] @@ -21,7 +21,4 @@ twox-hash = "1.6.3" anyhow = "1.0.0" cfg-if = { version = "1.0", default-features = false } -[features] -default = [ "std" ] -std = [ "frame-system/std", "sp-runtime/std" ] From 01261c7bae0e42ae0bc493063a1e41f55a34bf4f Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 16:46:22 +0100 Subject: [PATCH 49/81] PR review remove unused cargo dep --- .../parachains/runtimes/contracts/contracts-rococo/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml index b85b654bd7585..f5626a90f3642 100644 --- a/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/contracts/contracts-rococo/Cargo.toml @@ -52,7 +52,6 @@ pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/ pallet-utility = { path = "../../../../../substrate/frame/utility", default-features = false} pallet-sudo = { path = "../../../../../substrate/frame/sudo", default-features = false} pallet-contracts = { path = "../../../../../substrate/frame/contracts", default-features = false} -pallet-contracts-uapi = { path = "../../../../../substrate/frame/contracts/uapi", default-features = false} # Polkadot pallet-xcm = { path = "../../../../../polkadot/xcm/pallet-xcm", default-features = false } From a9a8e5029dc167b9c050ca8a133962005404f879 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 17:33:46 +0100 Subject: [PATCH 50/81] PR review Make sure fixtures are formatted --- substrate/frame/contracts/fixtures/build.rs | 55 ++++++++++++++++--- .../contracts/fixtures/contracts/call.rs | 3 +- .../contracts/fixtures/contracts/dummy.rs | 6 +- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 7ed825a0b8336..baf5110730cb5 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -132,6 +132,24 @@ codegen-units = 1 fs::write(output_dir.join("Cargo.toml"), cargo_toml).map_err(Into::into) } +/// Invoke `cargo fmt` to check that fixtures files are formatted. +fn invoke_fmt(current_dir: &Path, src_dir: &Path) -> Result<()> { + let fmt_res = Command::new("rustup") + .current_dir(current_dir) + .args(&["run", "nightly", "cargo", "fmt", "--check"]) + .output() + .unwrap(); + + if fmt_res.status.success() { + return Ok(()) + } + + let stdout = String::from_utf8_lossy(&fmt_res.stdout); + eprintln!("{}", stdout); + eprintln!("Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt {}/*.rs`", src_dir.display()); + anyhow::bail!("Fixtures files are not formatted") +} + /// Invoke `cargo build` to compile the contracts. fn invoke_build(current_dir: &Path) -> Result<()> { let encoded_rustflags = [ @@ -146,9 +164,7 @@ fn invoke_build(current_dir: &Path) -> Result<()> { let build_res = Command::new(env::var("CARGO")?) .current_dir(current_dir) .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) - .arg("build") - .arg("--release") - .arg("--target=wasm32-unknown-unknown") // TODO pass risc-v target here as well + .args(&["build", "--release", "--target=wasm32-unknown-unknown"]) .output() .unwrap(); @@ -188,20 +204,43 @@ fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result Ok(()) } +/// Returns the root path of the wasm workspace. +fn find_workspace_root(current_dir: &Path) -> Option { + let mut current_dir = current_dir.to_path_buf(); + + while current_dir.parent().is_some() { + if current_dir.join("Cargo.toml").exists() { + let cargo_toml_contents = std::fs::read_to_string(current_dir.join("Cargo.toml")).ok()?; + if cargo_toml_contents.contains("[workspace]") { + return Some(current_dir); + } + } + + current_dir.pop(); + } + + None +} + + fn main() -> Result<()> { - let input_dir: PathBuf = ".".into(); + let input_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); + let src_dir = input_dir.join("contracts"); let out_dir: PathBuf = env::var("OUT_DIR")?.into(); + let workspace_root = find_workspace_root(&input_dir).expect("workspace root exists; qed"); - let entries = collect_entries(&input_dir.join("contracts").canonicalize()?, &out_dir); + let entries = collect_entries(&src_dir, &out_dir); if entries.is_empty() { return Ok(()); } let tmp_dir = tempfile::tempdir()?; + let tmp_dir_path = tmp_dir.path(); + fs::copy(workspace_root.join(".rustfmt.toml"), tmp_dir_path.join(".rustfmt.toml"))?; create_cargo_toml(&input_dir, entries.iter(), tmp_dir.path())?; - invoke_build(tmp_dir.path())?; - write_output(tmp_dir.path(), &out_dir, entries)?; + invoke_fmt(tmp_dir_path, &src_dir)?; + invoke_build(tmp_dir_path)?; + write_output(tmp_dir_path, &out_dir, entries)?; - println!("cargo:rerun-if-changed=contracts"); Ok(()) } diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index d2a4600436596..1cfc01fc94363 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -44,5 +44,6 @@ pub fn call() { &buffer[value], &buffer[callee_input], None, - ).unwrap(); + ) + .unwrap(); } diff --git a/substrate/frame/contracts/fixtures/contracts/dummy.rs b/substrate/frame/contracts/fixtures/contracts/dummy.rs index 6270b5d9b380d..24898160c8363 100644 --- a/substrate/frame/contracts/fixtures/contracts/dummy.rs +++ b/substrate/frame/contracts/fixtures/contracts/dummy.rs @@ -20,9 +20,7 @@ extern crate common; #[no_mangle] -pub fn deploy() { -} +pub fn deploy() {} #[no_mangle] -pub fn call() { -} +pub fn call() {} From 04dbc5ec271c8325da562a17c587ef2fe381f7b9 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 17:36:11 +0100 Subject: [PATCH 51/81] Fix fmt & lock --- Cargo.lock | 1 - substrate/frame/contracts/fixtures/build.rs | 31 +++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13badd8469b1a..4fc2617875446 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3180,7 +3180,6 @@ dependencies = [ "pallet-balances", "pallet-collator-selection", "pallet-contracts", - "pallet-contracts-uapi", "pallet-insecure-randomness-collective-flip", "pallet-message-queue", "pallet-multisig", diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index baf5110730cb5..727886b849935 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -146,7 +146,10 @@ fn invoke_fmt(current_dir: &Path, src_dir: &Path) -> Result<()> { let stdout = String::from_utf8_lossy(&fmt_res.stdout); eprintln!("{}", stdout); - eprintln!("Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt {}/*.rs`", src_dir.display()); + eprintln!( + "Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt {}/*.rs`", + src_dir.display() + ); anyhow::bail!("Fixtures files are not formatted") } @@ -206,23 +209,23 @@ fn write_output(build_dir: &Path, out_dir: &Path, entries: Vec) -> Result /// Returns the root path of the wasm workspace. fn find_workspace_root(current_dir: &Path) -> Option { - let mut current_dir = current_dir.to_path_buf(); - - while current_dir.parent().is_some() { - if current_dir.join("Cargo.toml").exists() { - let cargo_toml_contents = std::fs::read_to_string(current_dir.join("Cargo.toml")).ok()?; - if cargo_toml_contents.contains("[workspace]") { - return Some(current_dir); - } - } + let mut current_dir = current_dir.to_path_buf(); + + while current_dir.parent().is_some() { + if current_dir.join("Cargo.toml").exists() { + let cargo_toml_contents = + std::fs::read_to_string(current_dir.join("Cargo.toml")).ok()?; + if cargo_toml_contents.contains("[workspace]") { + return Some(current_dir); + } + } - current_dir.pop(); - } + current_dir.pop(); + } - None + None } - fn main() -> Result<()> { let input_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); let src_dir = input_dir.join("contracts"); From d3871f37c7fbcc6c23d680c1c6f3f42ad613653e Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 17:38:30 +0100 Subject: [PATCH 52/81] rm println from build.rs --- substrate/frame/contracts/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/frame/contracts/build.rs b/substrate/frame/contracts/build.rs index 42bc45d563d32..83d5d368d4bc3 100644 --- a/substrate/frame/contracts/build.rs +++ b/substrate/frame/contracts/build.rs @@ -68,6 +68,5 @@ fn main() -> Result<(), Box> { version - 1, )?; - println!("cargo:rerun-if-changed=src/migration"); Ok(()) } From d2d6e0c29019a0d7629dfb4b4f25018531c9ff42 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 17:42:47 +0100 Subject: [PATCH 53/81] fix formatting --- substrate/frame/contracts/uapi/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index add4b90b535e9..334b2640006c7 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -46,7 +46,7 @@ macro_rules! define_error_codes { #[repr(u32)] pub enum ReturnErrorCode { /// API call successful. - Success = 0, + Success = 0, $( $( #[$attr] )* $name = $discr, From 2ced8ea38880c07765a2d9611dd9755da93169b2 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 17:44:53 +0100 Subject: [PATCH 54/81] Rename ptr_and_len_from_slice --- substrate/frame/contracts/uapi/src/lib.rs | 2 +- substrate/frame/contracts/uapi/src/wasm32.rs | 22 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 334b2640006c7..8117645aabd31 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -155,7 +155,7 @@ fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { #[inline(always)] #[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] -fn ptr_and_len_from_slice(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { +fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { match data { Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), None => (SENTINEL as _, 0), diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 6153930c688ff..634bd6875f1ab 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -13,7 +13,7 @@ // limitations under the License. // #![allow(unused_variables)] use super::{ - extract_from_slice, ptr_and_len_from_slice, ptr_from_slice, Api, CallFlags, Result, ReturnCode, + extract_from_slice, ptr_from_slice, ptr_len_or_sentinel, Api, CallFlags, Result, ReturnCode, ReturnFlags, }; @@ -375,8 +375,8 @@ impl Api for ApiImpl { mut output: Option<&mut [u8]>, salt: &[u8], ) -> Result { - let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let ret_code = unsafe { sys::instantiate( code_hash.as_ptr(), @@ -414,8 +414,8 @@ impl Api for ApiImpl { mut output: Option<&mut [u8]>, salt: &[u8], ) -> Result { - let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let ret_code = unsafe { sys::v1::instantiate( code_hash.as_ptr(), @@ -453,8 +453,8 @@ impl Api for ApiImpl { mut output: Option<&mut [u8]>, salt: &[u8], ) -> Result { - let (address_ptr, mut address_len) = ptr_and_len_from_slice(&mut address); - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let deposit_ptr = ptr_from_slice(&deposit); let ret_code = { @@ -495,7 +495,7 @@ impl Api for ApiImpl { input_data: &[u8], mut output: Option<&mut [u8]>, ) -> Result { - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let ret_code = { unsafe { sys::call( @@ -528,7 +528,7 @@ impl Api for ApiImpl { input_data: &[u8], mut output: Option<&mut [u8]>, ) -> Result { - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let ret_code = { unsafe { sys::v1::call( @@ -561,7 +561,7 @@ impl Api for ApiImpl { input_data: &[u8], mut output: Option<&mut [u8]>, ) -> Result { - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let deposit_ptr = ptr_from_slice(&deposit); let ret_code = { unsafe { @@ -598,7 +598,7 @@ impl Api for ApiImpl { input: &[u8], mut output: Option<&mut [u8]>, ) -> Result { - let (output_ptr, mut output_len) = ptr_and_len_from_slice(&mut output); + let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); let ret_code = { unsafe { sys::delegate_call( From 308c42fcf9ea1fc32808e41ec766412a2aab8036 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 17:46:53 +0100 Subject: [PATCH 55/81] Rename fn ptr_from_slice --- substrate/frame/contracts/uapi/src/lib.rs | 2 +- substrate/frame/contracts/uapi/src/wasm32.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 8117645aabd31..a988a247a99d6 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -164,7 +164,7 @@ fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { #[inline(always)] #[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] -fn ptr_from_slice(data: &Option<&[u8]>) -> *const u8 { +fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { match data { Some(ref data) => data.as_ptr(), None => SENTINEL as _, diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 634bd6875f1ab..322714ed82766 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -13,7 +13,7 @@ // limitations under the License. // #![allow(unused_variables)] use super::{ - extract_from_slice, ptr_from_slice, ptr_len_or_sentinel, Api, CallFlags, Result, ReturnCode, + extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel, Api, CallFlags, Result, ReturnCode, ReturnFlags, }; @@ -455,7 +455,7 @@ impl Api for ApiImpl { ) -> Result { let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); - let deposit_ptr = ptr_from_slice(&deposit); + let deposit_ptr = ptr_or_sentinel(&deposit); let ret_code = { unsafe { @@ -562,7 +562,7 @@ impl Api for ApiImpl { mut output: Option<&mut [u8]>, ) -> Result { let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); - let deposit_ptr = ptr_from_slice(&deposit); + let deposit_ptr = ptr_or_sentinel(&deposit); let ret_code = { unsafe { sys::v2::call( From 8907f0a86f5f13f9a2141ed11525c990d9c5ff33 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 17:48:28 +0100 Subject: [PATCH 56/81] rm todo comment --- substrate/frame/contracts/uapi/src/api.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 1a0454004dfbe..0a66210b6693b 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// TODO: -// - Add missing unstable methods - pub mod unstable; use crate::{CallFlags, Result, ReturnFlags}; use paste::paste; From 835d26f8af3504b0c9260c4a1194a81c1e94e480 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Mon, 27 Nov 2023 17:50:12 +0100 Subject: [PATCH 57/81] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen --- substrate/frame/contracts/uapi/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index da1e985ec49a2..e606f023c8cc9 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true license = "Apache-2.0" homepage = "https://substrate.io" repository.workspace = true -description = "This crates exposes all the host functions that a contract can import" +description = "Exposes all the host functions that a contract can import." [dependencies] scale-info = { version = "2.10.0", default-features = false, features = ["derive"], optional = true } From c997c881ab76b6f19f45888b5025b5f5bc698049 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 18:09:56 +0100 Subject: [PATCH 58/81] Update unstable comments --- substrate/frame/contracts/uapi/src/api.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 0a66210b6693b..1a0b717e50c74 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -45,7 +45,7 @@ pub trait Api { /// # Return /// /// Returns the number of times specified contract exists on the call stack. - #[deprecated(note = "Unstable")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn account_reentrance_count(account: &[u8]) -> u32; /// Stores the address of the current contract into the supplied buffer. @@ -66,7 +66,7 @@ pub trait Api { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - #[deprecated(note = "Unstable")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn add_delegate_dependency(code_hash: &[u8]); /// Stores the *free* balance of the current account into the supplied buffer. @@ -140,7 +140,7 @@ pub trait Api { /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] - #[deprecated(note = "Unstable, use `call_v1` instead")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn call_v2( flags: CallFlags, callee: &[u8], @@ -236,7 +236,7 @@ pub trait Api { /// /// A return value of `true` indicates that this contract is being called by a root origin, /// and `false` indicates that the caller is a signed origin. - #[deprecated(note = "Unstable")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn caller_is_root() -> u32; /// Clear the value at the given key in the contract storage. @@ -375,7 +375,7 @@ pub trait Api { /// # Parameters /// /// - `output`: A reference to the output data buffer to write the weight left. - #[deprecated(note = "Unstable, use `gas_left` instead")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn gas_left_v1(output: &mut &mut [u8]); /// Retrieve the value under the given key from storage. @@ -475,7 +475,7 @@ pub trait Api { /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - #[deprecated(note = "Unstable, use `instantiate_v1` instead")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn instantiate_v2( code_hash: &[u8], ref_time_limit: u64, @@ -538,7 +538,7 @@ pub trait Api { /// # Return /// /// Returns `0` when there is no reentrancy. - #[deprecated(note = "Unstable")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn reentrance_count() -> u32; /// Removes the delegate dependency from the contract. @@ -549,7 +549,7 @@ pub trait Api { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - #[deprecated(note = "Unstable")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn remove_delegate_dependency(code_hash: &[u8]); /// Cease contract execution and save a data buffer as a result of the execution. @@ -715,7 +715,7 @@ pub trait Api { /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. /// - `output`: A reference to the output data buffer to write the price. - #[deprecated(note = "Unstable, use `weight_to_fee` instead")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); /// Execute an XCM program locally, using the contract's address as the origin. @@ -732,7 +732,7 @@ pub trait Api { /// /// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM /// execution fails, `ReturnCode::XcmExecutionFailed` is returned - #[deprecated(note = "Unstable")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result; /// Send an XCM program from the contract to the specified destination. @@ -751,6 +751,6 @@ pub trait Api { /// /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. - #[deprecated(note = "Unstable")] + #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] fn xcm_send(dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result; } From 8e9f317d2d1c9c833f1eccfb2ee4a81211243776 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 20:11:17 +0100 Subject: [PATCH 59/81] rm inline --- substrate/frame/contracts/uapi/src/lib.rs | 3 --- substrate/frame/contracts/uapi/src/wasm32.rs | 11 ----------- 2 files changed, 14 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index a988a247a99d6..8810ce4ea1908 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -145,7 +145,6 @@ impl ReturnCode { type Result = core::result::Result<(), ReturnErrorCode>; -#[inline(always)] #[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { debug_assert!(new_len <= output.len()); @@ -153,7 +152,6 @@ fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { *output = &mut tmp[..new_len]; } -#[inline(always)] #[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { match data { @@ -162,7 +160,6 @@ fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { } } -#[inline(always)] #[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { match data { diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 322714ed82766..2c7af887d31cd 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -300,7 +300,6 @@ mod sys { macro_rules! impl_wrapper_for { (@impl_fn $( $mod:ident )::*, $suffix:literal, $name:ident) => { paste::paste! { - #[inline(always)] fn [<$name $suffix>](output: &mut &mut [u8]) { let mut output_len = output.len() as u32; unsafe { @@ -343,7 +342,6 @@ macro_rules! impl_hash_fn { /// A macro to implement the get_storage functions. macro_rules! impl_get_storage { ($fn_name:ident, $sys_get_storage:path) => { - #[inline(always)] fn $fn_name(key: &[u8], output: &mut &mut [u8]) -> Result { let mut output_len = output.len() as u32; let ret_code = { @@ -365,7 +363,6 @@ macro_rules! impl_get_storage { pub enum ApiImpl {} impl Api for ApiImpl { - #[inline(always)] fn instantiate( code_hash: &[u8], gas: u64, @@ -404,7 +401,6 @@ impl Api for ApiImpl { ret_code.into() } - #[inline(always)] fn instantiate_v1( code_hash: &[u8], gas: u64, @@ -441,7 +437,6 @@ impl Api for ApiImpl { ret_code.into() } - #[inline(always)] fn instantiate_v2( code_hash: &[u8], ref_time_limit: u64, @@ -519,7 +514,6 @@ impl Api for ApiImpl { ret_code.into() } - #[inline(always)] fn call_v1( flags: CallFlags, callee: &[u8], @@ -591,7 +585,6 @@ impl Api for ApiImpl { unsafe { sys::caller_is_root() }.into_u32() } - #[inline(always)] fn delegate_call( flags: CallFlags, code_hash: &[u8], @@ -684,7 +677,6 @@ impl Api for ApiImpl { impl_get_storage!(get_storage, sys::get_storage); impl_get_storage!(get_storage_v1, sys::v1::get_storage); - #[inline(always)] fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { let mut output_len = output.len() as u32; let ret_code = { @@ -719,7 +711,6 @@ impl Api for ApiImpl { unsafe { sys::v1::terminate(beneficiary.as_ptr()) } } - #[inline(always)] 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 = { @@ -737,7 +728,6 @@ impl Api for ApiImpl { ret_code.into_u32() } - #[inline(always)] fn input(output: &mut &mut [u8]) { let mut output_len = output.len() as u32; { @@ -760,7 +750,6 @@ impl Api for ApiImpl { (v1, "_v1") => [gas_left], } - #[inline(always)] fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { let mut output_len = output.len() as u32; { From a33ce943950f82b6f2480b8237c9ee005e2743cb Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 20:14:11 +0100 Subject: [PATCH 60/81] rm unneeded uapi dep --- substrate/bin/node/runtime/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/bin/node/runtime/Cargo.toml b/substrate/bin/node/runtime/Cargo.toml index 697cf9fe12e03..cb4970e7c9d60 100644 --- a/substrate/bin/node/runtime/Cargo.toml +++ b/substrate/bin/node/runtime/Cargo.toml @@ -74,7 +74,6 @@ pallet-broker = { path = "../../../frame/broker", default-features = false} pallet-child-bounties = { path = "../../../frame/child-bounties", default-features = false} pallet-collective = { path = "../../../frame/collective", default-features = false} pallet-contracts = { path = "../../../frame/contracts", default-features = false} -pallet-contracts-uapi = { path = "../../../frame/contracts/uapi", default-features = false} pallet-conviction-voting = { path = "../../../frame/conviction-voting", default-features = false} pallet-core-fellowship = { path = "../../../frame/core-fellowship", default-features = false} pallet-democracy = { path = "../../../frame/democracy", default-features = false} From f3a2a21566af7f13ac47bcf8eb84d92e971419fe Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 20:19:37 +0100 Subject: [PATCH 61/81] rm legacy call uapi --- substrate/frame/contracts/src/wasm/runtime.rs | 1 - substrate/frame/contracts/uapi/src/api.rs | 12 ------ substrate/frame/contracts/uapi/src/wasm32.rs | 37 ------------------- 3 files changed, 50 deletions(-) diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index 7886bc41e9ae3..b70c57b331bc7 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -1161,7 +1161,6 @@ pub mod env { } /// Make a call to another contract. - /// See [`pallet_contracts_uapi::Api::call`]. /// /// # Note /// diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 1a0b717e50c74..5d977f116f7c5 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -415,18 +415,6 @@ pub trait Api { /// - `output`: A reference to the output data buffer to write the input data. fn input(output: &mut &mut [u8]); - /// Instantiate a contract with the specified code hash. - #[deprecated(note = "Deprecated, use newer version instead")] - fn instantiate( - code_hash: &[u8], - gas: u64, - value: &[u8], - input: &[u8], - address: Option<&mut [u8]>, - output: Option<&mut [u8]>, - salt: &[u8], - ) -> Result; - /// Instantiate a contract with the specified code hash. /// /// Equivalent to the newer [`Self::instantiate_v2`] version but works diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/wasm32.rs index 2c7af887d31cd..350965310fc48 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/wasm32.rs @@ -363,43 +363,6 @@ macro_rules! impl_get_storage { pub enum ApiImpl {} impl Api for ApiImpl { - fn instantiate( - code_hash: &[u8], - gas: u64, - value: &[u8], - input: &[u8], - mut address: Option<&mut [u8]>, - mut output: Option<&mut [u8]>, - salt: &[u8], - ) -> Result { - let (address_ptr, mut address_len) = ptr_len_or_sentinel(&mut address); - let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); - let ret_code = unsafe { - sys::instantiate( - code_hash.as_ptr(), - code_hash.len() as u32, - gas, - value.as_ptr(), - value.len() as u32, - input.as_ptr(), - input.len() as u32, - address_ptr, - &mut address_len, - output_ptr, - &mut output_len, - salt.as_ptr(), - salt.len() as u32, - ) - }; - - if let Some(ref mut address) = address { - extract_from_slice(address, address_len as usize); - } - if let Some(ref mut output) = output { - extract_from_slice(output, output_len as usize); - } - ret_code.into() - } fn instantiate_v1( code_hash: &[u8], From 9c22f773d0d961932ddafd77af723d32f74e762d Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 20:26:51 +0100 Subject: [PATCH 62/81] rm pub on legacy_compile_module --- substrate/frame/contracts/fixtures/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/src/lib.rs b/substrate/frame/contracts/fixtures/src/lib.rs index b27f693893ee5..fbc2647709dce 100644 --- a/substrate/frame/contracts/fixtures/src/lib.rs +++ b/substrate/frame/contracts/fixtures/src/lib.rs @@ -33,7 +33,7 @@ fn wat_root_dir() -> PathBuf { /// with it's hash. /// /// The fixture files are located under the `fixtures/` directory. -pub fn legacy_compile_module( +fn legacy_compile_module( fixture_name: &str, ) -> anyhow::Result<(Vec, ::Output)> where From 4c15e5bcf7fa24c1e8b43a77d5bd8214a4138729 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 20:32:38 +0100 Subject: [PATCH 63/81] leftover --- Cargo.lock | 1 - substrate/frame/contracts/uapi/src/api.rs | 1 - substrate/frame/contracts/uapi/src/api/unstable.rs | 13 ------------- 3 files changed, 15 deletions(-) delete mode 100644 substrate/frame/contracts/uapi/src/api/unstable.rs diff --git a/Cargo.lock b/Cargo.lock index 4fc2617875446..8983f24d77a3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7217,7 +7217,6 @@ dependencies = [ "pallet-child-bounties", "pallet-collective", "pallet-contracts", - "pallet-contracts-uapi", "pallet-conviction-voting", "pallet-core-fellowship", "pallet-democracy", diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index 5d977f116f7c5..f0c56bd62e87a 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub mod unstable; use crate::{CallFlags, Result, ReturnFlags}; use paste::paste; diff --git a/substrate/frame/contracts/uapi/src/api/unstable.rs b/substrate/frame/contracts/uapi/src/api/unstable.rs deleted file mode 100644 index 8807a292de105..0000000000000 --- a/substrate/frame/contracts/uapi/src/api/unstable.rs +++ /dev/null @@ -1,13 +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. From b55b3706ca4966165bd3d9ea0c0b6d4a8f09f11c Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 21:19:21 +0100 Subject: [PATCH 64/81] moved riscv32 and wasm32 under api --- substrate/frame/contracts/uapi/src/api.rs | 16 +++++++-- .../frame/contracts/uapi/src/api/common.rs | 34 +++++++++++++++++++ .../contracts/uapi/src/{ => api}/riscv32.rs | 0 .../contracts/uapi/src/{ => api}/wasm32.rs | 21 ++---------- substrate/frame/contracts/uapi/src/lib.rs | 34 +------------------ 5 files changed, 52 insertions(+), 53 deletions(-) create mode 100644 substrate/frame/contracts/uapi/src/api/common.rs rename substrate/frame/contracts/uapi/src/{ => api}/riscv32.rs (100%) rename substrate/frame/contracts/uapi/src/{ => api}/wasm32.rs (97%) diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index f0c56bd62e87a..afde4a625edd9 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -11,10 +11,21 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - -use crate::{CallFlags, Result, ReturnFlags}; +use crate::{ CallFlags, Result, ReturnFlags}; use paste::paste; +cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + mod common; + mod wasm32; + pub use wasm32::ApiImpl; + } else if #[cfg(target_arch = "riscv32")] { + mod common; + mod riscv32; + pub use riscv32::ApiImpl; + } +} + macro_rules! hash_fn { ( $name:ident, $bytes:literal ) => { paste! { @@ -31,6 +42,7 @@ macro_rules! hash_fn { }; } + /// Defines all the user apis implemented by both wasm and RISC-V vms. pub trait Api { /// Returns the number of times specified contract exists on the call stack. Delegated calls are diff --git a/substrate/frame/contracts/uapi/src/api/common.rs b/substrate/frame/contracts/uapi/src/api/common.rs new file mode 100644 index 0000000000000..06bffc8beb03b --- /dev/null +++ b/substrate/frame/contracts/uapi/src/api/common.rs @@ -0,0 +1,34 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use crate::SENTINEL; + +pub 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]; +} + +pub fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { + match data { + Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), + None => (SENTINEL as _, 0), + } +} + +pub fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { + match data { + Some(ref data) => data.as_ptr(), + None => SENTINEL as _, + } +} diff --git a/substrate/frame/contracts/uapi/src/riscv32.rs b/substrate/frame/contracts/uapi/src/api/riscv32.rs similarity index 100% rename from substrate/frame/contracts/uapi/src/riscv32.rs rename to substrate/frame/contracts/uapi/src/api/riscv32.rs diff --git a/substrate/frame/contracts/uapi/src/wasm32.rs b/substrate/frame/contracts/uapi/src/api/wasm32.rs similarity index 97% rename from substrate/frame/contracts/uapi/src/wasm32.rs rename to substrate/frame/contracts/uapi/src/api/wasm32.rs index 350965310fc48..65ce8938d10a4 100644 --- a/substrate/frame/contracts/uapi/src/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/api/wasm32.rs @@ -13,9 +13,10 @@ // limitations under the License. // #![allow(unused_variables)] use super::{ - extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel, Api, CallFlags, Result, ReturnCode, - ReturnFlags, + Api, CallFlags, Result, + common::{extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel} }; +use crate::{ReturnCode, ReturnFlags,}; mod sys { use super::ReturnCode; @@ -113,22 +114,6 @@ mod sys { pub fn input(buf_ptr: *mut u8, buf_len_ptr: *mut u32); - pub fn instantiate( - code_hash_ptr: *const u8, - code_hash_len: u32, - gas: u64, - value_ptr: *const u8, - value_len: u32, - input_ptr: *const u8, - input_len: u32, - address_ptr: *mut u8, - address_len_ptr: *mut u32, - output_ptr: *mut u8, - output_len_ptr: *mut u32, - salt_ptr: *const u8, - salt_len: u32, - ) -> ReturnCode; - pub fn instantiation_nonce() -> u64; pub fn is_contract(account_id_ptr: *const u8) -> ReturnCode; diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 8810ce4ea1908..fa25a809490ad 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -22,17 +22,7 @@ mod flags; pub use flags::*; mod api; -pub use api::Api; - -cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - mod wasm32; - pub use wasm32::ApiImpl; - } else if #[cfg(target_arch = "riscv32")] { - mod riscv32; - pub use riscv32::ApiImpl; - } -} +pub use api::*; macro_rules! define_error_codes { ( @@ -145,25 +135,3 @@ impl ReturnCode { type Result = core::result::Result<(), ReturnErrorCode>; -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] -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]; -} - -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] -fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { - match data { - Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), - None => (SENTINEL as _, 0), - } -} - -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] -fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { - match data { - Some(ref data) => data.as_ptr(), - None => SENTINEL as _, - } -} From f14996e78059914ac5efbd0a500e8b3730918969 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 21:42:05 +0100 Subject: [PATCH 65/81] remove extern crate uapi --- substrate/frame/contracts/fixtures/build.rs | 1 + substrate/frame/contracts/fixtures/contracts/call.rs | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 727886b849935..6235ead75dd74 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -100,6 +100,7 @@ fn create_cargo_toml<'a>( [package] name = 'contracts' version = '0.1.0' +edition = '2021' # Binary targets are injected below. [[bin]] diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index 1cfc01fc94363..01ec57d1b40e1 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -20,7 +20,6 @@ #![no_main] extern crate common; -extern crate uapi; use uapi::{Api, ApiImpl as api, CallFlags}; #[no_mangle] From c1e60bdaf5105bb155d90e2a73c182245d5bc6ff Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 21:47:42 +0100 Subject: [PATCH 66/81] rm unneeded dep --- substrate/frame/contracts/fixtures/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 6235ead75dd74..9f18e858326b9 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -108,7 +108,6 @@ edition = '2021' [dependencies] uapi = {{ package = 'pallet-contracts-uapi', default-features = false, path = {uapi_path:?}}} common = {{ package = 'pallet-contracts-fixtures-common', path = {common_path:?}}} -cfg-if = {{ version = '1.0', default-features = false }} [profile.release] opt-level = 3 From 2a42b9faa121d49ffd81a9875d1afca925353404 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 22:07:06 +0100 Subject: [PATCH 67/81] move common inside contracts --- substrate/frame/contracts/fixtures/build.rs | 33 +++++++++++-------- .../{ => contracts}/common/Cargo.toml | 0 .../{ => contracts}/common/src/lib.rs | 0 3 files changed, 19 insertions(+), 14 deletions(-) rename substrate/frame/contracts/fixtures/{ => contracts}/common/Cargo.toml (100%) rename substrate/frame/contracts/fixtures/{ => contracts}/common/src/lib.rs (100%) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 9f18e858326b9..9f42e4d26f5d1 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -73,11 +73,16 @@ impl Entry { /// Collect all contract entries from the given source directory. /// Contracts that have already been compiled are filtered out. -fn collect_entries(src_dir: &Path, out_dir: &Path) -> Vec { - fs::read_dir(&src_dir) +fn collect_entries(contracts_dir: &Path, out_dir: &Path) -> Vec { + fs::read_dir(&contracts_dir) .expect("src dir exists; qed") .filter_map(|file| { - let entry = Entry::new(file.expect("file exists; qed").path()); + let path = file.expect("file exists; qed").path(); + if path.extension().map_or(true, |ext| ext != "rs") { + return None; + } + + let entry = Entry::new(path); if out_dir.join(&entry.hash).exists() { None } else { @@ -89,12 +94,12 @@ fn collect_entries(src_dir: &Path, out_dir: &Path) -> Vec { /// Create a `Cargo.toml` to compile the given contract entries. fn create_cargo_toml<'a>( - input_dir: &Path, + fixtures_dir: &Path, entries: impl Iterator, output_dir: &Path, ) -> Result<()> { - let uapi_path = input_dir.join("../uapi").canonicalize()?; - let common_path = input_dir.join("./common").canonicalize()?; + let uapi_path = fixtures_dir.join("../uapi").canonicalize()?; + let common_path = fixtures_dir.join("./contracts/common").canonicalize()?; let mut cargo_toml: toml::Value = toml::from_str(&format!( " [package] @@ -133,7 +138,7 @@ codegen-units = 1 } /// Invoke `cargo fmt` to check that fixtures files are formatted. -fn invoke_fmt(current_dir: &Path, src_dir: &Path) -> Result<()> { +fn invoke_fmt(current_dir: &Path, contracts_dir: &Path) -> Result<()> { let fmt_res = Command::new("rustup") .current_dir(current_dir) .args(&["run", "nightly", "cargo", "fmt", "--check"]) @@ -148,7 +153,7 @@ fn invoke_fmt(current_dir: &Path, src_dir: &Path) -> Result<()> { eprintln!("{}", stdout); eprintln!( "Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt {}/*.rs`", - src_dir.display() + contracts_dir.display() ); anyhow::bail!("Fixtures files are not formatted") } @@ -227,12 +232,12 @@ fn find_workspace_root(current_dir: &Path) -> Option { } fn main() -> Result<()> { - let input_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); - let src_dir = input_dir.join("contracts"); + let fixtures_dir: PathBuf = env::var("CARGO_MANIFEST_DIR")?.into(); + let contracts_dir = fixtures_dir.join("contracts"); let out_dir: PathBuf = env::var("OUT_DIR")?.into(); - let workspace_root = find_workspace_root(&input_dir).expect("workspace root exists; qed"); + let workspace_root = find_workspace_root(&fixtures_dir).expect("workspace root exists; qed"); - let entries = collect_entries(&src_dir, &out_dir); + let entries = collect_entries(&contracts_dir, &out_dir); if entries.is_empty() { return Ok(()); } @@ -240,8 +245,8 @@ fn main() -> Result<()> { let tmp_dir = tempfile::tempdir()?; let tmp_dir_path = tmp_dir.path(); fs::copy(workspace_root.join(".rustfmt.toml"), tmp_dir_path.join(".rustfmt.toml"))?; - create_cargo_toml(&input_dir, entries.iter(), tmp_dir.path())?; - invoke_fmt(tmp_dir_path, &src_dir)?; + create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?; + invoke_fmt(tmp_dir_path, &contracts_dir)?; invoke_build(tmp_dir_path)?; write_output(tmp_dir_path, &out_dir, entries)?; diff --git a/substrate/frame/contracts/fixtures/common/Cargo.toml b/substrate/frame/contracts/fixtures/contracts/common/Cargo.toml similarity index 100% rename from substrate/frame/contracts/fixtures/common/Cargo.toml rename to substrate/frame/contracts/fixtures/contracts/common/Cargo.toml diff --git a/substrate/frame/contracts/fixtures/common/src/lib.rs b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs similarity index 100% rename from substrate/frame/contracts/fixtures/common/src/lib.rs rename to substrate/frame/contracts/fixtures/contracts/common/src/lib.rs From a8efa67272e00593c4fad03127ebe7762526c4d3 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Mon, 27 Nov 2023 22:08:19 +0100 Subject: [PATCH 68/81] fmt --- Cargo.toml | 2 +- substrate/frame/contracts/uapi/src/api.rs | 47 ++++++++++++++----- .../frame/contracts/uapi/src/api/wasm32.rs | 7 ++- substrate/frame/contracts/uapi/src/lib.rs | 1 - 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 38409ead47ee8..b688695076ccf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -290,7 +290,7 @@ members = [ "substrate/frame/collective", "substrate/frame/contracts", "substrate/frame/contracts/fixtures", - "substrate/frame/contracts/fixtures/common", + "substrate/frame/contracts/fixtures/contracts/common", "substrate/frame/contracts/uapi", "substrate/frame/contracts/mock-network", "substrate/frame/contracts/proc-macro", diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/api.rs index afde4a625edd9..c1318d255a595 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/api.rs @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ CallFlags, Result, ReturnFlags}; +use crate::{CallFlags, Result, ReturnFlags}; use paste::paste; cfg_if::cfg_if! { @@ -42,7 +42,6 @@ macro_rules! hash_fn { }; } - /// Defines all the user apis implemented by both wasm and RISC-V vms. pub trait Api { /// Returns the number of times specified contract exists on the call stack. Delegated calls are @@ -56,7 +55,9 @@ pub trait Api { /// # Return /// /// Returns the number of times specified contract exists on the call stack. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn account_reentrance_count(account: &[u8]) -> u32; /// Stores the address of the current contract into the supplied buffer. @@ -77,7 +78,9 @@ pub trait Api { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn add_delegate_dependency(code_hash: &[u8]); /// Stores the *free* balance of the current account into the supplied buffer. @@ -151,7 +154,9 @@ pub trait Api { /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [NotCallable][`crate::ReturnErrorCode::NotCallable] - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn call_v2( flags: CallFlags, callee: &[u8], @@ -247,7 +252,9 @@ pub trait Api { /// /// A return value of `true` indicates that this contract is being called by a root origin, /// and `false` indicates that the caller is a signed origin. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn caller_is_root() -> u32; /// Clear the value at the given key in the contract storage. @@ -386,7 +393,9 @@ pub trait Api { /// # Parameters /// /// - `output`: A reference to the output data buffer to write the weight left. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn gas_left_v1(output: &mut &mut [u8]); /// Retrieve the value under the given key from storage. @@ -474,7 +483,9 @@ pub trait Api { /// - [CalleeTrapped][`crate::ReturnErrorCode::CalleeTrapped] /// - [TransferFailed][`crate::ReturnErrorCode::TransferFailed] /// - [CodeNotFound][`crate::ReturnErrorCode::CodeNotFound] - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn instantiate_v2( code_hash: &[u8], ref_time_limit: u64, @@ -537,7 +548,9 @@ pub trait Api { /// # Return /// /// Returns `0` when there is no reentrancy. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn reentrance_count() -> u32; /// Removes the delegate dependency from the contract. @@ -548,7 +561,9 @@ pub trait Api { /// /// - `code_hash`: The code hash of the dependency. Should be decodable as an `T::Hash`. Traps /// otherwise. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn remove_delegate_dependency(code_hash: &[u8]); /// Cease contract execution and save a data buffer as a result of the execution. @@ -714,7 +729,9 @@ pub trait Api { /// - `ref_time_limit`: The *ref_time* Weight limit to query the price for. /// - `proof_size_limit`: The *proof_size* Weight limit to query the price for. /// - `output`: A reference to the output data buffer to write the price. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn weight_to_fee_v1(ref_time_limit: u64, proof_size_limit: u64, output: &mut &mut [u8]); /// Execute an XCM program locally, using the contract's address as the origin. @@ -731,7 +748,9 @@ pub trait Api { /// /// Returns `Error::Success` when the XCM execution attempt is successful. When the XCM /// execution fails, `ReturnCode::XcmExecutionFailed` is returned - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn xcm_execute(msg: &[u8], output: &mut &mut [u8]) -> Result; /// Send an XCM program from the contract to the specified destination. @@ -750,6 +769,8 @@ pub trait Api { /// /// Returns `ReturnCode::Success` when the message was successfully sent. When the XCM /// execution fails, `ReturnErrorCode::XcmSendFailed` is returned. - #[deprecated(note = "Unstable function. Behaviour can change without further notice. Use only for testing")] + #[deprecated( + note = "Unstable function. Behaviour can change without further notice. Use only for testing." + )] fn xcm_send(dest: &[u8], msg: &[u8], output: &mut &mut [u8]) -> Result; } diff --git a/substrate/frame/contracts/uapi/src/api/wasm32.rs b/substrate/frame/contracts/uapi/src/api/wasm32.rs index 65ce8938d10a4..69fb4c85d6523 100644 --- a/substrate/frame/contracts/uapi/src/api/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/api/wasm32.rs @@ -13,10 +13,10 @@ // limitations under the License. // #![allow(unused_variables)] use super::{ - Api, CallFlags, Result, - common::{extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel} + common::{extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel}, + Api, CallFlags, Result, }; -use crate::{ReturnCode, ReturnFlags,}; +use crate::{ReturnCode, ReturnFlags}; mod sys { use super::ReturnCode; @@ -348,7 +348,6 @@ macro_rules! impl_get_storage { pub enum ApiImpl {} impl Api for ApiImpl { - fn instantiate_v1( code_hash: &[u8], gas: u64, diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index fa25a809490ad..efd99b5656cf0 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -134,4 +134,3 @@ impl ReturnCode { } type Result = core::result::Result<(), ReturnErrorCode>; - From 08a6325550765def72a96e279900924ad8d392e3 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 06:54:52 +0100 Subject: [PATCH 69/81] print both stdout & stderr --- substrate/frame/contracts/fixtures/build.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 9f42e4d26f5d1..4497e9af2a544 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -150,7 +150,8 @@ fn invoke_fmt(current_dir: &Path, contracts_dir: &Path) -> Result<()> { } let stdout = String::from_utf8_lossy(&fmt_res.stdout); - eprintln!("{}", stdout); + let stderr = String::from_utf8_lossy(&fmt_res.stderr); + eprintln!("{}\n{}", stdout, stderr); eprintln!( "Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt {}/*.rs`", contracts_dir.display() From 887b70fc5fa9b83198ce814653e5853cca96d6c2 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 10:23:17 +0100 Subject: [PATCH 70/81] Fix format - skip on macos CI --- substrate/frame/contracts/fixtures/build.rs | 39 ++++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 4497e9af2a544..ac3160c902cde 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -138,12 +138,27 @@ codegen-units = 1 } /// Invoke `cargo fmt` to check that fixtures files are formatted. -fn invoke_fmt(current_dir: &Path, contracts_dir: &Path) -> Result<()> { +fn invoke_cargo_fmt<'a, Files>(config_path: &Path, files: Files, contract_dir: &Path) -> Result<()> +where + Files: IntoIterator, +{ + // If rustfmt is not installed, skip the check. + if !Command::new("rustup") + .args(&["run", "nightly", "rustfmt", "--version"]) + .output() + .expect("failed to execute process") + .status + .success() + { + return Ok(()) + } + let fmt_res = Command::new("rustup") - .current_dir(current_dir) - .args(&["run", "nightly", "cargo", "fmt", "--check"]) + .args(&["run", "nightly", "rustfmt", "--check", "--config-path"]) + .arg(config_path) + .args(files) .output() - .unwrap(); + .expect("failed to execute process"); if fmt_res.status.success() { return Ok(()) @@ -153,8 +168,9 @@ fn invoke_fmt(current_dir: &Path, contracts_dir: &Path) -> Result<()> { let stderr = String::from_utf8_lossy(&fmt_res.stderr); eprintln!("{}\n{}", stdout, stderr); eprintln!( - "Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt {}/*.rs`", - contracts_dir.display() + "Fixtures files are not formatted.\nPlease run `rustup run nightly rustfmt --config-path {} {}/*.rs`", + config_path.display(), + contract_dir.display() ); anyhow::bail!("Fixtures files are not formatted") } @@ -175,7 +191,7 @@ fn invoke_build(current_dir: &Path) -> Result<()> { .env("CARGO_ENCODED_RUSTFLAGS", encoded_rustflags) .args(&["build", "--release", "--target=wasm32-unknown-unknown"]) .output() - .unwrap(); + .expect("failed to execute process"); if build_res.status.success() { return Ok(()) @@ -245,9 +261,14 @@ fn main() -> Result<()> { let tmp_dir = tempfile::tempdir()?; let tmp_dir_path = tmp_dir.path(); - fs::copy(workspace_root.join(".rustfmt.toml"), tmp_dir_path.join(".rustfmt.toml"))?; + create_cargo_toml(&fixtures_dir, entries.iter(), tmp_dir.path())?; - invoke_fmt(tmp_dir_path, &contracts_dir)?; + invoke_cargo_fmt( + &workspace_root.join(".rustfmt.toml"), + entries.iter().map(|entry| &entry.path as _), + &contracts_dir, + )?; + invoke_build(tmp_dir_path)?; write_output(tmp_dir_path, &out_dir, entries)?; From f5a542787b0e24c353982582440ea1fc395d26cd Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 10:39:24 +0100 Subject: [PATCH 71/81] nit --- substrate/frame/contracts/fixtures/build.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index ac3160c902cde..060f2d5eb9a74 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -138,10 +138,11 @@ codegen-units = 1 } /// Invoke `cargo fmt` to check that fixtures files are formatted. -fn invoke_cargo_fmt<'a, Files>(config_path: &Path, files: Files, contract_dir: &Path) -> Result<()> -where - Files: IntoIterator, -{ +fn invoke_cargo_fmt<'a>( + config_path: &Path, + files: impl Iterator, + contract_dir: &Path, +) -> Result<()> { // If rustfmt is not installed, skip the check. if !Command::new("rustup") .args(&["run", "nightly", "rustfmt", "--version"]) From 683e687a1a4a9ef415312605f948d3f09233a0d1 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 11:51:45 +0100 Subject: [PATCH 72/81] move cfg attribute to crate --- substrate/frame/contracts/fixtures/contracts/common/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs index 43aa8f189026a..29bdbfbb04200 100644 --- a/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs +++ b/substrate/frame/contracts/fixtures/contracts/common/src/lib.rs @@ -15,9 +15,9 @@ // See the License for the specific language governing permissions and // limitations under the License. #![no_std] +#![cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] #[panic_handler] -#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] fn panic(_info: &core::panic::PanicInfo) -> ! { #[cfg(target_arch = "wasm32")] core::arch::wasm32::unreachable(); From b978822d7aa3c4d3349c7019a1d4118fce7bf8ea Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 12:22:58 +0100 Subject: [PATCH 73/81] Update conditional compilation --- Cargo.lock | 1 - .../contracts/fixtures/contracts/call.rs | 2 +- substrate/frame/contracts/uapi/Cargo.toml | 1 - .../frame/contracts/uapi/src/api/riscv32.rs | 466 ------------------ .../contracts/uapi/src/{api.rs => host.rs} | 45 +- .../src/{api/common.rs => host/riscv32.rs} | 22 +- .../uapi/src/{api => host}/wasm32.rs | 9 +- substrate/frame/contracts/uapi/src/lib.rs | 7 +- 8 files changed, 41 insertions(+), 512 deletions(-) delete mode 100644 substrate/frame/contracts/uapi/src/api/riscv32.rs rename substrate/frame/contracts/uapi/src/{api.rs => host.rs} (97%) rename substrate/frame/contracts/uapi/src/{api/common.rs => host/riscv32.rs} (53%) rename substrate/frame/contracts/uapi/src/{api => host}/wasm32.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 8983f24d77a3d..d9f380e969046 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9867,7 +9867,6 @@ name = "pallet-contracts-uapi" version = "4.0.0-dev" dependencies = [ "bitflags 1.3.2", - "cfg-if", "parity-scale-codec", "paste", "scale-info", diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index 01ec57d1b40e1..e7a2930d4bf6a 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -20,7 +20,7 @@ #![no_main] extern crate common; -use uapi::{Api, ApiImpl as api, CallFlags}; +use uapi::{CallFlags, HostFn, HostFnImpl as api}; #[no_mangle] pub fn deploy() {} diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index e606f023c8cc9..38927c59cec38 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -14,7 +14,6 @@ scale = { package = "parity-scale-codec", version = "3.6.1", default-features = "derive", "max-encoded-len", ], optional = true } -cfg-if = { version = "1.0", default-features = false } paste = { version = "1.0", default-features = false } bitflags = "1.0" diff --git a/substrate/frame/contracts/uapi/src/api/riscv32.rs b/substrate/frame/contracts/uapi/src/api/riscv32.rs deleted file mode 100644 index 52b58056828f4..0000000000000 --- a/substrate/frame/contracts/uapi/src/api/riscv32.rs +++ /dev/null @@ -1,466 +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. -// TODO: bring up to date with wasm32.rs - -use super::{extract_from_slice, Result, ReturnCode}; -use crate::{engine::on_chain::EncodeScope, ReturnFlags}; -use scale::Encode; - -mod sys { - #[polkavm_derive::polkavm_import] - extern "C" { - #[polkavm_import(index = 1)] - pub fn set_storage( - key_ptr: *const u8, - key_len: u32, - value_ptr: *const u8, - value_len: u32, - ) -> u32; - - #[polkavm_import(index = 2)] - pub fn clear_storage(key_ptr: *const u8, key_len: u32) -> u32; - - #[polkavm_import(index = 3)] - pub fn get_storage( - key_ptr: *const u8, - key_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 4)] - pub fn contains_storage(key_ptr: *const u8, key_len: u32) -> u32; - - #[polkavm_import(index = 5)] - pub fn take_storage( - key_ptr: *const u8, - key_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 48)] - pub fn transfer(account_ptr: *const u8, value_ptr: *const u8) -> u32; - - #[polkavm_import(index = 7)] - pub fn call(ptr: *const u8) -> u32; - - #[polkavm_import(index = 9)] - pub fn delegate_call( - flags: u32, - code_hash_ptr: *const u8, - input_data_ptr: *const u8, - input_data_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 10)] - pub fn instantiate(ptr: *const u8) -> u32; - - #[polkavm_import(index = 12)] - pub fn terminate(beneficiary_ptr: *const u8); - - #[polkavm_import(index = 13)] - pub fn input(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 14)] - pub fn seal_return(flags: u32, data_ptr: *const u8, data_len: u32); - - #[polkavm_import(index = 15)] - pub fn caller(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 16)] - pub fn is_contract(account_ptr: *const u8) -> u32; - - #[polkavm_import(index = 17)] - pub fn code_hash(account_ptr: *const u8, out_ptr: *mut u8, out_len_ptr: *mut u32) -> u32; - - #[polkavm_import(index = 18)] - pub fn own_code_hash(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 19)] - pub fn caller_is_origin() -> u32; - - #[polkavm_import(index = 20)] - pub fn caller_is_root() -> u32; - - #[polkavm_import(index = 21)] - pub fn address(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 22)] - pub fn weight_to_fee(gas: u64, out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 24)] - pub fn gas_left(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 26)] - pub fn balance(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 27)] - pub fn value_transferred(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 28)] - pub fn now(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 29)] - pub fn minimum_balance(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 30)] - pub fn deposit_event( - topics_ptr: *const u8, - topics_len: u32, - data_ptr: *const u8, - data_len: u32, - ); - - #[polkavm_import(index = 31)] - pub fn block_number(out_ptr: *mut u8, out_len_ptr: *mut u32); - - #[polkavm_import(index = 32)] - pub fn hash_sha2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - - #[polkavm_import(index = 33)] - pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - - #[polkavm_import(index = 34)] - pub fn hash_blake2_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - - #[polkavm_import(index = 35)] - pub fn hash_blake2_128(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - - #[polkavm_import(index = 36)] - pub fn call_chain_extension( - id: u32, - input_ptr: *const u8, - input_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> u32; - - #[polkavm_import(index = 37)] - pub fn debug_message(str_ptr: *const u8, str_len: u32) -> u32; - - #[polkavm_import(index = 38)] - pub fn call_runtime(call_ptr: *const u8, call_len: u32) -> u32; - - #[polkavm_import(index = 39)] - pub fn ecdsa_recover( - signature_ptr: *const u8, - message_hash_ptr: *const u8, - out_ptr: *mut u8, - ) -> u32; - - #[polkavm_import(index = 40)] - pub fn sr25519_verify( - signature_ptr: *const u8, - pub_key_ptr: *const u8, - message_len: u32, - message_ptr: *const u8, - ) -> u32; - - #[polkavm_import(index = 41)] - pub fn set_code_hash(code_hash_ptr: *const u8) -> u32; - - #[polkavm_import(index = 42)] - pub fn ecdsa_to_eth_address(key_ptr: *const u8, out_ptr: *mut u8) -> u32; - } -} - -macro_rules! impl_wrapper_for { - ( $( $name:ident, )* ) => { - $( - fn $name(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { - sys::$name( - output.as_mut_ptr(), - &mut output_len, - ) - } - extract_from_slice(output, output_len as usize) - } - )* - } -} -macro_rules! impl_hash_fn { - ( $name:ident, $bytes_result:literal ) => { - paste::item! { - fn [](input: &[u8], output: &mut [u8; $bytes_result]) { - unsafe { - sys::[]( - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - ) - } - } - } - }; -} - -pub enum ApiImpl {} - -impl super::Api for ApiImpl { - 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 mut in_data = [0u8; 64]; - #[allow(trivial_casts)] - ( - code_hash.as_ptr() as u32, - gas_limit, - endowment.as_ptr() as u32, - input.as_ptr() as u32, - input.len() as u32, - out_address.as_mut_ptr() as u32, - &mut address_len as *mut _ as u32, - out_return_value.as_mut_ptr() as u32, - &mut return_value_len as *mut _ as u32, - salt.as_ptr() as u32, - salt.len() as u32, - ) - .encode_to(&mut EncodeScope::from(in_data.as_mut())); - let ret_val = unsafe { sys::instantiate(in_data.as_ptr()) }; - extract_from_slice(out_address, address_len as usize); - extract_from_slice(out_return_value, return_value_len as usize); - ReturnCode(ret_val).into() - } - - 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 mut in_data = [0u8; 64]; - #[allow(trivial_casts)] - ( - flags, - callee.as_ptr() as u32, - gas_limit, - value.as_ptr() as u32, - input.as_ptr() as u32, - input.len() as u32, - output.as_mut_ptr() as u32, - &mut output_len as *mut _ as u32, - ) - .encode_to(&mut EncodeScope::from(in_data.as_mut())); - let ret_val = unsafe { sys::call(in_data.as_ptr()) }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() - } - - 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_val = unsafe { - sys::delegate_call( - flags, - code_hash.as_ptr(), - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() - } - - fn transfer(account_id: &[u8], value: &[u8]) -> Result { - let ret_val = unsafe { sys::transfer(account_id.as_ptr(), value.as_ptr()) }; - ReturnCode(ret_val).into() - } - - fn deposit_event(topics: &[u8], data: &[u8]) { - unsafe { - sys::deposit_event( - topics.as_ptr(), - topics.len() as u32, - data.as_ptr(), - data.len() as u32, - ) - } - } - - fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { - let ret_val = unsafe { - sys::set_storage( - key.as_ptr(), - key.len() as u32, - encoded_value.as_ptr(), - encoded_value.len() as u32, - ) - }; - ReturnCode(ret_val).into() - } - - fn clear_storage(key: &[u8]) -> Option { - let ret_val = unsafe { sys::clear_storage(key.as_ptr(), key.len() as u32) }; - ret_val.into() - } - - fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::get_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() - } - - fn take_storage(key: &[u8], output: &mut &mut [u8]) -> Result { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::take_storage(key.as_ptr(), key.len() as u32, output.as_mut_ptr(), &mut output_len) - }; - extract_from_slice(output, output_len as usize); - ReturnCode(ret_val).into() - } - - fn storage_contains(key: &[u8]) -> Option { - let ret_val = unsafe { sys::contains_storage(key.as_ptr(), key.len() as u32) }; - ReturnCode(ret_val).into() - } - - fn terminate(beneficiary: &[u8]) -> ! { - unsafe { - sys::terminate(beneficiary.as_ptr()); - core::hint::unreachable_unchecked(); - } - } - - fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 { - let mut output_len = output.len() as u32; - let ret_val = unsafe { - sys::call_chain_extension( - func_id, - input.as_ptr(), - input.len() as u32, - output.as_mut_ptr(), - &mut output_len, - ) - }; - extract_from_slice(output, output_len as usize); - ret_val - } - - fn input(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::input(output.as_mut_ptr(), &mut output_len) } - extract_from_slice(output, output_len as usize); - } - - fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { - unsafe { - sys::seal_return(flags.into_u32(), return_value.as_ptr(), return_value.len() as u32); - core::hint::unreachable_unchecked(); - } - } - - fn call_runtime(call: &[u8]) -> Result { - let ret_val = unsafe { sys::call_runtime(call.as_ptr(), call.len() as u32) }; - ReturnCode(ret_val).into() - } - - impl_wrapper_for! { - caller, - block_number, - address, - balance, - gas_left, - value_transferred, - now, - minimum_balance, - } - - fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::weight_to_fee(gas, output.as_mut_ptr(), &mut output_len) } - extract_from_slice(output, output_len as usize); - } - - impl_hash_fn!(sha2_256, 32); - impl_hash_fn!(keccak_256, 32); - impl_hash_fn!(blake2_256, 32); - impl_hash_fn!(blake2_128, 16); - - fn ecdsa_recover( - signature: &[u8; 65], - message_hash: &[u8; 32], - output: &mut [u8; 33], - ) -> Result { - let ret_val = unsafe { - sys::ecdsa_recover(signature.as_ptr(), message_hash.as_ptr(), output.as_mut_ptr()) - }; - ReturnCode(ret_val).into() - } - - fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result { - let ret_val = unsafe { sys::ecdsa_to_eth_address(pubkey.as_ptr(), output.as_mut_ptr()) }; - ReturnCode(ret_val).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. - fn sr25519_verify(signature: &[u8; 64], message: &[u8], pub_key: &[u8; 32]) -> Result { - let ret_val = unsafe { - sys::sr25519_verify( - signature.as_ptr(), - pub_key.as_ptr(), - message.len() as u32, - message.as_ptr(), - ) - }; - ReturnCode(ret_val).into() - } - - fn is_contract(account_id: &[u8]) -> bool { - let ret_val = unsafe { sys::is_contract(account_id.as_ptr()) }; - ReturnCode(ret_val).into_bool() - } - - fn caller_is_origin() -> bool { - let ret_val = unsafe { sys::caller_is_origin() }; - ReturnCode(ret_val).into_bool() - } - - fn set_code_hash(code_hash: &[u8]) -> Result { - let ret_val = unsafe { sys::set_code_hash(code_hash.as_ptr()) }; - ReturnCode(ret_val).into() - } - - 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(account_id.as_ptr(), output.as_mut_ptr(), &mut output_len) }; - ReturnCode(ret_val).into() - } - - fn own_code_hash(output: &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::own_code_hash(output.as_mut_ptr(), &mut output_len) } - } -} diff --git a/substrate/frame/contracts/uapi/src/api.rs b/substrate/frame/contracts/uapi/src/host.rs similarity index 97% rename from substrate/frame/contracts/uapi/src/api.rs rename to substrate/frame/contracts/uapi/src/host.rs index c1318d255a595..f8b55d3822e9f 100644 --- a/substrate/frame/contracts/uapi/src/api.rs +++ b/substrate/frame/contracts/uapi/src/host.rs @@ -11,20 +11,14 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::{CallFlags, Result, ReturnFlags}; +use crate::{CallFlags, Result, ReturnFlags, SENTINEL}; use paste::paste; -cfg_if::cfg_if! { - if #[cfg(target_arch = "wasm32")] { - mod common; - mod wasm32; - pub use wasm32::ApiImpl; - } else if #[cfg(target_arch = "riscv32")] { - mod common; - mod riscv32; - pub use riscv32::ApiImpl; - } -} +#[cfg(target_arch = "wasm32")] +mod wasm32; + +#[cfg(target_arch = "riscv32")] +mod riscv32; macro_rules! hash_fn { ( $name:ident, $bytes:literal ) => { @@ -42,8 +36,31 @@ macro_rules! hash_fn { }; } -/// Defines all the user apis implemented by both wasm and RISC-V vms. -pub trait Api { +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]; +} + +fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { + match data { + Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), + None => (SENTINEL as _, 0), + } +} + +fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { + match data { + Some(ref data) => data.as_ptr(), + None => SENTINEL as _, + } +} + +/// Implements [`HostFn`] for each supported target architecture. +pub enum HostFnImpl {} + +/// Defines all the host apis implemented by both wasm and RISC-V vms. +pub trait HostFn { /// Returns the number of times specified contract exists on the call stack. Delegated calls are /// not counted as separate calls. /// diff --git a/substrate/frame/contracts/uapi/src/api/common.rs b/substrate/frame/contracts/uapi/src/host/riscv32.rs similarity index 53% rename from substrate/frame/contracts/uapi/src/api/common.rs rename to substrate/frame/contracts/uapi/src/host/riscv32.rs index 06bffc8beb03b..f58b8831f06d6 100644 --- a/substrate/frame/contracts/uapi/src/api/common.rs +++ b/substrate/frame/contracts/uapi/src/host/riscv32.rs @@ -11,24 +11,4 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -use crate::SENTINEL; - -pub 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]; -} - -pub fn ptr_len_or_sentinel(data: &mut Option<&mut [u8]>) -> (*mut u8, u32) { - match data { - Some(ref mut data) => (data.as_mut_ptr(), data.len() as _), - None => (SENTINEL as _, 0), - } -} - -pub fn ptr_or_sentinel(data: &Option<&[u8]>) -> *const u8 { - match data { - Some(ref data) => data.as_ptr(), - None => SENTINEL as _, - } -} +// TODO: bring up to date with wasm32.rs diff --git a/substrate/frame/contracts/uapi/src/api/wasm32.rs b/substrate/frame/contracts/uapi/src/host/wasm32.rs similarity index 98% rename from substrate/frame/contracts/uapi/src/api/wasm32.rs rename to substrate/frame/contracts/uapi/src/host/wasm32.rs index 69fb4c85d6523..ea8b1e58c72c8 100644 --- a/substrate/frame/contracts/uapi/src/api/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/host/wasm32.rs @@ -13,8 +13,7 @@ // limitations under the License. // #![allow(unused_variables)] use super::{ - common::{extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel}, - Api, CallFlags, Result, + extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel, CallFlags, HostFn, HostFnImpl, Result, }; use crate::{ReturnCode, ReturnFlags}; @@ -281,7 +280,7 @@ mod sys { } } -/// A macro to implement all Api functions with a signature of `fn(&mut &mut [u8])`. +/// A macro to implement all Host functions with a signature of `fn(&mut &mut [u8])`. macro_rules! impl_wrapper_for { (@impl_fn $( $mod:ident )::*, $suffix:literal, $name:ident) => { paste::paste! { @@ -345,9 +344,7 @@ macro_rules! impl_get_storage { }; } -pub enum ApiImpl {} - -impl Api for ApiImpl { +impl HostFn for HostFnImpl { fn instantiate_v1( code_hash: &[u8], gas: u64, diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index efd99b5656cf0..1811a8e98b3e0 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -21,8 +21,11 @@ mod flags; pub use flags::*; -mod api; -pub use api::*; +#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +mod host; + +#[cfg(any(target_arch = "wasm32", target_arch = "riscv32"))] +pub use host::*; macro_rules! define_error_codes { ( From 7fb1943a15a85279003e86a5d33be8eca861b13e Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 28 Nov 2023 12:44:31 +0100 Subject: [PATCH 74/81] Update substrate/frame/contracts/fixtures/build.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen --- substrate/frame/contracts/fixtures/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 060f2d5eb9a74..70baf21596974 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -111,7 +111,7 @@ edition = '2021' [[bin]] [dependencies] -uapi = {{ package = 'pallet-contracts-uapi', default-features = false, path = {uapi_path:?}}} +uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}}} common = {{ package = 'pallet-contracts-fixtures-common', path = {common_path:?}}} [profile.release] From 66c140d0ec8ea2ab85910ded88968b244dc07016 Mon Sep 17 00:00:00 2001 From: PG Herveou Date: Tue, 28 Nov 2023 13:04:39 +0100 Subject: [PATCH 75/81] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Theißen --- substrate/frame/contracts/fixtures/contracts/call.rs | 4 ++-- substrate/frame/contracts/fixtures/contracts/dummy.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/contracts/fixtures/contracts/call.rs b/substrate/frame/contracts/fixtures/contracts/call.rs index e7a2930d4bf6a..396b71d5e9695 100644 --- a/substrate/frame/contracts/fixtures/contracts/call.rs +++ b/substrate/frame/contracts/fixtures/contracts/call.rs @@ -23,10 +23,10 @@ extern crate common; use uapi::{CallFlags, HostFn, HostFnImpl as api}; #[no_mangle] -pub fn deploy() {} +pub extern "C" fn deploy() {} #[no_mangle] -pub fn call() { +pub extern "C" fn call() { let mut buffer = [0u8; 40]; let callee_input = 0..4; let callee_addr = 4..36; diff --git a/substrate/frame/contracts/fixtures/contracts/dummy.rs b/substrate/frame/contracts/fixtures/contracts/dummy.rs index 24898160c8363..98b9d494bbc67 100644 --- a/substrate/frame/contracts/fixtures/contracts/dummy.rs +++ b/substrate/frame/contracts/fixtures/contracts/dummy.rs @@ -20,7 +20,7 @@ extern crate common; #[no_mangle] -pub fn deploy() {} +pub extern "C" fn deploy() {} #[no_mangle] -pub fn call() {} +pub extern "C" fn call() {} From 6ef28663d2358a50b8c67001633209b326755c38 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 13:04:14 +0100 Subject: [PATCH 76/81] fix tab / space mixed ups --- substrate/frame/contracts/uapi/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/contracts/uapi/src/lib.rs b/substrate/frame/contracts/uapi/src/lib.rs index 1811a8e98b3e0..3d384bbb85ddf 100644 --- a/substrate/frame/contracts/uapi/src/lib.rs +++ b/substrate/frame/contracts/uapi/src/lib.rs @@ -35,10 +35,10 @@ macro_rules! define_error_codes { )* ) => { /// Every error that can be returned to a contract when it calls any of the host functions. - #[derive(Debug)] + #[derive(Debug)] #[repr(u32)] pub enum ReturnErrorCode { - /// API call successful. + /// API call successful. Success = 0, $( $( #[$attr] )* From 892225129d1aec80ac37597213eae0b0164a4156 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 13:06:17 +0100 Subject: [PATCH 77/81] rm deps reserved for risc-v --- Cargo.lock | 2 -- substrate/frame/contracts/uapi/Cargo.toml | 9 --------- 2 files changed, 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d9f380e969046..5950086bc6576 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9867,9 +9867,7 @@ name = "pallet-contracts-uapi" version = "4.0.0-dev" dependencies = [ "bitflags 1.3.2", - "parity-scale-codec", "paste", - "scale-info", ] [[package]] diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index 38927c59cec38..8ec65298422fc 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -9,15 +9,6 @@ repository.workspace = true description = "Exposes all the host functions that a contract can import." [dependencies] -scale-info = { version = "2.10.0", default-features = false, features = ["derive"], optional = true } -scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ - "derive", - "max-encoded-len", -], optional = true } paste = { version = "1.0", default-features = false } bitflags = "1.0" -[features] -default = [ "scale" ] -scale = [ "dep:scale", "scale-info" ] - From 78f8ddff406e430e98a517f295b8102e087af481 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 13:08:46 +0100 Subject: [PATCH 78/81] rm leftover --- substrate/frame/contracts/uapi/src/host/wasm32.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/substrate/frame/contracts/uapi/src/host/wasm32.rs b/substrate/frame/contracts/uapi/src/host/wasm32.rs index ea8b1e58c72c8..d30058daf3dff 100644 --- a/substrate/frame/contracts/uapi/src/host/wasm32.rs +++ b/substrate/frame/contracts/uapi/src/host/wasm32.rs @@ -11,7 +11,6 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// #![allow(unused_variables)] use super::{ extract_from_slice, ptr_len_or_sentinel, ptr_or_sentinel, CallFlags, HostFn, HostFnImpl, Result, }; From 97f22a40b7cb713e8610bfb66ae08ec291da6384 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Tue, 28 Nov 2023 13:12:29 +0100 Subject: [PATCH 79/81] fix doc links --- substrate/frame/contracts/src/wasm/runtime.rs | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/substrate/frame/contracts/src/wasm/runtime.rs b/substrate/frame/contracts/src/wasm/runtime.rs index b70c57b331bc7..871ef05c37e65 100644 --- a/substrate/frame/contracts/src/wasm/runtime.rs +++ b/substrate/frame/contracts/src/wasm/runtime.rs @@ -1005,7 +1005,7 @@ impl<'a, E: Ext + 'a> Runtime<'a, E> { pub mod env { /// Set the value at the given key in the contract storage. - /// See [`pallet_contracts_uapi::Api::set_storage`] + /// See [`pallet_contracts_uapi::HostFn::set_storage`] #[prefixed_alias] fn set_storage( ctx: _, @@ -1018,7 +1018,7 @@ pub mod env { } /// Set the value at the given key in the contract storage. - /// See [`pallet_contracts_uapi::Api::set_storage_v1`] + /// See [`pallet_contracts_uapi::HostFn::set_storage_v1`] #[version(1)] #[prefixed_alias] fn set_storage( @@ -1032,7 +1032,7 @@ pub mod env { } /// Set the value at the given key in the contract storage. - /// See [`pallet_contracts_uapi::Api::set_storage_v2`] + /// See [`pallet_contracts_uapi::HostFn::set_storage_v2`] #[version(2)] #[prefixed_alias] fn set_storage( @@ -1047,14 +1047,14 @@ pub mod env { } /// Clear the value at the given key in the contract storage. - /// See [`pallet_contracts_uapi::Api::clear_storage`] + /// See [`pallet_contracts_uapi::HostFn::clear_storage`] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32) -> Result<(), TrapReason> { ctx.clear_storage(memory, KeyType::Fix, key_ptr).map(|_| ()) } /// Clear the value at the given key in the contract storage. - /// See [`pallet_contracts_uapi::Api::clear_storage_v1`] + /// See [`pallet_contracts_uapi::HostFn::clear_storage_v1`] #[version(1)] #[prefixed_alias] fn clear_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { @@ -1062,7 +1062,7 @@ pub mod env { } /// Retrieve the value under the given key from storage. - /// See [`pallet_contracts_uapi::Api::get_storage`] + /// See [`pallet_contracts_uapi::HostFn::get_storage`] #[prefixed_alias] fn get_storage( ctx: _, @@ -1075,7 +1075,7 @@ pub mod env { } /// Retrieve the value under the given key from storage. - /// See [`pallet_contracts_uapi::Api::get_storage_v1`] + /// See [`pallet_contracts_uapi::HostFn::get_storage_v1`] #[version(1)] #[prefixed_alias] fn get_storage( @@ -1090,14 +1090,14 @@ pub mod env { } /// Checks whether there is a value stored under the given key. - /// See [`pallet_contracts_uapi::Api::contains_storage`] + /// See [`pallet_contracts_uapi::HostFn::contains_storage`] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32) -> Result { ctx.contains_storage(memory, KeyType::Fix, key_ptr) } /// Checks whether there is a value stored under the given key. - /// See [`pallet_contracts_uapi::Api::contains_storage_v1`] + /// See [`pallet_contracts_uapi::HostFn::contains_storage_v1`] #[version(1)] #[prefixed_alias] fn contains_storage(ctx: _, memory: _, key_ptr: u32, key_len: u32) -> Result { @@ -1105,7 +1105,7 @@ pub mod env { } /// Retrieve and remove the value under the given key from storage. - /// See [`pallet_contracts_uapi::Api::take_storage`] + /// See [`pallet_contracts_uapi::HostFn::take_storage`] #[prefixed_alias] fn take_storage( ctx: _, @@ -1136,7 +1136,7 @@ pub mod env { } /// Transfer some value to another account. - /// See [`pallet_contracts_uapi::Api::transfer`]. + /// See [`pallet_contracts_uapi::HostFn::transfer`]. #[prefixed_alias] fn transfer( ctx: _, @@ -1198,7 +1198,7 @@ pub mod env { } /// Make a call to another contract. - /// See [`pallet_contracts_uapi::Api::call_v1`]. + /// See [`pallet_contracts_uapi::HostFn::call_v1`]. #[version(1)] #[prefixed_alias] fn call( @@ -1230,7 +1230,7 @@ pub mod env { } /// Make a call to another contract. - /// See [`pallet_contracts_uapi::Api::call_v2`]. + /// See [`pallet_contracts_uapi::HostFn::call_v2`]. #[version(2)] #[unstable] fn call( @@ -1264,7 +1264,7 @@ pub mod env { } /// Execute code in the context (storage, caller, value) of the current contract. - /// See [`pallet_contracts_uapi::Api::delegate_call`]. + /// See [`pallet_contracts_uapi::HostFn::delegate_call`]. #[prefixed_alias] fn delegate_call( ctx: _, @@ -1288,7 +1288,7 @@ pub mod env { } /// Instantiate a contract with the specified code hash. - /// See [`pallet_contracts_uapi::Api::instantiate`]. + /// See [`pallet_contracts_uapi::HostFn::instantiate`]. /// /// # Note /// @@ -1331,7 +1331,7 @@ pub mod env { } /// Instantiate a contract with the specified code hash. - /// See [`pallet_contracts_uapi::Api::instantiate_v1`]. + /// See [`pallet_contracts_uapi::HostFn::instantiate_v1`]. #[version(1)] #[prefixed_alias] fn instantiate( @@ -1367,7 +1367,7 @@ pub mod env { } /// Instantiate a contract with the specified code hash. - /// See [`pallet_contracts_uapi::Api::instantiate_v2`]. + /// See [`pallet_contracts_uapi::HostFn::instantiate_v2`]. #[version(2)] #[unstable] fn instantiate( @@ -1405,7 +1405,7 @@ pub mod env { } /// Remove the calling account and transfer remaining balance. - /// See [`pallet_contracts_uapi::Api::terminate`]. + /// See [`pallet_contracts_uapi::HostFn::terminate`]. /// /// # Note /// @@ -1423,7 +1423,7 @@ pub mod env { } /// Remove the calling account and transfer remaining **free** balance. - /// See [`pallet_contracts_uapi::Api::terminate_v1`]. + /// See [`pallet_contracts_uapi::HostFn::terminate_v1`]. #[version(1)] #[prefixed_alias] fn terminate(ctx: _, memory: _, beneficiary_ptr: u32) -> Result<(), TrapReason> { @@ -1431,7 +1431,7 @@ pub mod env { } /// Stores the input passed by the caller into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::input`]. + /// See [`pallet_contracts_uapi::HostFn::input`]. #[prefixed_alias] fn input(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::InputBase)?; @@ -1447,7 +1447,7 @@ pub mod env { } /// Cease contract execution and save a data buffer as a result of the execution. - /// See [`pallet_contracts_uapi::Api::return_value`]. + /// See [`pallet_contracts_uapi::HostFn::return_value`]. fn seal_return( ctx: _, memory: _, @@ -1463,7 +1463,7 @@ pub mod env { } /// Stores the address of the caller into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::caller`]. + /// See [`pallet_contracts_uapi::HostFn::caller`]. #[prefixed_alias] fn caller(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Caller)?; @@ -1479,7 +1479,7 @@ pub mod env { } /// Checks whether a specified address belongs to a contract. - /// See [`pallet_contracts_uapi::Api::is_contract`]. + /// See [`pallet_contracts_uapi::HostFn::is_contract`]. #[prefixed_alias] fn is_contract(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::IsContract)?; @@ -1490,7 +1490,7 @@ pub mod env { } /// Retrieve the code hash for a specified contract address. - /// See [`pallet_contracts_uapi::Api::code_hash`]. + /// See [`pallet_contracts_uapi::HostFn::code_hash`]. #[prefixed_alias] fn code_hash( ctx: _, @@ -1518,7 +1518,7 @@ pub mod env { } /// Retrieve the code hash of the currently executing contract. - /// See [`pallet_contracts_uapi::Api::own_code_hash`]. + /// See [`pallet_contracts_uapi::HostFn::own_code_hash`]. #[prefixed_alias] fn own_code_hash(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::OwnCodeHash)?; @@ -1534,7 +1534,7 @@ pub mod env { } /// Checks whether the caller of the current contract is the origin of the whole call stack. - /// See [`pallet_contracts_uapi::Api::caller_is_origin`]. + /// See [`pallet_contracts_uapi::HostFn::caller_is_origin`]. #[prefixed_alias] fn caller_is_origin(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsOrigin)?; @@ -1542,7 +1542,7 @@ pub mod env { } /// Checks whether the caller of the current contract is root. - /// See [`pallet_contracts_uapi::Api::caller_is_root`]. + /// See [`pallet_contracts_uapi::HostFn::caller_is_root`]. #[unstable] fn caller_is_root(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::CallerIsRoot)?; @@ -1550,7 +1550,7 @@ pub mod env { } /// Stores the address of the current contract into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::address`]. + /// See [`pallet_contracts_uapi::HostFn::address`]. #[prefixed_alias] fn address(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Address)?; @@ -1565,7 +1565,7 @@ pub mod env { } /// Stores the price for the specified amount of gas into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::weight_to_fee`]. + /// See [`pallet_contracts_uapi::HostFn::weight_to_fee`]. #[prefixed_alias] fn weight_to_fee( ctx: _, @@ -1587,7 +1587,7 @@ pub mod env { } /// Stores the price for the specified amount of weight into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::weight_to_fee_v1`]. + /// See [`pallet_contracts_uapi::HostFn::weight_to_fee_v1`]. #[version(1)] #[unstable] fn weight_to_fee( @@ -1611,7 +1611,7 @@ pub mod env { } /// Stores the weight left into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::gas_left`]. + /// See [`pallet_contracts_uapi::HostFn::gas_left`]. #[prefixed_alias] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::GasLeft)?; @@ -1627,7 +1627,7 @@ pub mod env { } /// Stores the amount of weight left into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::gas_left_v1`]. + /// See [`pallet_contracts_uapi::HostFn::gas_left_v1`]. #[version(1)] #[unstable] fn gas_left(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { @@ -1644,7 +1644,7 @@ pub mod env { } /// Stores the *free* balance of the current account into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::balance`]. + /// See [`pallet_contracts_uapi::HostFn::balance`]. #[prefixed_alias] fn balance(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Balance)?; @@ -1659,7 +1659,7 @@ pub mod env { } /// Stores the value transferred along with this call/instantiate into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::value_transferred`]. + /// See [`pallet_contracts_uapi::HostFn::value_transferred`]. #[prefixed_alias] fn value_transferred( ctx: _, @@ -1759,7 +1759,7 @@ pub mod env { } /// Load the latest block timestamp into the supplied buffer - /// See [`pallet_contracts_uapi::Api::now`]. + /// See [`pallet_contracts_uapi::HostFn::now`]. #[prefixed_alias] fn now(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::Now)?; @@ -1774,7 +1774,7 @@ pub mod env { } /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::minimum_balance`]. + /// See [`pallet_contracts_uapi::HostFn::minimum_balance`]. #[prefixed_alias] fn minimum_balance( ctx: _, @@ -1923,7 +1923,7 @@ pub mod env { } /// Deposit a contract event with the data buffer and optional list of topics. - /// See [pallet_contracts_uapi::Api::deposit_event] + /// See [pallet_contracts_uapi::HostFn::deposit_event] #[prefixed_alias] fn deposit_event( ctx: _, @@ -1959,7 +1959,7 @@ pub mod env { } /// Stores the current block number of the current contract into the supplied buffer. - /// See [`pallet_contracts_uapi::Api::block_number`]. + /// See [`pallet_contracts_uapi::HostFn::block_number`]. #[prefixed_alias] fn block_number(ctx: _, memory: _, out_ptr: u32, out_len_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::BlockNumber)?; @@ -1974,7 +1974,7 @@ pub mod env { } /// Computes the SHA2 256-bit hash on the given input buffer. - /// See [`pallet_contracts_uapi::Api::hash_sha2_256`]. + /// See [`pallet_contracts_uapi::HostFn::hash_sha2_256`]. #[prefixed_alias] fn hash_sha2_256( ctx: _, @@ -1990,7 +1990,7 @@ pub mod env { } /// Computes the KECCAK 256-bit hash on the given input buffer. - /// See [`pallet_contracts_uapi::Api::hash_keccak_256`]. + /// See [`pallet_contracts_uapi::HostFn::hash_keccak_256`]. #[prefixed_alias] fn hash_keccak_256( ctx: _, @@ -2006,7 +2006,7 @@ pub mod env { } /// Computes the BLAKE2 256-bit hash on the given input buffer. - /// See [`pallet_contracts_uapi::Api::hash_blake2_256`]. + /// See [`pallet_contracts_uapi::HostFn::hash_blake2_256`]. #[prefixed_alias] fn hash_blake2_256( ctx: _, @@ -2022,7 +2022,7 @@ pub mod env { } /// Computes the BLAKE2 128-bit hash on the given input buffer. - /// See [`pallet_contracts_uapi::Api::hash_blake2_128`]. + /// See [`pallet_contracts_uapi::HostFn::hash_blake2_128`]. #[prefixed_alias] fn hash_blake2_128( ctx: _, @@ -2038,7 +2038,7 @@ pub mod env { } /// Call into the chain extension provided by the chain if any. - /// See [`pallet_contracts_uapi::Api::call_chain_extension`]. + /// See [`pallet_contracts_uapi::HostFn::call_chain_extension`]. #[prefixed_alias] fn call_chain_extension( ctx: _, @@ -2121,7 +2121,7 @@ pub mod env { } /// Execute an XCM program locally, using the contract's address as the origin. - /// See [`pallet_contracts_uapi::Api::execute_xcm`]. + /// See [`pallet_contracts_uapi::HostFn::execute_xcm`]. #[unstable] fn xcm_execute( ctx: _, @@ -2160,7 +2160,7 @@ pub mod env { } /// Send an XCM program from the contract to the specified destination. - /// See [`pallet_contracts_uapi::Api::send_xcm`]. + /// See [`pallet_contracts_uapi::HostFn::send_xcm`]. #[unstable] fn xcm_send( ctx: _, @@ -2198,7 +2198,7 @@ pub mod env { } /// Recovers the ECDSA public key from the given message hash and signature. - /// See [`pallet_contracts_uapi::Api::ecdsa_recover`]. + /// See [`pallet_contracts_uapi::HostFn::ecdsa_recover`]. #[prefixed_alias] fn ecdsa_recover( ctx: _, @@ -2229,7 +2229,7 @@ pub mod env { } /// Verify a sr25519 signature - /// See [`pallet_contracts_uapi::Api::sr25519_verify`]. + /// See [`pallet_contracts_uapi::HostFn::sr25519_verify`]. #[unstable] fn sr25519_verify( ctx: _, @@ -2257,7 +2257,7 @@ pub mod env { } /// Replace the contract code at the specified address with new code. - /// See [`pallet_contracts_uapi::Api::set_code_hash`]. + /// See [`pallet_contracts_uapi::HostFn::set_code_hash`]. #[prefixed_alias] fn set_code_hash(ctx: _, memory: _, code_hash_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::SetCodeHash)?; @@ -2273,7 +2273,7 @@ pub mod env { } /// Calculates Ethereum address from the ECDSA compressed public key and stores - /// See [`pallet_contracts_uapi::Api::ecdsa_to_eth_address`]. + /// See [`pallet_contracts_uapi::HostFn::ecdsa_to_eth_address`]. #[prefixed_alias] fn ecdsa_to_eth_address( ctx: _, @@ -2296,7 +2296,7 @@ pub mod env { /// Returns the number of times the currently executing contract exists on the call stack in /// addition to the calling instance. - /// See [`pallet_contracts_uapi::Api::reentrance_count`]. + /// See [`pallet_contracts_uapi::HostFn::reentrance_count`]. #[unstable] fn reentrance_count(ctx: _, memory: _) -> Result { ctx.charge_gas(RuntimeCosts::ReentrantCount)?; @@ -2305,7 +2305,7 @@ pub mod env { /// Returns the number of times specified contract exists on the call stack. Delegated calls are /// not counted as separate calls. - /// See [`pallet_contracts_uapi::Api::account_reentrance_count`]. + /// See [`pallet_contracts_uapi::HostFn::account_reentrance_count`]. #[unstable] fn account_reentrance_count(ctx: _, memory: _, account_ptr: u32) -> Result { ctx.charge_gas(RuntimeCosts::AccountEntranceCount)?; @@ -2315,14 +2315,14 @@ pub mod env { } /// Returns a nonce that is unique per contract instantiation. - /// See [`pallet_contracts_uapi::Api::instantiation_nonce`]. + /// See [`pallet_contracts_uapi::HostFn::instantiation_nonce`]. fn instantiation_nonce(ctx: _, _memory: _) -> Result { ctx.charge_gas(RuntimeCosts::InstantationNonce)?; Ok(ctx.ext.nonce()) } /// Adds a new delegate dependency to the contract. - /// See [`pallet_contracts_uapi::Api::add_delegate_dependency`]. + /// See [`pallet_contracts_uapi::HostFn::add_delegate_dependency`]. #[unstable] fn add_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::AddDelegateDependency)?; @@ -2332,7 +2332,7 @@ pub mod env { } /// Removes the delegate dependency from the contract. - /// see [`pallet_contracts_uapi::Api::remove_delegate_dependency`]. + /// see [`pallet_contracts_uapi::HostFn::remove_delegate_dependency`]. #[unstable] fn remove_delegate_dependency(ctx: _, memory: _, code_hash_ptr: u32) -> Result<(), TrapReason> { ctx.charge_gas(RuntimeCosts::RemoveDelegateDependency)?; From 39e423f41d3f6044c52a0e9e0f2d9c603839ad0b Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 29 Nov 2023 09:03:42 +0100 Subject: [PATCH 80/81] Add back scale to uapi The feature is required to build pallet-contracts that require scale encoding on some of the types exposed by uapi now --- Cargo.lock | 2 ++ substrate/frame/contracts/fixtures/build.rs | 2 +- substrate/frame/contracts/uapi/Cargo.toml | 9 +++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5950086bc6576..d9f380e969046 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9867,7 +9867,9 @@ name = "pallet-contracts-uapi" version = "4.0.0-dev" dependencies = [ "bitflags 1.3.2", + "parity-scale-codec", "paste", + "scale-info", ] [[package]] diff --git a/substrate/frame/contracts/fixtures/build.rs b/substrate/frame/contracts/fixtures/build.rs index 70baf21596974..ada2650c6db58 100644 --- a/substrate/frame/contracts/fixtures/build.rs +++ b/substrate/frame/contracts/fixtures/build.rs @@ -111,7 +111,7 @@ edition = '2021' [[bin]] [dependencies] -uapi = {{ package = 'pallet-contracts-uapi', path = {uapi_path:?}}} +uapi = {{ package = 'pallet-contracts-uapi', default-features = false, path = {uapi_path:?}}} common = {{ package = 'pallet-contracts-fixtures-common', path = {common_path:?}}} [profile.release] diff --git a/substrate/frame/contracts/uapi/Cargo.toml b/substrate/frame/contracts/uapi/Cargo.toml index 8ec65298422fc..6717d574dfed1 100644 --- a/substrate/frame/contracts/uapi/Cargo.toml +++ b/substrate/frame/contracts/uapi/Cargo.toml @@ -11,4 +11,13 @@ description = "Exposes all the host functions that a contract can import." [dependencies] paste = { version = "1.0", default-features = false } bitflags = "1.0" +scale-info = { version = "2.10.0", default-features = false, features = ["derive"], optional = true } +scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = [ + "derive", + "max-encoded-len", +], optional = true } + +[features] +default = [ "scale" ] +scale = [ "dep:scale", "scale-info" ] From 7c9322baafaa14ad213e7675c24e1648da2e1ab6 Mon Sep 17 00:00:00 2001 From: pgherveou Date: Wed, 29 Nov 2023 09:14:32 +0100 Subject: [PATCH 81/81] fix merge - rm call.wat --- substrate/frame/contracts/fixtures/data/call.wat | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 substrate/frame/contracts/fixtures/data/call.wat diff --git a/substrate/frame/contracts/fixtures/data/call.wat b/substrate/frame/contracts/fixtures/data/call.wat deleted file mode 100644 index e69de29bb2d1d..0000000000000