mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 18:41:03 +00:00
contracts: Add salt argument to contract instantiation (#7482)
* pallet-contracts: Fix seal_restore_to to output proper module errors Those errors where part of the decl_error for some time but where never actually returned. This allows proper debugging of failed restorations. Previously, any error did return the misleading `ContractTrapped`. * Bind UncheckedFrom<T::Hash> + AsRef<[u8]> everywhere This allows us to make assumptions about the AccoutId that are necessary for testing and in order to benchmark the module properly. This also groups free standing functions into inherent functions in order to minimize the places where the new bounds need to be specified. * 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. * Remove TrieIdGenerator The new trait bounds allows us to remove this workaround from the configuration trait. * Remove default parameters for config trait It should be solely the responsiblity to determine proper values for these parameter. As a matter of fact most runtime weren't using these values anyways. * Fix tests for new account id type Because of the new bounds on the trait tests can't get away by using u64 as accound id. Replacing the 8 byte value by a 32 byte value creates out quite a bit of code churn. * Fix benchmarks The benchmarks need adaption to the new instantiate semantics. * Fix compile errors caused by adding new trait bounds * Fix compile errors caused by renaming storage and rent functions * Adapt host functions and dispatchables to the new salt * Add tests for instantiate host functions (was not possible before) * Add benchmark results * Adapt to the new WeightInfo The new benchmarks add a new parameter for salt "s" to the instantiate weights that needs to be applied. * Fix deploying_wasm_contract_should_work integration test This test is adapted to use the new instantiate signature. * Break overlong line * Break more long lines Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
This commit is contained in:
committed by
GitHub
parent
26830a20df
commit
c3ca78fae3
@@ -93,14 +93,18 @@ pub mod weights;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use crate::exec::ExecutionContext;
|
||||
use crate::wasm::{WasmLoader, WasmVm};
|
||||
use crate::weights::WeightInfo;
|
||||
|
||||
pub use crate::gas::{Gas, GasMeter};
|
||||
pub use crate::wasm::ReturnCode as RuntimeReturnCode;
|
||||
pub use crate::schedule::{Schedule, HostFnWeights, InstructionWeights, Limits};
|
||||
|
||||
pub use crate::{
|
||||
gas::{Gas, GasMeter},
|
||||
wasm::ReturnCode as RuntimeReturnCode,
|
||||
weights::WeightInfo,
|
||||
schedule::{Schedule, HostFnWeights, InstructionWeights, Limits},
|
||||
};
|
||||
use crate::{
|
||||
exec::ExecutionContext,
|
||||
wasm::{WasmLoader, WasmVm},
|
||||
rent::Rent,
|
||||
storage::Storage,
|
||||
};
|
||||
use sp_core::crypto::UncheckedFrom;
|
||||
use sp_std::{prelude::*, marker::PhantomData, fmt::Debug};
|
||||
use codec::{Codec, Encode, Decode};
|
||||
@@ -112,7 +116,7 @@ use sp_runtime::{
|
||||
};
|
||||
use frame_support::{
|
||||
decl_module, decl_event, decl_storage, decl_error, ensure,
|
||||
parameter_types, storage::child::ChildInfo,
|
||||
storage::child::ChildInfo,
|
||||
dispatch::{DispatchResult, DispatchResultWithPostInfo},
|
||||
traits::{OnUnbalanced, Currency, Get, Time, Randomness},
|
||||
};
|
||||
@@ -125,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)]
|
||||
@@ -257,66 +256,11 @@ impl<T: Trait> From<AliveContractInfo<T>> for ContractInfo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a trie id (trie id must be unique and collision resistant depending upon its context).
|
||||
/// Note that it is different than encode because trie id should be collision resistant
|
||||
/// (being a proper unique identifier).
|
||||
pub trait TrieIdGenerator<AccountId> {
|
||||
/// Get a trie id for an account, using reference to parent account trie id to ensure
|
||||
/// uniqueness of trie id.
|
||||
///
|
||||
/// The implementation must ensure every new trie id is unique: two consecutive calls with the
|
||||
/// same parameter needs to return different trie id values.
|
||||
fn trie_id(account_id: &AccountId) -> TrieId;
|
||||
}
|
||||
|
||||
/// Get trie id from `account_id`.
|
||||
pub struct TrieIdFromParentCounter<T: Trait>(PhantomData<T>);
|
||||
|
||||
/// This generator uses inner counter for account id and applies the hash over `AccountId +
|
||||
/// accountid_counter`.
|
||||
impl<T: Trait> TrieIdGenerator<T::AccountId> for TrieIdFromParentCounter<T>
|
||||
where
|
||||
T::AccountId: AsRef<[u8]>
|
||||
{
|
||||
fn trie_id(account_id: &T::AccountId) -> TrieId {
|
||||
// Note that skipping a value due to error is not an issue here.
|
||||
// We only need uniqueness, not sequence.
|
||||
let new_seed = AccountCounter::mutate(|v| {
|
||||
*v = v.wrapping_add(1);
|
||||
*v
|
||||
});
|
||||
|
||||
let mut buf = Vec::new();
|
||||
buf.extend_from_slice(account_id.as_ref());
|
||||
buf.extend_from_slice(&new_seed.to_le_bytes()[..]);
|
||||
T::Hashing::hash(&buf[..]).as_ref().into()
|
||||
}
|
||||
}
|
||||
|
||||
pub type BalanceOf<T> =
|
||||
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::Balance;
|
||||
pub type NegativeImbalanceOf<T> =
|
||||
<<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;
|
||||
|
||||
parameter_types! {
|
||||
/// A reasonable default value for [`Trait::SignedClaimedHandicap`].
|
||||
pub const DefaultSignedClaimHandicap: u32 = 2;
|
||||
/// A reasonable default value for [`Trait::TombstoneDeposit`].
|
||||
pub const DefaultTombstoneDeposit: u32 = 16;
|
||||
/// A reasonable default value for [`Trait::StorageSizeOffset`].
|
||||
pub const DefaultStorageSizeOffset: u32 = 8;
|
||||
/// A reasonable default value for [`Trait::RentByteFee`].
|
||||
pub const DefaultRentByteFee: u32 = 4;
|
||||
/// A reasonable default value for [`Trait::RentDepositOffset`].
|
||||
pub const DefaultRentDepositOffset: u32 = 1000;
|
||||
/// A reasonable default value for [`Trait::SurchargeReward`].
|
||||
pub const DefaultSurchargeReward: u32 = 150;
|
||||
/// A reasonable default value for [`Trait::MaxDepth`].
|
||||
pub const DefaultMaxDepth: u32 = 32;
|
||||
/// A reasonable default value for [`Trait::MaxValueSize`].
|
||||
pub const DefaultMaxValueSize: u32 = 16_384;
|
||||
}
|
||||
|
||||
pub trait Trait: frame_system::Trait {
|
||||
type Time: Time;
|
||||
type Randomness: Randomness<Self::Hash>;
|
||||
@@ -327,12 +271,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>;
|
||||
|
||||
/// Handler for rent payments.
|
||||
type RentPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
|
||||
|
||||
@@ -383,32 +321,13 @@ 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> {
|
||||
pub enum Error for Module<T: Trait>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash>,
|
||||
T::AccountId: AsRef<[u8]>,
|
||||
{
|
||||
/// A new schedule must have a greater version than the current one.
|
||||
InvalidScheduleVersion,
|
||||
/// An origin must be signed or inherent and auxiliary sender only provided on inherent.
|
||||
@@ -455,12 +374,21 @@ decl_error! {
|
||||
ContractTrapped,
|
||||
/// The size defined in `T::MaxValueSize` was exceeded.
|
||||
ValueTooLarge,
|
||||
/// The action performed is not allowed while the contract performing it is already
|
||||
/// on the call stack. Those actions are contract self destruction and restoration
|
||||
/// of a tombstone.
|
||||
ReentranceDenied,
|
||||
}
|
||||
}
|
||||
|
||||
decl_module! {
|
||||
/// Contracts module.
|
||||
pub struct Module<T: Trait> for enum Call where origin: <T as frame_system::Trait>::Origin {
|
||||
pub struct Module<T: Trait> for enum Call
|
||||
where
|
||||
origin: T::Origin,
|
||||
T::AccountId: UncheckedFrom<T::Hash>,
|
||||
T::AccountId: AsRef<[u8]>,
|
||||
{
|
||||
type Error = Error<T>;
|
||||
|
||||
/// Number of block delay an extrinsic claim surcharge has.
|
||||
@@ -563,29 +491,38 @@ 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
|
||||
/// upon any call received by this account.
|
||||
/// - The contract is initialized.
|
||||
#[weight = T::WeightInfo::instantiate(data.len() as u32 / 1024).saturating_add(*gas_limit)]
|
||||
#[weight =
|
||||
T::WeightInfo::instantiate(
|
||||
data.len() as u32 / 1024,
|
||||
salt.len() as u32 / 1024,
|
||||
).saturating_add(*gas_limit)
|
||||
]
|
||||
pub fn instantiate(
|
||||
origin,
|
||||
#[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)
|
||||
@@ -619,7 +556,7 @@ decl_module! {
|
||||
};
|
||||
|
||||
// If poking the contract has lead to eviction of the contract, give out the rewards.
|
||||
if rent::snitch_contract_should_be_evicted::<T>(&dest, handicap) {
|
||||
if Rent::<T>::snitch_contract_should_be_evicted(&dest, handicap) {
|
||||
T::Currency::deposit_into_existing(&rewarded, T::SurchargeReward::get())?;
|
||||
}
|
||||
}
|
||||
@@ -627,7 +564,10 @@ decl_module! {
|
||||
}
|
||||
|
||||
/// Public APIs provided by the contracts module.
|
||||
impl<T: Trait> Module<T> {
|
||||
impl<T: Trait> Module<T>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
/// Perform a call to a specified contract.
|
||||
///
|
||||
/// This function is similar to `Self::call`, but doesn't perform any address lookups and better
|
||||
@@ -659,12 +599,12 @@ impl<T: Trait> Module<T> {
|
||||
.get_alive()
|
||||
.ok_or(ContractAccessError::IsTombstone)?;
|
||||
|
||||
let maybe_value = storage::read_contract_storage(&contract_info.trie_id, &key);
|
||||
let maybe_value = Storage::<T>::read(&contract_info.trie_id, &key);
|
||||
Ok(maybe_value)
|
||||
}
|
||||
|
||||
pub fn rent_projection(address: T::AccountId) -> RentProjectionResult<T::BlockNumber> {
|
||||
rent::compute_rent_projection::<T>(&address)
|
||||
Rent::<T>::compute_projection(&address)
|
||||
}
|
||||
|
||||
/// Put code for benchmarks which does not check or instrument the code.
|
||||
@@ -674,9 +614,34 @@ impl<T: Trait> Module<T> {
|
||||
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.
|
||||
///
|
||||
/// 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> {
|
||||
impl<T: Trait> Module<T>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
|
||||
{
|
||||
fn execute_wasm(
|
||||
origin: T::AccountId,
|
||||
gas_meter: &mut GasMeter<T>,
|
||||
@@ -734,7 +699,10 @@ decl_event! {
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> as Contracts {
|
||||
trait Store for Module<T: Trait> as Contracts
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
{
|
||||
/// Current cost schedule for contracts.
|
||||
CurrentSchedule get(fn current_schedule) config(): Schedule<T> = Default::default();
|
||||
/// A mapping from an original code hash to the original code, untouched by instrumentation.
|
||||
@@ -762,7 +730,10 @@ pub struct Config<T: Trait> {
|
||||
pub max_value_size: u32,
|
||||
}
|
||||
|
||||
impl<T: Trait> Config<T> {
|
||||
impl<T: Trait> Config<T>
|
||||
where
|
||||
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>
|
||||
{
|
||||
fn preload() -> Config<T> {
|
||||
Config {
|
||||
schedule: <Module<T>>::current_schedule(),
|
||||
|
||||
Reference in New Issue
Block a user