Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Prev Previous commit
Next Next commit
Rework contract address determination
* Do not allow override by runtime author
* Instantiate gained a new parameter "salt"

This change is done now in expecation of the upcoming code rent
which needs to change the instantiation dispatchable and
host function anyways.

The situation in where we have only something that is like CREATE2
makes it impossible for UIs to help the user to create an arbitrary
amount of instantiations from the same code.

With this change we have the same functionality as ethereum with
a CREATE and CREATE2 instantation semantic.
  • Loading branch information
athei committed Nov 10, 2020
commit f1ca2916db17835c6f71ac641a32b9d2212c7162
1 change: 0 additions & 1 deletion bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,6 @@ impl pallet_contracts::Trait for Runtime {
type Randomness = RandomnessCollectiveFlip;
type Currency = Balances;
type Event = Event;
type DetermineContractAddress = pallet_contracts::SimpleAddressDeterminer<Runtime>;
type TrieIdGenerator = pallet_contracts::TrieIdFromParentCounter<Runtime>;
type RentPayment = ();
type SignedClaimHandicap = pallet_contracts::DefaultSignedClaimHandicap;
Expand Down
13 changes: 6 additions & 7 deletions frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

use crate::{
CodeHash, Config, ContractAddressFor, Event, RawEvent, Trait,
CodeHash, Config, Event, RawEvent, Trait, Module as Contracts,
TrieId, BalanceOf, ContractInfo, TrieIdGenerator,
gas::GasMeter, rent::Rent, storage::{self, Storage}, Error, ContractInfoOf
};
Expand Down Expand Up @@ -76,6 +76,7 @@ pub trait Ext {
value: BalanceOf<Self::T>,
gas_meter: &mut GasMeter<Self::T>,
input_data: Vec<u8>,
salt: &[u8],
) -> Result<(AccountIdOf<Self::T>, ExecReturnValue), ExecError>;

/// Transfer some amount of funds into the specified account.
Expand Down Expand Up @@ -310,18 +311,15 @@ where
gas_meter: &mut GasMeter<T>,
code_hash: &CodeHash<T>,
input_data: Vec<u8>,
salt: &[u8],
) -> Result<(T::AccountId, ExecReturnValue), ExecError> {
if self.depth == self.config.max_depth as usize {
Err(Error::<T>::MaxCallDepthReached)?
}

let transactor_kind = self.transactor_kind();
let caller = self.self_account.clone();
let dest = T::DetermineContractAddress::contract_address_for(
code_hash,
&input_data,
&caller,
);
let dest = Contracts::<T>::contract_address(&caller, code_hash, salt);

// TrieId has not been generated yet and storage is empty since contract is new.
//
Expand Down Expand Up @@ -537,8 +535,9 @@ where
endowment: BalanceOf<T>,
gas_meter: &mut GasMeter<T>,
input_data: Vec<u8>,
salt: &[u8],
) -> Result<(AccountIdOf<T>, ExecReturnValue), ExecError> {
self.ctx.instantiate(endowment, gas_meter, code_hash, input_data)
self.ctx.instantiate(endowment, gas_meter, code_hash, input_data, salt)
}

