#![cfg(feature = "runtime-benchmarks")]
use crate::{
Pallet as Contracts,
call_builder::{CallSetup, Contract, VmBinaryModule, caller_funding, default_deposit_limit},
evm::{
TransactionLegacyUnsigned, TransactionSigned, TransactionUnsigned,
block_hash::EthereumBlockBuilder, block_storage,
},
exec::{Key, Origin as ExecOrigin, PrecompileExt},
limits,
precompiles::{
self, BenchmarkStorage, BenchmarkSystem, BuiltinPrecompile,
alloy::sol_types::{
SolType,
sol_data::{Bool, Bytes, FixedBytes, Uint},
},
run::builtin as run_builtin_precompile,
},
storage::WriteOutcome,
vm::{
evm,
evm::{Interpreter, instructions, instructions::utility::IntoAddress},
pvm,
},
*,
};
use alloc::{vec, vec::Vec};
use alloy_core::sol_types::{SolInterface, SolValue};
use codec::{Encode, MaxEncodedLen};
use frame_benchmarking::v2::*;
use frame_support::{
self, assert_ok,
migrations::SteppedMigration,
storage::child,
traits::{Hooks, fungible::InspectHold},
weights::{Weight, WeightMeter},
};
use frame_system::RawOrigin;
use k256::ecdsa::SigningKey;
use pallet_revive_uapi::{
CallFlags, ReturnErrorCode, StorageFlags, pack_hi_lo,
precompiles::{storage::IStorage, system::ISystem},
};
use revm::bytecode::Bytecode;
use sp_consensus_aura::AURA_ENGINE_ID;
use sp_consensus_babe::{
BABE_ENGINE_ID,
digests::{PreDigest, PrimaryPreDigest},
};
use sp_consensus_slots::Slot;
use sp_runtime::{generic::DigestItem, traits::Zero};
const API_BENCHMARK_RUNS: u32 = 1600;
macro_rules! memory(
($($bytes:expr,)*) => {{
vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::<Vec<_>>()
}};
);
macro_rules! build_runtime(
($runtime:ident, $memory:ident: [$($segment:expr,)*]) => {
build_runtime!($runtime, _contract, $memory: [$($segment,)*]);
};
($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => {
build_runtime!($runtime, $contract);
let mut $memory = memory!($($bytes,)*);
};
($runtime:ident, $contract:ident) => {
let mut setup = CallSetup::<T>::default();
let $contract = setup.contract();
let input = setup.data();
let (mut ext, _) = setup.ext();
let mut $runtime = $crate::vm::pvm::Runtime::<_, [u8]>::new(&mut ext, input);
};
);
fn whitelisted_pallet_account<T: Config>() -> T::AccountId {
let pallet_account = Pallet::<T>::account_id();
whitelist_account!(pallet_account);
pallet_account
}
#[benchmarks(
where
T: Config,
<T as Config>::RuntimeCall: From<frame_system::Call<T>>,
<T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
OriginFor<T>: From<Origin<T>>,
)]
mod benchmarks {
use super::*;
#[benchmark(pov_mode = Measured)]
fn deletion_queue_batch() {
#[block]
{
ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
}
}
#[benchmark(pov_mode = Measured)]
fn deletion_queue_per_entry() -> Result<(), BenchmarkError> {
let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
ContractInfo::<T>::queue_for_deletion(
instance.info()?.trie_id,
instance.account_id.clone(),
);
#[block]
{
ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
}
assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn deletion_queue_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
let instance =
Contract::<T>::with_storage(VmBinaryModule::dummy(), k, limits::STORAGE_BYTES)?;
ContractInfo::<T>::queue_for_deletion(
instance.info()?.trie_id,
instance.account_id.clone(),
);
#[block]
{
ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
}
assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn deletion_queue_per_native_deposit_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
use frame_benchmarking::v2::account;
let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
for i in 0..k {
let payer: T::AccountId = account("payer", i, 0);
NativeDepositOf::<T>::insert(&instance.account_id, &payer, BalanceOf::<T>::default());
}
ContractInfo::<T>::queue_for_deletion(
instance.info()?.trie_id,
instance.account_id.clone(),
);
#[block]
{
ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
}
assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn call_with_pvm_code_per_byte(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
let instance =
Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::sized(c), vec![])?;
let value = Pallet::<T>::min_balance();
let storage_deposit = default_deposit_limit::<T>();
#[extrinsic_call]
call(
RawOrigin::Signed(instance.caller.clone()),
instance.address,
value,
Weight::MAX,
storage_deposit,
vec![],
);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn call_with_evm_code_per_byte(c: Linear<1, { 10 * 1024 }>) -> Result<(), BenchmarkError> {
let instance = Contract::<T>::with_caller(
whitelisted_caller(),
VmBinaryModule::evm_init_code_for_runtime_size(c),
vec![],
)?;
let value = Pallet::<T>::min_balance();
let storage_deposit = default_deposit_limit::<T>();
let code_len = PristineCode::<T>::get(instance.info()?.code_hash)
.expect("code should be stored")
.len();
assert_eq!(
code_len, c as usize,
"runtime bytecode should be exactly {c} bytes, got {code_len}"
);
#[extrinsic_call]
call(
RawOrigin::Signed(instance.caller.clone()),
instance.address,
value,
Weight::MAX,
storage_deposit,
vec![],
);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn basic_block_compilation(b: Linear<0, 1>) -> Result<(), BenchmarkError> {
let instance = Contract::<T>::with_caller(
whitelisted_caller(),
VmBinaryModule::with_num_instructions(limits::code::BASIC_BLOCK_SIZE),
vec![],
)?;
let value = Pallet::<T>::min_balance();
let storage_deposit = default_deposit_limit::<T>();
#[block]
{
Pallet::<T>::call(
RawOrigin::Signed(instance.caller.clone()).into(),
instance.address,
value,
Weight::MAX,
storage_deposit,
vec![],
)?;
}
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn instantiate_with_code(
c: Linear<0, { 100 * 1024 }>,
i: Linear<0, { limits::CALLDATA_BYTES }>,
) {
let pallet_account = whitelisted_pallet_account::<T>();
let input = vec![42u8; i as usize];
let salt = [42u8; 32];
let value = Pallet::<T>::min_balance();
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
let origin = RawOrigin::Signed(caller.clone());
if !T::AddressMapper::is_mapped(&caller) {
T::AddressMapper::map(&caller).unwrap();
}
let deployer = T::AddressMapper::to_address(&caller);
let addr = crate::address::create2(&deployer, &code, &input, &salt);
let account_id = T::AddressMapper::to_fallback_account_id(&addr);
let storage_deposit = default_deposit_limit::<T>();
#[extrinsic_call]
_(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt));
let deposit =
T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
let code_deposit = T::Currency::balance_on_hold(
&HoldReason::CodeUploadDepositReserve.into(),
&pallet_account,
);
let mapping_deposit =
T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller);
assert_eq!(
T::Currency::balance(&caller),
caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
);
assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
}
#[benchmark(pov_mode = Measured)]
fn eth_instantiate_with_code(
c: Linear<0, { 100 * 1024 }>,
i: Linear<0, { limits::CALLDATA_BYTES }>,
d: Linear<0, 1>,
) -> Result<(), BenchmarkError> {
let input = vec![42u8; i as usize];
let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
let value = Pallet::<T>::min_balance();
let dust = 42u32 * d;
let evm_value =
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
let origin = Origin::EthTransaction(caller.clone());
if !T::AddressMapper::is_mapped(&caller) {
T::AddressMapper::map(&caller).unwrap();
}
let deployer = T::AddressMapper::to_address(&caller);
let nonce = System::<T>::account_nonce(&caller).try_into().unwrap_or_default();
let addr = crate::address::create1(&deployer, nonce);
assert!(AccountInfoOf::<T>::get(&deployer).is_none());
<T as Config>::FeeInfo::deposit_txfee(
<T as Config>::Currency::issue(caller_funding::<T>()),
);
#[extrinsic_call]
_(
origin,
evm_value,
Weight::MAX,
U256::MAX,
code,
input,
TransactionSigned::default().signed_payload(),
effective_gas_price,
0,
);
assert_eq!(Pallet::<T>::evm_balance(&addr), evm_value);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn deposit_eth_extrinsic_revert_event() {
#[block]
{
Pallet::<T>::deposit_event(Event::<T>::EthExtrinsicRevert {
dispatch_error: crate::Error::<T>::BenchmarkingError.into(),
});
}
}
#[benchmark(pov_mode = Measured)]
fn instantiate(i: Linear<0, { limits::CALLDATA_BYTES }>) -> Result<(), BenchmarkError> {
let pallet_account = whitelisted_pallet_account::<T>();
let input = vec![42u8; i as usize];
let salt = [42u8; 32];
let value = Pallet::<T>::min_balance();
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let origin = RawOrigin::Signed(caller.clone());
if !T::AddressMapper::is_mapped(&caller) {
T::AddressMapper::map(&caller).unwrap();
}
let VmBinaryModule { code, .. } = VmBinaryModule::dummy();
let storage_deposit = default_deposit_limit::<T>();
let deployer = T::AddressMapper::to_address(&caller);
let addr = crate::address::create2(&deployer, &code, &input, &salt);
let hash = Contracts::<T>::bare_upload_code(origin.clone().into(), code, storage_deposit)?
.code_hash;
let account_id = T::AddressMapper::to_fallback_account_id(&addr);
#[extrinsic_call]
_(origin, value, Weight::MAX, storage_deposit, hash, input, Some(salt));
let deposit =
T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
let code_deposit = T::Currency::balance_on_hold(
&HoldReason::CodeUploadDepositReserve.into(),
&pallet_account,
);
let mapping_deposit =
T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &account_id);
assert_eq!(
T::Currency::total_balance(&caller),
caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
);
assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn call() -> Result<(), BenchmarkError> {
let pallet_account = whitelisted_pallet_account::<T>();
let data = vec![42u8; 1024];
let instance =
Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
let value = Pallet::<T>::min_balance();
let origin = RawOrigin::Signed(instance.caller.clone());
let before = T::Currency::balance(&instance.account_id);
let storage_deposit = default_deposit_limit::<T>();
#[extrinsic_call]
_(origin, instance.address, value, Weight::MAX, storage_deposit, data);
let deposit = T::Currency::balance_on_hold(
&HoldReason::StorageDepositReserve.into(),
&instance.account_id,
);
let code_deposit = T::Currency::balance_on_hold(
&HoldReason::CodeUploadDepositReserve.into(),
&pallet_account,
);
let mapping_deposit =
T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller);
assert_eq!(
T::Currency::balance(&instance.caller),
caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
);
assert_eq!(T::Currency::balance(&instance.account_id), before + value);
instance.info()?;
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn eth_call(d: Linear<0, 1>) -> Result<(), BenchmarkError> {
let data = vec![42u8; 1024];
let instance =
Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
let value = Pallet::<T>::min_balance();
let dust = 42u32 * d;
let evm_value =
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
<T as Config>::FeeInfo::deposit_txfee(
<T as Config>::Currency::issue(caller_funding::<T>()),
);
let origin = Origin::EthTransaction(instance.caller.clone());
let before = Pallet::<T>::evm_balance(&instance.address);
#[extrinsic_call]
_(
origin,
instance.address,
evm_value,
Weight::MAX,
U256::MAX,
data,
TransactionSigned::default().signed_payload(),
effective_gas_price,
0,
);
assert_eq!(Pallet::<T>::evm_balance(&instance.address), before + evm_value);
instance.info()?;
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn eth_substrate_call(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let origin = Origin::EthTransaction(caller);
let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
#[extrinsic_call]
_(origin, Box::new(dispatchable), vec![42u8; c as usize]);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn upload_code(c: Linear<0, { 100 * 1024 }>) {
let caller = whitelisted_caller();
let pallet_account = whitelisted_pallet_account::<T>();
T::Currency::set_balance(&caller, caller_funding::<T>());
let VmBinaryModule { code, hash, .. } = VmBinaryModule::sized(c);
let origin = RawOrigin::Signed(caller.clone());
let storage_deposit = default_deposit_limit::<T>();
#[extrinsic_call]
_(origin, code, storage_deposit);
assert!(T::Currency::total_balance_on_hold(&pallet_account) > 0u32.into());
assert!(<Contract<T>>::code_exists(&hash));
}
#[benchmark(pov_mode = Measured)]
fn remove_code() -> Result<(), BenchmarkError> {
let caller = whitelisted_caller();
let pallet_account = whitelisted_pallet_account::<T>();
T::Currency::set_balance(&caller, caller_funding::<T>());
let VmBinaryModule { code, hash, .. } = VmBinaryModule::dummy();
let origin = RawOrigin::Signed(caller.clone());
let storage_deposit = default_deposit_limit::<T>();
let uploaded =
<Contracts<T>>::bare_upload_code(origin.clone().into(), code, storage_deposit)?;
assert_eq!(uploaded.code_hash, hash);
assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&pallet_account));
assert!(<Contract<T>>::code_exists(&hash));
#[extrinsic_call]
_(origin, hash);
assert_eq!(T::Currency::total_balance_on_hold(&pallet_account), 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(), VmBinaryModule::dummy(), vec![])?;
let VmBinaryModule { code, .. } = VmBinaryModule::dummy_unique(128);
let origin = RawOrigin::Signed(instance.caller.clone());
let storage_deposit = default_deposit_limit::<T>();
let hash =
<Contracts<T>>::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash;
assert_ne!(instance.info()?.code_hash, hash);
#[extrinsic_call]
_(RawOrigin::Root, instance.address, hash);
assert_eq!(instance.info()?.code_hash, hash);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn map_account() {
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let origin = RawOrigin::Signed(caller.clone());
if T::AddressMapper::is_mapped(&caller) {
T::AddressMapper::unmap(&caller).unwrap();
}
assert!(!T::AddressMapper::is_mapped(&caller));
#[extrinsic_call]
_(origin);
assert!(T::AddressMapper::is_mapped(&caller));
}
#[benchmark(pov_mode = Measured)]
fn unmap_account() {
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let origin = RawOrigin::Signed(caller.clone());
if !T::AddressMapper::is_mapped(&caller) {
T::AddressMapper::map(&caller).unwrap();
}
assert!(T::AddressMapper::is_mapped(&caller));
#[extrinsic_call]
_(origin);
assert!(!T::AddressMapper::is_mapped(&caller));
}
#[benchmark(pov_mode = Measured)]
fn dispatch_as_fallback_account() {
let caller = whitelisted_caller();
T::Currency::set_balance(&caller, caller_funding::<T>());
let origin = RawOrigin::Signed(caller.clone());
let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
#[extrinsic_call]
_(origin, Box::new(dispatchable));
}
#[benchmark(pov_mode = Measured)]
fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) {
let mut setup = CallSetup::<T>::new(VmBinaryModule::noop());
let (mut ext, module) = setup.ext();
let prepared = CallSetup::<T>::prepare_call(&mut ext, module, r.encode(), 0);
#[block]
{
prepared.call().unwrap();
}
}
#[benchmark(pov_mode = Measured)]
fn seal_caller() {
let len = H160::len_bytes();
build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
let result;
#[block]
{
result = runtime.bench_caller(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(
<H160 as Decode>::decode(&mut &memory[..]).unwrap(),
T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap())
);
}
#[benchmark(pov_mode = Measured)]
fn seal_origin() {
let len = H160::len_bytes();
build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
let result;
#[block]
{
result = runtime.bench_origin(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(
<H160 as Decode>::decode(&mut &memory[..]).unwrap(),
T::AddressMapper::to_address(&runtime.ext().origin().account_id().unwrap())
);
}
#[benchmark(pov_mode = Measured)]
fn to_account_id() {
let account_id = account("precompile_to_account_id", 0, 0);
let address = {
T::Currency::set_balance(&account_id, caller_funding::<T>());
if !T::AddressMapper::is_mapped(&account_id) {
T::AddressMapper::map(&account_id).unwrap();
}
T::AddressMapper::to_address(&account_id)
};
let input_bytes = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
input: address.0.into(),
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
let raw_data = result.unwrap().data;
let data = Bytes::abi_decode(&raw_data).expect("decoding failed");
assert_ne!(
data.0.as_ref()[20..32],
[0xEE; 12],
"fallback suffix found where none should be"
);
assert_eq!(T::AccountId::decode(&mut data.as_ref()), Ok(account_id),);
}
#[benchmark(pov_mode = Measured)]
fn seal_code_hash() {
let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
let len = <sp_core::H256 as MaxEncodedLen>::max_encoded_len() as u32;
build_runtime!(runtime, memory: [vec![0u8; len as _], contract.account_id.encode(), ]);
let result;
#[block]
{
result = runtime.bench_code_hash(memory.as_mut_slice(), len, 0);
}
assert_ok!(result);
assert_eq!(
<sp_core::H256 as Decode>::decode(&mut &memory[..]).unwrap(),
contract.info().unwrap().code_hash
);
}
#[benchmark(pov_mode = Measured)]
fn own_code_hash() {
let input_bytes =
ISystem::ISystemCalls::ownCodeHash(ISystem::ownCodeHashCall {}).abi_encode();
let mut call_setup = CallSetup::<T>::default();
let contract_acc = call_setup.contract().account_id.clone();
let caller = call_setup.contract().address;
call_setup.set_origin(ExecOrigin::from_account_id(contract_acc));
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
assert!(result.is_ok());
let caller_code_hash = ext.code_hash(&caller);
assert_eq!(caller_code_hash.0.to_vec(), result.unwrap().data);
}
#[benchmark(pov_mode = Measured)]
fn seal_code_size() {
let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
build_runtime!(runtime, memory: [contract.address.encode(),]);
let result;
#[block]
{
result = runtime.bench_code_size(memory.as_mut_slice(), 0);
}
assert_eq!(result.unwrap(), VmBinaryModule::dummy().code.len() as u64);
}
#[benchmark(pov_mode = Measured)]
fn caller_is_origin() {
let input_bytes =
ISystem::ISystemCalls::callerIsOrigin(ISystem::callerIsOriginCall {}).abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
let raw_data = result.unwrap().data;
let is_origin = Bool::abi_decode(&raw_data[..]).expect("decoding failed");
assert!(is_origin);
}
#[benchmark(pov_mode = Measured)]
fn caller_is_root() {
let input_bytes =
ISystem::ISystemCalls::callerIsRoot(ISystem::callerIsRootCall {}).abi_encode();
let mut setup = CallSetup::<T>::default();
setup.set_origin(ExecOrigin::Root);
let (mut ext, _) = setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
let raw_data = result.unwrap().data;
let is_root = Bool::abi_decode(&raw_data).expect("decoding failed");
assert!(is_root);
}
#[benchmark(pov_mode = Measured)]
fn seal_address() {
let len = H160::len_bytes();
build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
let result;
#[block]
{
result = runtime.bench_address(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(<H160 as Decode>::decode(&mut &memory[..]).unwrap(), runtime.ext().address());
}
#[benchmark(pov_mode = Measured)]
fn weight_left() {
let input_bytes =
ISystem::ISystemCalls::weightLeft(ISystem::weightLeftCall {}).abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let weight_left_before = ext.frame_meter().weight_left().unwrap();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
let weight_left_after = ext.frame_meter().weight_left().unwrap();
assert_ne!(weight_left_after.ref_time(), 0);
assert!(weight_left_before.ref_time() > weight_left_after.ref_time());
let raw_data = result.unwrap().data;
type MyTy = (Uint<64>, Uint<64>);
let foo = MyTy::abi_decode(&raw_data[..]).unwrap();
assert_eq!(weight_left_after.ref_time(), foo.0);
}
#[benchmark(pov_mode = Measured)]
fn seal_ref_time_left() {
build_runtime!(runtime, memory: [vec![], ]);
let result;
#[block]
{
result = runtime.bench_ref_time_left(memory.as_mut_slice());
}
assert_eq!(result.unwrap(), runtime.ext().gas_left());
}
#[benchmark(pov_mode = Measured)]
fn seal_balance() {
build_runtime!(runtime, contract, memory: [[0u8;32], ]);
contract.set_balance(BalanceWithDust::new_unchecked::<T>(
Pallet::<T>::min_balance() * 2u32.into(),
42u32,
));
let result;
#[block]
{
result = runtime.bench_balance(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(
U256::from_little_endian(&memory[..]),
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
Pallet::<T>::min_balance(),
42
))
);
}
#[benchmark(pov_mode = Measured)]
fn seal_balance_of() {
let len = <sp_core::U256 as MaxEncodedLen>::max_encoded_len();
let account = account::<T::AccountId>("target", 0, 0);
<T as Config>::AddressMapper::map_no_deposit(&account).unwrap();
let address = T::AddressMapper::to_address(&account);
let balance = Pallet::<T>::min_balance() * 2u32.into();
T::Currency::set_balance(&account, balance);
AccountInfoOf::<T>::insert(&address, AccountInfo { dust: 42, ..Default::default() });
build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]);
let result;
#[block]
{
result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0);
}
assert_ok!(result);
assert_eq!(
U256::from_little_endian(&memory[..len]),
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
Pallet::<T>::min_balance(),
42
))
);
}
#[benchmark(pov_mode = Measured)]
fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
let len = n as usize;
let immutable_data = vec![1u8; len];
build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]);
<ImmutableDataOf<T>>::insert::<_, BoundedVec<_, _>>(
contract.address,
immutable_data.clone().try_into().unwrap(),
);
let result;
#[block]
{
result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32);
}
assert_ok!(result);
assert_eq!(&memory[0..4], (len as u32).encode());
assert_eq!(&memory[4..len + 4], &immutable_data);
}
#[benchmark(pov_mode = Measured)]
fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
let len = n as usize;
let mut memory = vec![1u8; len];
let mut setup = CallSetup::<T>::default();
let input = setup.data();
let (mut ext, _) = setup.ext();
ext.override_export(crate::exec::ExportedFunction::Constructor);
let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
let result;
#[block]
{
result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n);
}
assert_ok!(result);
assert_eq!(&memory[..], &<ImmutableDataOf<T>>::get(setup.contract().address).unwrap()[..]);
}
#[benchmark(pov_mode = Measured)]
fn seal_value_transferred() {
build_runtime!(runtime, memory: [[0u8;32], ]);
let result;
#[block]
{
result = runtime.bench_value_transferred(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().value_transferred());
}
#[benchmark(pov_mode = Measured)]
fn minimum_balance() {
let input_bytes =
ISystem::ISystemCalls::minimumBalance(ISystem::minimumBalanceCall {}).abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
let min: U256 = crate::Pallet::<T>::convert_native_to_evm(T::Currency::minimum_balance());
let min =
crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&min.to_big_endian())
.unwrap();
let raw_data = result.unwrap().data;
let returned_min =
crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&raw_data)
.expect("decoding failed");
assert_eq!(returned_min, min);
}
#[benchmark(pov_mode = Measured)]
fn seal_return_data_size() {
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
let mut memory = memory!(vec![],);
*runtime.ext().last_frame_output_mut() =
ExecReturnValue { data: vec![42; 256], ..Default::default() };
let result;
#[block]
{
result = runtime.bench_return_data_size(memory.as_mut_slice());
}
assert_eq!(result.unwrap(), 256);
}
#[benchmark(pov_mode = Measured)]
fn seal_call_data_size() {
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 128 as usize]);
let mut memory = memory!(vec![0u8; 4],);
let result;
#[block]
{
result = runtime.bench_call_data_size(memory.as_mut_slice());
}
assert_eq!(result.unwrap(), 128);
}
#[benchmark(pov_mode = Measured)]
fn seal_gas_limit() {
build_runtime!(runtime, memory: []);
let result;
#[block]
{
result = runtime.bench_gas_limit(&mut memory);
}
assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_block_gas_limit());
}
#[benchmark(pov_mode = Measured)]
fn seal_gas_price() {
build_runtime!(runtime, memory: []);
let result;
#[block]
{
result = runtime.bench_gas_price(memory.as_mut_slice());
}
assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_base_fee());
}
#[benchmark(pov_mode = Measured)]
fn seal_base_fee() {
build_runtime!(runtime, memory: [[1u8;32], ]);
let result;
#[block]
{
result = runtime.bench_base_fee(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(U256::from_little_endian(&memory[..]), <crate::Pallet<T>>::evm_base_fee());
}
#[benchmark(pov_mode = Measured)]
fn seal_block_number() {
build_runtime!(runtime, memory: [[0u8;32], ]);
let result;
#[block]
{
result = runtime.bench_block_number(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number());
}
#[benchmark(pov_mode = Measured)]
fn seal_block_author() {
build_runtime!(runtime, memory: [[123u8; 20], ]);
for i in 0..16 {
frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
[i, i, i, i],
vec![i; 128],
));
frame_system::Pallet::<T>::deposit_log(DigestItem::Consensus(
[i, i, i, i],
vec![i; 128],
));
frame_system::Pallet::<T>::deposit_log(DigestItem::Seal([i, i, i, i], vec![i; 128]));
frame_system::Pallet::<T>::deposit_log(DigestItem::Other(vec![i; 128]));
}
let primary_pre_digest = vec![0; <PrimaryPreDigest as MaxEncodedLen>::max_encoded_len()];
let pre_digest =
PreDigest::Primary(PrimaryPreDigest::decode(&mut &primary_pre_digest[..]).unwrap());
frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
BABE_ENGINE_ID,
pre_digest.encode(),
));
frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(
BABE_ENGINE_ID,
pre_digest.encode(),
));
let slot = Slot::default();
frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
AURA_ENGINE_ID,
slot.encode(),
));
frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(AURA_ENGINE_ID, slot.encode()));
let result;
#[block]
{
result = runtime.bench_block_author(memory.as_mut_slice(), 0);
}
assert_ok!(result);
let block_author = runtime.ext().block_author();
assert_eq!(&memory[..], block_author.as_bytes());
}
#[benchmark(pov_mode = Measured)]
fn seal_block_hash() {
let mut memory = vec![0u8; 64];
let mut setup = CallSetup::<T>::default();
let input = setup.data();
let (mut ext, _) = setup.ext();
ext.set_block_number(BlockNumberFor::<T>::from(1u32));
let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
let block_hash = H256::from([1; 32]);
crate::BlockHash::<T>::insert(crate::BlockNumberFor::<T>::from(0u32), block_hash);
let result;
#[block]
{
result = runtime.bench_block_hash(memory.as_mut_slice(), 32, 0);
}
assert_ok!(result);
assert_eq!(&memory[..32], &block_hash.0);
}
#[benchmark(pov_mode = Measured)]
fn seal_now() {
build_runtime!(runtime, memory: [[0u8;32], ]);
let result;
#[block]
{
result = runtime.bench_now(memory.as_mut_slice(), 0);
}
assert_ok!(result);
assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now());
}
#[benchmark(pov_mode = Measured)]
fn seal_copy_to_contract(n: Linear<0, { limits::code::BLOB_BYTES - 4 }>) {
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
let mut memory = memory!(n.encode(), vec![0u8; n as usize],);
let result;
#[block]
{
result = runtime.write_sandbox_output(
memory.as_mut_slice(),
4,
0,
&vec![42u8; n as usize],
false,
|_| None,
);
}
assert_ok!(result);
assert_eq!(&memory[..4], &n.encode());
assert_eq!(&memory[4..], &vec![42u8; n as usize]);
}
#[benchmark(pov_mode = Measured)]
fn seal_call_data_load() {
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 32]);
let mut memory = memory!(vec![0u8; 32],);
let result;
#[block]
{
result = runtime.bench_call_data_load(memory.as_mut_slice(), 0, 0);
}
assert_ok!(result);
assert_eq!(&memory[..], &vec![42u8; 32]);
}
#[benchmark(pov_mode = Measured)]
fn seal_call_data_copy(n: Linear<0, { limits::code::BLOB_BYTES }>) {
let mut setup = CallSetup::<T>::default();
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; n as usize]);
let mut memory = memory!(vec![0u8; n as usize],);
let result;
#[block]
{
result = runtime.bench_call_data_copy(memory.as_mut_slice(), 0, n, 0);
}
assert_ok!(result);
assert_eq!(&memory[..], &vec![42u8; n as usize]);
}
#[benchmark(pov_mode = Measured)]
fn seal_return(n: Linear<0, { limits::CALLDATA_BYTES }>) {
build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]);
let result;
#[block]
{
result = runtime.bench_seal_return(memory.as_mut_slice(), 0, 0, n);
}
assert!(matches!(
result,
Err(crate::vm::pvm::TrapReason::Return(crate::vm::pvm::ReturnData { .. }))
));
}
#[benchmark(pov_mode = Measured)]
fn seal_terminate(r: Linear<0, 1>) -> Result<(), BenchmarkError> {
let delete_code = r == 1;
let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
build_runtime!(runtime, instance, memory: [beneficiary.encode(),]);
let code_hash = instance.info()?.code_hash;
if !delete_code {
<CodeInfo<T>>::increment_refcount(code_hash).unwrap();
}
let result;
#[block]
{
result = runtime.bench_terminate(memory.as_mut_slice(), 0);
}
assert!(matches!(result, Err(crate::vm::pvm::TrapReason::Termination)));
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_terminate_logic() -> Result<(), BenchmarkError> {
let caller = whitelisted_caller();
let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
T::AddressMapper::map_no_deposit(&beneficiary)?;
build_runtime!(_runtime, instance, _memory: [vec![0u8; 0], ]);
let code_hash = instance.info()?.code_hash;
assert!(PristineCode::<T>::get(code_hash).is_some());
T::Currency::set_balance(&instance.account_id, Pallet::<T>::min_balance() * 10u32.into());
let storage_deposit = T::Currency::balance_on_hold(
&HoldReason::StorageDepositReserve.into(),
&instance.account_id,
);
NativeDepositOf::<T>::insert(&instance.account_id, &caller, storage_deposit);
let mut transaction_meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
weight_limit: Default::default(),
deposit_limit: BalanceOf::<T>::max_value(),
})
.unwrap();
let exec_config = ExecConfig::new_substrate_tx();
let contract_account = &instance.account_id;
let origin = &ExecOrigin::from_account_id(caller);
let beneficiary_clone = beneficiary.clone();
let trie_id = instance.info()?.trie_id.clone();
let code_hash = instance.info()?.code_hash;
let only_if_same_tx = false;
let result;
#[block]
{
result = crate::exec::bench_do_terminate::<T>(
&mut transaction_meter,
&exec_config,
contract_account,
&origin,
beneficiary_clone,
trie_id,
code_hash,
only_if_same_tx,
);
}
result.unwrap();
assert!(PristineCode::<T>::get(code_hash).is_none());
let balance = <T as Config>::Currency::total_balance(&instance.account_id);
assert_eq!(balance, 0u32.into());
let balance = <T as Config>::Currency::balance(&beneficiary);
assert_eq!(balance, Pallet::<T>::min_balance() + Pallet::<T>::min_balance() * 9u32.into());
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_deposit_event(
t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>,
n: Linear<0, { limits::EVENT_BYTES }>,
) {
let num_topic = t as u32;
let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::<Vec<_>>();
let topics_data =
topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::<Vec<u8>>();
let data = vec![42u8; n as _];
build_runtime!(runtime, instance, memory: [ topics_data, data, ]);
let result;
#[block]
{
result = runtime.bench_deposit_event(
memory.as_mut_slice(),
0, num_topic,
topics_data.len() as u32, n, );
}
assert_ok!(result);
let events = System::<T>::events();
let record = &events[events.len() - 1];
assert_eq!(
record.event,
crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(),
);
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn get_storage_empty() -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = vec![0u8; max_key_len as usize];
let max_value_len = limits::STORAGE_BYTES as usize;
let value = vec![1u8; max_value_len];
let instance = Contract::<T>::new(VmBinaryModule::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 = limits::STORAGE_KEY_BYTES;
let key = vec![0u8; max_key_len as usize];
let max_value_len = limits::STORAGE_BYTES;
let value = vec![1u8; max_value_len as usize];
let instance = Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::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 = limits::STORAGE_KEY_BYTES;
let key = vec![0u8; max_key_len as usize];
let max_value_len = limits::STORAGE_BYTES as usize;
let value = vec![1u8; max_value_len];
let instance = Contract::<T>::new(VmBinaryModule::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 = limits::STORAGE_KEY_BYTES;
let key = vec![0u8; max_key_len as usize];
let max_value_len = limits::STORAGE_BYTES;
let value = vec![1u8; max_value_len as usize];
let instance = Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::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, { limits::STORAGE_BYTES }>,
o: Linear<0, { limits::STORAGE_BYTES }>,
) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::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.unhashed(), 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 = runtime.bench_set_storage(
memory.as_mut_slice(),
StorageFlags::empty().bits(),
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 clear_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
flags: StorageFlags::empty().bits(),
key: vec![0u8; max_key_len as usize].into(),
isFixedKey: false,
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
ext.set_storage(&key, Some(vec![42u8; max_key_len as usize]), false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
assert_ok!(result);
assert!(ext.get_storage(&key).is_none());
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn seal_get_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, instance, memory: [ key.unhashed(), 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 = runtime.bench_get_storage(
memory.as_mut_slice(),
StorageFlags::empty().bits(),
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 contains_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
flags: StorageFlags::TRANSIENT.bits(),
key: vec![0u8; max_key_len as usize].into(),
isFixedKey: false,
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
ext.set_storage(&key, Some(vec![42u8; max_key_len as usize]), false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
assert_ok!(result);
assert!(ext.get_storage(&key).is_some());
Ok(())
}
#[benchmark(skip_meta, pov_mode = Measured)]
fn take_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![3u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
flags: StorageFlags::empty().bits(),
key: vec![3u8; max_key_len as usize].into(),
isFixedKey: false,
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
ext.set_storage(&key, Some(vec![42u8; max_key_len as usize]), false)
.map_err(|_| "Failed to write to storage during setup.")?;
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
assert_ok!(result);
assert!(ext.get_storage(&key).is_none());
Ok(())
}
#[benchmark(pov_mode = Ignored)]
fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
let max_value_len = limits::STORAGE_BYTES;
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::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 = pvm::Runtime::<_, [u8]>::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 = limits::STORAGE_BYTES;
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::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(limits::TRANSIENT_STORAGE_BYTES);
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::<_, [u8]>::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 = limits::STORAGE_BYTES;
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::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 = pvm::Runtime::<_, [u8]>::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 = limits::STORAGE_BYTES;
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::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(limits::TRANSIENT_STORAGE_BYTES);
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::<_, [u8]>::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 = limits::STORAGE_BYTES;
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::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(limits::TRANSIENT_STORAGE_BYTES);
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::<_, [u8]>::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, { limits::STORAGE_BYTES }>,
o: Linear<0, { limits::STORAGE_BYTES }>,
) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::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.unhashed(), 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 = runtime.bench_set_storage(
memory.as_mut_slice(),
StorageFlags::TRANSIENT.bits(),
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, { limits::STORAGE_BYTES }>,
) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
flags: StorageFlags::TRANSIENT.bits(),
key: vec![0u8; max_key_len as usize].into(),
isFixedKey: false,
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
ext.set_transient_storage(&key, Some(vec![42u8; max_key_len as usize]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
assert_ok!(result);
assert!(ext.get_transient_storage(&key).is_none());
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_get_transient_storage(
n: Linear<0, { limits::STORAGE_BYTES }>,
) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
build_runtime!(runtime, memory: [ key.unhashed(), 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 = runtime.bench_get_storage(
memory.as_mut_slice(),
StorageFlags::TRANSIENT.bits(),
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, { limits::STORAGE_BYTES }>,
) -> Result<(), BenchmarkError> {
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
flags: StorageFlags::TRANSIENT.bits(),
key: vec![0u8; max_key_len as usize].into(),
isFixedKey: false,
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
ext.set_transient_storage(&key, Some(vec![42u8; max_key_len as usize]), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
assert!(result.is_ok());
assert!(ext.get_transient_storage(&key).is_some());
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_take_transient_storage(
n: Linear<0, { limits::STORAGE_BYTES }>,
) -> Result<(), BenchmarkError> {
let n = limits::STORAGE_BYTES;
let value = vec![42u8; n as usize];
let max_key_len = limits::STORAGE_KEY_BYTES;
let key = Key::try_from_var(vec![0u8; max_key_len as usize])
.map_err(|_| "Key has wrong length")?;
let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
flags: StorageFlags::TRANSIENT.bits(),
key: vec![0u8; max_key_len as usize].into(),
isFixedKey: false,
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
ext.set_transient_storage(&key, Some(value), false)
.map_err(|_| "Failed to write to transient storage during setup.")?;
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
assert!(result.is_ok());
assert!(ext.get_transient_storage(&key).is_none());
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_call(t: Linear<0, 1>, d: Linear<0, 1>, i: Linear<0, { limits::code::BLOB_BYTES }>) {
let Contract { account_id: callee, address: callee_addr, .. } =
Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
let callee_bytes = callee.encode();
let callee_len = callee_bytes.len() as u32;
let value: BalanceOf<T> = (1_000_000u32 * t).into();
let dust = 100u32 * d;
let evm_value =
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
let value_bytes = evm_value.encode();
let deposit: BalanceOf<T> = (u32::MAX - 100).into();
let deposit_bytes = Into::<U256>::into(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(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
let result;
#[block]
{
result = runtime.bench_call(
memory.as_mut_slice(),
pack_hi_lo(CallFlags::CLONE_INPUT.bits(), 0), u64::MAX, u64::MAX, pack_hi_lo(callee_len, callee_len + deposit_len), pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
}
assert_eq!(result.unwrap(), ReturnErrorCode::Success);
assert_eq!(
Pallet::<T>::evm_balance(&callee_addr),
evm_value,
"{callee_addr:?} balance should hold {evm_value:?}"
);
}
#[benchmark(pov_mode = Measured)]
fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
use alloy_core::sol_types::SolInterface;
use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
let callee_bytes = if d == 1 {
BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
} else {
BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
};
let callee_len = callee_bytes.len() as u32;
let deposit: BalanceOf<T> = (u32::MAX - 100).into();
let deposit_bytes = Into::<U256>::into(deposit).encode();
let deposit_len = deposit_bytes.len() as u32;
let value: BalanceOf<T> = Zero::zero();
let value_bytes = Into::<U256>::into(value).encode();
let value_len = value_bytes.len() as u32;
let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
input: vec![42_u8; i as usize].into(),
})
.abi_encode();
let input_len = input_bytes.len() as u32;
let mut setup = CallSetup::<T>::default();
setup.set_storage_deposit_limit(deposit);
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
let mut do_benchmark = || {
runtime.bench_call(
memory.as_mut_slice(),
pack_hi_lo(0, 0), u64::MAX, u64::MAX, pack_hi_lo(callee_len, callee_len + deposit_len),
pack_hi_lo(input_len, callee_len + deposit_len + value_len),
pack_hi_lo(0, SENTINEL), )
};
assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
let result;
#[block]
{
result = do_benchmark();
}
assert_eq!(result.unwrap(), ReturnErrorCode::Success);
}
#[benchmark(pov_mode = Measured)]
fn seal_delegate_call() -> Result<(), BenchmarkError> {
let Contract { account_id: address, .. } =
Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
let address_bytes = address.encode();
let address_len = address_bytes.len() as u32;
let deposit: BalanceOf<T> = (u32::MAX - 100).into();
let deposit_bytes = Into::<U256>::into(deposit).encode();
let mut setup = CallSetup::<T>::default();
setup.set_storage_deposit_limit(deposit);
setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
let mut memory = memory!(address_bytes, deposit_bytes,);
let result;
#[block]
{
result = runtime.bench_delegate_call(
memory.as_mut_slice(),
pack_hi_lo(0, 0), u64::MAX, u64::MAX, address_len, pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
}
assert_eq!(result.unwrap(), ReturnErrorCode::Success);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn seal_instantiate(
t: Linear<0, 1>,
d: Linear<0, 1>,
i: Linear<0, { limits::CALLDATA_BYTES }>,
) -> Result<(), BenchmarkError> {
let code = VmBinaryModule::dummy();
let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
let hash_bytes = hash.encode();
let value: BalanceOf<T> = (1_000_000u32 * t).into();
let dust = 100u32 * d;
let evm_value =
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
let value_bytes = evm_value.encode();
let value_len = value_bytes.len() as u32;
let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
let deposit_bytes = Into::<U256>::into(deposit).encode();
let deposit_len = deposit_bytes.len() as u32;
let mut setup = CallSetup::<T>::default();
setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
let account_id = &setup.contract().account_id.clone();
let (mut ext, _) = setup.ext();
let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
let input = vec![42u8; i as _];
let input_len = hash_bytes.len() as u32 + input.len() as u32;
let salt = [42u8; 32];
let deployer = T::AddressMapper::to_address(&account_id);
let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
let mut offset = {
let mut current = 0u32;
move |after: u32| {
current += after;
current
}
};
assert!(AccountInfoOf::<T>::get(&addr).is_none());
let result;
#[block]
{
result = runtime.bench_instantiate(
memory.as_mut_slice(),
u64::MAX, u64::MAX, pack_hi_lo(offset(input_len), offset(deposit_len)), pack_hi_lo(input_len, 0), pack_hi_lo(0, SENTINEL), pack_hi_lo(SENTINEL, offset(value_len)), );
}
assert_eq!(result.unwrap(), ReturnErrorCode::Success);
assert!(AccountInfo::<T>::load_contract(&addr).is_some());
assert_eq!(
Pallet::<T>::evm_balance(&addr),
evm_value,
"{addr:?} balance should hold {evm_value:?}"
);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn evm_instantiate(
t: Linear<0, 1>,
d: Linear<0, 1>,
i: Linear<{ 10 * 1024 }, { 48 * 1024 }>,
) -> Result<(), BenchmarkError> {
use crate::vm::evm::instructions::BENCH_INIT_CODE;
let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_init_code_for_runtime_size(0));
setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
setup.set_balance(caller_funding::<T>());
let (mut ext, _) = setup.ext();
let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
let value = {
let value: BalanceOf<T> = (1_000_000u32 * t).into();
let dust = 100u32 * d;
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust))
};
let init_code = vec![BENCH_INIT_CODE; i as usize];
let _ = interpreter.memory.resize(0, init_code.len());
let salt = U256::from(42u64);
interpreter.memory.set_data(0, 0, init_code.len(), &init_code);
let _ = interpreter.stack.push(salt);
let _ = interpreter.stack.push(U256::from(init_code.len()));
let _ = interpreter.stack.push(U256::zero());
let _ = interpreter.stack.push(value);
let result;
#[block]
{
result = instructions::contract::create::<true, _>(&mut interpreter);
}
assert!(result.is_continue());
let addr = interpreter.stack.top().unwrap().into_address();
assert!(AccountInfo::<T>::load_contract(&addr).is_some());
assert_eq!(Pallet::<T>::code(&addr).len(), revm::primitives::eip170::MAX_CODE_SIZE);
assert_eq!(Pallet::<T>::evm_balance(&addr), value, "balance should hold {value:?}");
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
let input = vec![0u8; n as usize];
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160::from_low_u64_be(2).as_fixed_bytes(),
input.clone(),
);
}
assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
}
#[benchmark(pov_mode = Measured)]
fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
let input = vec![0u8; n as usize];
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160::from_low_u64_be(4).as_fixed_bytes(),
input.clone(),
);
}
assert_eq!(input, result.unwrap().data);
}
#[benchmark(pov_mode = Measured)]
fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
use ripemd::Digest;
let input = vec![0u8; n as usize];
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160::from_low_u64_be(3).as_fixed_bytes(),
input.clone(),
);
}
let mut expected = [0u8; 32];
expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
assert_eq!(expected.to_vec(), result.unwrap().data);
}
#[benchmark(pov_mode = Measured)]
fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
let result;
#[block]
{
result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
}
assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
let input = vec![0u8; n as usize];
let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
input: input.clone().into(),
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
let truth: [u8; 32] = sp_io::hashing::blake2_256(&input);
let truth = FixedBytes::<32>::abi_encode(&truth);
let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed");
let raw_data = result.unwrap().data;
let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed");
assert_eq!(truth, ret_hash);
}
#[benchmark(pov_mode = Measured)]
fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
let input = vec![0u8; n as usize];
let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
input: input.clone().into(),
})
.abi_encode();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
input_bytes,
);
}
let truth: [u8; 16] = sp_io::hashing::blake2_128(&input);
let truth = FixedBytes::<16>::abi_encode(&truth);
let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed");
let raw_data = result.unwrap().data;
let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed");
assert_eq!(truth, ret_hash);
}
#[benchmark(pov_mode = Measured)]
fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 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 = runtime.bench_sr25519_verify(
memory.as_mut_slice(),
0, sig_len, message_len, sig_len + pub_key.len() as u32, );
}
assert_eq!(result.unwrap(), ReturnErrorCode::Success);
}
#[benchmark(pov_mode = Measured)]
fn ecdsa_recover() {
use hex_literal::hex;
let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result =
run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
}
assert_eq!(result.unwrap().data, expected);
}
#[benchmark(pov_mode = Measured)]
fn p256_verify() {
use hex_literal::hex;
let input = hex!("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").to_vec();
let expected = U256::one().to_big_endian();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result = run_builtin_precompile(
&mut ext,
H160::from_low_u64_be(0x100).as_fixed_bytes(),
input,
);
}
assert_eq!(result.unwrap().data, expected);
}
#[benchmark(pov_mode = Measured)]
fn bn128_add() {
use hex_literal::hex;
let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
let expected = hex!(
"0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
);
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result =
run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
}
assert_eq!(result.unwrap().data, expected);
}
#[benchmark(pov_mode = Measured)]
fn bn128_mul() {
use hex_literal::hex;
let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
let expected = hex!(
"0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
);
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result =
run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
}
assert_eq!(result.unwrap().data, expected);
}
#[benchmark(pov_mode = Measured)]
fn bn128_pairing(n: Linear<0, { 20 }>) {
fn generate_random_ecpairs(n: usize) -> Vec<u8> {
use bn::{AffineG1, AffineG2, Fr, G1, G2, Group};
use rand::SeedableRng;
use rand_pcg::Pcg64;
let mut rng = Pcg64::seed_from_u64(1);
let mut buffer = vec![0u8; n * 192];
let mut write = |element: &bn::Fq, offset: &mut usize| {
element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
*offset += 32
};
for i in 0..n {
let mut offset = i * 192;
let scalar = Fr::random(&mut rng);
let g1 = G1::one() * scalar;
let g2 = G2::one() * scalar;
let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
write(&a.x(), &mut offset);
write(&a.y(), &mut offset);
write(&b.x().imaginary(), &mut offset);
write(&b.x().real(), &mut offset);
write(&b.y().imaginary(), &mut offset);
write(&b.y().real(), &mut offset);
}
buffer
}
let input = generate_random_ecpairs(n as usize);
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result =
run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
}
assert_ok!(result);
}
#[benchmark(pov_mode = Measured)]
fn blake2f(n: Linear<0, 1200>) {
use hex_literal::hex;
let input = hex!(
"48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
);
let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
let mut call_setup = CallSetup::<T>::default();
let (mut ext, _) = call_setup.ext();
let result;
#[block]
{
result =
run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
}
assert_ok!(result);
}
#[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 = runtime.bench_ecdsa_to_eth_address(
memory.as_mut_slice(),
20, 0, );
}
assert_ok!(result);
assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
}
#[benchmark(pov_mode = Measured)]
fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
let module = VmBinaryModule::evm_noop(r);
let inputs = vec![];
let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
let mut setup = CallSetup::<T>::new(module);
let (mut ext, _) = setup.ext();
let result;
#[block]
{
result = evm::call(code, &mut ext, inputs);
}
assert!(result.is_ok());
Ok(())
}
#[benchmark(pov_mode = Ignored)]
fn instr(r: Linear<0, 10_000>) {
use rand::{SeedableRng, seq::SliceRandom};
use rand_pcg::Pcg64;
const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
const CACHE_LINE_SIZE: u64 = 64;
const MISALIGNMENT: u64 = 60;
const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
assert!(
u64::from(r) <= NUM_ADDRESSES / 2,
"If we do too many iterations we run into the risk of loading from warm cache lines",
);
let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
let (mut ext, module) = setup.ext();
let mut prepared =
CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
assert!(
u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
"aux data base must be cache aligned"
);
let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
for i in 1..NUM_ADDRESSES {
let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
addresses.push(addr);
}
let mut rng = Pcg64::seed_from_u64(1337);
addresses.shuffle(&mut rng);
let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
for address in addresses {
memory.extend_from_slice(&address);
memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
}
prepared
.setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
.unwrap();
#[block]
{
prepared.call().unwrap();
}
}
#[benchmark(pov_mode = Ignored)]
fn instr_empty_loop(r: Linear<0, 10_000>) {
let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
let (mut ext, module) = setup.ext();
let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
prepared.setup_aux_data(&[], 0, r.into()).unwrap();
#[block]
{
prepared.call().unwrap();
}
}
#[benchmark(pov_mode = Measured)]
fn extcodecopy(n: Linear<1_000, 10_000>) -> Result<(), BenchmarkError> {
let module = VmBinaryModule::sized(n);
let mut setup = CallSetup::<T>::new(module);
let contract = setup.contract();
let (mut ext, _) = setup.ext();
let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
let _ = interpreter.stack.push(U256::from(n));
let _ = interpreter.stack.push(U256::from(0u32));
let _ = interpreter.stack.push(U256::from(0u32));
let _ = interpreter.stack.push(contract.address);
let result;
#[block]
{
result = instructions::host::extcodecopy(&mut interpreter);
}
assert!(result.is_continue());
assert_eq!(
*interpreter.memory.slice(0..n as usize),
PristineCode::<T>::get(contract.info()?.code_hash).unwrap()[0..n as usize],
"Memory should contain the contract's code after extcodecopy"
);
Ok(())
}
#[benchmark]
fn v1_migration_step() {
use crate::migrations::v1;
let addr = H160::from([1u8; 20]);
let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
let mut meter = WeightMeter::new();
assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
#[block]
{
v1::Migration::<T>::step(None, &mut meter).unwrap();
}
assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
}
#[benchmark]
fn v2_migration_step() {
use crate::migrations::v2;
let code_hash = H256::from([0; 32]);
let old_code_info = v2::Migration::<T>::create_old_code_info(
whitelisted_caller(),
1000u32.into(),
1,
100,
0,
);
v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
let mut meter = WeightMeter::new();
#[block]
{
v2::Migration::<T>::step(None, &mut meter).unwrap();
}
v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
}
#[benchmark]
fn v3_migration_step() {
use crate::migrations::v3;
let _ = frame_system::Account::<T>::clear(u32::MAX, None);
let account = account::<T::AccountId>("target", 0, 0);
T::Currency::mint_into(&account, Pallet::<T>::min_balance())
.expect("should mint into account");
let addr = T::AddressMapper::to_address(&account);
crate::OriginalAccount::<T>::remove(addr);
assert!(!T::AddressMapper::is_mapped(&account));
let mut meter = WeightMeter::new();
#[block]
{
v3::Migration::<T>::step(None, &mut meter).unwrap();
}
assert!(T::AddressMapper::is_mapped(&account));
assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v3_migration_step() * 2);
}
#[benchmark]
fn v4_code_upload_step() {
use crate::migrations::v4;
let _ = CodeInfoOf::<T>::clear(u32::MAX, None);
let owner: T::AccountId = whitelisted_caller();
let deposit: BalanceOf<T> = 1_000u32.into();
let pallet_account = Pallet::<T>::account_id();
T::Currency::mint_into(&pallet_account, Pallet::<T>::min_balance()).unwrap();
T::Currency::mint_into(&pallet_account, deposit).unwrap();
T::Currency::hold(&HoldReason::CodeUploadDepositReserve.into(), &pallet_account, deposit)
.unwrap();
CodeInfoOf::<T>::insert(
H256::from([1u8; 32]),
CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
);
CodeInfoOf::<T>::insert(
H256::from([2u8; 32]),
CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
);
let first = match v4::Migration::<T>::step_once(None) {
Some(v4::Cursor::CodeUpload(h)) => h,
other => panic!("expected CodeUpload cursor, got {other:?}"),
};
let cursor = Some(v4::Cursor::CodeUpload(first));
#[block]
{
let _ = v4::Migration::<T>::step_once(cursor);
}
assert_eq!(
NativeDepositOf::<T>::get(&pallet_account, &owner),
deposit + deposit,
"both code uploads credited to owner",
);
}
#[benchmark]
fn v4_contract_step() {
use crate::migrations::v4;
let _ = AccountInfoOf::<T>::clear(u32::MAX, None);
let code_hash = H256::from([0u8; 32]);
let deposit: BalanceOf<T> = 1_000u32.into();
for byte in [0x41u8, 0x42u8] {
let addr = H160::from([byte; 20]);
let contract_account = T::AddressMapper::to_account_id(&addr);
let info =
ContractInfo::<T>::new(&addr, 1u32.into(), code_hash).expect("fresh contract info");
AccountInfoOf::<T>::insert(
addr,
crate::storage::AccountInfo::<T> {
account_type: crate::storage::AccountType::Contract(info),
dust: 0,
},
);
T::Currency::mint_into(&contract_account, Pallet::<T>::min_balance()).unwrap();
T::Currency::mint_into(&contract_account, deposit).unwrap();
T::Currency::hold(
&HoldReason::StorageDepositReserve.into(),
&contract_account,
deposit,
)
.unwrap();
}
let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::Contract(None))) {
Some(v4::Cursor::Contract(Some(addr))) => addr,
other => panic!("expected Contract cursor, got {other:?}"),
};
let cursor = Some(v4::Cursor::Contract(Some(first)));
#[block]
{
let _ = v4::Migration::<T>::step_once(cursor);
}
if T::Deposit::SUPPORTS_PGAS {
for byte in [0x41u8, 0x42u8] {
let addr = H160::from([byte; 20]);
let contract_account = T::AddressMapper::to_account_id(&addr);
assert_eq!(
T::Currency::balance_on_hold(
&HoldReason::StorageDepositReserve.into(),
&contract_account,
),
0u32.into(),
"native storage deposit burned for {addr:?}",
);
}
}
}
#[benchmark]
fn v4_deletion_queue_step() {
use crate::migrations::v4;
let _ = v4::old::DeletionQueue::<T>::clear(u32::MAX, None);
let trie_a: TrieId = vec![0xAAu8; 16].try_into().unwrap();
let trie_b: TrieId = vec![0xBBu8; 24].try_into().unwrap();
v4::old::DeletionQueue::<T>::insert(0u32, trie_a);
v4::old::DeletionQueue::<T>::insert(1u32, trie_b);
let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::DeletionQueue(None))) {
Some(v4::Cursor::DeletionQueue(Some(key))) => key,
other => panic!("expected DeletionQueue cursor, got {other:?}"),
};
let cursor = Some(v4::Cursor::DeletionQueue(Some(first)));
#[block]
{
let _ = v4::Migration::<T>::step_once(cursor);
}
assert!(
DeletionQueue::<T>::get(0u32).is_some() && DeletionQueue::<T>::get(1u32).is_some(),
"both legacy entries rewritten into the new format",
);
}
fn create_test_signer<T: Config>() -> (T::AccountId, SigningKey, H160) {
use hex_literal::hex;
let signer_account_id = hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
let signer_priv_key =
hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133");
let signer_key = SigningKey::from_bytes(&signer_priv_key.into()).expect("valid key");
let signer_address = H160::from_slice(&signer_account_id);
let signer_caller = T::AddressMapper::to_fallback_account_id(&signer_address);
(signer_caller, signer_key, signer_address)
}
fn create_signed_transaction<T: Config>(
signer_key: &SigningKey,
target_address: H160,
value: U256,
input_data: Vec<u8>,
) -> Vec<u8> {
let unsigned_tx: TransactionUnsigned = TransactionLegacyUnsigned {
to: Some(target_address),
value,
chain_id: Some(T::ChainId::get().into()),
input: input_data.into(),
..Default::default()
}
.into();
let hashed_payload = sp_io::hashing::keccak_256(&unsigned_tx.unsigned_payload());
let (signature, recovery_id) =
signer_key.sign_prehash_recoverable(&hashed_payload).expect("signing success");
let mut sig_bytes = [0u8; 65];
sig_bytes[..64].copy_from_slice(&signature.to_bytes());
sig_bytes[64] = recovery_id.to_byte();
let signed_tx = unsigned_tx.with_signature(sig_bytes);
signed_tx.signed_payload()
}
fn setup_finalize_block_benchmark<T>()
-> Result<(Contract<T>, BalanceOf<T>, U256, SigningKey, BlockNumberFor<T>), BenchmarkError>
where
BalanceOf<T>: Into<U256> + TryFrom<U256>,
T: Config,
MomentOf<T>: Into<U256>,
<T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
{
let (signer_caller, signer_key, _signer_address) = create_test_signer::<T>();
whitelist_account!(signer_caller);
let instance =
Contract::<T>::with_caller(signer_caller.clone(), VmBinaryModule::dummy(), vec![])?;
let storage_deposit = default_deposit_limit::<T>();
let value = Pallet::<T>::min_balance();
let evm_value =
Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, 0));
let current_block = BlockNumberFor::<T>::from(1u32);
frame_system::Pallet::<T>::set_block_number(current_block);
Ok((instance, storage_deposit, evm_value, signer_key, current_block))
}
#[benchmark(pov_mode = Measured)]
fn on_finalize_per_transaction(n: Linear<0, 200>) -> Result<(), BenchmarkError> {
let (instance, _storage_deposit, evm_value, signer_key, current_block) =
setup_finalize_block_benchmark::<T>()?;
let fixed_payload_size = 100usize;
if n > 0 {
let _ = Pallet::<T>::on_initialize(current_block);
let input_data = vec![0x42u8; fixed_payload_size];
let receipt_gas_info = ReceiptGasInfo {
gas_used: U256::from(1_000_000),
effective_gas_price: Pallet::<T>::evm_base_fee(),
};
for _ in 0..n {
let signed_transaction = create_signed_transaction::<T>(
&signer_key,
instance.address,
evm_value,
input_data.clone(),
);
let _ = block_storage::bench_with_ethereum_context(|| {
let (encoded_logs, bloom) =
block_storage::get_receipt_details().unwrap_or_default();
let block_builder_ir = EthBlockBuilderIR::<T>::get();
let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
block_builder.process_transaction(
signed_transaction,
true,
receipt_gas_info.clone(),
encoded_logs,
bloom,
);
EthBlockBuilderIR::<T>::put(block_builder.to_ir());
});
}
}
#[block]
{
let _ = Pallet::<T>::on_finalize(current_block);
}
assert_eq!(Pallet::<T>::eth_block().transactions.len(), n as usize);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn on_finalize_per_transaction_data(d: Linear<0, 1000>) -> Result<(), BenchmarkError> {
let (instance, _storage_deposit, evm_value, signer_key, current_block) =
setup_finalize_block_benchmark::<T>()?;
let fixed_tx_count = 10u32;
let _ = Pallet::<T>::on_initialize(current_block);
let input_data = vec![0x42u8; d as usize];
let receipt_gas_info = ReceiptGasInfo {
gas_used: U256::from(1_000_000),
effective_gas_price: Pallet::<T>::evm_base_fee(),
};
for _ in 0..fixed_tx_count {
let signed_transaction = create_signed_transaction::<T>(
&signer_key,
instance.address,
evm_value,
input_data.clone(),
);
let _ = block_storage::bench_with_ethereum_context(|| {
let (encoded_logs, bloom) =
block_storage::get_receipt_details().unwrap_or_default();
let block_builder_ir = EthBlockBuilderIR::<T>::get();
let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
block_builder.process_transaction(
signed_transaction,
true,
receipt_gas_info.clone(),
encoded_logs,
bloom,
);
EthBlockBuilderIR::<T>::put(block_builder.to_ir());
});
}
#[block]
{
let _ = Pallet::<T>::on_finalize(current_block);
}
assert_eq!(Pallet::<T>::eth_block().transactions.len(), fixed_tx_count as usize);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn on_finalize_per_event(e: Linear<0, 100>) -> Result<(), BenchmarkError> {
let (instance, _storage_deposit, evm_value, signer_key, current_block) =
setup_finalize_block_benchmark::<T>()?;
let input_data = vec![0x42u8; 100];
let signed_transaction = create_signed_transaction::<T>(
&signer_key,
instance.address,
evm_value,
input_data.clone(),
);
let receipt_gas_info = ReceiptGasInfo {
gas_used: U256::from(1_000_000),
effective_gas_price: Pallet::<T>::evm_base_fee(),
};
let _ = block_storage::bench_with_ethereum_context(|| {
let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
let block_builder_ir = EthBlockBuilderIR::<T>::get();
let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
block_builder.process_transaction(
signed_transaction,
true,
receipt_gas_info.clone(),
encoded_logs,
bloom,
);
EthBlockBuilderIR::<T>::put(block_builder.to_ir());
});
for _ in 0..e {
block_storage::capture_ethereum_log(&instance.address, &vec![], &vec![]);
}
#[block]
{
let _ = Pallet::<T>::on_initialize(current_block);
let _ = Pallet::<T>::on_finalize(current_block);
}
assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn on_finalize_per_event_data(d: Linear<0, 16384>) -> Result<(), BenchmarkError> {
let (instance, _storage_deposit, evm_value, signer_key, current_block) =
setup_finalize_block_benchmark::<T>()?;
let input_data = vec![0x42u8; 100];
let signed_transaction = create_signed_transaction::<T>(
&signer_key,
instance.address,
evm_value,
input_data.clone(),
);
let receipt_gas_info = ReceiptGasInfo {
gas_used: U256::from(1_000_000),
effective_gas_price: Pallet::<T>::evm_base_fee(),
};
let _ = block_storage::bench_with_ethereum_context(|| {
let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
let block_builder_ir = EthBlockBuilderIR::<T>::get();
let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
block_builder.process_transaction(
signed_transaction,
true,
receipt_gas_info,
encoded_logs,
bloom,
);
EthBlockBuilderIR::<T>::put(block_builder.to_ir());
});
let (event_data, topics) = if d < 32 {
(vec![0x42u8; d as usize], vec![])
} else {
let num_topics = core::cmp::min(limits::NUM_EVENT_TOPICS, d / 32);
let topic_bytes_used = num_topics * 32;
let data_bytes_remaining = d - topic_bytes_used;
let mut topics = Vec::new();
for topic_index in 0..num_topics {
let topic_data = [topic_index as u8; 32];
topics.push(H256::from(topic_data));
}
let event_data = vec![0x42u8; data_bytes_remaining as usize];
(event_data, topics)
};
block_storage::capture_ethereum_log(&instance.address, &event_data, &topics);
#[block]
{
let _ = Pallet::<T>::on_initialize(current_block);
let _ = Pallet::<T>::on_finalize(current_block);
}
assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
Ok(())
}
impl_benchmark_test_suite!(
Contracts,
crate::tests::ExtBuilder::default().build(),
crate::tests::Test,
);
}