#![cfg(feature = "runtime-benchmarks")]
mod call_builder;
mod code;
mod sandbox;
use self::{
call_builder::CallSetup,
code::{body, ImportedMemory, Location, ModuleDefinition, WasmModule},
sandbox::Sandbox,
};
use crate::{
exec::{Key, SeedOf},
migration::{
codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, v15, v16, MigrationStep,
},
storage::WriteOutcome,
wasm::BenchEnv,
Pallet as Contracts, *,
};
use alloc::{vec, vec::Vec};
use codec::{Encode, MaxEncodedLen};
use frame_benchmarking::v2::*;
use frame_support::{
self, assert_ok,
pallet_prelude::StorageVersion,
storage::child,
traits::{fungible::InspectHold, Currency},
weights::{Weight, WeightMeter},
};
use frame_system::RawOrigin;
use pallet_balances;
use pallet_contracts_uapi::{CallFlags, ReturnErrorCode};
use sp_runtime::traits::{Bounded, Hash};
use wasm_instrument::parity_wasm::elements::{Instruction, Local, ValueType};
const API_BENCHMARK_RUNS: u32 = 1600;
const INSTR_BENCHMARK_RUNS: u32 = 5000;
const UNBALANCED_TRIE_LAYERS: u32 = 20;
#[derive(Clone)]
struct Contract<T: Config> {
caller: T::AccountId,
account_id: T::AccountId,
addr: AccountIdLookupOf<T>,
value: BalanceOf<T>,
}
impl<T> Contract<T>
where
T: Config + pallet_balances::Config,
<BalanceOf<T> as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode,
{
fn new(module: WasmModule<T>, data: Vec<u8>) -> Result<Contract<T>, &'static str> {
Self::with_index(0, module, data)
}
fn with_index(
index: u32,
module: WasmModule<T>,
data: Vec<u8>,
) -> Result<Contract<T>, &'static str> {
Self::with_caller(account("instantiator", index, 0), module, data)
}
fn with_caller(
caller: T::AccountId,
module: WasmModule<T>,
data: Vec<u8>,
) -> Result<Contract<T>, &'static str> {
let value = Pallet::<T>::min_balance();
T::Currency::set_balance(&caller, caller_funding::<T>());
let salt = vec![0xff];
let addr = Contracts::<T>::contract_address(&caller, &module.hash, &data, &salt);
Contracts::<T>::store_code_raw(module.code, caller.clone())?;
Contracts::<T>::instantiate(
RawOrigin::Signed(caller.clone()).into(),
value,
Weight::MAX,
None,
module.hash,
data,
salt,
)?;
let result =
Contract { caller, account_id: addr.clone(), addr: T::Lookup::unlookup(addr), value };
ContractInfoOf::<T>::insert(&result.account_id, result.info()?);
Ok(result)
}
fn with_storage(
code: WasmModule<T>,
stor_num: u32,
stor_size: u32,
) -> Result<Self, &'static str> {
let contract = Contract::<T>::new(code, vec![])?;
let storage_items = (0..stor_num)
.map(|i| {
let hash = T::Hashing::hash_of(&i)
.as_ref()
.try_into()
.map_err(|_| "Hash too big for storage key")?;
Ok((hash, vec![42u8; stor_size as usize]))
})
.collect::<Result<Vec<_>, &'static str>>()?;
contract.store(&storage_items)?;
Ok(contract)
}
fn store(&self, items: &Vec<([u8; 32], Vec<u8>)>) -> Result<(), &'static str> {
let info = self.info()?;
for item in items {
info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false)
.map_err(|_| "Failed to write storage to restoration dest")?;
}
<ContractInfoOf<T>>::insert(&self.account_id, info);
Ok(())
}
fn with_unbalanced_storage_trie(code: WasmModule<T>, key: &[u8]) -> Result<Self, &'static str> {
if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 {
return Err("Key size too small to create the specified trie");
}
let value = vec![16u8; T::Schedule::get().limits.payload_len as usize];
let contract = Contract::<T>::new(code, vec![])?;
let info = contract.info()?;
let child_trie_info = info.child_trie_info();
child::put_raw(&child_trie_info, &key, &value);
for l in 0..UNBALANCED_TRIE_LAYERS {
let pos = l as usize / 2;
let mut key_new = key.to_vec();
for i in 0u8..16 {
key_new[pos] = if l % 2 == 0 {
(key_new[pos] & 0xF0) | i
} else {
(key_new[pos] & 0x0F) | (i << 4)
};
if key == &key_new {
continue
}
child::put_raw(&child_trie_info, &key_new, &value);
}
}
Ok(contract)
}
fn address_info(addr: &T::AccountId) -> Result<ContractInfo<T>, &'static str> {
ContractInfoOf::<T>::get(addr).ok_or("Expected contract to exist at this point.")
}
fn info(&self) -> Result<ContractInfo<T>, &'static str> {
Self::address_info(&self.account_id)
}
fn set_balance(&self, balance: BalanceOf<T>) {
T::Currency::set_balance(&self.account_id, balance);
}
fn code_exists(hash: &CodeHash<T>) -> bool {
<PristineCode<T>>::contains_key(hash) && <CodeInfoOf<T>>::contains_key(&hash)
}
fn code_removed(hash: &CodeHash<T>) -> bool {
!<PristineCode<T>>::contains_key(hash) && !<CodeInfoOf<T>>::contains_key(&hash)
}
}
fn caller_funding<T: Config>() -> BalanceOf<T> {
BalanceOf::<T>::max_value() / 10_000u32.into()
}
#[benchmarks(
where
<BalanceOf<T> as codec::HasCompact>::Type: Clone + Eq + PartialEq + core::fmt::Debug + scale_info::TypeInfo + codec::Encode,
T: Config + pallet_balances::Config,
BalanceOf<T>: From<<pallet_balances::Pallet<T> as Currency<T::AccountId>>::Balance>,
<pallet_balances::Pallet<T> as Currency<T::AccountId>>::Balance: From<BalanceOf<T>>,
)]
mod benchmarks {
use super::*;
#[benchmark(pov_mode = Measured)]
fn on_process_deletion_queue_batch() {
#[block]
{
ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
}
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn on_initialize_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
let instance = Contract::<T>::with_storage(
WasmModule::dummy(),
k,
T::Schedule::get().limits.payload_len,
)?;
instance.info()?.queue_trie_for_deletion();
#[block]
{
ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
}
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn v9_migration_step(c: Linear<0, { T::MaxCodeLen::get() }>) {
v09::store_old_dummy_code::<T>(c as usize);
let mut m = v09::Migration::<T>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
}
#[benchmark(pov_mode = Measured)]
fn v10_migration_step() -> Result<(), BenchmarkError> {
let contract =
<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
v10::store_old_contract_info::<T, pallet_balances::Pallet<T>>(
contract.account_id.clone(),
contract.info()?,
);
let mut m = v10::Migration::<T, pallet_balances::Pallet<T>>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn v11_migration_step(k: Linear<0, 1024>) {
v11::fill_old_queue::<T>(k as usize);
let mut m = v11::Migration::<T>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
}
#[benchmark(pov_mode = Measured)]
fn v12_migration_step(c: Linear<0, { T::MaxCodeLen::get() }>) {
v12::store_old_dummy_code::<T, pallet_balances::Pallet<T>>(
c as usize,
account::<T::AccountId>("account", 0, 0),
);
let mut m = v12::Migration::<T, pallet_balances::Pallet<T>>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
}
#[benchmark(pov_mode = Measured)]
fn v13_migration_step() -> Result<(), BenchmarkError> {
let contract =
<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
v13::store_old_contract_info::<T>(contract.account_id.clone(), contract.info()?);
let mut m = v13::Migration::<T>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn v14_migration_step() {
let account = account::<T::AccountId>("account", 0, 0);
T::Currency::set_balance(&account, caller_funding::<T>());
v14::store_dummy_code::<T, pallet_balances::Pallet<T>>(account);
let mut m = v14::Migration::<T, pallet_balances::Pallet<T>>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
}
#[benchmark(pov_mode = Measured)]
fn v15_migration_step() -> Result<(), BenchmarkError> {
let contract =
<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
v15::store_old_contract_info::<T>(contract.account_id.clone(), contract.info()?);
let mut m = v15::Migration::<T>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn v16_migration_step() -> Result<(), BenchmarkError> {
let contract =
<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
let info = contract.info()?;
let base_deposit = v16::store_old_contract_info::<T>(contract.account_id.clone(), &info);
let mut m = v16::Migration::<T>::default();
#[block]
{
m.step(&mut WeightMeter::new());
}
let ed = Pallet::<T>::min_balance();
let info = v16::ContractInfoOf::<T>::get(&contract.account_id).unwrap();
assert_eq!(info.storage_base_deposit, base_deposit - ed);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn migration_noop() {
let version = LATEST_MIGRATION_VERSION;
StorageVersion::new(version).put::<Pallet<T>>();
#[block]
{
Migration::<T>::migrate(&mut WeightMeter::new());
}
assert_eq!(StorageVersion::get::<Pallet<T>>(), version);
}
#[benchmark(pov_mode = Measured)]
fn migrate() {
let latest_version = LATEST_MIGRATION_VERSION;
StorageVersion::new(latest_version - 2).put::<Pallet<T>>();
<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
#[extrinsic_call]
_(RawOrigin::Signed(whitelisted_caller()), Weight::MAX);
assert_eq!(StorageVersion::get::<Pallet<T>>(), latest_version - 1);
}
#[benchmark(pov_mode = Measured)]
fn on_runtime_upgrade_noop() {
let latest_version = LATEST_MIGRATION_VERSION;
StorageVersion::new(latest_version).put::<Pallet<T>>();
#[block]
{
<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
}
assert!(MigrationInProgress::<T>::get().is_none());
}
#[benchmark(pov_mode = Measured)]
fn on_runtime_upgrade_in_progress() {
let latest_version = LATEST_MIGRATION_VERSION;
StorageVersion::new(latest_version - 2).put::<Pallet<T>>();
let v = vec![42u8].try_into().ok();
MigrationInProgress::<T>::set(v.clone());
#[block]
{
<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
}
assert!(MigrationInProgress::<T>::get().is_some());
assert_eq!(MigrationInProgress::<T>::get(), v);
}
#[benchmark(pov_mode = Measured)]
fn on_runtime_upgrade() {
let latest_version = LATEST_MIGRATION_VERSION;
StorageVersion::new(latest_version - 2).put::<Pallet<T>>();
#[block]
{
<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
}
assert!(MigrationInProgress::<T>::get().is_some());
}
#[benchmark(pov_mode = Measured)]
fn call_with_code_per_byte(
c: Linear<0, { T::MaxCodeLen::get() }>,
) -> Result<(), BenchmarkError> {
let instance = Contract::<T>::with_caller(
whitelisted_caller(),
WasmModule::sized(c, Location::Deploy, false),
vec![],
)?;
let value = Pallet::<T>::min_balance();
let callee = instance.addr;
#[extrinsic_call]
call(RawOrigin::Signed(instance.caller.clone()), callee, value, Weight::MAX, None, vec![]);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn instantiate_with_code(
c: Linear<0, { T::MaxCodeLen::get() }>,
i: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>,
s: Linear<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::set_balance(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call, false);
let origin = RawOrigin::Signed(caller.clone());
let addr = Contracts::<T>::contract_address(&caller, &hash, &input, &salt);
#[extrinsic_call]
_(origin, value, Weight::MAX, None, code, input, salt);
let deposit =
T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr);
let code_deposit =
T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller);
assert_eq!(
T::Currency::balance(&caller),
caller_funding::<T>() - value - deposit - code_deposit - Pallet::<T>::min_balance(),
);
assert_eq!(T::Currency::balance(&addr), value + Pallet::<T>::min_balance());
}
#[benchmark(pov_mode = Measured)]
fn instantiate(
i: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>,
s: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>,
) -> Result<(), BenchmarkError> {
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::set_balance(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::dummy();
let addr = Contracts::<T>::contract_address(&caller, &hash, &input, &salt);
Contracts::<T>::store_code_raw(code, caller.clone())?;
#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), value, Weight::MAX, None, hash, input, salt);
let deposit =
T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr);
assert_eq!(
T::Currency::balance(&caller),
caller_funding::<T>() - value - deposit - Pallet::<T>::min_balance(),
);
assert_eq!(T::Currency::balance(&addr), value + Pallet::<T>::min_balance());
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn call() -> Result<(), BenchmarkError> {
let data = vec![42u8; 1024];
let instance =
Contract::<T>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
let value = Pallet::<T>::min_balance();
let origin = RawOrigin::Signed(instance.caller.clone());
let callee = instance.addr.clone();
let before = T::Currency::balance(&instance.account_id);
#[extrinsic_call]
_(origin, callee, value, Weight::MAX, None, data);
let deposit = T::Currency::balance_on_hold(
&HoldReason::StorageDepositReserve.into(),
&instance.account_id,
);
assert_eq!(
T::Currency::balance(&instance.caller),
caller_funding::<T>() - instance.value - value - deposit - Pallet::<T>::min_balance(),
);
assert_eq!(T::Currency::balance(&instance.account_id), before + value);
instance.info()?;
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn upload_code_determinism_enforced(c: Linear<0, { T::MaxCodeLen::get() }>) {
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call, false);
let origin = RawOrigin::Signed(caller.clone());
#[extrinsic_call]
upload_code(origin, code, None, Determinism::Enforced);
assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into());
assert!(<Contract<T>>::code_exists(&hash));
}
#[benchmark(pov_mode = Measured)]
fn upload_code_determinism_relaxed(c: Linear<0, { T::MaxCodeLen::get() }>) {
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call, true);
let origin = RawOrigin::Signed(caller.clone());
#[extrinsic_call]
upload_code(origin, code, None, Determinism::Relaxed);
assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into());
assert!(<Contract<T>>::code_exists(&hash));
assert_eq!(CodeInfoOf::<T>::get(&hash).unwrap().determinism(), Determinism::Relaxed);
}
#[benchmark(pov_mode = Measured)]
fn remove_code() -> Result<(), BenchmarkError> {
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let WasmModule { code, hash, .. } = WasmModule::<T>::dummy();
let origin = RawOrigin::Signed(caller.clone());
let uploaded =
<Contracts<T>>::bare_upload_code(caller.clone(), code, None, Determinism::Enforced)?;
assert_eq!(uploaded.code_hash, hash);
assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&caller));
assert!(<Contract<T>>::code_exists(&hash));
#[extrinsic_call]
_(origin, hash);
assert_eq!(T::Currency::total_balance_on_hold(&caller), 0u32.into());
assert!(<Contract<T>>::code_removed(&hash));
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn set_code() -> Result<(), BenchmarkError> {
let instance =
<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
let WasmModule { code, hash, .. } = <WasmModule<T>>::dummy_with_bytes(128);
<Contracts<T>>::store_code_raw(code, instance.caller.clone())?;
let callee = instance.addr.clone();
assert_ne!(instance.info()?.code_hash, hash);
#[extrinsic_call]
_(RawOrigin::Root, callee, hash);
assert_eq!(instance.info()?.code_hash, hash);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) {
let mut setup = CallSetup::<T>::new(WasmModule::noop(r));
let (mut ext, module) = setup.ext();
let func = CallSetup::<T>::prepare_call(&mut ext, module, vec![]);
#[block]
{
func.call();
}
}
#[benchmark(pov_mode = Measured)]
fn seal_caller() {
let len = <T::AccountId as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_caller(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
&<T::AccountId as Decode>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().caller().account_id().unwrap()
);
}
#[benchmark(pov_mode = Measured)]
fn seal_is_contract() {
let Contract { account_id, .. } =
Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
build_runtime!(runtime, memory: [account_id.encode(), ]);
let result;
#[block]
{
result = BenchEnv::seal0_is_contract(&mut runtime, &mut memory, 0);
}
assert_eq!(result.unwrap(), 1);
}
#[benchmark(pov_mode = Measured)]
fn seal_code_hash() {
let contract = Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
let len = <CodeHash<T> as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], contract.account_id.encode(), ]);
let result;
#[block]
{
result = BenchEnv::seal0_code_hash(&mut runtime, &mut memory, 4 + len, 4, 0);
}
assert_ok!(result);
assert_eq!(
<CodeHash<T> as Decode>::decode(&mut &memory[4..]).unwrap(),
contract.info().unwrap().code_hash
);
}
#[benchmark(pov_mode = Measured)]
fn seal_own_code_hash() {
let len = <CodeHash<T> as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, contract, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_own_code_hash(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
<CodeHash<T> as Decode>::decode(&mut &memory[4..]).unwrap(),
contract.info().unwrap().code_hash
);
}
#[benchmark(pov_mode = Measured)]
fn seal_caller_is_origin() {
build_runtime!(runtime, memory: []);
let result;
#[block]
{
result = BenchEnv::seal0_caller_is_origin(&mut runtime, &mut memory);
}
assert_eq!(result.unwrap(), 1u32);
}
#[benchmark(pov_mode = Measured)]
fn seal_caller_is_root() {
let mut setup = CallSetup::<T>::default();
setup.set_origin(Origin::Root);
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
let result;
#[block]
{
result = BenchEnv::seal0_caller_is_root(&mut runtime, &mut [0u8; 0]);
}
assert_eq!(result.unwrap(), 1u32);
}
#[benchmark(pov_mode = Measured)]
fn seal_address() {
let len = <AccountIdOf<T> as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_address(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
&<T::AccountId as Decode>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().address()
);
}
#[benchmark(pov_mode = Measured)]
fn seal_gas_left() {
let len = 18u32;
assert!(<Weight as MaxEncodedLen>::max_encoded_len() as u32 != len);
build_runtime!(runtime, memory: [32u32.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal1_gas_left(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
<Weight as Decode>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().gas_meter().gas_left()
);
}
#[benchmark(pov_mode = Measured)]
fn seal_balance() {
let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_seal_balance(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
<T::Balance as Decode>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().balance().into()
);
}
#[benchmark(pov_mode = Measured)]
fn seal_value_transferred() {
let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_value_transferred(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
<T::Balance as Decode>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().value_transferred().into()
);
}
#[benchmark(pov_mode = Measured)]
fn seal_minimum_balance() {
let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_minimum_balance(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
<T::Balance as Decode>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().minimum_balance().into()
);
}
#[benchmark(pov_mode = Measured)]
fn seal_block_number() {
let len = <BlockNumberFor<T> as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_seal_block_number(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(
<BlockNumberFor<T>>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().block_number()
);
}
#[benchmark(pov_mode = Measured)]
fn seal_now() {
let len = <MomentOf<T> as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let result;
#[block]
{
result = BenchEnv::seal0_seal_now(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(<MomentOf<T>>::decode(&mut &memory[4..]).unwrap(), *runtime.ext().now());
}
#[benchmark(pov_mode = Measured)]
fn seal_weight_to_fee() {
let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
let weight = Weight::from_parts(500_000, 300_000);
let result;
#[block]
{
result = BenchEnv::seal1_weight_to_fee(
&mut runtime,
&mut memory,
weight.ref_time(),
weight.proof_size(),
4,
0,
);
}
assert_ok!(result);
assert_eq!(
<BalanceOf<T>>::decode(&mut &memory[4..]).unwrap(),
runtime.ext().get_weight_price(weight)
);
}
#[benchmark(pov_mode = Measured)]
fn seal_input(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 - 4 }>) {
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![42u8; n as usize]);
let mut memory = memory!(n.to_le_bytes(), vec![0u8; n as usize],);
let result;
#[block]
{
result = BenchEnv::seal0_input(&mut runtime, &mut memory, 4, 0);
}
assert_ok!(result);
assert_eq!(&memory[4..], &vec![42u8; n as usize]);
}
#[benchmark(pov_mode = Measured)]
fn seal_return(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 - 4 }>) {
build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]);
let result;
#[block]
{
result = BenchEnv::seal0_seal_return(&mut runtime, &mut memory, 0, 0, n);
}
assert!(matches!(
result,
Err(crate::wasm::TrapReason::Return(crate::wasm::ReturnData { .. }))
));
}
#[benchmark(pov_mode = Measured)]
fn seal_terminate(
n: Linear<0, { T::MaxDelegateDependencies::get() }>,
) -> Result<(), BenchmarkError> {
let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
let caller = whitelisted_caller();
build_runtime!(runtime, memory: [beneficiary.encode(),]);
T::Currency::set_balance(&caller, caller_funding::<T>());
(0..n).for_each(|i| {
let new_code = WasmModule::<T>::dummy_with_bytes(65 + i);
Contracts::<T>::store_code_raw(new_code.code, caller.clone()).unwrap();
runtime.ext().lock_delegate_dependency(new_code.hash).unwrap();
});
let result;
#[block]
{
result = BenchEnv::seal1_terminate(&mut runtime, &mut memory, 0);
}
assert!(matches!(result, Err(crate::wasm::TrapReason::Termination)));
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_random() {
let subject_len = T::Schedule::get().limits.subject_len;
assert!(subject_len < 1024);
let output_len =
<(SeedOf<T>, BlockNumberFor<T>) as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [
output_len.to_le_bytes(),
vec![42u8; subject_len as _],
vec![0u8; output_len as _],
]);
let result;
#[block]
{
result = BenchEnv::seal0_random(
&mut runtime,
&mut memory,
4, subject_len, subject_len + 4, 0, );
}
assert_ok!(result);
assert_ok!(<(SeedOf<T>, BlockNumberFor<T>)>::decode(&mut &memory[subject_len as _..]));
}
#[benchmark(pov_mode = Measured)]
fn seal_deposit_event(
t: Linear<0, { T::Schedule::get().limits.event_topics }>,
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) {
let topics = (0..t).map(|i| T::Hashing::hash_of(&i)).collect::<Vec<_>>().encode();
let topics_len = topics.len() as u32;
build_runtime!(runtime, memory: [
n.to_le_bytes(),
topics,
vec![0u8; n as _],
]);
let result;
#[block]
{
result = BenchEnv::seal0_deposit_event(
&mut runtime,
&mut memory,
4, topics_len, 4 + topics_len, 0, );
}
assert_ok!(result);
}
#[benchmark]
fn seal_debug_message(
i: Linear<
0,
{
(T::Schedule::get().limits.memory_pages * 64 * 1024)
.min(T::MaxDebugBufferLen::get())
},
>,
) {
let mut setup = CallSetup::<T>::default();
setup.enable_debug_message();
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
let mut memory = (0..i).zip((32..127).cycle()).map(|i| i.1).collect::<Vec<_>>();
let result;
#[block]
{
result = BenchEnv::seal0_debug_message(&mut runtime, &mut memory, 0, i);
}
assert_ok!(result);
assert_eq!(setup.debug_message().unwrap().len() as u32, i);
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn get_storage_empty() -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = vec![0u8; max_key_len as usize];
let max_value_len = T::Schedule::get().limits.payload_len as usize;
let value = vec![1u8; max_value_len];
let instance = Contract::<T>::new(WasmModule::dummy(), vec![])?;
let info = instance.info()?;
let child_trie_info = info.child_trie_info();
info.bench_write_raw(&key, Some(value.clone()), false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = child::get_raw(&child_trie_info, &key);
}
assert_eq!(result, Some(value));
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn get_storage_full() -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = vec![0u8; max_key_len as usize];
let max_value_len = T::Schedule::get().limits.payload_len;
let value = vec![1u8; max_value_len as usize];
let instance = Contract::<T>::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?;
let info = instance.info()?;
let child_trie_info = info.child_trie_info();
info.bench_write_raw(&key, Some(value.clone()), false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = child::get_raw(&child_trie_info, &key);
}
assert_eq!(result, Some(value));
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn set_storage_empty() -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = vec![0u8; max_key_len as usize];
let max_value_len = T::Schedule::get().limits.payload_len as usize;
let value = vec![1u8; max_value_len];
let instance = Contract::<T>::new(WasmModule::dummy(), vec![])?;
let info = instance.info()?;
let child_trie_info = info.child_trie_info();
info.bench_write_raw(&key, Some(vec![42u8; max_value_len]), false)
.map_err(|_| "Failed to write to storage during setup.")?;
let val = Some(value.clone());
let result;
#[block]
{
result = info.bench_write_raw(&key, val, true);
}
assert_ok!(result);
assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn set_storage_full() -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = vec![0u8; max_key_len as usize];
let max_value_len = T::Schedule::get().limits.payload_len;
let value = vec![1u8; max_value_len as usize];
let instance = Contract::<T>::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?;
let info = instance.info()?;
let child_trie_info = info.child_trie_info();
info.bench_write_raw(&key, Some(vec![42u8; max_value_len as usize]), false)
.map_err(|_| "Failed to write to storage during setup.")?;
let val = Some(value.clone());
let result;
#[block]
{
result = info.bench_write_raw(&key, val, true);
}
assert_ok!(result);
assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn seal_set_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
o: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let value = vec![1u8; n as usize];
build_runtime!(runtime, instance, memory: [ key.to_vec(), value.clone(), ]);
let info = instance.info()?;
info.write(&key, Some(vec![42u8; o as usize]), None, false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = BenchEnv::seal2_set_storage(
&mut runtime,
&mut memory,
0, max_key_len, max_key_len, n, );
}
assert_ok!(result);
assert_eq!(info.read(&key).unwrap(), value);
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn seal_clear_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, instance, memory: [ key.to_vec(), ]);
let info = instance.info()?;
info.write(&key, Some(vec![42u8; n as usize]), None, false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = BenchEnv::seal1_clear_storage(&mut runtime, &mut memory, 0, max_key_len);
}
assert_ok!(result);
assert!(info.read(&key).is_none());
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn seal_get_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, instance, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
let info = instance.info()?;
info.write(&key, Some(vec![42u8; n as usize]), None, false)
.map_err(|_| "Failed to write to storage during setup.")?;
let out_ptr = max_key_len + 4;
let result;
#[block]
{
result = BenchEnv::seal1_get_storage(
&mut runtime,
&mut memory,
0, max_key_len, out_ptr, max_key_len, );
}
assert_ok!(result);
assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn seal_contains_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, instance, memory: [ key.to_vec(), ]);
let info = instance.info()?;
info.write(&key, Some(vec![42u8; n as usize]), None, false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = BenchEnv::seal1_contains_storage(&mut runtime, &mut memory, 0, max_key_len);
}
assert_eq!(result.unwrap(), n);
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn seal_take_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, instance, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
let info = instance.info()?;
let value = vec![42u8; n as usize];
info.write(&key, Some(value.clone()), None, false)
.map_err(|_| "Failed to write to storage during setup.")?;
let out_ptr = max_key_len + 4;
let result;
#[block]
{
result = BenchEnv::seal0_take_storage(
&mut runtime,
&mut memory,
0, max_key_len, out_ptr, max_key_len, );
}
assert_ok!(result);
assert!(&info.read(&key).is_none());
assert_eq!(&value, &memory[out_ptr as usize..]);
Ok(())
}
#[benchmark(pov_mode = Ignored)]
fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
let max_value_len = T::Schedule::get().limits.payload_len;
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let value = Some(vec![42u8; max_value_len as _]);
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
let result;
#[block]
{
result = runtime.ext().set_transient_storage(&key, value, false);
}
assert_eq!(result, Ok(WriteOutcome::New));
assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
Ok(())
}
#[benchmark(pov_mode = Ignored)]
fn set_transient_storage_full() -> Result<(), BenchmarkError> {
let max_value_len = T::Schedule::get().limits.payload_len;
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let value = Some(vec![42u8; max_value_len as _]);
let mut setup = CallSetup::<T>::default();
setup.set_transient_storage_size(T::MaxTransientStorageSize::get());
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
let result;
#[block]
{
result = runtime.ext().set_transient_storage(&key, value, false);
}
assert_eq!(result, Ok(WriteOutcome::New));
assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
Ok(())
}
#[benchmark(pov_mode = Ignored)]
fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
let max_value_len = T::Schedule::get().limits.payload_len;
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
runtime
.ext()
.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result = runtime.ext().get_transient_storage(&key);
}
assert_eq!(result, Some(vec![42u8; max_value_len as _]));
Ok(())
}
#[benchmark(pov_mode = Ignored)]
fn get_transient_storage_full() -> Result<(), BenchmarkError> {
let max_value_len = T::Schedule::get().limits.payload_len;
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let mut setup = CallSetup::<T>::default();
setup.set_transient_storage_size(T::MaxTransientStorageSize::get());
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
runtime
.ext()
.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result = runtime.ext().get_transient_storage(&key);
}
assert_eq!(result, Some(vec![42u8; max_value_len as _]));
Ok(())
}
#[benchmark(pov_mode = Ignored)]
fn rollback_transient_storage() -> Result<(), BenchmarkError> {
let max_value_len = T::Schedule::get().limits.payload_len;
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let mut setup = CallSetup::<T>::default();
setup.set_transient_storage_size(T::MaxTransientStorageSize::get());
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
runtime.ext().transient_storage().start_transaction();
runtime
.ext()
.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
#[block]
{
runtime.ext().transient_storage().rollback_transaction();
}
assert_eq!(runtime.ext().get_transient_storage(&key), None);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_set_transient_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
o: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let value = vec![1u8; n as usize];
build_runtime!(runtime, memory: [ key.to_vec(), value.clone(), ]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
runtime
.ext()
.set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result = BenchEnv::seal0_set_transient_storage(
&mut runtime,
&mut memory,
0, max_key_len, max_key_len, n, );
}
assert_ok!(result);
assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_clear_transient_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, memory: [ key.to_vec(), ]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
runtime
.ext()
.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result =
BenchEnv::seal0_clear_transient_storage(&mut runtime, &mut memory, 0, max_key_len);
}
assert_ok!(result);
assert!(runtime.ext().get_transient_storage(&key).is_none());
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_get_transient_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
runtime
.ext()
.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let out_ptr = max_key_len + 4;
let result;
#[block]
{
result = BenchEnv::seal0_get_transient_storage(
&mut runtime,
&mut memory,
0, max_key_len, out_ptr, max_key_len, );
}
assert_ok!(result);
assert_eq!(
&runtime.ext().get_transient_storage(&key).unwrap(),
&memory[out_ptr as usize..]
);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_contains_transient_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, memory: [ key.to_vec(), ]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
runtime
.ext()
.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result = BenchEnv::seal0_contains_transient_storage(
&mut runtime,
&mut memory,
0,
max_key_len,
);
}
assert_eq!(result.unwrap(), n);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_take_transient_storage(
n: Linear<0, { T::Schedule::get().limits.payload_len }>,
) -> Result<(), BenchmarkError> {
let n = T::Schedule::get().limits.payload_len;
let max_key_len = T::MaxStorageKeyLen::get();
let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
let value = vec![42u8; n as usize];
runtime
.ext()
.set_transient_storage(&key, Some(value.clone()), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let out_ptr = max_key_len + 4;
let result;
#[block]
{
result = BenchEnv::seal0_take_transient_storage(
&mut runtime,
&mut memory,
0, max_key_len, out_ptr, max_key_len, );
}
assert_ok!(result);
assert!(&runtime.ext().get_transient_storage(&key).is_none());
assert_eq!(&value, &memory[out_ptr as usize..]);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_transfer() {
let account = account::<T::AccountId>("receiver", 0, 0);
let value = Pallet::<T>::min_balance();
assert!(value > 0u32.into());
let mut setup = CallSetup::<T>::default();
setup.set_balance(value);
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
let account_bytes = account.encode();
let account_len = account_bytes.len() as u32;
let value_bytes = value.encode();
let value_len = value_bytes.len() as u32;
let mut memory = memory!(account_bytes, value_bytes,);
let result;
#[block]
{
result = BenchEnv::seal0_transfer(
&mut runtime,
&mut memory,
0, account_len,
account_len,
value_len,
);
}
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn seal_call(t: Linear<0, 1>, i: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
let Contract { account_id: callee, .. } =
Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
let callee_bytes = callee.encode();
let callee_len = callee_bytes.len() as u32;
let value: BalanceOf<T> = t.into();
let value_bytes = value.encode();
let deposit: BalanceOf<T> = (u32::MAX - 100).into();
let deposit_bytes = deposit.encode();
let deposit_len = deposit_bytes.len() as u32;
let mut setup = CallSetup::<T>::default();
setup.set_storage_deposit_limit(deposit);
setup.set_data(vec![42; i as usize]);
setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
let result;
#[block]
{
result = BenchEnv::seal2_call(
&mut runtime,
&mut memory,
CallFlags::CLONE_INPUT.bits(), 0, 0, 0, callee_len, callee_len + deposit_len, 0, 0, SENTINEL, 0, );
}
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn seal_delegate_call() -> Result<(), BenchmarkError> {
let hash = Contract::<T>::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash;
let mut setup = CallSetup::<T>::default();
setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
let mut memory = memory!(hash.encode(),);
let result;
#[block]
{
result = BenchEnv::seal0_delegate_call(
&mut runtime,
&mut memory,
0, 0, 0, 0, SENTINEL, 0,
);
}
assert_ok!(result);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_instantiate(
i: Linear<0, { (code::max_pages::<T>() - 1) * 64 * 1024 }>,
s: Linear<0, { (code::max_pages::<T>() - 1) * 64 * 1024 }>,
) -> Result<(), BenchmarkError> {
let hash = Contract::<T>::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash;
let hash_bytes = hash.encode();
let hash_len = hash_bytes.len() as u32;
let value: BalanceOf<T> = 1u32.into();
let value_bytes = value.encode();
let value_len = value_bytes.len() as u32;
let deposit: BalanceOf<T> = 0u32.into();
let deposit_bytes = deposit.encode();
let deposit_len = deposit_bytes.len() as u32;
let mut setup = CallSetup::<T>::default();
setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
setup.set_balance(value + (Pallet::<T>::min_balance() * 2u32.into()));
let account_id = &setup.contract().account_id.clone();
let (mut ext, _) = setup.ext();
let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
let input = vec![42u8; i as _];
let salt = vec![42u8; s as _];
let addr = Contracts::<T>::contract_address(&account_id, &hash, &input, &salt);
let mut memory = memory!(hash_bytes, deposit_bytes, value_bytes, input, salt,);
let mut offset = {
let mut current = 0u32;
move |after: u32| {
current += after;
current
}
};
assert!(ContractInfoOf::<T>::get(&addr).is_none());
let result;
#[block]
{
result = BenchEnv::seal2_instantiate(
&mut runtime,
&mut memory,
0, 0, 0, offset(hash_len), offset(deposit_len), offset(value_len), i, SENTINEL, 0, SENTINEL, 0, offset(i), s, );
}
assert_ok!(result);
assert!(ContractInfoOf::<T>::get(&addr).is_some());
assert_eq!(T::Currency::balance(&addr), Pallet::<T>::min_balance() + value);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_hash_sha2_256(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
let result;
#[block]
{
result = BenchEnv::seal0_hash_sha2_256(&mut runtime, &mut memory, 32, n, 0);
}
assert_eq!(sp_io::hashing::sha2_256(&memory[32..]), &memory[0..32]);
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn seal_hash_keccak_256(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
let result;
#[block]
{
result = BenchEnv::seal0_hash_keccak_256(&mut runtime, &mut memory, 32, n, 0);
}
assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn seal_hash_blake2_256(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
let result;
#[block]
{
result = BenchEnv::seal0_hash_blake2_256(&mut runtime, &mut memory, 32, n, 0);
}
assert_eq!(sp_io::hashing::blake2_256(&memory[32..]), &memory[0..32]);
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn seal_hash_blake2_128(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
build_runtime!(runtime, memory: [[0u8; 16], vec![0u8; n as usize], ]);
let result;
#[block]
{
result = BenchEnv::seal0_hash_blake2_128(&mut runtime, &mut memory, 16, n, 0);
}
assert_eq!(sp_io::hashing::blake2_128(&memory[16..]), &memory[0..16]);
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn seal_sr25519_verify(n: Linear<0, { T::MaxCodeLen::get() - 255 }>) {
let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
let message_len = message.len() as u32;
let key_type = sp_core::crypto::KeyTypeId(*b"code");
let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
let sig =
sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
let sig_len = sig.len() as u32;
build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
let result;
#[block]
{
result = BenchEnv::seal0_sr25519_verify(
&mut runtime,
&mut memory,
0, sig_len, message_len, sig_len + pub_key.len() as u32, );
}
assert_eq!(result.unwrap(), ReturnErrorCode::Success);
}
#[benchmark(pov_mode = Measured)]
fn seal_ecdsa_recover() {
let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes());
let key_type = sp_core::crypto::KeyTypeId(*b"code");
let signature = {
let pub_key = sp_io::crypto::ecdsa_generate(key_type, None);
let sig = sp_io::crypto::ecdsa_sign_prehashed(key_type, &pub_key, &message_hash)
.expect("Generates signature");
AsRef::<[u8; 65]>::as_ref(&sig).to_vec()
};
build_runtime!(runtime, memory: [signature, message_hash, [0u8; 33], ]);
let result;
#[block]
{
result = BenchEnv::seal0_ecdsa_recover(
&mut runtime,
&mut memory,
0, 65, 65 + 32, );
}
assert_eq!(result.unwrap(), ReturnErrorCode::Success);
}
#[benchmark(pov_mode = Measured)]
fn seal_ecdsa_to_eth_address() {
let key_type = sp_core::crypto::KeyTypeId(*b"code");
let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
let result;
#[block]
{
result = BenchEnv::seal0_ecdsa_to_eth_address(
&mut runtime,
&mut memory,
20, 0, );
}
assert_ok!(result);
assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
}
#[benchmark(pov_mode = Measured)]
fn seal_set_code_hash() -> Result<(), BenchmarkError> {
let code_hash =
Contract::<T>::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash;
build_runtime!(runtime, memory: [ code_hash.encode(),]);
let result;
#[block]
{
result = BenchEnv::seal0_set_code_hash(&mut runtime, &mut memory, 0);
}
assert_ok!(result);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn lock_delegate_dependency() -> Result<(), BenchmarkError> {
let code_hash = Contract::<T>::with_index(1, WasmModule::dummy_with_bytes(1), vec![])?
.info()?
.code_hash;
build_runtime!(runtime, memory: [ code_hash.encode(),]);
let result;
#[block]
{
result = BenchEnv::seal0_lock_delegate_dependency(&mut runtime, &mut memory, 0);
}
assert_ok!(result);
Ok(())
}
#[benchmark]
fn unlock_delegate_dependency() -> Result<(), BenchmarkError> {
let code_hash = Contract::<T>::with_index(1, WasmModule::dummy_with_bytes(1), vec![])?
.info()?
.code_hash;
build_runtime!(runtime, memory: [ code_hash.encode(),]);
BenchEnv::seal0_lock_delegate_dependency(&mut runtime, &mut memory, 0).unwrap();
let result;
#[block]
{
result = BenchEnv::seal0_unlock_delegate_dependency(&mut runtime, &mut memory, 0);
}
assert_ok!(result);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_reentrance_count() {
build_runtime!(runtime, memory: []);
let result;
#[block]
{
result = BenchEnv::seal0_reentrance_count(&mut runtime, &mut memory)
}
assert_eq!(result.unwrap(), 0);
}
#[benchmark(pov_mode = Measured)]
fn seal_account_reentrance_count() {
let Contract { account_id, .. } =
Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
build_runtime!(runtime, memory: [account_id.encode(),]);
let result;
#[block]
{
result = BenchEnv::seal0_account_reentrance_count(&mut runtime, &mut memory, 0);
}
assert_eq!(result.unwrap(), 0);
}
#[benchmark(pov_mode = Measured)]
fn seal_instantiation_nonce() {
build_runtime!(runtime, memory: []);
let result;
#[block]
{
result = BenchEnv::seal0_instantiation_nonce(&mut runtime, &mut memory);
}
assert_eq!(result.unwrap(), 1);
}
#[benchmark(pov_mode = Ignored)]
fn instr_i64_load_store(r: Linear<0, INSTR_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> {
use rand::prelude::*;
let mut rng = rand_pcg::Pcg32::seed_from_u64(8446744073709551615);
let memory = ImportedMemory::max::<T>();
let bytes_per_page = 65536;
let bytes_per_memory = memory.max_pages * bytes_per_page;
let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
memory: Some(memory),
call_body: Some(body::repeated_with_locals_using(
&[Local::new(1, ValueType::I64)],
r,
|| {
let c0: i32 = rng.gen_range(0..bytes_per_memory as i32);
let c1: i32 = rng.gen_range(0..bytes_per_memory as i32);
[
Instruction::I32Const(c0), Instruction::I64Load8S(0, 0),
Instruction::SetLocal(0), Instruction::I32Const(c1), Instruction::GetLocal(0), Instruction::I64Store8(0, 0),
]
},
)),
..Default::default()
}));
#[block]
{
sbox.invoke();
}
Ok(())
}
#[benchmark(extra, pov_mode = Ignored)]
fn print_schedule() -> Result<(), BenchmarkError> {
let max_weight = <T as frame_system::Config>::BlockWeights::get().max_block;
let (weight_per_key, key_budget) =
ContractInfo::<T>::deletion_budget(&mut WeightMeter::with_limit(max_weight));
let schedule = T::Schedule::get();
log::info!(target: LOG_TARGET, "
{schedule:#?}
###############################################
Lazy deletion weight per key: {weight_per_key}
Lazy deletion keys per block: {key_budget}
");
#[block]
{}
Err(BenchmarkError::Skip)
}
impl_benchmark_test_suite!(
Contracts,
crate::tests::ExtBuilder::default().build(),
crate::tests::Test,
);
}