Unverified Commit 1daf6a8e authored by Andrew Jones's avatar Andrew Jones Committed by GitHub
Browse files

[env] pass salt argument to instantiate host function (#599)

* WIP

* Add salt argument to instantiate call

* Fix lang compilation

* Fix salt to [u8; 4]

* Make create builder generic over Salt

* Use type state instead of ()

* Rename salt to salt_bytes

* Fmt

* Apply review suggestion: type state enum

* Apply review suggestion: separate impl block for salt_bytes getter

* Apply review suggestion: take salt bytes directly to avoid copy overhead

* Apply review suggestion: use Salt type state instead of leftover concrete type

* Apply review suggestion: fix doc tests
parent 01f987d7
Pipeline #121925 failed with stages
in 7 minutes and 47 seconds
......@@ -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<T, Args, C>(
params: &CreateParams<T, Args, C>,
pub fn instantiate_contract<T, Args, Salt, C>(
params: &CreateParams<T, Args, Salt, C>,
) -> Result<T::AccountId>
where
T: Environment,
Args: scale::Encode,
Salt: AsRef<[u8]>,
{
<EnvInstance as OnInstance>::on_instance(|instance| {
TypedEnvBackend::instantiate_contract::<T, Args, C>(instance, params)
TypedEnvBackend::instantiate_contract::<T, Args, Salt, C>(instance, params)
})
}
......
......@@ -293,13 +293,14 @@ pub trait TypedEnvBackend: EnvBackend {
/// # Note
///
/// For more details visit: [`ink_env::instantiate_contract`]
fn instantiate_contract<T, Args, C>(
fn instantiate_contract<T, Args, Salt, C>(
&mut self,
params: &CreateParams<T, Args, C>,
params: &CreateParams<T, Args, Salt, C>,
) -> Result<T::AccountId>
where
T: Environment,
Args: scale::Encode;
Args: scale::Encode,
Salt: AsRef<[u8]>;
/// Restores a smart contract tombstone.
///
......
......@@ -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<E, Args, R>
pub struct CreateParams<E, Args, Salt, R>
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<Args>,
/// The salt for determining the hash for the contract account ID.
salt_bytes: Salt,
/// The type of the instantiated contract.
return_type: ReturnType<R>,
}
#[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<E, Args, R> CreateParams<E, Args, R>
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<E, Args, Salt, R> CreateParams<E, Args, Salt, R>
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<Args> {
&self.exec_input
/// The raw encoded input data.
#[inline]
pub(crate) fn exec_input(&self) -> &ExecutionInput<Args> {
&self.exec_input
}
}
impl<E, Args, Salt, R> CreateParams<E, Args, Salt, R>
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<E, Args, R> CreateParams<E, Args, R>
impl<E, Args, Salt, R> CreateParams<E, Args, Salt, R>
where
E: Environment,
Args: scale::Encode,
Salt: AsRef<[u8]>,
R: FromAccountId<E>,
{
/// Instantiates the contract and returns its account ID back to the caller.
......@@ -109,7 +133,7 @@ where
}
/// Builds up contract instantiations.
pub struct CreateBuilder<E, CodeHash, GasLimit, Endowment, Args, R>
pub struct CreateBuilder<E, CodeHash, GasLimit, Endowment, Args, Salt, R>
where
E: Environment,
{
......@@ -118,6 +142,7 @@ where
gas_limit: GasLimit,
endowment: Endowment,
exec_input: Args,
salt: Salt,
return_type: ReturnType<R>,
}
......@@ -145,6 +170,7 @@ where
/// # };
/// # type Hash = <DefaultEnvironment as Environment>::Hash;
/// # type AccountId = <DefaultEnvironment as Environment>::AccountId;
/// # type Salt = &'static [u8];
/// # struct MyContract;
/// # impl FromAccountId<DefaultEnvironment> 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<E, R>() -> CreateBuilder<
Unset<u64>,
Unset<E::Balance>,
Unset<ExecutionInput<EmptyArgumentList>>,
Unset<state::Salt>,
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<E, GasLimit, Endowment, Args, R>
CreateBuilder<E, Unset<E::Hash>, GasLimit, Endowment, Args, R>
impl<E, GasLimit, Endowment, Args, Salt, R>
CreateBuilder<E, Unset<E::Hash>, GasLimit, Endowment, Args, Salt, R>
where
E: Environment,
{
......@@ -200,20 +229,21 @@ where
pub fn code_hash(
self,
code_hash: E::Hash,
) -> CreateBuilder<E, Set<E::Hash>, GasLimit, Endowment, Args, R> {
) -> CreateBuilder<E, Set<E::Hash>, 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<E, CodeHash, Endowment, Args, R>
CreateBuilder<E, CodeHash, Unset<u64>, Endowment, Args, R>
impl<E, CodeHash, Endowment, Args, Salt, R>
CreateBuilder<E, CodeHash, Unset<u64>, Endowment, Args, Salt, R>
where
E: Environment,
{
......@@ -222,20 +252,21 @@ where
pub fn gas_limit(
self,
gas_limit: u64,
) -> CreateBuilder<E, CodeHash, Set<u64>, Endowment, Args, R> {
) -> CreateBuilder<E, CodeHash, Set<u64>, 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<E, CodeHash, GasLimit, Args, R>
CreateBuilder<E, CodeHash, GasLimit, Unset<E::Balance>, Args, R>
impl<E, CodeHash, GasLimit, Args, Salt, R>
CreateBuilder<E, CodeHash, GasLimit, Unset<E::Balance>, Args, Salt, R>
where
E: Environment,
{
......@@ -244,25 +275,27 @@ where
pub fn endowment(
self,
endowment: E::Balance,
) -> CreateBuilder<E, CodeHash, GasLimit, Set<E::Balance>, Args, R> {
) -> CreateBuilder<E, CodeHash, GasLimit, Set<E::Balance>, 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<E, CodeHash, GasLimit, Endowment, R>
impl<E, CodeHash, GasLimit, Endowment, Salt, R>
CreateBuilder<
E,
CodeHash,
GasLimit,
Endowment,
Unset<ExecutionInput<EmptyArgumentList>>,
Salt,
R,
>
where
......@@ -273,7 +306,7 @@ where
pub fn exec_input<Args>(
self,
exec_input: ExecutionInput<Args>,
) -> CreateBuilder<E, CodeHash, GasLimit, Endowment, Set<ExecutionInput<Args>>, R>
) -> CreateBuilder<E, CodeHash, GasLimit, Endowment, Set<ExecutionInput<Args>>, 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<E, CodeHash, GasLimit, Endowment, Args, R>
CreateBuilder<E, CodeHash, GasLimit, Endowment, Args, Unset<state::Salt>, R>
where
E: Environment,
{
/// Sets the value transferred upon the execution of the call.
#[inline]
pub fn salt_bytes<Salt>(
self,
salt: Salt,
) -> CreateBuilder<E, CodeHash, GasLimit, Endowment, Args, Set<Salt>, 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<E, GasLimit, Args, R>
impl<E, GasLimit, Args, Salt, R>
CreateBuilder<
E,
Set<E::Hash>,
GasLimit,
Set<E::Balance>,
Set<ExecutionInput<Args>>,
Set<Salt>,
R,
>
where
......@@ -301,30 +362,33 @@ where
{
/// Sets the value transferred upon the execution of the call.
#[inline]
pub fn params(self) -> CreateParams<E, Args, R> {
pub fn params(self) -> CreateParams<E, Args, Salt, R> {
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<E, GasLimit, Args, R>
impl<E, GasLimit, Args, Salt, R>
CreateBuilder<
E,
Set<E::Hash>,
GasLimit,
Set<E::Balance>,
Set<ExecutionInput<Args>>,
Set<Salt>,
R,
>
where
E: Environment,
GasLimit: Unwrap<Output = u64>,
Args: scale::Encode,
Salt: AsRef<[u8]>,
R: FromAccountId<E>,
{
/// Instantiates the contract using the given instantiation parameters.
......
......@@ -48,6 +48,7 @@ pub use self::{
},
create_builder::{
build_create,
state,
CreateBuilder,
CreateParams,
FromAccountId,
......
......@@ -411,9 +411,9 @@ impl TypedEnvBackend for EnvInstance {
unimplemented!("off-chain environment does not support contract evaluation")
}
fn instantiate_contract<T, Args, C>(
fn instantiate_contract<T, Args, Salt, C>(
&mut self,
_params: &CreateParams<T, Args, C>,
_params: &CreateParams<T, Args, Salt, C>,
) -> Result<T::AccountId>
where
T: Environment,
......
......@@ -210,6 +210,8 @@ mod sys {
address_len_ptr: Ptr32Mut<u32>,
output_ptr: Ptr32Mut<[u8]>,
output_len_ptr: Ptr32Mut<u32>,
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,
)
}
};
......
......@@ -378,13 +378,14 @@ impl TypedEnvBackend for EnvInstance {
self.invoke_contract_impl(call_params)
}
fn instantiate_contract<T, Args, C>(
fn instantiate_contract<T, Args, Salt, C>(
&mut self,
params: &CreateParams<T, Args, C>,
params: &CreateParams<T, Args, Salt, C>,
) -> Result<T::AccountId>
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)
......
......@@ -621,6 +621,7 @@ impl CrossCalling<'_> {
::ink_env::call::utils::Unset<u64>,
::ink_env::call::utils::Unset<Balance>,
::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::<Environment, Self>()
::ink_env::call::build_create::<Environment, Salt, Self>()
.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<u64>,
::ink_env::call::utils::Unset<Balance>,
::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::<Environment, Self>()
......
......@@ -249,14 +249,15 @@ where
/// # Note
///
/// For more details visit: [`ink_env::instantiate_contract`]
pub fn instantiate_contract<Args, C>(
pub fn instantiate_contract<Args, Salt, C>(
self,
params: &CreateParams<T, Args, C>,
params: &CreateParams<T, Args, Salt, C>,
) -> Result<T::AccountId>
where
Args: scale::Encode,
Salt: AsRef<[u8]>,
{
ink_env::instantiate_contract::<T, Args, C>(params)
ink_env::instantiate_contract::<T, Args, Salt, C>(params)
}
/// Restores a smart contract in tombstone state.
......
......@@ -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 {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment