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
@@ -26,7 +26,6 @@
use crate::{Config, Determinism};
use frame_support::traits::Get;
use sp_core::crypto::UncheckedFrom;
use sp_runtime::traits::Hash;
use sp_std::{borrow::ToOwned, prelude::*};
use wasm_instrument::{
@@ -105,11 +104,7 @@ pub struct ImportedMemory {
}
impl ImportedMemory {
pub fn max<T: Config>() -> Self
where
T: Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
pub fn max<T: Config>() -> Self {
let pages = max_pages::<T>();
Self { min_pages: pages, max_pages: pages }
}
@@ -130,11 +125,7 @@ pub struct WasmModule<T: Config> {
pub memory: Option<ImportedMemory>,
}
impl<T: Config> From<ModuleDefinition> for WasmModule<T>
where
T: Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
impl<T: Config> From<ModuleDefinition> for WasmModule<T> {
fn from(def: ModuleDefinition) -> Self {
// internal functions start at that offset.
let func_offset = u32::try_from(def.imported_functions.len()).unwrap();
@@ -259,11 +250,7 @@ where
}
}
impl<T: Config> WasmModule<T>
where
T: Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
impl<T: Config> WasmModule<T> {
/// Uses the supplied wasm module and instruments it when requested.
pub fn instrumented(code: &[u8], inject_gas: bool, inject_stack: bool) -> Self {
let module = {
@@ -533,11 +520,7 @@ pub mod body {
}
/// The maximum amount of pages any contract is allowed to have according to the current `Schedule`.
pub fn max_pages<T: Config>() -> u32
where
T: Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
pub fn max_pages<T: Config>() -> u32 {
T::Schedule::get().limits.memory_pages
}
@@ -63,8 +63,6 @@ struct Contract<T: Config> {
impl<T: Config> Contract<T>
where
T: Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
<BalanceOf<T> as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode,
{
/// Create new contract and use a default account id as instantiator.
@@ -90,7 +88,7 @@ where
let value = Pallet::<T>::min_balance();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let salt = vec![0xff];
let addr = Contracts::<T>::contract_address(&caller, &module.hash, &salt);
let addr = Contracts::<T>::contract_address(&caller, &module.hash, &data, &salt);
Contracts::<T>::store_code_raw(module.code, caller.clone())?;
Contracts::<T>::instantiate(
@@ -203,8 +201,6 @@ macro_rules! load_benchmark {
benchmarks! {
where_clause { where
T::AccountId: UncheckedFrom<T::Hash>,
T::AccountId: AsRef<[u8]>,
<BalanceOf<T> as codec::HasCompact>::Type: Clone + Eq + PartialEq + sp_std::fmt::Debug + scale_info::TypeInfo + codec::Encode,
}
@@ -270,6 +266,7 @@ benchmarks! {
// a code of that size into the sandbox.
//
// `c`: Size of the code in kilobytes.
// `i`: Size of the input in kilobytes.
// `s`: Size of the salt in kilobytes.
//
// # Note
@@ -278,15 +275,17 @@ benchmarks! {
// to be larger than the maximum size **after instrumentation**.
instantiate_with_code {
let c in 0 .. Perbill::from_percent(49).mul_ceil(T::MaxCodeLen::get());
let i in 0 .. code::max_pages::<T>() * 64 * 1024;
let s in 0 .. code::max_pages::<T>() * 64 * 1024;
let input = vec![42u8; i as usize];
let salt = vec![42u8; s as usize];
let value = Pallet::<T>::min_balance();
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call);
let origin = RawOrigin::Signed(caller.clone());
let addr = Contracts::<T>::contract_address(&caller, &hash, &salt);
}: _(origin, value, Weight::MAX, None, code, vec![], salt)
let addr = Contracts::<T>::contract_address(&caller, &hash, &input, &salt);
}: _(origin, value, Weight::MAX, None, code, input, salt)
verify {
// the contract itself does not trigger any reserves
let deposit = T::Currency::reserved_balance(&addr);
@@ -303,18 +302,21 @@ benchmarks! {
}
// Instantiate uses a dummy contract constructor to measure the overhead of the instantiate.
// `i`: Size of the input in kilobytes.
// `s`: Size of the salt in kilobytes.
instantiate {
let i in 0 .. code::max_pages::<T>() * 64 * 1024;
let s in 0 .. code::max_pages::<T>() * 64 * 1024;
let input = vec![42u8; i as usize];
let salt = vec![42u8; s as usize];
let value = Pallet::<T>::min_balance();
let caller = whitelisted_caller();
T::Currency::make_free_balance_be(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::dummy();
let origin = RawOrigin::Signed(caller.clone());
let addr = Contracts::<T>::contract_address(&caller, &hash, &salt);
let addr = Contracts::<T>::contract_address(&caller, &hash, &input, &salt);
Contracts::<T>::store_code_raw(code, caller.clone())?;
}: _(origin, value, Weight::MAX, None, hash, vec![], salt)
}: _(origin, value, Weight::MAX, None, hash, input, salt)
verify {
// the contract itself does not trigger any reserves
let deposit = T::Currency::reserved_balance(&addr);
@@ -1779,7 +1781,7 @@ benchmarks! {
let addresses = hashes
.iter()
.map(|hash| Contracts::<T>::contract_address(
&instance.account_id, hash, &[],
&instance.account_id, hash, &[], &[],
))
.collect::<Vec<_>>();
@@ -1796,8 +1798,9 @@ benchmarks! {
}
}
seal_instantiate_per_transfer_salt_kb {
seal_instantiate_per_transfer_input_salt_kb {
let t in 0 .. 1;
let i in 0 .. (code::max_pages::<T>() - 1) * 64;
let s in 0 .. (code::max_pages::<T>() - 1) * 64;
let callee_code = WasmModule::<T>::dummy();
let hash = callee_code.hash;
@@ -1865,14 +1868,14 @@ benchmarks! {
Regular(Instruction::I64Const(0)), // gas
Regular(Instruction::I32Const(value_offset as i32)), // value_ptr
Regular(Instruction::I32Const(value_len as i32)), // value_len
Regular(Instruction::I32Const(0)), // input_data_ptr
Regular(Instruction::I32Const(0)), // input_data_len
Counter(salt_offset as u32, salt_len as u32), // input_data_ptr
Regular(Instruction::I32Const((i * 1024) as i32)), // input_data_len
Regular(Instruction::I32Const((addr_len_offset + addr_len) as i32)), // address_ptr
Regular(Instruction::I32Const(addr_len_offset as i32)), // address_len_ptr
Regular(Instruction::I32Const(SENTINEL as i32)), // output_ptr
Regular(Instruction::I32Const(0)), // output_len_ptr
Counter(salt_offset as u32, salt_len as u32), // salt_ptr
Regular(Instruction::I32Const((s * 1024).max(salt_len as u32) as i32)), // salt_len
Regular(Instruction::I32Const((s * 1024) as i32)), // salt_len
Regular(Instruction::Call(0)),
Regular(Instruction::I32Eqz),
Regular(Instruction::If(BlockType::NoResult)),
@@ -20,7 +20,6 @@
/// ! environment that provides the seal interface as imported functions.
use super::{code::WasmModule, Config};
use crate::wasm::{Environment, PrefabWasmModule};
use sp_core::crypto::UncheckedFrom;
use wasmi::{errors::LinkerError, Func, Linker, StackLimits, Store};
/// Minimal execution environment without any imported functions.
@@ -36,11 +35,7 @@ impl Sandbox {
}
}
impl<T: Config> From<&WasmModule<T>> for Sandbox
where
T: Config,
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
impl<T: Config> From<&WasmModule<T>> for Sandbox {
/// Creates an instance from the supplied module and supplies as much memory
/// to the instance as the module declares as imported.
fn from(module: &WasmModule<T>) -> Self {