fn transfer(
Expand Down
64 changes: 29 additions & 35 deletions frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,6 @@ use frame_support::weights::Weight;
pub type CodeHash<T> = <T as frame_system::Trait>::Hash;
pub type TrieId = Vec<u8>;

/// A function that generates an `AccountId` for a contract upon instantiation.
pub trait ContractAddressFor<CodeHash, AccountId> {
fn contract_address_for(code_hash: &CodeHash, data: &[u8], origin: &AccountId) -> AccountId;
}

/// Information for managing an account and its sub trie abstraction.
/// This is the required info to cache for an account
#[derive(Encode, Decode, RuntimeDebug)]
Expand Down Expand Up @@ -331,9 +326,6 @@ pub trait Trait: frame_system::Trait {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;

/// A function type to get the contract address given the instantiator.
type DetermineContractAddress: ContractAddressFor<CodeHash<Self>, Self::AccountId>;

/// trie id generator
type TrieIdGenerator: TrieIdGenerator<Self::AccountId>;

Expand Down Expand Up @@ -387,29 +379,6 @@ pub trait Trait: frame_system::Trait {
type WeightInfo: WeightInfo;
}

/// Simple contract address determiner.
///
/// Address calculated from the code (of the constructor), input data to the constructor,
/// and the account id that requested the account creation.
///
/// Formula: `blake2_256(blake2_256(code) + blake2_256(data) + origin)`
pub struct SimpleAddressDeterminer<T: Trait>(PhantomData<T>);
impl<T: Trait> ContractAddressFor<CodeHash<T>, T::AccountId> for SimpleAddressDeterminer<T>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
{
fn contract_address_for(code_hash: &CodeHash<T>, data: &[u8], origin: &T::AccountId) -> T::AccountId {
let data_hash = T::Hashing::hash(data);

let mut buf = Vec::new();
buf.extend_from_slice(code_hash.as_ref());
buf.extend_from_slice(data_hash.as_ref());
buf.extend_from_slice(origin.as_ref());

UncheckedFrom::unchecked_from(T::Hashing::hash(&buf[..]))
}
}

decl_error! {
/// Error for the contracts module.
pub enum Error for Module<T: Trait>
Expand Down Expand Up @@ -580,11 +549,13 @@ decl_module! {
gas_meter.into_dispatch_result(result)
}

/// Instantiates a new contract from the `codehash` generated by `put_code`, optionally transferring some balance.
/// Instantiates a new contract from the `code_hash` generated by `put_code`, optionally transferring some balance.
///
/// The supplied `salt` is used for contract address deriviation. See `fn contract_address`.
///
/// Instantiation is executed as follows:
///
/// - The destination address is computed based on the sender and hash of the code.
/// - The destination address is computed based on the sender, code_hash and the salt.
/// - The smart-contract account is created at the computed address.
/// - The `ctor_code` is executed in the context of the newly-created account. Buffer returned
/// after the execution is saved as the `code` of the account. That code will be invoked
Expand All @@ -596,13 +567,14 @@ decl_module! {
#[compact] endowment: BalanceOf<T>,
#[compact] gas_limit: Gas,
code_hash: CodeHash<T>,
data: Vec<u8>
data: Vec<u8>,
salt: Vec<u8>,
) -> DispatchResultWithPostInfo {
let origin = ensure_signed(origin)?;
let mut gas_meter = GasMeter::new(gas_limit);

let result = Self::execute_wasm(origin, &mut gas_meter, |ctx, gas_meter| {
ctx.instantiate(endowment, gas_meter, &code_hash, data)
ctx.instantiate(endowment, gas_meter, &code_hash, data, &salt)
.map(|(_address, output)| output)
});
gas_meter.into_dispatch_result(result)
Expand Down Expand Up @@ -694,6 +666,28 @@ where
let result = wasm::save_code_raw::<T>(code, &schedule);
result.map(|_| ()).map_err(Into::into)
}

/// Determine the address of a contract,
///
/// This is the address generation function used by contract instantation. Its result
/// is only dependend on its inputs. It can therefore be used to reliably predict the
/// address of a contract. This is akin to the formular of eth's CRATE2 opcode. There
/// is no CREATE equivalent because CREATE2 is strictly more powerful.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure we want ethereum terminology (CREATE2) in docs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did not think that intensely about that. What is your recommendation?

///
/// Formula: `hash(deploying_address ++ code_hash ++ salt)`
pub fn contract_address(
deploying_address: &T::AccountId,
code_hash: &CodeHash<T>,
salt: &[u8],
) -> T::AccountId
{
let buf: Vec<_> = deploying_address.as_ref().iter()
.chain(code_hash.as_ref())
.chain(salt)
.cloned()
.collect();
UncheckedFrom::unchecked_from(T::Hashing::hash(&buf))
}
}

impl<T: Trait> Module<T>
Expand Down
10 changes: 8 additions & 2 deletions frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,8 @@ define_env!(Env, <E: Ext>,
// - output_ptr: a pointer where the output buffer is copied to.
// - output_len_ptr: in-out pointer to where the length of the buffer is read from
// and the actual length is written to.
// - salt_ptr: Pointer to raw bytes used for address deriviation. See `fn contract_address`.
// - salt_len: length in bytes of the supplied salt.
//
// # Errors
//
Expand Down Expand Up @@ -854,13 +856,16 @@ define_env!(Env, <E: Ext>,
address_ptr: u32,
address_len_ptr: u32,
output_ptr: u32,
output_len_ptr: u32
output_len_ptr: u32,
salt_ptr: u32,
salt_len: u32
) -> ReturnCode => {
ctx.charge_gas(RuntimeToken::InstantiateBase(input_data_len))?;
let code_hash: CodeHash<<E as Ext>::T> =
ctx.read_sandbox_memory_as(code_hash_ptr, code_hash_len)?;
let value: BalanceOf<<E as Ext>::T> = ctx.read_sandbox_memory_as(value_ptr, value_len)?;
let input_data = ctx.read_sandbox_memory(input_data_ptr, input_data_len)?;
let salt = ctx.read_sandbox_memory(salt_ptr, salt_len)?;

let nested_gas_limit = if gas == 0 {
ctx.gas_meter.gas_left()
Expand All @@ -875,7 +880,8 @@ define_env!(Env, <E: Ext>,
&code_hash,
value,
nested_meter,
input_data
input_data,
&salt,
)
}
// there is not enough gas to allocate for the nested call.
Expand Down