contracts: Improve contract address derivation (#12883)

* Add prefix to address derivation

* Extend benchmark

* Fix node test

* ".git/.scripts/bench-bot.sh" pallet dev pallet_contracts

* Adapt to new benchmark

* Update dispatchable benchmarks

* ".git/.scripts/bench-bot.sh" pallet dev pallet_contracts

* Use benchmark results

* Apply suggestions from code review

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* Don't use T::AdressGenerator directly

* Rename constructor_args to input_data

Co-authored-by: command-bot <>
Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
Alexander Theißen
2022-12-22 15:52:48 +01:00
committed by GitHub
parent af15848be0
commit 4083b5358a
18 changed files with 1559 additions and 1479 deletions
+24 -37
View File
@@ -105,7 +105,7 @@ use crate::{
wasm::{OwnerInfo, PrefabWasmModule, TryInstantiate},
weights::WeightInfo,
};
use codec::{Codec, Encode, HasCompact};
use codec::{Codec, Decode, Encode, HasCompact};
use frame_support::{
dispatch::{Dispatchable, GetDispatchInfo, Pays, PostDispatchInfo},
ensure,
@@ -123,8 +123,7 @@ use pallet_contracts_primitives::{
StorageDeposit,
};
use scale_info::TypeInfo;
use sp_core::crypto::UncheckedFrom;
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup};
use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup, TrailingZeroInput};
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
pub use crate::{
@@ -155,7 +154,7 @@ const SENTINEL: u32 = u32::MAX;
/// Provides the contract address generation method.
///
/// See [`DefaultAddressGenerator`] for the default implementation.
pub trait AddressGenerator<T: frame_system::Config> {
pub trait AddressGenerator<T: Config> {
/// Generate the address of a contract based on the given instantiate parameters.
///
/// # Note for implementors
@@ -166,6 +165,7 @@ pub trait AddressGenerator<T: frame_system::Config> {
fn generate_address(
deploying_address: &T::AccountId,
code_hash: &CodeHash<T>,
input_data: &[u8],
salt: &[u8],
) -> T::AccountId;
}
@@ -176,28 +176,21 @@ pub trait AddressGenerator<T: frame_system::Config> {
/// is only dependant on its inputs. It can therefore be used to reliably predict the
/// address of a contract. This is akin to the formula of eth's CREATE2 opcode. There
/// is no CREATE equivalent because CREATE2 is strictly more powerful.
///
/// Formula: `hash(deploying_address ++ code_hash ++ salt)`
/// Formula:
/// `hash("contract_addr_v1" ++ deploying_address ++ code_hash ++ input_data ++ salt)`
pub struct DefaultAddressGenerator;
impl<T> AddressGenerator<T> for DefaultAddressGenerator
where
T: frame_system::Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
impl<T: Config> AddressGenerator<T> for DefaultAddressGenerator {
fn generate_address(
deploying_address: &T::AccountId,
code_hash: &CodeHash<T>,
input_data: &[u8],
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))
let entropy = (b"contract_addr_v1", deploying_address, code_hash, input_data, salt)
.using_encoded(T::Hashing::hash);
Decode::decode(&mut TrailingZeroInput::new(entropy.as_ref()))
.expect("infinite length input; no invalid inputs for type; qed")
}
}
@@ -352,11 +345,7 @@ pub mod pallet {
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>
where
T::AccountId: UncheckedFrom<T::Hash>,
T::AccountId: AsRef<[u8]>,
{
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight {
Storage::<T>::process_deletion_queue_batch(remaining_weight)
.saturating_add(T::WeightInfo::on_process_deletion_queue_batch())
@@ -385,8 +374,6 @@ pub mod pallet {
#[pallet::call]
impl<T: Config> Pallet<T>
where
T::AccountId: UncheckedFrom<T::Hash>,
T::AccountId: AsRef<[u8]>,
<BalanceOf<T> as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode,
{
/// Deprecated version if [`Self::call`] for use in an in-storage `Call`.
@@ -415,7 +402,7 @@ pub mod pallet {
/// Deprecated version if [`Self::instantiate_with_code`] for use in an in-storage `Call`.
#[pallet::call_index(1)]
#[pallet::weight(
T::WeightInfo::instantiate_with_code(code.len() as u32, salt.len() as u32)
T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32)
.saturating_add(<Pallet<T>>::compat_weight(*gas_limit))
)]
#[allow(deprecated)]
@@ -445,7 +432,7 @@ pub mod pallet {
/// Deprecated version if [`Self::instantiate`] for use in an in-storage `Call`.
#[pallet::call_index(2)]
#[pallet::weight(
T::WeightInfo::instantiate(salt.len() as u32).saturating_add(<Pallet<T>>::compat_weight(*gas_limit))
T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(<Pallet<T>>::compat_weight(*gas_limit))
)]
#[allow(deprecated)]
#[deprecated(note = "1D weight is used in this extrinsic, please migrate to `instantiate`")]
@@ -633,7 +620,7 @@ pub mod pallet {
/// - The `deploy` function is executed in the context of the newly-created account.
#[pallet::call_index(7)]
#[pallet::weight(
T::WeightInfo::instantiate_with_code(code.len() as u32, salt.len() as u32)
T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32)
.saturating_add(*gas_limit)
)]
pub fn instantiate_with_code(
@@ -647,6 +634,7 @@ pub mod pallet {
) -> DispatchResultWithPostInfo {
let origin = ensure_signed(origin)?;
let code_len = code.len() as u32;
let data_len = data.len() as u32;
let salt_len = salt.len() as u32;
let mut output = Self::internal_instantiate(
origin,
@@ -665,7 +653,7 @@ pub mod pallet {
}
output.gas_meter.into_dispatch_result(
output.result.map(|(_address, result)| result),
T::WeightInfo::instantiate_with_code(code_len, salt_len),
T::WeightInfo::instantiate_with_code(code_len, data_len, salt_len),
)
}
@@ -676,7 +664,7 @@ pub mod pallet {
/// must be supplied.
#[pallet::call_index(8)]
#[pallet::weight(
T::WeightInfo::instantiate(salt.len() as u32).saturating_add(*gas_limit)
T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(*gas_limit)
)]
pub fn instantiate(
origin: OriginFor<T>,
@@ -688,6 +676,7 @@ pub mod pallet {
salt: Vec<u8>,
) -> DispatchResultWithPostInfo {
let origin = ensure_signed(origin)?;
let data_len = data.len() as u32;
let salt_len = salt.len() as u32;
let mut output = Self::internal_instantiate(
origin,
@@ -706,7 +695,7 @@ pub mod pallet {
}
output.gas_meter.into_dispatch_result(
output.result.map(|(_address, output)| output),
T::WeightInfo::instantiate(salt_len),
T::WeightInfo::instantiate(data_len, salt_len),
)
}
}
@@ -943,10 +932,7 @@ struct InternalOutput<T: Config, O> {
result: Result<O, ExecError>,
}
impl<T: Config> Pallet<T>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
impl<T: Config> Pallet<T> {
/// Perform a call to a specified contract.
///
/// This function is similar to [`Self::call`], but doesn't perform any address lookups
@@ -1081,9 +1067,10 @@ where
pub fn contract_address(
deploying_address: &T::AccountId,
code_hash: &CodeHash<T>,
input_data: &[u8],
salt: &[u8],
) -> T::AccountId {
T::AddressGenerator::generate_address(deploying_address, code_hash, salt)
T::AddressGenerator::generate_address(deploying_address, code_hash, input_data, salt)
}
/// Returns the code hash of the contract specified by `account` ID.