diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 9216001e39b..3fa22bb90f6 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -322,15 +322,16 @@ where /// - If the instantiation process runs out of gas. /// - If given insufficient endowment. /// - If the returned account ID failed to decode properly. -pub fn instantiate_contract( - params: &CreateParams, +pub fn instantiate_contract( + params: &CreateParams, ) -> Result where T: Environment, Args: scale::Encode, + Salt: AsRef<[u8]>, { ::on_instance(|instance| { - TypedEnvBackend::instantiate_contract::(instance, params) + TypedEnvBackend::instantiate_contract::(instance, params) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index dabd9e6c7bc..48ac4b17484 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -293,13 +293,14 @@ pub trait TypedEnvBackend: EnvBackend { /// # Note /// /// For more details visit: [`ink_env::instantiate_contract`] - fn instantiate_contract( + fn instantiate_contract( &mut self, - params: &CreateParams, + params: &CreateParams, ) -> Result where T: Environment, - Args: scale::Encode; + Args: scale::Encode, + Salt: AsRef<[u8]>; /// Restores a smart contract tombstone. /// diff --git a/crates/env/src/call/create_builder.rs b/crates/env/src/call/create_builder.rs index 455709a9bd9..ffff508929d 100644 --- a/crates/env/src/call/create_builder.rs +++ b/crates/env/src/call/create_builder.rs @@ -28,7 +28,15 @@ use crate::{ }; use core::marker::PhantomData; -/// Contracts that can be contructed from an `AccountId`. +pub mod state { + //! Type states that tell what state of a instantiation argument has not + //! yet been set properly for a valid construction. + + /// Type state for the salt used for contract instantiation. + pub enum Salt {} +} + +/// Contracts that can be constructed from an `AccountId`. /// /// # Note /// @@ -44,7 +52,7 @@ where /// Builds up contract instantiations. #[derive(Debug)] -pub struct CreateParams +pub struct CreateParams where E: Environment, { @@ -54,51 +62,67 @@ where gas_limit: u64, /// The endowment for the instantiated contract. endowment: E::Balance, - /// The input data for the instantation. + /// The input data for the instantiation. exec_input: ExecutionInput, + /// The salt for determining the hash for the contract account ID. + salt_bytes: Salt, /// The type of the instantiated contract. return_type: ReturnType, } -#[cfg( +cfg_if::cfg_if! { // We do not currently support cross-contract instantiation in the off-chain // environment so we do not have to provide these getters in case of // off-chain environment compilation. - all(not(feature = "std"), target_arch = "wasm32") -)] -impl CreateParams -where - E: Environment, -{ - /// The code hash of the contract. - #[inline] - pub(crate) fn code_hash(&self) -> &E::Hash { - &self.code_hash - } + if #[cfg(all(not(feature = "std"), target_arch = "wasm32"))] { + impl CreateParams + where + E: Environment, + { + /// The code hash of the contract. + #[inline] + pub(crate) fn code_hash(&self) -> &E::Hash { + &self.code_hash + } - /// The gas limit for the contract instantiation. - #[inline] - pub(crate) fn gas_limit(&self) -> u64 { - self.gas_limit - } + /// The gas limit for the contract instantiation. + #[inline] + pub(crate) fn gas_limit(&self) -> u64 { + self.gas_limit + } - /// The endowment for the instantiated contract. - #[inline] - pub(crate) fn endowment(&self) -> &E::Balance { - &self.endowment - } + /// The endowment for the instantiated contract. + #[inline] + pub(crate) fn endowment(&self) -> &E::Balance { + &self.endowment + } - /// The raw encoded input data. - #[inline] - pub(crate) fn exec_input(&self) -> &ExecutionInput { - &self.exec_input + /// The raw encoded input data. + #[inline] + pub(crate) fn exec_input(&self) -> &ExecutionInput { + &self.exec_input + } + } + + impl CreateParams + where + E: Environment, + Salt: AsRef<[u8]>, + { + /// The salt for determining the hash for the contract account ID. + #[inline] + pub(crate) fn salt_bytes(&self) -> &Salt { + &self.salt_bytes + } + } } } -impl CreateParams +impl CreateParams where E: Environment, Args: scale::Encode, + Salt: AsRef<[u8]>, R: FromAccountId, { /// Instantiates the contract and returns its account ID back to the caller. @@ -109,7 +133,7 @@ where } /// Builds up contract instantiations. -pub struct CreateBuilder +pub struct CreateBuilder where E: Environment, { @@ -118,6 +142,7 @@ where gas_limit: GasLimit, endowment: Endowment, exec_input: Args, + salt: Salt, return_type: ReturnType, } @@ -145,6 +170,7 @@ where /// # }; /// # type Hash = ::Hash; /// # type AccountId = ::AccountId; +/// # type Salt = &'static [u8]; /// # struct MyContract; /// # impl FromAccountId for MyContract { /// # fn from_account_id(account_id: AccountId) -> Self { Self } @@ -159,6 +185,7 @@ where /// .push_arg(true) /// .push_arg(&[0x10u8; 32]) /// ) +/// .salt_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]) /// .params() /// .instantiate() /// .unwrap(); @@ -174,6 +201,7 @@ pub fn build_create() -> CreateBuilder< Unset, Unset, Unset>, + Unset, R, > where @@ -186,12 +214,13 @@ where gas_limit: Default::default(), endowment: Default::default(), exec_input: Default::default(), + salt: Default::default(), return_type: Default::default(), } } -impl - CreateBuilder, GasLimit, Endowment, Args, R> +impl + CreateBuilder, GasLimit, Endowment, Args, Salt, R> where E: Environment, { @@ -200,20 +229,21 @@ where pub fn code_hash( self, code_hash: E::Hash, - ) -> CreateBuilder, GasLimit, Endowment, Args, R> { + ) -> CreateBuilder, GasLimit, Endowment, Args, Salt, R> { CreateBuilder { env: Default::default(), code_hash: Set(code_hash), gas_limit: self.gas_limit, endowment: self.endowment, exec_input: self.exec_input, + salt: self.salt, return_type: self.return_type, } } } -impl - CreateBuilder, Endowment, Args, R> +impl + CreateBuilder, Endowment, Args, Salt, R> where E: Environment, { @@ -222,20 +252,21 @@ where pub fn gas_limit( self, gas_limit: u64, - ) -> CreateBuilder, Endowment, Args, R> { + ) -> CreateBuilder, Endowment, Args, Salt, R> { CreateBuilder { env: Default::default(), code_hash: self.code_hash, gas_limit: Set(gas_limit), endowment: self.endowment, exec_input: self.exec_input, + salt: self.salt, return_type: self.return_type, } } } -impl - CreateBuilder, Args, R> +impl + CreateBuilder, Args, Salt, R> where E: Environment, { @@ -244,25 +275,27 @@ where pub fn endowment( self, endowment: E::Balance, - ) -> CreateBuilder, Args, R> { + ) -> CreateBuilder, Args, Salt, R> { CreateBuilder { env: Default::default(), code_hash: self.code_hash, gas_limit: self.gas_limit, endowment: Set(endowment), exec_input: self.exec_input, + salt: self.salt, return_type: self.return_type, } } } -impl +impl CreateBuilder< E, CodeHash, GasLimit, Endowment, Unset>, + Salt, R, > where @@ -273,7 +306,7 @@ where pub fn exec_input( self, exec_input: ExecutionInput, - ) -> CreateBuilder>, R> + ) -> CreateBuilder>, Salt, R> { CreateBuilder { env: Default::default(), @@ -281,18 +314,46 @@ where gas_limit: self.gas_limit, endowment: self.endowment, exec_input: Set(exec_input), + salt: self.salt, + return_type: self.return_type, + } + } +} + +impl + CreateBuilder, R> +where + E: Environment, +{ + /// Sets the value transferred upon the execution of the call. + #[inline] + pub fn salt_bytes( + self, + salt: Salt, + ) -> CreateBuilder, R> + where + Salt: AsRef<[u8]>, + { + CreateBuilder { + env: Default::default(), + code_hash: self.code_hash, + gas_limit: self.gas_limit, + endowment: self.endowment, + exec_input: self.exec_input, + salt: Set(salt), return_type: self.return_type, } } } -impl +impl CreateBuilder< E, Set, GasLimit, Set, Set>, + Set, R, > where @@ -301,30 +362,33 @@ where { /// Sets the value transferred upon the execution of the call. #[inline] - pub fn params(self) -> CreateParams { + pub fn params(self) -> CreateParams { CreateParams { code_hash: self.code_hash.value(), gas_limit: self.gas_limit.unwrap_or_else(|| 0), endowment: self.endowment.value(), exec_input: self.exec_input.value(), + salt_bytes: self.salt.value(), return_type: self.return_type, } } } -impl +impl CreateBuilder< E, Set, GasLimit, Set, Set>, + Set, R, > where E: Environment, GasLimit: Unwrap, Args: scale::Encode, + Salt: AsRef<[u8]>, R: FromAccountId, { /// Instantiates the contract using the given instantiation parameters. diff --git a/crates/env/src/call/mod.rs b/crates/env/src/call/mod.rs index f4a6d7abef2..826bff22ede 100644 --- a/crates/env/src/call/mod.rs +++ b/crates/env/src/call/mod.rs @@ -48,6 +48,7 @@ pub use self::{ }, create_builder::{ build_create, + state, CreateBuilder, CreateParams, FromAccountId, diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index d971de478bc..3aa458eb058 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -411,9 +411,9 @@ impl TypedEnvBackend for EnvInstance { unimplemented!("off-chain environment does not support contract evaluation") } - fn instantiate_contract( + fn instantiate_contract( &mut self, - _params: &CreateParams, + _params: &CreateParams, ) -> Result where T: Environment, diff --git a/crates/env/src/engine/on_chain/ext.rs b/crates/env/src/engine/on_chain/ext.rs index 7ca131eadb9..feed9566300 100644 --- a/crates/env/src/engine/on_chain/ext.rs +++ b/crates/env/src/engine/on_chain/ext.rs @@ -210,6 +210,8 @@ mod sys { address_len_ptr: Ptr32Mut, output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut, + salt_ptr: Ptr32<[u8]>, + salt_len: u32, ) -> ReturnCode; pub fn seal_call( @@ -350,6 +352,7 @@ pub fn instantiate( 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; @@ -367,6 +370,8 @@ pub fn instantiate( 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, ) } }; diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index cbc20428d8b..dd3b62a38a1 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -378,13 +378,14 @@ impl TypedEnvBackend for EnvInstance { self.invoke_contract_impl(call_params) } - fn instantiate_contract( + fn instantiate_contract( &mut self, - params: &CreateParams, + params: &CreateParams, ) -> Result where T: Environment, Args: scale::Encode, + Salt: AsRef<[u8]>, { let mut scoped = self.scoped_buffer(); let gas_limit = params.gas_limit(); @@ -395,6 +396,7 @@ impl TypedEnvBackend for EnvInstance { // 1024 bytes. Beyond that limit ink! contracts will trap for now. // In the default configuration encoded `AccountId` require 32 bytes. let out_address = &mut scoped.take(1024); + let salt = params.salt_bytes().as_ref(); let out_return_value = &mut scoped.take_rest(); // We currently do nothing with the `out_return_value` buffer. // This should change in the future but for that we need to add support @@ -407,6 +409,7 @@ impl TypedEnvBackend for EnvInstance { enc_input, out_address, out_return_value, + salt, )?; let account_id = scale::Decode::decode(&mut &out_address[..])?; Ok(account_id) diff --git a/crates/lang/codegen/src/generator/cross_calling.rs b/crates/lang/codegen/src/generator/cross_calling.rs index 72142c5e2bc..104303d405f 100644 --- a/crates/lang/codegen/src/generator/cross_calling.rs +++ b/crates/lang/codegen/src/generator/cross_calling.rs @@ -621,6 +621,7 @@ impl CrossCalling<'_> { ::ink_env::call::utils::Unset, ::ink_env::call::utils::Unset, ::ink_env::call::utils::Set<::ink_env::call::ExecutionInput<#arg_list>>, + ::ink_env::call::utils::Unset<::ink_env::call::state::Salt>, Self, >; @@ -629,7 +630,7 @@ impl CrossCalling<'_> { fn #ident( #( #input_bindings : #input_types ),* ) -> Self::#output_ident { - ::ink_env::call::build_create::() + ::ink_env::call::build_create::() .exec_input( ::ink_env::call::ExecutionInput::new( ::ink_env::call::Selector::new([ #( #composed_selector ),* ]) @@ -724,6 +725,7 @@ impl CrossCalling<'_> { ::ink_env::call::utils::Unset, ::ink_env::call::utils::Unset, ::ink_env::call::utils::Set<::ink_env::call::ExecutionInput<#arg_list>>, + ::ink_env::call::utils::Unset<::ink_env::call::state::Salt>, Self, > { ::ink_env::call::build_create::() diff --git a/crates/lang/src/env_access.rs b/crates/lang/src/env_access.rs index 08a9471e89b..faf316f9a71 100644 --- a/crates/lang/src/env_access.rs +++ b/crates/lang/src/env_access.rs @@ -249,14 +249,15 @@ where /// # Note /// /// For more details visit: [`ink_env::instantiate_contract`] - pub fn instantiate_contract( + pub fn instantiate_contract( self, - params: &CreateParams, + params: &CreateParams, ) -> Result where Args: scale::Encode, + Salt: AsRef<[u8]>, { - ink_env::instantiate_contract::(params) + ink_env::instantiate_contract::(params) } /// Restores a smart contract in tombstone state. diff --git a/examples/delegator/lib.rs b/examples/delegator/lib.rs index 076de31d638..af593f54b14 100644 --- a/examples/delegator/lib.rs +++ b/examples/delegator/lib.rs @@ -80,24 +80,29 @@ mod delegator { #[ink(constructor)] pub fn new( init_value: i32, + version: u32, accumulator_code_hash: Hash, adder_code_hash: Hash, subber_code_hash: Hash, ) -> Self { let total_balance = Self::env().balance(); + let salt = version.to_le_bytes(); let accumulator = Accumulator::new(init_value) .endowment(total_balance / 4) .code_hash(accumulator_code_hash) + .salt_bytes(salt) .instantiate() .expect("failed at instantiating the `Accumulator` contract"); let adder = Adder::new(accumulator.clone()) .endowment(total_balance / 4) .code_hash(adder_code_hash) + .salt_bytes(salt) .instantiate() .expect("failed at instantiating the `Adder` contract"); let subber = Subber::new(accumulator.clone()) .endowment(total_balance / 4) .code_hash(subber_code_hash) + .salt_bytes(salt) .instantiate() .expect("failed at instantiating the `Subber` contract"); Self {