1#![cfg(feature = "runtime-benchmarks")]
21use crate::{
22 Pallet as Contracts,
23 access_list::{AccessEntry, AccessList, MAX_ACCESS_LIST_ENTRIES, StorageOp, Warmth},
24 call_builder::{CallSetup, Contract, VmBinaryModule, caller_funding, default_deposit_limit},
25 evm::{
26 TransactionLegacyUnsigned, TransactionSigned, TransactionUnsigned,
27 block_hash::EthereumBlockBuilder, block_storage,
28 },
29 exec::{Ext, Key, Origin as ExecOrigin, PrecompileExt},
30 limits::{self, EVM_STACK_LIMIT},
31 precompiles::{
32 self, BenchmarkStorage, BenchmarkSystem, BuiltinPrecompile,
33 alloy::sol_types::{
34 SolType,
35 sol_data::{Bool, Bytes, FixedBytes, Uint},
36 },
37 run::builtin as run_builtin_precompile,
38 },
39 storage::WriteOutcome,
40 vm::{
41 evm,
42 evm::{ExtBytecode, Halt, Interpreter, instructions, instructions::utility::IntoAddress},
43 pvm,
44 },
45 *,
46};
47use alloc::{vec, vec::Vec};
48use alloy_core::sol_types::{SolInterface, SolValue};
49use codec::{Encode, MaxEncodedLen};
50use core::ops::ControlFlow;
51use frame_benchmarking::v2::*;
52use frame_support::{
53 self, assert_ok,
54 migrations::SteppedMigration,
55 storage::child,
56 traits::{Hooks, fungible::InspectHold},
57 weights::{Weight, WeightMeter},
58};
59use frame_system::RawOrigin;
60use k256::ecdsa::SigningKey;
61use pallet_revive_uapi::{
62 CallFlags, ReturnErrorCode, StorageFlags, pack_hi_lo,
63 precompiles::{storage::IStorage, system::ISystem},
64};
65use rand::{Rng, SeedableRng, seq::SliceRandom};
66use rand_pcg::Pcg64;
67use revm::bytecode::{
68 Bytecode,
69 opcode::{ADDMOD, DIV, MOD, MULMOD, POP, SDIV, SMOD},
70};
71use sp_consensus_aura::AURA_ENGINE_ID;
72use sp_consensus_babe::{
73 BABE_ENGINE_ID,
74 digests::{PreDigest, PrimaryPreDigest},
75};
76use sp_consensus_slots::Slot;
77use sp_runtime::{generic::DigestItem, traits::Zero};
78
79const API_BENCHMARK_RUNS: u32 = 1600;
85
86macro_rules! memory(
87 ($($bytes:expr,)*) => {{
88 vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::<Vec<_>>()
89 }};
90);
91
92macro_rules! build_runtime(
93 ($runtime:ident, $memory:ident: [$($segment:expr,)*]) => {
94 build_runtime!($runtime, _contract, $memory: [$($segment,)*]);
95 };
96 ($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => {
97 build_runtime!($runtime, $contract);
98 let mut $memory = memory!($($bytes,)*);
99 };
100 ($runtime:ident, $contract:ident) => {
101 let mut setup = CallSetup::<T>::default();
102 let $contract = setup.contract();
103 let input = setup.data();
104 let (mut ext, _) = setup.ext();
105 let mut $runtime = $crate::vm::pvm::Runtime::<_, [u8]>::new(&mut ext, input);
106 };
107);
108
109fn whitelisted_pallet_account<T: Config>() -> T::AccountId {
112 let pallet_account = Pallet::<T>::account_id();
113 whitelist_account!(pallet_account);
114 pallet_account
115}
116
117fn delegated_eoa<T: Config>(address: H160, target: H160) -> Result<T::AccountId, BenchmarkError> {
122 let account_id = T::AddressMapper::to_fallback_account_id(&address);
123 AccountInfo::<T>::set_delegation(&address, Some(target), &account_id)
124 .map_err(|_| "set_delegation failed")?;
125 Ok(account_id)
126}
127
128fn evict_caches() {
132 const EVICTION_SIZE: usize = 8 * 1024 * 1024;
133 core::hint::black_box(vec![1u8; EVICTION_SIZE]);
134}
135
136fn setup_stack<E: Ext>(interpreter: &mut Interpreter<E>, values: impl IntoIterator<Item = U256>) {
138 values
139 .into_iter()
140 .for_each(|value| interpreter.stack.push(value).continue_value().unwrap());
141}
142
143fn unsigned_division_operands(rng: &mut impl Rng, denominators: &[U256]) -> [U256; 2] {
145 let denominator = denominators.choose(rng).copied().unwrap();
146 [denominator, knuth_division_operands::NUMERATOR]
147}
148
149fn signed_division_operands(rng: &mut impl Rng, denominators: &[U256]) -> [U256; 2] {
151 let [magnitude, numerator] = unsigned_division_operands(rng, denominators);
152 let divisor =
153 if rng.gen_bool(0.5) { magnitude } else { U256::zero().overflowing_sub(magnitude).0 };
154 [divisor, numerator]
155}
156
157mod knuth_division_operands {
175 use super::{Rng, U256};
176
177 pub fn fixed_signed_operands(rng: &mut impl Rng, magnitude: U256) -> [U256; 2] {
178 let _ = rng.gen_range(0..DENOMINATORS.len() as u32);
180 let divisor =
181 if rng.gen_bool(0.5) { magnitude } else { U256::zero().overflowing_sub(magnitude).0 };
182 [divisor, NUMERATOR]
183 }
184
185 pub const NUMERATOR: U256 = U256([0, 0, 0, 1 << 63]);
188 pub const DIV_FIXED_DIVISOR: U256 = U256([0x05f0_ac84_84d2_a8f7, 0x1, 0, 0]);
190 pub const SDIV_FIXED_DIVISOR: U256 = U256([0x16d5_ec4a_0e7b_8cdb, 0x1, 0, 0]);
192 pub const MOD_FIXED_DIVISOR: U256 = U256([0x0007_f84c_6624_4131, 0x1, 0, 0]);
194 pub const SMOD_FIXED_DIVISOR: U256 = U256([0x5141_1d54_0d50_9ec1, 0x1, 0, 0]);
196 pub const ADDMOD_FIXED_DIVISOR: U256 = MOD_FIXED_DIVISOR;
198
199 pub const DENOMINATORS: [U256; 34] = [
201 U256([0x1ac6_6dea_3270_0980, 0x1, 0, 0]),
203 U256([0x1150_77a4_12e2_ccc0, 0x1, 0, 0]),
204 U256([0x3c9b_ee44_0b3f_dba7, 0x1, 0, 0]),
206 U256([0x62aa_fc46_0845_aad9, 0x1, 0, 0]),
207 U256([0x2806_4b94_70ef_f3d9, 0x1, 0, 0]),
209 U256([0x61d1_c4c6_195a_2d43, 0x1, 0, 0]),
210 SDIV_FIXED_DIVISOR,
212 U256([0x3ef8_7550_1dce_eb99, 0x1, 0, 0]),
213 U256([0xce1e_8ac2_290c_265f, 0x1, 0, 0]),
215 U256([0xd736_05ee_66e1_a76b, 0x1, 0, 0]),
216 U256([0xeb2b_fb00_8fa9_e0f7, 0x1, 0, 0]),
218 SMOD_FIXED_DIVISOR,
219 U256([0x500c_237e_4578_d88b, 0x1, 0, 0]),
221 U256([0x0164_a34e_0b1c_a4fd, 0x1, 0, 0]),
222 MOD_FIXED_DIVISOR,
224 DIV_FIXED_DIVISOR,
225 U256([0x3db2_354e_f8e8_6ca5, 0x96aa_3b25, 0, 0]),
227 U256([0x2848_08be_ea22_56a3, 0x9339_ddd9, 0, 0]),
228 U256([0x2534_a796_ffdf_b85e, 0xc250_8262, 0, 0]),
230 U256([0x4726_e939_da3c_08fe, 0xc198_a4ce, 0, 0]),
231 U256([0x5d5b_2f53_f03d_bec8, 0x909d_ad3b, 0, 0]),
233 U256([0x8e31_4add_736d_ee08, 0x40ce_7f0c, 0, 0]),
234 U256([0x0c62_030f_fabf_0fc7, 0xb51d_e6cb, 0, 0]),
236 U256([0x0332_58df_e522_494c, 0xb4db_915d, 0, 0]),
237 U256([0x0b07_4c7e_fb00_0c1f, 0xa5b9_8f9a, 0, 0]),
239 U256([0x26a5_a3c7_fce7_abd6, 0xa4a5_d36a, 0, 0]),
240 U256([0x6654_d104_1c8e_6943, 0x2, 0, 0]),
242 U256([0x64b6_7f9c_0cf1_dbff, 0x2, 0, 0]),
243 U256([0xeff9_3d22_eeac_89cf, 0x87, 0, 0]),
245 U256([0xadb2_0f65_e0ba_c8df, 0x12, 0, 0]),
246 U256([0xf486_a407_463b_dfde, 0x253, 0, 0]),
248 U256([0xaea8_360c_3043_f5f9, 0x1b4, 0, 0]),
249 U256([0xecbe_4a28_bb14_dbf0, 0x43, 0, 0]),
251 U256([0x64e2_acbc_9934_b1f2, 0x43, 0, 0]),
252 ];
253}
254
255mod one_limb_division_operands {
265 use super::U256;
266
267 pub const DENOMINATORS: [U256; 16] = [
269 U256([0xfff9_9928_4b39_14e9, 0, 0, 0]),
271 U256([0xfff7_2ef6_07d7_0402, 0, 0, 0]),
272 U256([0xfffc_adff_2ef2_7bf4, 0, 0, 0]),
274 U256([0xffc7_1287_6d60_c1f0, 0, 0, 0]),
275 U256([0xfff8_74b0_648f_da8a, 0, 0, 0]),
277 U256([0xfff7_1d06_39ab_3877, 0, 0, 0]),
278 U256([0xf974_5f46_74c9_5adf, 0, 0, 0]),
280 U256([0xf7e1_3204_7bd0_15af, 0, 0, 0]),
281 U256([0xfff7_ac60_75f1_501f, 0, 0, 0]),
283 U256([0xffe8_555d_5f54_a346, 0, 0, 0]),
284 U256([0xfe7a_ca58_4afc_ed70, 0, 0, 0]),
286 U256([0xfd2b_4905_64bd_9797, 0, 0, 0]),
287 U256([0xfcd0_0e3d_ccbd_7da4, 0, 0, 0]),
289 U256([0xee06_4d5d_bfab_b524, 0, 0, 0]),
290 U256([0xf42a_21ef_e080_fc89, 0, 0, 0]),
292 U256([0xf095_aea7_fc96_fbde, 0, 0, 0]),
293 ];
294}
295
296mod mixed_division_operands {
298 use super::{
299 Rng, U256, knuth_division_operands, one_limb_division_operands, signed_division_operands,
300 unsigned_division_operands,
301 };
302
303 pub fn unsigned_operands(rng: &mut impl Rng) -> [U256; 2] {
304 let denominators = denominators(rng);
305 unsigned_division_operands(rng, denominators)
306 }
307
308 pub fn signed_operands(rng: &mut impl Rng) -> [U256; 2] {
309 let denominators = denominators(rng);
310 signed_division_operands(rng, denominators)
311 }
312
313 fn denominators(rng: &mut impl Rng) -> &'static [U256] {
314 if rng.gen_bool(0.5) {
315 &knuth_division_operands::DENOMINATORS
316 } else {
317 &one_limb_division_operands::DENOMINATORS
318 }
319 }
320}
321
322#[benchmarks(
323 where
324 T: Config,
325 <T as Config>::RuntimeCall: From<frame_system::Call<T>>,
326 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
327 OriginFor<T>: From<Origin<T>>,
328)]
329mod benchmarks {
330 use super::*;
331
332 #[benchmark(pov_mode = Measured)]
334 fn deletion_queue_batch() {
335 #[block]
336 {
337 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
338 }
339 }
340
341 #[benchmark(pov_mode = Measured)]
345 fn process_new_account_authorization(n: Linear<0, 255>) -> Result<(), BenchmarkError> {
346 use crate::evm::eip7702;
347 use sp_io::hashing::keccak_256;
348
349 let caller: T::AccountId = whitelisted_caller();
350 T::Currency::set_balance(&caller, caller_funding::<T>());
351 <T as Config>::FeeInfo::deposit_txfee(
352 <T as Config>::Currency::issue(caller_funding::<T>()),
353 );
354 let chain_id = U256::from(T::ChainId::get());
355 let exec_config = ExecConfig::new_eth_tx(U256::from(1), 0, Weight::MAX);
356
357 let mut authorization_list = vec![];
362 for i in 0..n {
363 let target_contract =
364 Contract::<T>::with_index(i + 1, VmBinaryModule::dummy_unique(i), vec![])?;
365 let target = target_contract.address;
366
367 let key_material = keccak_256(&i.to_le_bytes());
368 let key = SigningKey::from_bytes(&key_material.into()).expect("valid key; qed");
369 let signed_auth = eip7702::sign_authorization(&key, chain_id, target, U256::zero());
370 authorization_list.push(signed_auth);
371 }
372
373 let auth_result;
374 #[block]
375 {
376 auth_result =
377 eip7702::process_authorizations::<T>(&authorization_list, &caller, &exec_config);
378 }
379
380 assert_eq!(auth_result.new_accounts, n as u32, "All authorizations should be new");
381 Ok(())
382 }
383
384 #[benchmark(pov_mode = Measured)]
402 fn process_existing_account_authorization(n: Linear<0, 255>) -> Result<(), BenchmarkError> {
403 use crate::evm::eip7702;
404 use sp_io::hashing::keccak_256;
405
406 let caller: T::AccountId = whitelisted_caller();
407 T::Currency::set_balance(&caller, caller_funding::<T>());
408 <T as Config>::FeeInfo::deposit_txfee(
409 <T as Config>::Currency::issue(caller_funding::<T>()),
410 );
411
412 let setup_payer: T::AccountId = account("setup_payer", 0, 0);
416 T::Currency::set_balance(&setup_payer, caller_funding::<T>());
417 <T as Config>::FeeInfo::deposit_txfee(
418 <T as Config>::Currency::issue(caller_funding::<T>()),
419 );
420
421 let chain_id = U256::from(T::ChainId::get());
422 let exec_config = ExecConfig::new_eth_tx(U256::from(1), 0, Weight::MAX);
423
424 let mut authorization_list = vec![];
425 for i in 0..n {
426 let old_target =
428 Contract::<T>::with_index(2 * i + 1, VmBinaryModule::dummy_unique(2 * i), vec![])?;
429 let old_code_hash = <AccountInfoOf<T>>::get(&old_target.address)
430 .and_then(|info| match info.account_type {
431 AccountType::Contract(c) => Some(c.code_hash),
432 _ => None,
433 })
434 .ok_or("old_target should be a Contract")?;
435
436 let key_material = keccak_256(&i.to_le_bytes());
439 let key = SigningKey::from_bytes(&key_material.into()).expect("valid key; qed");
440 let setup_auth =
441 eip7702::sign_authorization(&key, chain_id, old_target.address, U256::zero());
442 let _ = eip7702::process_authorizations::<T>(&[setup_auth], &setup_payer, &exec_config);
443
444 let _ =
448 CodeInfo::<T>::decrement_refcount(old_code_hash).map_err(|_| "decrement failed")?;
449
450 let new_target = Contract::<T>::with_index(
452 2 * i + 2,
453 VmBinaryModule::dummy_unique(2 * i + 1),
454 vec![],
455 )?;
456
457 let signed_auth =
459 eip7702::sign_authorization(&key, chain_id, new_target.address, U256::one());
460 authorization_list.push(signed_auth);
461 }
462
463 let auth_result;
464 #[block]
465 {
466 auth_result =
467 eip7702::process_authorizations::<T>(&authorization_list, &caller, &exec_config);
468 }
469
470 assert_eq!(auth_result.new_accounts, 0u32);
471 assert_eq!(auth_result.existing_accounts, n as u32);
472 Ok(())
473 }
474
475 #[benchmark(pov_mode = Measured)]
479 fn process_invalid_authorization(n: Linear<0, 255>) -> Result<(), BenchmarkError> {
480 use crate::evm::eip7702;
481 use sp_io::hashing::keccak_256;
482
483 let chain_id = U256::from(T::ChainId::get());
484 let target_contract = Contract::<T>::with_index(0, VmBinaryModule::dummy(), vec![])?;
485 let target = target_contract.address;
486 let caller: T::AccountId = whitelisted_caller();
487 T::Currency::set_balance(&caller, caller_funding::<T>());
488 <T as Config>::FeeInfo::deposit_txfee(
489 <T as Config>::Currency::issue(caller_funding::<T>()),
490 );
491 let exec_config = ExecConfig::new_eth_tx(U256::from(1), 0, Weight::MAX);
492
493 let mut authorization_list = vec![];
494 for i in 0..n {
495 let key_material = keccak_256(&(i as u32).to_le_bytes());
496 let key = SigningKey::from_bytes(&key_material.into()).expect("valid key; qed");
497 let signed_auth = eip7702::sign_authorization(&key, chain_id, target, U256::one());
499 authorization_list.push(signed_auth);
500 }
501
502 let auth_result;
503 #[block]
504 {
505 auth_result =
506 eip7702::process_authorizations::<T>(&authorization_list, &caller, &exec_config);
507 }
508
509 assert_eq!(auth_result.new_accounts, 0u32);
510 assert_eq!(auth_result.existing_accounts, 0u32);
511 Ok(())
512 }
513
514 #[benchmark(pov_mode = Measured)]
517 fn deletion_queue_per_entry() -> Result<(), BenchmarkError> {
518 let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
519 ContractInfo::<T>::queue_for_deletion(
520 instance.info()?.trie_id,
521 instance.account_id.clone(),
522 );
523
524 #[block]
525 {
526 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
527 }
528
529 assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
530 Ok(())
531 }
532
533 #[benchmark(skip_meta, pov_mode = Measured)]
534 fn deletion_queue_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
535 let instance =
536 Contract::<T>::with_storage(VmBinaryModule::dummy(), k, limits::STORAGE_BYTES)?;
537 ContractInfo::<T>::queue_for_deletion(
538 instance.info()?.trie_id,
539 instance.account_id.clone(),
540 );
541
542 #[block]
543 {
544 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
545 }
546
547 assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
548 Ok(())
549 }
550
551 #[benchmark(skip_meta, pov_mode = Measured)]
556 fn deletion_queue_per_native_deposit_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
557 use frame_benchmarking::v2::account;
558
559 let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
561 for i in 0..k {
562 let payer: T::AccountId = account("payer", i, 0);
563 NativeDepositOf::<T>::insert(&instance.account_id, &payer, BalanceOf::<T>::default());
564 }
565 ContractInfo::<T>::queue_for_deletion(
566 instance.info()?.trie_id,
567 instance.account_id.clone(),
568 );
569
570 #[block]
571 {
572 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
573 }
574
575 assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
576 Ok(())
577 }
578
579 #[benchmark(pov_mode = Measured)]
591 fn call_with_pvm_code_per_byte(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
592 let instance =
593 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::sized(c), vec![])?;
594 let value = Pallet::<T>::min_balance();
595 let storage_deposit = default_deposit_limit::<T>();
596
597 #[extrinsic_call]
598 call(
599 RawOrigin::Signed(instance.caller.clone()),
600 instance.address,
601 value,
602 Weight::MAX,
603 storage_deposit,
604 vec![],
605 );
606
607 Ok(())
608 }
609
610 #[benchmark(pov_mode = Measured)]
614 fn call_with_evm_code_per_byte(c: Linear<1, { 10 * 1024 }>) -> Result<(), BenchmarkError> {
615 let instance = Contract::<T>::with_caller(
616 whitelisted_caller(),
617 VmBinaryModule::evm_init_code_for_runtime_size(c),
618 vec![],
619 )?;
620 let value = Pallet::<T>::min_balance();
621 let storage_deposit = default_deposit_limit::<T>();
622
623 let code_len = PristineCode::<T>::get(instance.info()?.code_hash)
624 .expect("code should be stored")
625 .len();
626 assert_eq!(
627 code_len, c as usize,
628 "runtime bytecode should be exactly {c} bytes, got {code_len}"
629 );
630
631 #[extrinsic_call]
632 call(
633 RawOrigin::Signed(instance.caller.clone()),
634 instance.address,
635 value,
636 Weight::MAX,
637 storage_deposit,
638 vec![],
639 );
640
641 Ok(())
642 }
643
644 #[benchmark(pov_mode = Measured)]
655 fn basic_block_compilation(b: Linear<0, 1>) -> Result<(), BenchmarkError> {
656 let instance = Contract::<T>::with_caller(
657 whitelisted_caller(),
658 VmBinaryModule::with_num_instructions(limits::code::BASIC_BLOCK_SIZE),
659 vec![],
660 )?;
661 let value = Pallet::<T>::min_balance();
662 let storage_deposit = default_deposit_limit::<T>();
663
664 #[block]
665 {
666 Pallet::<T>::call(
667 RawOrigin::Signed(instance.caller.clone()).into(),
668 instance.address,
669 value,
670 Weight::MAX,
671 storage_deposit,
672 vec![],
673 )?;
674 }
675
676 Ok(())
677 }
678
679 #[benchmark(pov_mode = Measured)]
682 fn instantiate_with_code(
683 c: Linear<0, { 100 * 1024 }>,
684 i: Linear<0, { limits::CALLDATA_BYTES }>,
685 ) {
686 let pallet_account = whitelisted_pallet_account::<T>();
687 let input = vec![42u8; i as usize];
688 let salt = [42u8; 32];
689 let value = Pallet::<T>::min_balance();
690 let caller = whitelisted_caller();
691 T::Currency::set_balance(&caller, caller_funding::<T>());
692 let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
693 let origin = RawOrigin::Signed(caller.clone());
694 if !T::AddressMapper::is_mapped(&caller) {
695 T::AddressMapper::map(&caller).unwrap();
696 }
697 let deployer = T::AddressMapper::to_address(&caller);
698 let addr = crate::address::create2(&deployer, &code, &input, &salt);
699 let account_id = T::AddressMapper::to_fallback_account_id(&addr);
700 let storage_deposit = default_deposit_limit::<T>();
701 #[extrinsic_call]
702 _(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt));
703
704 let deposit =
705 T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
706 let code_deposit = T::Currency::balance_on_hold(
708 &HoldReason::CodeUploadDepositReserve.into(),
709 &pallet_account,
710 );
711 let mapping_deposit =
712 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller);
713 assert_eq!(
714 T::Currency::balance(&caller),
715 caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
716 );
717 assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
719 }
720
721 #[benchmark(pov_mode = Measured)]
725 fn eth_instantiate_with_code(
726 c: Linear<0, { 100 * 1024 }>,
727 i: Linear<0, { limits::CALLDATA_BYTES }>,
728 d: Linear<0, 1>,
729 ) -> Result<(), BenchmarkError> {
730 let input = vec![42u8; i as usize];
731
732 let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
736 let value = Pallet::<T>::min_balance();
737 let dust = 42u32 * d;
738 let evm_value =
739 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
740 let caller = whitelisted_caller();
741 T::Currency::set_balance(&caller, caller_funding::<T>());
742 let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
743 let origin = Origin::EthTransaction(caller.clone());
744 if !T::AddressMapper::is_mapped(&caller) {
745 T::AddressMapper::map(&caller).unwrap();
746 }
747 let deployer = T::AddressMapper::to_address(&caller);
748 let nonce = System::<T>::account_nonce(&caller).try_into().unwrap_or_default();
749 let addr = crate::address::create1(&deployer, nonce);
750
751 assert!(AccountInfoOf::<T>::get(&deployer).is_none());
752
753 <T as Config>::FeeInfo::deposit_txfee(
754 <T as Config>::Currency::issue(caller_funding::<T>()),
755 );
756
757 #[extrinsic_call]
758 _(
759 origin,
760 evm_value,
761 Weight::MAX,
762 U256::MAX,
763 code,
764 input,
765 TransactionSigned::default().signed_payload(),
766 effective_gas_price,
767 0,
768 );
769
770 assert_eq!(Pallet::<T>::evm_balance(&addr), evm_value);
772 Ok(())
773 }
774
775 #[benchmark(pov_mode = Measured)]
776 fn deposit_eth_extrinsic_revert_event() {
777 #[block]
778 {
779 Pallet::<T>::deposit_event(Event::<T>::EthExtrinsicRevert {
780 dispatch_error: crate::Error::<T>::BenchmarkingError.into(),
781 });
782 }
783 }
784
785 #[benchmark(pov_mode = Measured)]
788 fn instantiate(i: Linear<0, { limits::CALLDATA_BYTES }>) -> Result<(), BenchmarkError> {
789 let pallet_account = whitelisted_pallet_account::<T>();
790 let input = vec![42u8; i as usize];
791 let salt = [42u8; 32];
792 let value = Pallet::<T>::min_balance();
793 let caller = whitelisted_caller();
794 T::Currency::set_balance(&caller, caller_funding::<T>());
795 let origin = RawOrigin::Signed(caller.clone());
796 if !T::AddressMapper::is_mapped(&caller) {
797 T::AddressMapper::map(&caller).unwrap();
798 }
799 let VmBinaryModule { code, .. } = VmBinaryModule::dummy();
800 let storage_deposit = default_deposit_limit::<T>();
801 let deployer = T::AddressMapper::to_address(&caller);
802 let addr = crate::address::create2(&deployer, &code, &input, &salt);
803 let hash = Contracts::<T>::bare_upload_code(origin.clone().into(), code, storage_deposit)?
804 .code_hash;
805 let account_id = T::AddressMapper::to_fallback_account_id(&addr);
806
807 #[extrinsic_call]
808 _(origin, value, Weight::MAX, storage_deposit, hash, input, Some(salt));
809
810 let deposit =
811 T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
812 let code_deposit = T::Currency::balance_on_hold(
813 &HoldReason::CodeUploadDepositReserve.into(),
814 &pallet_account,
815 );
816 let mapping_deposit =
817 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &account_id);
818 assert_eq!(
820 T::Currency::total_balance(&caller),
821 caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
822 );
823 assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
825
826 Ok(())
827 }
828
829 #[benchmark(pov_mode = Measured)]
837 fn call() -> Result<(), BenchmarkError> {
838 let pallet_account = whitelisted_pallet_account::<T>();
839 let data = vec![42u8; 1024];
840 let instance =
841 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
842 let value = Pallet::<T>::min_balance();
843 let origin = RawOrigin::Signed(instance.caller.clone());
844 let before = T::Currency::balance(&instance.account_id);
845 let storage_deposit = default_deposit_limit::<T>();
846 #[extrinsic_call]
847 _(origin, instance.address, value, Weight::MAX, storage_deposit, data);
848 let deposit = T::Currency::balance_on_hold(
849 &HoldReason::StorageDepositReserve.into(),
850 &instance.account_id,
851 );
852 let code_deposit = T::Currency::balance_on_hold(
853 &HoldReason::CodeUploadDepositReserve.into(),
854 &pallet_account,
855 );
856 let mapping_deposit =
857 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller);
858 assert_eq!(
860 T::Currency::balance(&instance.caller),
861 caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
862 );
863 assert_eq!(T::Currency::balance(&instance.account_id), before + value);
865 instance.info()?;
867
868 Ok(())
869 }
870
871 #[benchmark(pov_mode = Measured)]
873 fn eth_call(d: Linear<0, 1>) -> Result<(), BenchmarkError> {
874 let data = vec![42u8; 1024];
875 let instance =
876 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
877
878 let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
882 let value = Pallet::<T>::min_balance();
883 let dust = 42u32 * d;
884 let evm_value =
885 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
886
887 <T as Config>::FeeInfo::deposit_txfee(
889 <T as Config>::Currency::issue(caller_funding::<T>()),
890 );
891
892 let origin = Origin::EthTransaction(instance.caller.clone());
893 let before = Pallet::<T>::evm_balance(&instance.address);
894
895 #[extrinsic_call]
896 _(
897 origin,
898 instance.address,
899 evm_value,
900 Weight::MAX,
901 U256::MAX,
902 data,
903 TransactionSigned::default().signed_payload(),
904 effective_gas_price,
905 0,
906 vec![],
907 );
908
909 assert_eq!(Pallet::<T>::evm_balance(&instance.address), before + evm_value);
911 instance.info()?;
913
914 Ok(())
915 }
916
917 #[benchmark(pov_mode = Measured)]
919 fn eth_substrate_call(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
920 let caller = whitelisted_caller();
921 T::Currency::set_balance(&caller, caller_funding::<T>());
922 let origin = Origin::EthTransaction(caller);
923 let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
924 #[extrinsic_call]
925 _(origin, Box::new(dispatchable), vec![42u8; c as usize]);
926 Ok(())
927 }
928
929 #[benchmark(pov_mode = Measured)]
933 fn upload_code(c: Linear<0, { 100 * 1024 }>) {
934 let caller = whitelisted_caller();
935 let pallet_account = whitelisted_pallet_account::<T>();
936 T::Currency::set_balance(&caller, caller_funding::<T>());
937 let VmBinaryModule { code, hash, .. } = VmBinaryModule::sized(c);
938 let origin = RawOrigin::Signed(caller.clone());
939 let storage_deposit = default_deposit_limit::<T>();
940 #[extrinsic_call]
941 _(origin, code, storage_deposit);
942 assert!(T::Currency::total_balance_on_hold(&pallet_account) > 0u32.into());
944 assert!(<Contract<T>>::code_exists(&hash));
945 }
946
947 #[benchmark(pov_mode = Measured)]
951 fn remove_code() -> Result<(), BenchmarkError> {
952 let caller = whitelisted_caller();
953 let pallet_account = whitelisted_pallet_account::<T>();
954 T::Currency::set_balance(&caller, caller_funding::<T>());
955 let VmBinaryModule { code, hash, .. } = VmBinaryModule::dummy();
956 let origin = RawOrigin::Signed(caller.clone());
957 let storage_deposit = default_deposit_limit::<T>();
958 let uploaded =
959 <Contracts<T>>::bare_upload_code(origin.clone().into(), code, storage_deposit)?;
960 assert_eq!(uploaded.code_hash, hash);
961 assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&pallet_account));
962 assert!(<Contract<T>>::code_exists(&hash));
963 #[extrinsic_call]
964 _(origin, hash);
965 assert_eq!(T::Currency::total_balance_on_hold(&pallet_account), 0u32.into());
967 assert!(<Contract<T>>::code_removed(&hash));
968 Ok(())
969 }
970
971 #[benchmark(pov_mode = Measured)]
972 fn set_code() -> Result<(), BenchmarkError> {
973 let instance =
974 <Contract<T>>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
975 let VmBinaryModule { code, .. } = VmBinaryModule::dummy_unique(128);
977 let origin = RawOrigin::Signed(instance.caller.clone());
978 let storage_deposit = default_deposit_limit::<T>();
979 let hash =
980 <Contracts<T>>::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash;
981 assert_ne!(instance.info()?.code_hash, hash);
982 #[extrinsic_call]
983 _(RawOrigin::Root, instance.address, hash);
984 assert_eq!(instance.info()?.code_hash, hash);
985 Ok(())
986 }
987
988 #[benchmark(pov_mode = Measured)]
989 fn map_account() {
990 let caller = whitelisted_caller();
991 T::Currency::set_balance(&caller, caller_funding::<T>());
992 let origin = RawOrigin::Signed(caller.clone());
993 if T::AddressMapper::is_mapped(&caller) {
994 T::AddressMapper::unmap(&caller).unwrap();
995 }
996 assert!(!T::AddressMapper::is_mapped(&caller));
997 #[extrinsic_call]
998 _(origin);
999 assert!(T::AddressMapper::is_mapped(&caller));
1000 }
1001
1002 #[benchmark(pov_mode = Measured)]
1003 fn unmap_account() {
1004 let caller = whitelisted_caller();
1005 T::Currency::set_balance(&caller, caller_funding::<T>());
1006 let origin = RawOrigin::Signed(caller.clone());
1007 if !T::AddressMapper::is_mapped(&caller) {
1008 T::AddressMapper::map(&caller).unwrap();
1009 }
1010 assert!(T::AddressMapper::is_mapped(&caller));
1011 #[extrinsic_call]
1012 _(origin);
1013 assert!(!T::AddressMapper::is_mapped(&caller));
1014 }
1015
1016 #[benchmark(pov_mode = Measured)]
1021 fn batch_map_accounts(a: Linear<0, 1024>) -> Result<(), BenchmarkError> {
1022 use frame_benchmarking::v2::account;
1023
1024 let caller: T::AccountId = whitelisted_caller();
1025 T::Currency::set_balance(&caller, caller_funding::<T>());
1026
1027 let deposit = T::DepositPerByte::get()
1029 .saturating_mul(52u32.into())
1030 .saturating_add(T::DepositPerItem::get());
1031
1032 let mut accounts = Vec::with_capacity(a as usize);
1033 for i in 0..a {
1034 let account_id: T::AccountId = account("to_map", i, 0);
1035 T::Currency::set_balance(&account_id, caller_funding::<T>());
1036 T::Currency::hold(&HoldReason::AddressMapping.into(), &account_id, deposit)?;
1037 accounts.push(account_id);
1038 }
1039
1040 #[extrinsic_call]
1041 _(RawOrigin::Signed(caller), accounts.clone());
1042
1043 for account_id in &accounts {
1044 assert!(T::AddressMapper::is_mapped(account_id));
1045 assert_eq!(
1046 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), account_id),
1047 0u32.into(),
1048 );
1049 }
1050
1051 Ok(())
1052 }
1053
1054 #[benchmark(pov_mode = Measured)]
1055 fn dispatch_as_fallback_account() {
1056 let caller = whitelisted_caller();
1057 T::Currency::set_balance(&caller, caller_funding::<T>());
1058 let origin = RawOrigin::Signed(caller.clone());
1059 let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
1060 #[extrinsic_call]
1061 _(origin, Box::new(dispatchable));
1062 }
1063
1064 #[benchmark(pov_mode = Measured)]
1065 fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) {
1066 let mut setup = CallSetup::<T>::new(VmBinaryModule::noop());
1067 let (mut ext, module) = setup.ext();
1068 let prepared = CallSetup::<T>::prepare_call(&mut ext, module, r.encode(), 0);
1069 #[block]
1070 {
1071 prepared.call().unwrap();
1072 }
1073 }
1074
1075 #[benchmark(pov_mode = Measured)]
1076 fn seal_caller() {
1077 let len = H160::len_bytes();
1078 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
1079
1080 let result;
1081 #[block]
1082 {
1083 result = runtime.bench_caller(memory.as_mut_slice(), 0);
1084 }
1085
1086 assert_ok!(result);
1087 assert_eq!(
1088 <H160 as Decode>::decode(&mut &memory[..]).unwrap(),
1089 T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap())
1090 );
1091 }
1092
1093 #[benchmark(pov_mode = Measured)]
1094 fn seal_origin() {
1095 let len = H160::len_bytes();
1096 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
1097
1098 let result;
1099 #[block]
1100 {
1101 result = runtime.bench_origin(memory.as_mut_slice(), 0);
1102 }
1103
1104 assert_ok!(result);
1105 assert_eq!(
1106 <H160 as Decode>::decode(&mut &memory[..]).unwrap(),
1107 T::AddressMapper::to_address(&runtime.ext().origin().account_id().unwrap())
1108 );
1109 }
1110
1111 #[benchmark(pov_mode = Measured)]
1112 fn to_account_id() {
1113 let account_id = account("precompile_to_account_id", 0, 0);
1116 let address = {
1117 T::Currency::set_balance(&account_id, caller_funding::<T>());
1118 if !T::AddressMapper::is_mapped(&account_id) {
1119 T::AddressMapper::map(&account_id).unwrap();
1120 }
1121 T::AddressMapper::to_address(&account_id)
1122 };
1123
1124 let input_bytes = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
1125 input: address.0.into(),
1126 })
1127 .abi_encode();
1128
1129 let mut call_setup = CallSetup::<T>::default();
1130 let (mut ext, _) = call_setup.ext();
1131
1132 let result;
1133 #[block]
1134 {
1135 result = run_builtin_precompile(
1136 &mut ext,
1137 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1138 input_bytes,
1139 );
1140 }
1141 let raw_data = result.unwrap().data;
1142 let data = Bytes::abi_decode(&raw_data).expect("decoding failed");
1143 assert_ne!(
1144 data.0.as_ref()[20..32],
1145 [0xEE; 12],
1146 "fallback suffix found where none should be"
1147 );
1148 assert_eq!(T::AccountId::decode(&mut data.as_ref()), Ok(account_id),);
1149 }
1150
1151 #[benchmark(pov_mode = Measured)]
1152 fn seal_code_hash() {
1153 let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
1154 let len = <sp_core::H256 as MaxEncodedLen>::max_encoded_len() as u32;
1155 build_runtime!(runtime, memory: [vec![0u8; len as _], contract.account_id.encode(), ]);
1156
1157 let result;
1158 #[block]
1159 {
1160 result = runtime.bench_code_hash(memory.as_mut_slice(), len, 0);
1161 }
1162
1163 assert_ok!(result);
1164 assert_eq!(
1165 <sp_core::H256 as Decode>::decode(&mut &memory[..]).unwrap(),
1166 contract.info().unwrap().code_hash
1167 );
1168 }
1169
1170 #[benchmark(pov_mode = Measured)]
1171 fn own_code_hash() {
1172 let input_bytes =
1173 ISystem::ISystemCalls::ownCodeHash(ISystem::ownCodeHashCall {}).abi_encode();
1174 let mut call_setup = CallSetup::<T>::default();
1175 let contract_acc = call_setup.contract().account_id.clone();
1176 let caller = call_setup.contract().address;
1177 call_setup.set_origin(ExecOrigin::from_account_id(contract_acc));
1178 let (mut ext, _) = call_setup.ext();
1179
1180 let result;
1181 #[block]
1182 {
1183 result = run_builtin_precompile(
1184 &mut ext,
1185 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1186 input_bytes,
1187 );
1188 }
1189 assert!(result.is_ok());
1190 let caller_code_hash = ext.code_hash(&caller);
1191 assert_eq!(caller_code_hash.0.to_vec(), result.unwrap().data);
1192 }
1193
1194 #[benchmark(pov_mode = Measured)]
1195 fn seal_code_size() {
1196 let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
1197 build_runtime!(runtime, memory: [contract.address.encode(),]);
1198
1199 let result;
1200 #[block]
1201 {
1202 result = runtime.bench_code_size(memory.as_mut_slice(), 0);
1203 }
1204
1205 assert_eq!(result.unwrap(), VmBinaryModule::dummy().code.len() as u64);
1206 }
1207
1208 #[benchmark(pov_mode = Measured)]
1209 fn caller_is_origin() {
1210 let input_bytes =
1211 ISystem::ISystemCalls::callerIsOrigin(ISystem::callerIsOriginCall {}).abi_encode();
1212
1213 let mut call_setup = CallSetup::<T>::default();
1214 let (mut ext, _) = call_setup.ext();
1215
1216 let result;
1217 #[block]
1218 {
1219 result = run_builtin_precompile(
1220 &mut ext,
1221 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1222 input_bytes,
1223 );
1224 }
1225 let raw_data = result.unwrap().data;
1226 let is_origin = Bool::abi_decode(&raw_data[..]).expect("decoding failed");
1227 assert!(is_origin);
1228 }
1229
1230 #[benchmark(pov_mode = Measured)]
1231 fn caller_is_root() {
1232 let input_bytes =
1233 ISystem::ISystemCalls::callerIsRoot(ISystem::callerIsRootCall {}).abi_encode();
1234
1235 let mut setup = CallSetup::<T>::default();
1236 setup.set_origin(ExecOrigin::Root);
1237 let (mut ext, _) = setup.ext();
1238
1239 let result;
1240 #[block]
1241 {
1242 result = run_builtin_precompile(
1243 &mut ext,
1244 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1245 input_bytes,
1246 );
1247 }
1248 let raw_data = result.unwrap().data;
1249 let is_root = Bool::abi_decode(&raw_data).expect("decoding failed");
1250 assert!(is_root);
1251 }
1252
1253 #[benchmark(pov_mode = Measured)]
1254 fn origin_is_root() {
1255 let input_bytes =
1256 ISystem::ISystemCalls::originIsRoot(ISystem::originIsRootCall {}).abi_encode();
1257
1258 let mut setup = CallSetup::<T>::default();
1259 setup.set_origin(ExecOrigin::Root);
1260 let (mut ext, _) = setup.ext();
1261
1262 let result;
1263 #[block]
1264 {
1265 result = run_builtin_precompile(
1266 &mut ext,
1267 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1268 input_bytes,
1269 );
1270 }
1271 let raw_data = result.unwrap().data;
1272 let is_root = Bool::abi_decode(&raw_data).expect("decoding failed");
1273 assert!(is_root);
1274 }
1275
1276 #[benchmark(pov_mode = Measured)]
1277 fn seal_address() {
1278 let len = H160::len_bytes();
1279 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
1280
1281 let result;
1282 #[block]
1283 {
1284 result = runtime.bench_address(memory.as_mut_slice(), 0);
1285 }
1286 assert_ok!(result);
1287 assert_eq!(<H160 as Decode>::decode(&mut &memory[..]).unwrap(), runtime.ext().address());
1288 }
1289
1290 #[benchmark(pov_mode = Measured)]
1291 fn weight_left() {
1292 let input_bytes =
1293 ISystem::ISystemCalls::weightLeft(ISystem::weightLeftCall {}).abi_encode();
1294
1295 let mut call_setup = CallSetup::<T>::default();
1296 let (mut ext, _) = call_setup.ext();
1297
1298 let weight_left_before = ext.frame_meter().weight_left().unwrap();
1299 let result;
1300 #[block]
1301 {
1302 result = run_builtin_precompile(
1303 &mut ext,
1304 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1305 input_bytes,
1306 );
1307 }
1308 let weight_left_after = ext.frame_meter().weight_left().unwrap();
1309 assert_ne!(weight_left_after.ref_time(), 0);
1310 assert!(weight_left_before.ref_time() > weight_left_after.ref_time());
1311
1312 let raw_data = result.unwrap().data;
1313 type MyTy = (Uint<64>, Uint<64>);
1314 let foo = MyTy::abi_decode(&raw_data[..]).unwrap();
1315 assert_eq!(weight_left_after.ref_time(), foo.0);
1316 }
1317
1318 #[benchmark(pov_mode = Measured)]
1319 fn seal_ref_time_left() {
1320 build_runtime!(runtime, memory: [vec![], ]);
1321
1322 let result;
1323 #[block]
1324 {
1325 result = runtime.bench_ref_time_left(memory.as_mut_slice());
1326 }
1327 assert_eq!(result.unwrap(), runtime.ext().gas_left());
1328 }
1329
1330 #[benchmark(pov_mode = Measured)]
1331 fn seal_balance() {
1332 build_runtime!(runtime, contract, memory: [[0u8;32], ]);
1333 contract.set_balance(BalanceWithDust::new_unchecked::<T>(
1334 Pallet::<T>::min_balance() * 2u32.into(),
1335 42u32,
1336 ));
1337
1338 let result;
1339 #[block]
1340 {
1341 result = runtime.bench_balance(memory.as_mut_slice(), 0);
1342 }
1343 assert_ok!(result);
1344 assert_eq!(
1345 U256::from_little_endian(&memory[..]),
1346 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
1347 Pallet::<T>::min_balance(),
1348 42
1349 ))
1350 );
1351 }
1352
1353 #[benchmark(pov_mode = Measured)]
1354 fn seal_balance_of() {
1355 let len = <sp_core::U256 as MaxEncodedLen>::max_encoded_len();
1356 let account = account::<T::AccountId>("target", 0, 0);
1357 <T as Config>::AddressMapper::map_no_deposit_unchecked(&account).unwrap();
1358
1359 let address = T::AddressMapper::to_address(&account);
1360 let balance = Pallet::<T>::min_balance() * 2u32.into();
1361 T::Currency::set_balance(&account, balance);
1362 AccountInfoOf::<T>::insert(&address, AccountInfo { dust: 42, ..Default::default() });
1363
1364 build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]);
1365
1366 let result;
1367 #[block]
1368 {
1369 result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0);
1370 }
1371
1372 assert_ok!(result);
1373 assert_eq!(
1374 U256::from_little_endian(&memory[..len]),
1375 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
1376 Pallet::<T>::min_balance(),
1377 42
1378 ))
1379 );
1380 }
1381
1382 #[benchmark(pov_mode = Measured)]
1383 fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
1384 let len = n as usize;
1385 let immutable_data = vec![1u8; len];
1386
1387 build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]);
1388
1389 <ImmutableDataOf<T>>::insert::<_, BoundedVec<_, _>>(
1390 contract.address,
1391 immutable_data.clone().try_into().unwrap(),
1392 );
1393
1394 let result;
1395 #[block]
1396 {
1397 result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32);
1398 }
1399
1400 assert_ok!(result);
1401 assert_eq!(&memory[0..4], (len as u32).encode());
1402 assert_eq!(&memory[4..len + 4], &immutable_data);
1403 }
1404
1405 #[benchmark(pov_mode = Measured)]
1406 fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
1407 let len = n as usize;
1408 let mut memory = vec![1u8; len];
1409 let mut setup = CallSetup::<T>::default();
1410 let input = setup.data();
1411 let (mut ext, _) = setup.ext();
1412 ext.override_export(crate::exec::ExportedFunction::Constructor);
1413
1414 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
1415
1416 let result;
1417 #[block]
1418 {
1419 result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n);
1420 }
1421
1422 assert_ok!(result);
1423 assert_eq!(&memory[..], &<ImmutableDataOf<T>>::get(setup.contract().address).unwrap()[..]);
1424 }
1425
1426 #[benchmark(pov_mode = Measured)]
1427 fn seal_value_transferred() {
1428 build_runtime!(runtime, memory: [[0u8;32], ]);
1429 let result;
1430 #[block]
1431 {
1432 result = runtime.bench_value_transferred(memory.as_mut_slice(), 0);
1433 }
1434 assert_ok!(result);
1435 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().value_transferred());
1436 }
1437
1438 #[benchmark(pov_mode = Measured)]
1439 fn minimum_balance() {
1440 let input_bytes =
1441 ISystem::ISystemCalls::minimumBalance(ISystem::minimumBalanceCall {}).abi_encode();
1442
1443 let mut call_setup = CallSetup::<T>::default();
1444 let (mut ext, _) = call_setup.ext();
1445
1446 let result;
1447 #[block]
1448 {
1449 result = run_builtin_precompile(
1450 &mut ext,
1451 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1452 input_bytes,
1453 );
1454 }
1455 let min: U256 = crate::Pallet::<T>::convert_native_to_evm(T::Currency::minimum_balance());
1456 let min =
1457 crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&min.to_big_endian())
1458 .unwrap();
1459
1460 let raw_data = result.unwrap().data;
1461 let returned_min =
1462 crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&raw_data)
1463 .expect("decoding failed");
1464 assert_eq!(returned_min, min);
1465 }
1466
1467 #[benchmark(pov_mode = Measured)]
1468 fn seal_return_data_size() {
1469 let mut setup = CallSetup::<T>::default();
1470 let (mut ext, _) = setup.ext();
1471 let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
1472 let mut memory = memory!(vec![],);
1473 *runtime.ext().last_frame_output_mut() =
1474 ExecReturnValue { data: vec![42; 256], ..Default::default() };
1475 let result;
1476 #[block]
1477 {
1478 result = runtime.bench_return_data_size(memory.as_mut_slice());
1479 }
1480 assert_eq!(result.unwrap(), 256);
1481 }
1482
1483 #[benchmark(pov_mode = Measured)]
1484 fn seal_call_data_size() {
1485 let mut setup = CallSetup::<T>::default();
1486 let (mut ext, _) = setup.ext();
1487 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 128 as usize]);
1488 let mut memory = memory!(vec![0u8; 4],);
1489 let result;
1490 #[block]
1491 {
1492 result = runtime.bench_call_data_size(memory.as_mut_slice());
1493 }
1494 assert_eq!(result.unwrap(), 128);
1495 }
1496
1497 #[benchmark(pov_mode = Measured)]
1498 fn seal_gas_limit() {
1499 build_runtime!(runtime, memory: []);
1500 let result;
1501 #[block]
1502 {
1503 result = runtime.bench_gas_limit(&mut memory);
1504 }
1505 assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_block_gas_limit());
1506 }
1507
1508 #[benchmark(pov_mode = Measured)]
1509 fn seal_gas_price() {
1510 build_runtime!(runtime, memory: []);
1511 let result;
1512 #[block]
1513 {
1514 result = runtime.bench_gas_price(memory.as_mut_slice());
1515 }
1516 assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_base_fee());
1517 }
1518
1519 #[benchmark(pov_mode = Measured)]
1520 fn seal_base_fee() {
1521 build_runtime!(runtime, memory: [[1u8;32], ]);
1522 let result;
1523 #[block]
1524 {
1525 result = runtime.bench_base_fee(memory.as_mut_slice(), 0);
1526 }
1527 assert_ok!(result);
1528 assert_eq!(U256::from_little_endian(&memory[..]), <crate::Pallet<T>>::evm_base_fee());
1529 }
1530
1531 #[benchmark(pov_mode = Measured)]
1532 fn seal_block_number() {
1533 build_runtime!(runtime, memory: [[0u8;32], ]);
1534 let result;
1535 #[block]
1536 {
1537 result = runtime.bench_block_number(memory.as_mut_slice(), 0);
1538 }
1539 assert_ok!(result);
1540 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number());
1541 }
1542
1543 #[benchmark(pov_mode = Measured)]
1544 fn seal_block_author() {
1545 build_runtime!(runtime, memory: [[123u8; 20], ]);
1546
1547 for i in 0..16 {
1551 frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1552 [i, i, i, i],
1553 vec![i; 128],
1554 ));
1555 frame_system::Pallet::<T>::deposit_log(DigestItem::Consensus(
1556 [i, i, i, i],
1557 vec![i; 128],
1558 ));
1559 frame_system::Pallet::<T>::deposit_log(DigestItem::Seal([i, i, i, i], vec![i; 128]));
1560 frame_system::Pallet::<T>::deposit_log(DigestItem::Other(vec![i; 128]));
1561 }
1562
1563 let primary_pre_digest = vec![0; <PrimaryPreDigest as MaxEncodedLen>::max_encoded_len()];
1569 let pre_digest =
1570 PreDigest::Primary(PrimaryPreDigest::decode(&mut &primary_pre_digest[..]).unwrap());
1571 frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1572 BABE_ENGINE_ID,
1573 pre_digest.encode(),
1574 ));
1575 frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(
1576 BABE_ENGINE_ID,
1577 pre_digest.encode(),
1578 ));
1579
1580 let slot = Slot::default();
1582 frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1583 AURA_ENGINE_ID,
1584 slot.encode(),
1585 ));
1586 frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(AURA_ENGINE_ID, slot.encode()));
1587
1588 let result;
1589 #[block]
1590 {
1591 result = runtime.bench_block_author(memory.as_mut_slice(), 0);
1592 }
1593 assert_ok!(result);
1594
1595 let block_author = runtime.ext().block_author();
1596 assert_eq!(&memory[..], block_author.as_bytes());
1597 }
1598
1599 #[benchmark(pov_mode = Measured)]
1600 fn seal_block_hash() {
1601 let mut memory = vec![0u8; 64];
1602 let mut setup = CallSetup::<T>::default();
1603 let input = setup.data();
1604 let (mut ext, _) = setup.ext();
1605 ext.set_block_number(BlockNumberFor::<T>::from(1u32));
1606
1607 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
1608
1609 let block_hash = H256::from([1; 32]);
1610
1611 crate::BlockHash::<T>::insert(crate::BlockNumberFor::<T>::from(0u32), block_hash);
1613
1614 let result;
1615 #[block]
1616 {
1617 result = runtime.bench_block_hash(memory.as_mut_slice(), 32, 0);
1618 }
1619 assert_ok!(result);
1620 assert_eq!(&memory[..32], &block_hash.0);
1621 }
1622
1623 #[benchmark(pov_mode = Measured)]
1624 fn seal_now() {
1625 build_runtime!(runtime, memory: [[0u8;32], ]);
1626 let result;
1627 #[block]
1628 {
1629 result = runtime.bench_now(memory.as_mut_slice(), 0);
1630 }
1631 assert_ok!(result);
1632 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now());
1633 }
1634
1635 #[benchmark(pov_mode = Measured)]
1636 fn seal_copy_to_contract(n: Linear<0, { limits::code::BLOB_BYTES - 4 }>) {
1637 let mut setup = CallSetup::<T>::default();
1638 let (mut ext, _) = setup.ext();
1639 let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
1640 let mut memory = memory!(n.encode(), vec![0u8; n as usize],);
1641 let result;
1642 #[block]
1643 {
1644 result = runtime.write_sandbox_output(
1645 memory.as_mut_slice(),
1646 4,
1647 0,
1648 &vec![42u8; n as usize],
1649 false,
1650 |_| None,
1651 );
1652 }
1653 assert_ok!(result);
1654 assert_eq!(&memory[..4], &n.encode());
1655 assert_eq!(&memory[4..], &vec![42u8; n as usize]);
1656 }
1657
1658 #[benchmark(pov_mode = Measured)]
1659 fn seal_call_data_load() {
1660 let mut setup = CallSetup::<T>::default();
1661 let (mut ext, _) = setup.ext();
1662 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 32]);
1663 let mut memory = memory!(vec![0u8; 32],);
1664 let result;
1665 #[block]
1666 {
1667 result = runtime.bench_call_data_load(memory.as_mut_slice(), 0, 0);
1668 }
1669 assert_ok!(result);
1670 assert_eq!(&memory[..], &vec![42u8; 32]);
1671 }
1672
1673 #[benchmark(pov_mode = Measured)]
1674 fn seal_call_data_copy(n: Linear<0, { limits::code::BLOB_BYTES }>) {
1675 let mut setup = CallSetup::<T>::default();
1676 let (mut ext, _) = setup.ext();
1677 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; n as usize]);
1678 let mut memory = memory!(vec![0u8; n as usize],);
1679 let result;
1680 #[block]
1681 {
1682 result = runtime.bench_call_data_copy(memory.as_mut_slice(), 0, n, 0);
1683 }
1684 assert_ok!(result);
1685 assert_eq!(&memory[..], &vec![42u8; n as usize]);
1686 }
1687
1688 #[benchmark(pov_mode = Measured)]
1689 fn seal_return(n: Linear<0, { limits::CALLDATA_BYTES }>) {
1690 build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]);
1691
1692 let result;
1693 #[block]
1694 {
1695 result = runtime.bench_seal_return(memory.as_mut_slice(), 0, 0, n);
1696 }
1697
1698 assert!(matches!(
1699 result,
1700 Err(crate::vm::pvm::TrapReason::Return(crate::vm::pvm::ReturnData { .. }))
1701 ));
1702 }
1703
1704 #[benchmark(pov_mode = Measured)]
1708 fn seal_terminate(r: Linear<0, 1>) -> Result<(), BenchmarkError> {
1709 let delete_code = r == 1;
1710 let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1711
1712 build_runtime!(runtime, instance, memory: [beneficiary.encode(),]);
1713 let code_hash = instance.info()?.code_hash;
1714
1715 if !delete_code {
1717 <CodeInfo<T>>::increment_refcount(code_hash).unwrap();
1718 }
1719
1720 let result;
1721 #[block]
1722 {
1723 result = runtime.bench_terminate(memory.as_mut_slice(), 0);
1724 }
1725
1726 assert!(matches!(result, Err(crate::vm::pvm::TrapReason::Termination)));
1727
1728 Ok(())
1729 }
1730
1731 #[benchmark(pov_mode = Measured)]
1732 fn seal_terminate_logic() -> Result<(), BenchmarkError> {
1733 let caller = whitelisted_caller();
1734 let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1735 T::AddressMapper::map_no_deposit_unchecked(&beneficiary)?;
1736
1737 build_runtime!(_runtime, instance, _memory: [vec![0u8; 0], ]);
1738 let code_hash = instance.info()?.code_hash;
1739
1740 assert!(PristineCode::<T>::get(code_hash).is_some());
1741
1742 T::Currency::set_balance(&instance.account_id, Pallet::<T>::min_balance() * 10u32.into());
1743
1744 let storage_deposit = T::Currency::balance_on_hold(
1745 &HoldReason::StorageDepositReserve.into(),
1746 &instance.account_id,
1747 );
1748 NativeDepositOf::<T>::insert(&instance.account_id, &caller, storage_deposit);
1749
1750 let mut transaction_meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
1751 weight_limit: Default::default(),
1752 deposit_limit: BalanceOf::<T>::max_value(),
1753 })
1754 .unwrap();
1755 let exec_config = ExecConfig::new_substrate_tx();
1756 let contract_account = &instance.account_id;
1757 let origin = &ExecOrigin::from_account_id(caller);
1758 let beneficiary_clone = beneficiary.clone();
1759 let trie_id = instance.info()?.trie_id.clone();
1760 let code_hash = instance.info()?.code_hash;
1761 let only_if_same_tx = false;
1762
1763 let result;
1764 #[block]
1765 {
1766 result = crate::exec::bench_do_terminate::<T>(
1767 &mut transaction_meter,
1768 &exec_config,
1769 contract_account,
1770 &origin,
1771 beneficiary_clone,
1772 trie_id,
1773 code_hash,
1774 only_if_same_tx,
1775 );
1776 }
1777 result.unwrap();
1778
1779 assert!(PristineCode::<T>::get(code_hash).is_none());
1781
1782 let balance = <T as Config>::Currency::total_balance(&instance.account_id);
1784 assert_eq!(balance, 0u32.into());
1785
1786 let balance = <T as Config>::Currency::balance(&beneficiary);
1788 assert_eq!(balance, Pallet::<T>::min_balance() + Pallet::<T>::min_balance() * 9u32.into());
1789
1790 Ok(())
1791 }
1792
1793 #[benchmark(pov_mode = Measured)]
1797 fn seal_deposit_event(
1798 t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>,
1799 n: Linear<0, { limits::EVENT_BYTES }>,
1800 ) {
1801 let num_topic = t as u32;
1802 let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::<Vec<_>>();
1803 let topics_data =
1804 topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::<Vec<u8>>();
1805 let data = vec![42u8; n as _];
1806 build_runtime!(runtime, instance, memory: [ topics_data, data, ]);
1807
1808 let result;
1812 #[block]
1813 {
1814 result = block_storage::bench_with_ethereum_context(|| {
1815 runtime.bench_deposit_event(
1816 memory.as_mut_slice(),
1817 0, num_topic,
1819 topics_data.len() as u32, n, )
1822 });
1823 }
1824 assert_ok!(result);
1825
1826 let events = System::<T>::events();
1827 let record = &events[events.len() - 1];
1828
1829 assert_eq!(
1830 record.event,
1831 crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(),
1832 );
1833 }
1834
1835 enum TrieFill {
1836 Empty,
1837 Full,
1838 }
1839
1840 enum SlotAccess {
1841 Cold,
1842 Hot,
1843 }
1844
1845 fn build_storage_contract<T: Config>(
1846 op: StorageOp,
1847 fill: TrieFill,
1848 ) -> Result<(ContractInfo<T>, Vec<u8>, Vec<u8>), BenchmarkError> {
1849 let key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1850 let value = vec![1u8; limits::STORAGE_BYTES as usize];
1851 let initial_value = match op {
1852 StorageOp::Read => value.clone(),
1853 StorageOp::Write => vec![42u8; limits::STORAGE_BYTES as usize],
1854 };
1855
1856 let instance = match fill {
1857 TrieFill::Full => {
1858 Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?
1859 },
1860 TrieFill::Empty => Contract::<T>::new(VmBinaryModule::dummy(), vec![])?,
1861 };
1862 let info = instance.info()?;
1863 info.bench_write_raw(&key, Some(initial_value), false)
1864 .map_err(|_| "Failed to write to storage during setup.")?;
1865 Ok((info, key, value))
1866 }
1867
1868 enum StorageCall {
1869 Clear,
1870 Contains,
1871 Take,
1872 }
1873
1874 fn setup_precompile_bench<T: Config>(
1875 op: StorageCall,
1876 key_byte: u8,
1877 access: SlotAccess,
1878 ) -> Result<(CallSetup<T>, Key, Vec<u8>), BenchmarkError> {
1879 let max_key_len = limits::STORAGE_KEY_BYTES;
1880 let key = Key::try_from_var(vec![key_byte; max_key_len as usize])
1881 .map_err(|_| "Key has wrong length")?;
1882 let raw_key = vec![key_byte; max_key_len as usize].into();
1883 let input_bytes = match op {
1884 StorageCall::Clear => {
1885 IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
1886 flags: StorageFlags::empty().bits(),
1887 key: raw_key,
1888 isFixedKey: false,
1889 })
1890 },
1891 StorageCall::Contains => {
1892 IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
1893 flags: StorageFlags::empty().bits(),
1894 key: raw_key,
1895 isFixedKey: false,
1896 })
1897 },
1898 StorageCall::Take => IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
1899 flags: StorageFlags::empty().bits(),
1900 key: raw_key,
1901 isFixedKey: false,
1902 }),
1903 }
1904 .abi_encode();
1905
1906 let call_setup = CallSetup::<T>::default();
1907 if matches!(access, SlotAccess::Hot) {
1908 let info = call_setup.contract().info()?;
1909 frame_benchmarking::add_to_whitelist_child(
1910 info.child_trie_info().storage_key().to_vec(),
1911 key.hash(),
1912 );
1913 }
1914 Ok((call_setup, key, input_bytes))
1915 }
1916
1917 #[benchmark(skip_meta, pov_mode = Measured)]
1918 fn get_storage_empty() -> Result<(), BenchmarkError> {
1919 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Empty)?;
1920 let child_trie_info = info.child_trie_info();
1921
1922 let result;
1923 #[block]
1924 {
1925 result = child::get_raw(&child_trie_info, &key);
1926 }
1927
1928 assert_eq!(result, Some(value));
1929 Ok(())
1930 }
1931
1932 #[benchmark(skip_meta, pov_mode = Measured)]
1933 fn get_storage_full() -> Result<(), BenchmarkError> {
1934 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Full)?;
1935 let child_trie_info = info.child_trie_info();
1936
1937 let result;
1938 #[block]
1939 {
1940 result = child::get_raw(&child_trie_info, &key);
1941 }
1942
1943 assert_eq!(result, Some(value));
1944 Ok(())
1945 }
1946
1947 #[benchmark(skip_meta, pov_mode = Measured)]
1948 fn set_storage_empty() -> Result<(), BenchmarkError> {
1949 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Empty)?;
1950
1951 let val = Some(value.clone());
1952 let result;
1953 #[block]
1954 {
1955 result = info.bench_write_raw(&key, val, true);
1956 }
1957
1958 assert_ok!(result);
1959 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1960 Ok(())
1961 }
1962
1963 #[benchmark(skip_meta, pov_mode = Measured)]
1964 fn set_storage_full() -> Result<(), BenchmarkError> {
1965 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Full)?;
1966
1967 let val = Some(value.clone());
1968 let result;
1969 #[block]
1970 {
1971 result = info.bench_write_raw(&key, val, true);
1972 }
1973
1974 assert_ok!(result);
1975 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1976 Ok(())
1977 }
1978
1979 fn shared_prefix_keys(count: usize, suffix_of: impl Fn(u64) -> u64) -> Vec<Vec<u8>> {
1981 (0..count as u64)
1982 .map(|i| {
1983 let mut key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1984 key[limits::STORAGE_KEY_BYTES as usize - 8..]
1985 .copy_from_slice(&suffix_of(i).to_be_bytes());
1986 key
1987 })
1988 .collect()
1989 }
1990
1991 fn setup_stored_keys<T: Config>(
1994 count: usize,
1995 value_byte: u8,
1996 suffix_of: impl Fn(u64) -> u64,
1997 ) -> Result<(ContractInfo<T>, Vec<Vec<u8>>), BenchmarkError> {
1998 let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1999 let info = instance.info()?;
2000 let child_trie_info = info.child_trie_info();
2001 let value = vec![value_byte; limits::STORAGE_BYTES as usize];
2002 let stored_keys = shared_prefix_keys(count, suffix_of);
2003 for key in &stored_keys {
2004 info.bench_write_raw(key, Some(value.clone()), false)
2005 .map_err(|_| "Failed to write to storage during setup.")?;
2006 frame_benchmarking::add_to_whitelist_child(
2007 child_trie_info.storage_key().to_vec(),
2008 key.clone(),
2009 );
2010 }
2011 Ok((info, stored_keys))
2012 }
2013
2014 #[benchmark(skip_meta, pov_mode = Measured)]
2015 fn overlay_probe_full(
2016 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
2017 ) -> Result<(), BenchmarkError> {
2018 let value_byte = 42;
2019 let (info, stored_keys) =
2021 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
2022 let child_trie_info = info.child_trie_info();
2023 let fill_keys = shared_prefix_keys(MAX_ACCESS_LIST_ENTRIES, |i| (i + 1) * 2);
2025
2026 let mut result = None;
2027 #[block]
2028 {
2029 for key in &fill_keys {
2031 child::put_raw(&child_trie_info, key, &[0u8]);
2032 }
2033 for i in 0..n {
2034 let index = i as usize % stored_keys.len();
2035 result = child::get_raw(&child_trie_info, &stored_keys[index]);
2036 }
2037 }
2038
2039 if n > 0 {
2040 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
2041 assert_eq!(result, Some(expected), "the stored value must be read back");
2042 }
2043 Ok(())
2044 }
2045
2046 #[benchmark(skip_meta, pov_mode = Measured)]
2047 fn overlay_probe_empty(
2048 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
2049 ) -> Result<(), BenchmarkError> {
2050 let value_byte = 42;
2051 let (info, stored_keys) =
2053 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
2054 let child_trie_info = info.child_trie_info();
2055
2056 let mut result = None;
2057 #[block]
2058 {
2059 for i in 0..n {
2060 let index = i as usize % stored_keys.len();
2061 result = child::get_raw(&child_trie_info, &stored_keys[index]);
2062 }
2063 }
2064
2065 if n > 0 {
2066 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
2067 assert_eq!(result, Some(expected), "the stored value must be read back");
2068 }
2069 Ok(())
2070 }
2071
2072 #[benchmark(skip_meta, pov_mode = Measured)]
2075 fn seal_set_storage(
2076 n: Linear<0, { limits::STORAGE_BYTES }>,
2077 o: Linear<0, { limits::STORAGE_BYTES }>,
2078 ) -> Result<(), BenchmarkError> {
2079 let max_key_len = limits::STORAGE_KEY_BYTES;
2080 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2081 .map_err(|_| "Key has wrong length")?;
2082 let value = vec![1u8; n as usize];
2083
2084 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
2085 let info = instance.info()?;
2086
2087 info.write(&key, Some(vec![42u8; o as usize]), None, false)
2088 .map_err(|_| "Failed to write to storage during setup.")?;
2089
2090 let result;
2091 #[block]
2092 {
2093 result = runtime.bench_set_storage(
2094 memory.as_mut_slice(),
2095 StorageFlags::empty().bits(),
2096 0, max_key_len, max_key_len, n, );
2101 }
2102
2103 assert_ok!(result);
2104 assert_eq!(info.read(&key).unwrap(), value);
2105 Ok(())
2106 }
2107
2108 #[benchmark(skip_meta, pov_mode = Measured)]
2109 fn seal_set_storage_hot(
2110 n: Linear<0, { limits::STORAGE_BYTES }>,
2111 o: Linear<0, { limits::STORAGE_BYTES }>,
2112 ) -> Result<(), BenchmarkError> {
2113 let max_key_len = limits::STORAGE_KEY_BYTES;
2114 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2115 .map_err(|_| "Key has wrong length")?;
2116 let value = vec![1u8; n as usize];
2117
2118 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
2119 let info = instance.info()?;
2120
2121 info.write(&key, Some(vec![42u8; o as usize]), None, false)
2122 .map_err(|_| "Failed to write to storage during setup.")?;
2123
2124 frame_benchmarking::add_to_whitelist_child(
2125 info.child_trie_info().storage_key().to_vec(),
2126 key.hash(),
2127 );
2128
2129 runtime.ext().touch_storage_access(&key, StorageOp::Write);
2131
2132 let result;
2133 #[block]
2134 {
2135 result = runtime.bench_set_storage(
2136 memory.as_mut_slice(),
2137 StorageFlags::empty().bits(),
2138 0, max_key_len, max_key_len, n, );
2143 }
2144
2145 assert_ok!(result);
2146 assert_eq!(info.read(&key).unwrap(), value);
2147 Ok(())
2148 }
2149
2150 #[benchmark(skip_meta, pov_mode = Measured)]
2151 fn clear_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2152 let key_byte = 0;
2153 let (mut call_setup, key, input_bytes) =
2154 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Cold)?;
2155 let (mut ext, _) = call_setup.ext();
2156 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2157 .map_err(|_| "Failed to write to storage during setup.")?;
2158
2159 let result;
2160 #[block]
2161 {
2162 result = run_builtin_precompile(
2163 &mut ext,
2164 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2165 input_bytes,
2166 );
2167 }
2168 assert_ok!(result);
2169 assert!(ext.get_storage(&key).is_none());
2170
2171 Ok(())
2172 }
2173
2174 #[benchmark(skip_meta, pov_mode = Measured)]
2175 fn clear_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2176 let key_byte = 0;
2177 let (mut call_setup, key, input_bytes) =
2178 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Hot)?;
2179 let (mut ext, _) = call_setup.ext();
2180 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2181 .map_err(|_| "Failed to write to storage during setup.")?;
2182
2183 ext.touch_storage_access(&key, StorageOp::Write);
2184
2185 let result;
2186 #[block]
2187 {
2188 result = run_builtin_precompile(
2189 &mut ext,
2190 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2191 input_bytes,
2192 );
2193 }
2194 assert_ok!(result);
2195 assert!(ext.get_storage(&key).is_none());
2196
2197 Ok(())
2198 }
2199
2200 #[benchmark(skip_meta, pov_mode = Measured)]
2201 fn seal_get_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2202 let max_key_len = limits::STORAGE_KEY_BYTES;
2203 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2204 .map_err(|_| "Key has wrong length")?;
2205 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2206 let info = instance.info()?;
2207
2208 info.write(&key, Some(vec![42u8; n as usize]), None, false)
2209 .map_err(|_| "Failed to write to storage during setup.")?;
2210
2211 let out_ptr = max_key_len + 4;
2212 let result;
2213 #[block]
2214 {
2215 result = runtime.bench_get_storage(
2216 memory.as_mut_slice(),
2217 StorageFlags::empty().bits(),
2218 0, max_key_len, out_ptr, max_key_len, );
2223 }
2224
2225 assert_ok!(result);
2226 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
2227 Ok(())
2228 }
2229
2230 #[benchmark(skip_meta, pov_mode = Measured)]
2231 fn seal_get_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2232 let max_key_len = limits::STORAGE_KEY_BYTES;
2233 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2234 .map_err(|_| "Key has wrong length")?;
2235 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2236 let info = instance.info()?;
2237
2238 info.write(&key, Some(vec![42u8; n as usize]), None, false)
2239 .map_err(|_| "Failed to write to storage during setup.")?;
2240
2241 frame_benchmarking::add_to_whitelist_child(
2242 info.child_trie_info().storage_key().to_vec(),
2243 key.hash(),
2244 );
2245
2246 runtime.ext().touch_storage_access(&key, StorageOp::Read);
2247
2248 let out_ptr = max_key_len + 4;
2249 let result;
2250 #[block]
2251 {
2252 result = runtime.bench_get_storage(
2253 memory.as_mut_slice(),
2254 StorageFlags::empty().bits(),
2255 0, max_key_len, out_ptr, max_key_len, );
2260 }
2261
2262 assert_ok!(result);
2263 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
2264 Ok(())
2265 }
2266
2267 #[benchmark(skip_meta, pov_mode = Measured)]
2268 fn contains_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2269 let key_byte = 0;
2270 let (mut call_setup, key, input_bytes) =
2271 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Cold)?;
2272 let (mut ext, _) = call_setup.ext();
2273 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2274 .map_err(|_| "Failed to write to storage during setup.")?;
2275
2276 let result;
2277 #[block]
2278 {
2279 result = run_builtin_precompile(
2280 &mut ext,
2281 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2282 input_bytes,
2283 );
2284 }
2285 assert_ok!(result);
2286 assert!(ext.get_storage(&key).is_some());
2287
2288 Ok(())
2289 }
2290
2291 #[benchmark(skip_meta, pov_mode = Measured)]
2292 fn contains_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2293 let key_byte = 0;
2294 let (mut call_setup, key, input_bytes) =
2295 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Hot)?;
2296 let (mut ext, _) = call_setup.ext();
2297 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2298 .map_err(|_| "Failed to write to storage during setup.")?;
2299
2300 ext.touch_storage_access(&key, StorageOp::Read);
2301
2302 let result;
2303 #[block]
2304 {
2305 result = run_builtin_precompile(
2306 &mut ext,
2307 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2308 input_bytes,
2309 );
2310 }
2311 assert_ok!(result);
2312 assert!(ext.get_storage(&key).is_some());
2313
2314 Ok(())
2315 }
2316
2317 #[benchmark(skip_meta, pov_mode = Measured)]
2318 fn take_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2319 let key_byte = 3;
2320 let (mut call_setup, key, input_bytes) =
2321 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Cold)?;
2322 let (mut ext, _) = call_setup.ext();
2323 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2324 .map_err(|_| "Failed to write to storage during setup.")?;
2325
2326 let result;
2327 #[block]
2328 {
2329 result = run_builtin_precompile(
2330 &mut ext,
2331 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2332 input_bytes,
2333 );
2334 }
2335 assert_ok!(result);
2336 assert!(ext.get_storage(&key).is_none());
2337
2338 Ok(())
2339 }
2340
2341 #[benchmark(skip_meta, pov_mode = Measured)]
2342 fn take_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2343 let key_byte = 3;
2344 let (mut call_setup, key, input_bytes) =
2345 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Hot)?;
2346 let (mut ext, _) = call_setup.ext();
2347 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2348 .map_err(|_| "Failed to write to storage during setup.")?;
2349
2350 ext.touch_storage_access(&key, StorageOp::Write);
2351
2352 let result;
2353 #[block]
2354 {
2355 result = run_builtin_precompile(
2356 &mut ext,
2357 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2358 input_bytes,
2359 );
2360 }
2361 assert_ok!(result);
2362 assert!(ext.get_storage(&key).is_none());
2363
2364 Ok(())
2365 }
2366
2367 fn worst_case_slot() -> crate::access_list::Slot {
2368 let key = Key::try_from_var(vec![0xFFu8; limits::STORAGE_KEY_BYTES as usize])
2369 .expect("key fits STORAGE_KEY_BYTES bound; qed");
2370 crate::access_list::Slot::from(&key)
2371 }
2372
2373 fn near_full_access_list() -> crate::access_list::AccessList {
2374 let mut al = AccessList::new();
2375 for i in 0..(MAX_ACCESS_LIST_ENTRIES - 1) {
2376 al.touch(
2377 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(i as u64) },
2378 StorageOp::Read,
2379 );
2380 }
2381 al
2382 }
2383
2384 #[benchmark(pov_mode = Ignored)]
2385 fn access_list_touch_cold_full() -> Result<(), BenchmarkError> {
2386 let mut al = near_full_access_list();
2387 let entry =
2389 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2390 let outcome;
2391 #[block]
2392 {
2393 outcome = al.touch(entry, StorageOp::Read);
2394 }
2395 assert!(outcome.is_cold());
2396 Ok(())
2397 }
2398
2399 #[benchmark(pov_mode = Ignored)]
2400 fn access_list_touch_hot_full() -> Result<(), BenchmarkError> {
2401 let mut al = near_full_access_list();
2402 let entry = AccessEntry {
2404 slot: worst_case_slot(),
2405 address: H160::from_low_u64_be(MAX_ACCESS_LIST_ENTRIES as u64 - 2),
2406 };
2407
2408 let touched = entry.clone();
2409 let outcome;
2410 #[block]
2411 {
2412 outcome = al.touch(touched, StorageOp::Write);
2413 }
2414 assert_eq!(
2415 outcome,
2416 Warmth::Hot { charged: StorageOp::Read },
2417 "the fill seeded this entry read-paid"
2418 );
2419 assert_eq!(
2420 al.peek(&entry),
2421 Warmth::Hot { charged: StorageOp::Write },
2422 "the write upgraded the entry"
2423 );
2424 Ok(())
2425 }
2426
2427 #[benchmark(pov_mode = Ignored)]
2428 fn access_list_touch_cold_empty() -> Result<(), BenchmarkError> {
2429 let mut al = AccessList::new();
2430 let entry =
2431 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2432 let outcome;
2433 #[block]
2434 {
2435 outcome = al.touch(entry, StorageOp::Read);
2436 }
2437 assert!(outcome.is_cold());
2438 Ok(())
2439 }
2440
2441 #[benchmark(pov_mode = Ignored)]
2442 fn access_list_touch_hot_single_element() -> Result<(), BenchmarkError> {
2443 let mut al = AccessList::new();
2444 let entry =
2445 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2446 al.touch(entry.clone(), StorageOp::Read);
2447 let outcome;
2448 #[block]
2449 {
2450 outcome = al.touch(entry, StorageOp::Read);
2451 }
2452 assert!(!outcome.is_cold());
2453 Ok(())
2454 }
2455
2456 #[benchmark(pov_mode = Ignored)]
2460 fn access_list_rollback_amortization() -> Result<(), BenchmarkError> {
2461 let mut al = near_full_access_list();
2462 al.enter_frame();
2463 al.touch(
2464 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) },
2465 StorageOp::Read,
2466 );
2467 #[block]
2468 {
2469 al.rollback_frame();
2470 }
2471 Ok(())
2472 }
2473
2474 #[benchmark(pov_mode = Ignored)]
2479 fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
2480 let max_value_len = limits::STORAGE_BYTES;
2481 let max_key_len = limits::STORAGE_KEY_BYTES;
2482 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2483 .map_err(|_| "Key has wrong length")?;
2484 let value = Some(vec![42u8; max_value_len as _]);
2485 let mut setup = CallSetup::<T>::default();
2486 let (mut ext, _) = setup.ext();
2487 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2488 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2489 let result;
2490 #[block]
2491 {
2492 result = runtime.ext().set_transient_storage(&key, value, false);
2493 }
2494
2495 assert_eq!(result, Ok(WriteOutcome::New));
2496 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2497 Ok(())
2498 }
2499
2500 #[benchmark(pov_mode = Ignored)]
2501 fn set_transient_storage_full() -> Result<(), BenchmarkError> {
2502 let max_value_len = limits::STORAGE_BYTES;
2503 let max_key_len = limits::STORAGE_KEY_BYTES;
2504 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2505 .map_err(|_| "Key has wrong length")?;
2506 let value = Some(vec![42u8; max_value_len as _]);
2507 let mut setup = CallSetup::<T>::default();
2508 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2509 let (mut ext, _) = setup.ext();
2510 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2511 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2512 let result;
2513 #[block]
2514 {
2515 result = runtime.ext().set_transient_storage(&key, value, false);
2516 }
2517
2518 assert_eq!(result, Ok(WriteOutcome::New));
2519 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2520 Ok(())
2521 }
2522
2523 #[benchmark(pov_mode = Ignored)]
2524 fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
2525 let max_value_len = limits::STORAGE_BYTES;
2526 let max_key_len = limits::STORAGE_KEY_BYTES;
2527 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2528 .map_err(|_| "Key has wrong length")?;
2529
2530 let mut setup = CallSetup::<T>::default();
2531 let (mut ext, _) = setup.ext();
2532 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2533 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2534 runtime
2535 .ext()
2536 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2537 .map_err(|_| "Failed to write to transient storage during setup.")?;
2538 let result;
2539 #[block]
2540 {
2541 result = runtime.ext().get_transient_storage(&key);
2542 }
2543
2544 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2545 Ok(())
2546 }
2547
2548 #[benchmark(pov_mode = Ignored)]
2549 fn get_transient_storage_full() -> Result<(), BenchmarkError> {
2550 let max_value_len = limits::STORAGE_BYTES;
2551 let max_key_len = limits::STORAGE_KEY_BYTES;
2552 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2553 .map_err(|_| "Key has wrong length")?;
2554
2555 let mut setup = CallSetup::<T>::default();
2556 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2557 let (mut ext, _) = setup.ext();
2558 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2559 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2560 runtime
2561 .ext()
2562 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2563 .map_err(|_| "Failed to write to transient storage during setup.")?;
2564 let result;
2565 #[block]
2566 {
2567 result = runtime.ext().get_transient_storage(&key);
2568 }
2569
2570 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2571 Ok(())
2572 }
2573
2574 #[benchmark(pov_mode = Ignored)]
2576 fn rollback_transient_storage() -> Result<(), BenchmarkError> {
2577 let max_value_len = limits::STORAGE_BYTES;
2578 let max_key_len = limits::STORAGE_KEY_BYTES;
2579 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2580 .map_err(|_| "Key has wrong length")?;
2581
2582 let mut setup = CallSetup::<T>::default();
2583 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2584 let (mut ext, _) = setup.ext();
2585 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2586 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2587 runtime.ext().transient_storage().start_transaction();
2588 runtime
2589 .ext()
2590 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2591 .map_err(|_| "Failed to write to transient storage during setup.")?;
2592 #[block]
2593 {
2594 runtime.ext().transient_storage().rollback_transaction();
2595 }
2596
2597 assert_eq!(runtime.ext().get_transient_storage(&key), None);
2598 Ok(())
2599 }
2600
2601 #[benchmark(pov_mode = Measured)]
2604 fn seal_set_transient_storage(
2605 n: Linear<0, { limits::STORAGE_BYTES }>,
2606 o: Linear<0, { limits::STORAGE_BYTES }>,
2607 ) -> Result<(), BenchmarkError> {
2608 let max_key_len = limits::STORAGE_KEY_BYTES;
2609 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2610 .map_err(|_| "Key has wrong length")?;
2611 let value = vec![1u8; n as usize];
2612 build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]);
2613 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2614 runtime
2615 .ext()
2616 .set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
2617 .map_err(|_| "Failed to write to transient storage during setup.")?;
2618
2619 let result;
2620 #[block]
2621 {
2622 result = runtime.bench_set_storage(
2623 memory.as_mut_slice(),
2624 StorageFlags::TRANSIENT.bits(),
2625 0, max_key_len, max_key_len, n, );
2630 }
2631
2632 assert_ok!(result);
2633 assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
2634 Ok(())
2635 }
2636
2637 #[benchmark(pov_mode = Measured)]
2638 fn seal_clear_transient_storage(
2639 n: Linear<0, { limits::STORAGE_BYTES }>,
2640 ) -> Result<(), BenchmarkError> {
2641 let max_key_len = limits::STORAGE_KEY_BYTES;
2642 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2643 .map_err(|_| "Key has wrong length")?;
2644 let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
2645 flags: StorageFlags::TRANSIENT.bits(),
2646 key: vec![0u8; max_key_len as usize].into(),
2647 isFixedKey: false,
2648 })
2649 .abi_encode();
2650
2651 let mut call_setup = CallSetup::<T>::default();
2652 let (mut ext, _) = call_setup.ext();
2653 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2654 .map_err(|_| "Failed to write to transient storage during setup.")?;
2655
2656 let result;
2657 #[block]
2658 {
2659 result = run_builtin_precompile(
2660 &mut ext,
2661 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2662 input_bytes,
2663 );
2664 }
2665 assert_ok!(result);
2666 assert!(ext.get_transient_storage(&key).is_none());
2667
2668 Ok(())
2669 }
2670
2671 #[benchmark(pov_mode = Measured)]
2672 fn seal_get_transient_storage(
2673 n: Linear<0, { limits::STORAGE_BYTES }>,
2674 ) -> Result<(), BenchmarkError> {
2675 let max_key_len = limits::STORAGE_KEY_BYTES;
2676 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2677 .map_err(|_| "Key has wrong length")?;
2678 build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2679 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2680 runtime
2681 .ext()
2682 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2683 .map_err(|_| "Failed to write to transient storage during setup.")?;
2684
2685 let out_ptr = max_key_len + 4;
2686 let result;
2687 #[block]
2688 {
2689 result = runtime.bench_get_storage(
2690 memory.as_mut_slice(),
2691 StorageFlags::TRANSIENT.bits(),
2692 0, max_key_len, out_ptr, max_key_len, );
2697 }
2698
2699 assert_ok!(result);
2700 assert_eq!(
2701 &runtime.ext().get_transient_storage(&key).unwrap(),
2702 &memory[out_ptr as usize..]
2703 );
2704 Ok(())
2705 }
2706
2707 #[benchmark(pov_mode = Measured)]
2708 fn seal_contains_transient_storage(
2709 n: Linear<0, { limits::STORAGE_BYTES }>,
2710 ) -> Result<(), BenchmarkError> {
2711 let max_key_len = limits::STORAGE_KEY_BYTES;
2712 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2713 .map_err(|_| "Key has wrong length")?;
2714
2715 let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
2716 flags: StorageFlags::TRANSIENT.bits(),
2717 key: vec![0u8; max_key_len as usize].into(),
2718 isFixedKey: false,
2719 })
2720 .abi_encode();
2721
2722 let mut call_setup = CallSetup::<T>::default();
2723 let (mut ext, _) = call_setup.ext();
2724 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2725 .map_err(|_| "Failed to write to transient storage during setup.")?;
2726
2727 let result;
2728 #[block]
2729 {
2730 result = run_builtin_precompile(
2731 &mut ext,
2732 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2733 input_bytes,
2734 );
2735 }
2736 assert!(result.is_ok());
2737 assert!(ext.get_transient_storage(&key).is_some());
2738
2739 Ok(())
2740 }
2741
2742 #[benchmark(pov_mode = Measured)]
2743 fn seal_take_transient_storage(
2744 n: Linear<0, { limits::STORAGE_BYTES }>,
2745 ) -> Result<(), BenchmarkError> {
2746 let n = limits::STORAGE_BYTES;
2747 let value = vec![42u8; n as usize];
2748 let max_key_len = limits::STORAGE_KEY_BYTES;
2749 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2750 .map_err(|_| "Key has wrong length")?;
2751
2752 let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
2753 flags: StorageFlags::TRANSIENT.bits(),
2754 key: vec![0u8; max_key_len as usize].into(),
2755 isFixedKey: false,
2756 })
2757 .abi_encode();
2758
2759 let mut call_setup = CallSetup::<T>::default();
2760 let (mut ext, _) = call_setup.ext();
2761 ext.set_transient_storage(&key, Some(value), false)
2762 .map_err(|_| "Failed to write to transient storage during setup.")?;
2763
2764 let result;
2765 #[block]
2766 {
2767 result = run_builtin_precompile(
2768 &mut ext,
2769 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2770 input_bytes,
2771 );
2772 }
2773 assert!(result.is_ok());
2774 assert!(ext.get_transient_storage(&key).is_none());
2775
2776 Ok(())
2777 }
2778
2779 #[benchmark(pov_mode = Measured)]
2783 fn seal_call(
2784 t: Linear<0, 1>,
2785 d: Linear<0, 1>,
2786 i: Linear<0, { limits::code::BLOB_BYTES }>,
2787 ) -> Result<(), BenchmarkError> {
2788 let target = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?;
2794 let callee_addr = H160([0x42; 20]);
2795 let callee = delegated_eoa::<T>(callee_addr, target.address)?;
2796 T::Currency::set_balance(&callee, Pallet::<T>::min_balance());
2801
2802 let callee_bytes = callee.encode();
2803 let callee_len = callee_bytes.len() as u32;
2804
2805 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2806 let dust = 100u32 * d;
2807 let evm_value =
2808 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2809 let value_bytes = evm_value.encode();
2810
2811 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2812 let deposit_bytes = Into::<U256>::into(deposit).encode();
2813 let deposit_len = deposit_bytes.len() as u32;
2814
2815 let mut setup = CallSetup::<T>::default();
2816 setup.set_storage_deposit_limit(deposit);
2817 setup.set_data(vec![42; i as usize]);
2820 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2821 setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
2822
2823 let (mut ext, _) = setup.ext();
2824 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2825 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
2826 let before = Pallet::<T>::evm_balance(&callee_addr);
2827
2828 let result;
2829 #[block]
2830 {
2831 result = runtime.bench_call(
2832 memory.as_mut_slice(),
2833 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), );
2840 }
2841
2842 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2843 assert_eq!(
2844 Pallet::<T>::evm_balance(&callee_addr),
2845 before + evm_value,
2846 "{callee_addr:?} balance should have grown by {evm_value:?}"
2847 );
2848
2849 Ok(())
2850 }
2851
2852 #[benchmark(pov_mode = Measured)]
2855 fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
2856 use alloy_core::sol_types::SolInterface;
2857 use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
2858
2859 let callee_bytes = if d == 1 {
2860 BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
2861 } else {
2862 BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
2863 };
2864 let callee_len = callee_bytes.len() as u32;
2865
2866 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2867 let deposit_bytes = Into::<U256>::into(deposit).encode();
2868 let deposit_len = deposit_bytes.len() as u32;
2869
2870 let value: BalanceOf<T> = Zero::zero();
2871 let value_bytes = Into::<U256>::into(value).encode();
2872 let value_len = value_bytes.len() as u32;
2873
2874 let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
2875 input: vec![42_u8; i as usize].into(),
2876 })
2877 .abi_encode();
2878 let input_len = input_bytes.len() as u32;
2879
2880 let mut setup = CallSetup::<T>::default();
2881 setup.set_storage_deposit_limit(deposit);
2882
2883 let (mut ext, _) = setup.ext();
2884 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2885 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
2886
2887 let mut do_benchmark = || {
2888 runtime.bench_call(
2889 memory.as_mut_slice(),
2890 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), )
2899 };
2900
2901 assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
2904
2905 let result;
2906 #[block]
2907 {
2908 result = do_benchmark();
2909 }
2910
2911 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2912 }
2913
2914 #[benchmark(pov_mode = Measured)]
2915 fn seal_delegate_call() -> Result<(), BenchmarkError> {
2916 let target = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?;
2919 let address = delegated_eoa::<T>(H160([0x43; 20]), target.address)?;
2920
2921 let address_bytes = address.encode();
2922 let address_len = address_bytes.len() as u32;
2923
2924 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2925 let deposit_bytes = Into::<U256>::into(deposit).encode();
2926
2927 let mut setup = CallSetup::<T>::default();
2928 setup.set_storage_deposit_limit(deposit);
2929 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2930
2931 let (mut ext, _) = setup.ext();
2932 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2933 let mut memory = memory!(address_bytes, deposit_bytes,);
2934
2935 let result;
2936 #[block]
2937 {
2938 result = runtime.bench_delegate_call(
2939 memory.as_mut_slice(),
2940 pack_hi_lo(0, 0), u64::MAX, u64::MAX, address_len, pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
2947 }
2948
2949 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2950 Ok(())
2951 }
2952
2953 #[benchmark(pov_mode = Measured)]
2957 fn seal_instantiate(
2958 t: Linear<0, 1>,
2959 d: Linear<0, 1>,
2960 i: Linear<0, { limits::CALLDATA_BYTES }>,
2961 ) -> Result<(), BenchmarkError> {
2962 let code = VmBinaryModule::dummy();
2963 let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
2964 let hash_bytes = hash.encode();
2965
2966 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2967 let dust = 100u32 * d;
2968 let evm_value =
2969 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2970 let value_bytes = evm_value.encode();
2971 let value_len = value_bytes.len() as u32;
2972
2973 let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
2974 let deposit_bytes = Into::<U256>::into(deposit).encode();
2975 let deposit_len = deposit_bytes.len() as u32;
2976
2977 let mut setup = CallSetup::<T>::default();
2978 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2979 setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
2980
2981 let account_id = &setup.contract().account_id.clone();
2982 let (mut ext, _) = setup.ext();
2983 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2984
2985 let input = vec![42u8; i as _];
2986 let input_len = hash_bytes.len() as u32 + input.len() as u32;
2987 let salt = [42u8; 32];
2988 let deployer = T::AddressMapper::to_address(&account_id);
2989 let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
2990 let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
2991
2992 let mut offset = {
2993 let mut current = 0u32;
2994 move |after: u32| {
2995 current += after;
2996 current
2997 }
2998 };
2999
3000 assert!(AccountInfoOf::<T>::get(&addr).is_none());
3001
3002 let result;
3003 #[block]
3004 {
3005 result = runtime.bench_instantiate(
3006 memory.as_mut_slice(),
3007 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)), );
3014 }
3015
3016 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
3017 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
3018
3019 assert_eq!(
3020 Pallet::<T>::evm_balance(&addr),
3021 evm_value,
3022 "{addr:?} balance should hold {evm_value:?}"
3023 );
3024 Ok(())
3025 }
3026
3027 #[benchmark(pov_mode = Measured)]
3031 fn evm_instantiate(
3032 t: Linear<0, 1>,
3033 d: Linear<0, 1>,
3034 i: Linear<{ 10 * 1024 }, { 48 * 1024 }>,
3035 ) -> Result<(), BenchmarkError> {
3036 use crate::vm::evm::instructions::BENCH_INIT_CODE;
3037 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_init_code_for_runtime_size(0));
3038 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
3039 setup.set_balance(caller_funding::<T>());
3040
3041 let (mut ext, _) = setup.ext();
3042 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
3043
3044 let value = {
3045 let value: BalanceOf<T> = (1_000_000u32 * t).into();
3046 let dust = 100u32 * d;
3047 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust))
3048 };
3049
3050 let init_code = vec![BENCH_INIT_CODE; i as usize];
3051 let _ = interpreter.memory.resize(0, init_code.len());
3052 let salt = U256::from(42u64);
3053 interpreter.memory.set_data(0, 0, init_code.len(), &init_code);
3054
3055 let _ = interpreter.stack.push(salt);
3057 let _ = interpreter.stack.push(U256::from(init_code.len()));
3058 let _ = interpreter.stack.push(U256::zero());
3059 let _ = interpreter.stack.push(value);
3060
3061 let result;
3062 #[block]
3063 {
3064 result = instructions::contract::create::<true, _>(&mut interpreter);
3065 }
3066
3067 assert!(result.is_continue());
3068 let addr = interpreter.stack.top().unwrap().into_address();
3069 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
3070 assert_eq!(Pallet::<T>::code(&addr).len(), revm::primitives::eip170::MAX_CODE_SIZE);
3071 assert_eq!(Pallet::<T>::evm_balance(&addr), value, "balance should hold {value:?}");
3072 Ok(())
3073 }
3074
3075 #[benchmark(pov_mode = Measured)]
3077 fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3078 let input = vec![0u8; n as usize];
3079 let mut call_setup = CallSetup::<T>::default();
3080 let (mut ext, _) = call_setup.ext();
3081
3082 let result;
3083 #[block]
3084 {
3085 result = run_builtin_precompile(
3086 &mut ext,
3087 H160::from_low_u64_be(2).as_fixed_bytes(),
3088 input.clone(),
3089 );
3090 }
3091 assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
3092 }
3093
3094 #[benchmark(pov_mode = Measured)]
3095 fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3096 let input = vec![0u8; n as usize];
3097 let mut call_setup = CallSetup::<T>::default();
3098 let (mut ext, _) = call_setup.ext();
3099
3100 let result;
3101 #[block]
3102 {
3103 result = run_builtin_precompile(
3104 &mut ext,
3105 H160::from_low_u64_be(4).as_fixed_bytes(),
3106 input.clone(),
3107 );
3108 }
3109 assert_eq!(input, result.unwrap().data);
3110 }
3111
3112 #[benchmark(pov_mode = Measured)]
3114 fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3115 use ripemd::Digest;
3116 let input = vec![0u8; n as usize];
3117 let mut call_setup = CallSetup::<T>::default();
3118 let (mut ext, _) = call_setup.ext();
3119
3120 let result;
3121 #[block]
3122 {
3123 result = run_builtin_precompile(
3124 &mut ext,
3125 H160::from_low_u64_be(3).as_fixed_bytes(),
3126 input.clone(),
3127 );
3128 }
3129 let mut expected = [0u8; 32];
3130 expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
3131
3132 assert_eq!(expected.to_vec(), result.unwrap().data);
3133 }
3134
3135 #[benchmark(pov_mode = Measured)]
3137 fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3138 build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
3139
3140 let result;
3141 #[block]
3142 {
3143 result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
3144 }
3145 assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
3146 assert_ok!(result);
3147 }
3148
3149 #[benchmark(pov_mode = Measured)]
3151 fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3152 let input = vec![0u8; n as usize];
3153 let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
3154 input: input.clone().into(),
3155 })
3156 .abi_encode();
3157
3158 let mut call_setup = CallSetup::<T>::default();
3159 let (mut ext, _) = call_setup.ext();
3160
3161 let result;
3162 #[block]
3163 {
3164 result = run_builtin_precompile(
3165 &mut ext,
3166 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
3167 input_bytes,
3168 );
3169 }
3170 let truth: [u8; 32] = sp_io::hashing::blake2_256(&input);
3171 let truth = FixedBytes::<32>::abi_encode(&truth);
3172 let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed");
3173
3174 let raw_data = result.unwrap().data;
3175 let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed");
3176 assert_eq!(truth, ret_hash);
3177 }
3178
3179 #[benchmark(pov_mode = Measured)]
3181 fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3182 let input = vec![0u8; n as usize];
3183 let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
3184 input: input.clone().into(),
3185 })
3186 .abi_encode();
3187
3188 let mut call_setup = CallSetup::<T>::default();
3189 let (mut ext, _) = call_setup.ext();
3190
3191 let result;
3192 #[block]
3193 {
3194 result = run_builtin_precompile(
3195 &mut ext,
3196 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
3197 input_bytes,
3198 );
3199 }
3200 let truth: [u8; 16] = sp_io::hashing::blake2_128(&input);
3201 let truth = FixedBytes::<16>::abi_encode(&truth);
3202 let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed");
3203
3204 let raw_data = result.unwrap().data;
3205 let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed");
3206 assert_eq!(truth, ret_hash);
3207 }
3208
3209 #[benchmark(pov_mode = Measured)]
3212 fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) {
3213 let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
3214 let message_len = message.len() as u32;
3215
3216 let key_type = sp_core::crypto::KeyTypeId(*b"code");
3217 let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
3218 let sig =
3219 sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
3220 let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
3221 let sig_len = sig.len() as u32;
3222
3223 build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
3224
3225 let result;
3226 #[block]
3227 {
3228 result = runtime.bench_sr25519_verify(
3229 memory.as_mut_slice(),
3230 0, sig_len, message_len, sig_len + pub_key.len() as u32, );
3235 }
3236
3237 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
3238 }
3239
3240 #[benchmark(pov_mode = Measured)]
3241 fn ecdsa_recover() {
3242 use hex_literal::hex;
3243 let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
3244 let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
3245 let mut call_setup = CallSetup::<T>::default();
3246 let (mut ext, _) = call_setup.ext();
3247
3248 let result;
3249
3250 #[block]
3251 {
3252 result =
3253 run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
3254 }
3255
3256 assert_eq!(result.unwrap().data, expected);
3257 }
3258
3259 #[benchmark(pov_mode = Measured)]
3260 fn p256_verify() {
3261 use hex_literal::hex;
3262 let input = hex!("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").to_vec();
3263 let expected = U256::one().to_big_endian();
3264 let mut call_setup = CallSetup::<T>::default();
3265 let (mut ext, _) = call_setup.ext();
3266
3267 let result;
3268
3269 #[block]
3270 {
3271 result = run_builtin_precompile(
3272 &mut ext,
3273 H160::from_low_u64_be(0x100).as_fixed_bytes(),
3274 input,
3275 );
3276 }
3277
3278 assert_eq!(result.unwrap().data, expected);
3279 }
3280
3281 #[benchmark(pov_mode = Measured)]
3282 fn bn128_add() {
3283 use hex_literal::hex;
3284 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
3285 let expected = hex!(
3286 "0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
3287 );
3288 let mut call_setup = CallSetup::<T>::default();
3289 let (mut ext, _) = call_setup.ext();
3290
3291 let result;
3292 #[block]
3293 {
3294 result =
3295 run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
3296 }
3297
3298 assert_eq!(result.unwrap().data, expected);
3299 }
3300
3301 #[benchmark(pov_mode = Measured)]
3302 fn bn128_mul() {
3303 use hex_literal::hex;
3304 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
3305 let expected = hex!(
3306 "0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
3307 );
3308 let mut call_setup = CallSetup::<T>::default();
3309 let (mut ext, _) = call_setup.ext();
3310
3311 let result;
3312 #[block]
3313 {
3314 result =
3315 run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
3316 }
3317
3318 assert_eq!(result.unwrap().data, expected);
3319 }
3320
3321 #[benchmark(pov_mode = Measured)]
3323 fn bn128_pairing(n: Linear<0, { 20 }>) {
3324 fn generate_random_ecpairs(n: usize) -> Vec<u8> {
3325 use bn::{AffineG1, AffineG2, Fr, G1, G2, Group};
3326 use rand::SeedableRng;
3327 use rand_pcg::Pcg64;
3328 let mut rng = Pcg64::seed_from_u64(1);
3329
3330 let mut buffer = vec![0u8; n * 192];
3331
3332 let mut write = |element: &bn::Fq, offset: &mut usize| {
3333 element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
3334 *offset += 32
3335 };
3336
3337 for i in 0..n {
3338 let mut offset = i * 192;
3339 let scalar = Fr::random(&mut rng);
3340
3341 let g1 = G1::one() * scalar;
3342 let g2 = G2::one() * scalar;
3343 let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
3344 let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
3345
3346 write(&a.x(), &mut offset);
3347 write(&a.y(), &mut offset);
3348 write(&b.x().imaginary(), &mut offset);
3349 write(&b.x().real(), &mut offset);
3350 write(&b.y().imaginary(), &mut offset);
3351 write(&b.y().real(), &mut offset);
3352 }
3353
3354 buffer
3355 }
3356
3357 let input = generate_random_ecpairs(n as usize);
3358 let mut call_setup = CallSetup::<T>::default();
3359 let (mut ext, _) = call_setup.ext();
3360
3361 let result;
3362 #[block]
3363 {
3364 result =
3365 run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
3366 }
3367 assert_ok!(result);
3368 }
3369
3370 #[benchmark(pov_mode = Measured)]
3372 fn blake2f(n: Linear<0, 1200>) {
3373 use hex_literal::hex;
3374 let input = hex!(
3375 "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
3376 );
3377 let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
3378 let mut call_setup = CallSetup::<T>::default();
3379 let (mut ext, _) = call_setup.ext();
3380
3381 let result;
3382 #[block]
3383 {
3384 result =
3385 run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
3386 }
3387 assert_ok!(result);
3388 }
3389
3390 #[benchmark(pov_mode = Measured)]
3394 fn seal_ecdsa_to_eth_address() {
3395 let key_type = sp_core::crypto::KeyTypeId(*b"code");
3396 let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
3397 build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
3398
3399 let result;
3400 #[block]
3401 {
3402 result = runtime.bench_ecdsa_to_eth_address(
3403 memory.as_mut_slice(),
3404 20, 0, );
3407 }
3408
3409 assert_ok!(result);
3410 assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
3411 }
3412
3413 #[benchmark(pov_mode = Measured)]
3415 fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
3416 let module = VmBinaryModule::evm_noop(r);
3417 let inputs = vec![];
3418
3419 let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
3420 let mut setup = CallSetup::<T>::new(module);
3421 let (mut ext, _) = setup.ext();
3422
3423 let result;
3424 #[block]
3425 {
3426 result = evm::call(code, &mut ext, inputs);
3427 }
3428
3429 assert!(result.is_ok());
3430 Ok(())
3431 }
3432
3433 #[benchmark(pov_mode = Measured)]
3435 fn evm_pop_opcode(r: Linear<0, EVM_STACK_LIMIT>) {
3436 let code = Bytecode::new_raw(vec![POP; r as usize].into());
3437 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3438 let (mut ext, _) = setup.ext();
3439 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3440 setup_stack(&mut interpreter, vec![U256::MAX; r as usize]);
3441
3442 evict_caches();
3443 let result;
3444 #[block]
3445 {
3446 result = evm::run_plain(&mut interpreter);
3447 }
3448
3449 let ControlFlow::Break(halt) = result;
3450 assert!(matches!(halt, Halt::Stop));
3451 assert_eq!(interpreter.stack.len(), 0);
3452 }
3453
3454 #[benchmark(pov_mode = Measured)]
3462 fn evm_div_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3463 use knuth_division_operands::DENOMINATORS;
3464
3465 let mut rng = Pcg64::seed_from_u64(1337);
3466 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3467
3468 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3469 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3470 let (mut ext, _) = setup.ext();
3471 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3472 setup_stack(&mut interpreter, operands);
3473
3474 evict_caches();
3475 let result;
3476 #[block]
3477 {
3478 result = evm::run_plain(&mut interpreter);
3479 }
3480
3481 let ControlFlow::Break(halt) = result;
3482 assert!(matches!(halt, Halt::Stop));
3483 assert_eq!(interpreter.stack.len(), 0);
3484 }
3485
3486 #[benchmark(pov_mode = Measured)]
3488 fn evm_div_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3489 use one_limb_division_operands::DENOMINATORS;
3490
3491 let mut rng = Pcg64::seed_from_u64(1337);
3492 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3493
3494 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3495 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3496 let (mut ext, _) = setup.ext();
3497 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3498 setup_stack(&mut interpreter, operands);
3499
3500 evict_caches();
3501 let result;
3502 #[block]
3503 {
3504 result = evm::run_plain(&mut interpreter);
3505 }
3506
3507 let ControlFlow::Break(halt) = result;
3508 assert!(matches!(halt, Halt::Stop));
3509 assert_eq!(interpreter.stack.len(), 0);
3510 }
3511
3512 #[benchmark(pov_mode = Measured)]
3514 fn evm_div_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3515 let mut rng = Pcg64::seed_from_u64(1337);
3516 let operands = (0..r).flat_map(|_| mixed_division_operands::unsigned_operands(&mut rng));
3517
3518 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3519 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3520 let (mut ext, _) = setup.ext();
3521 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3522 setup_stack(&mut interpreter, operands);
3523
3524 evict_caches();
3525 let result;
3526 #[block]
3527 {
3528 result = evm::run_plain(&mut interpreter);
3529 }
3530
3531 let ControlFlow::Break(halt) = result;
3532 assert!(matches!(halt, Halt::Stop));
3533 assert_eq!(interpreter.stack.len(), 0);
3534 }
3535
3536 #[benchmark(pov_mode = Measured)]
3538 fn evm_div_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3539 use knuth_division_operands::{DIV_FIXED_DIVISOR, NUMERATOR};
3540
3541 let operands = (0..r).flat_map(|_| [DIV_FIXED_DIVISOR, NUMERATOR]);
3542 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3543 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3544 let (mut ext, _) = setup.ext();
3545 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3546 setup_stack(&mut interpreter, operands);
3547
3548 evict_caches();
3549 let result;
3550 #[block]
3551 {
3552 result = evm::run_plain(&mut interpreter);
3553 }
3554
3555 let ControlFlow::Break(halt) = result;
3556 assert!(matches!(halt, Halt::Stop));
3557 assert_eq!(interpreter.stack.len(), 0);
3558 }
3559
3560 #[benchmark(pov_mode = Measured)]
3573 fn evm_sdiv_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3574 use knuth_division_operands::DENOMINATORS;
3575
3576 let mut rng = Pcg64::seed_from_u64(1337);
3577 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3578
3579 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3580 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3581 let (mut ext, _) = setup.ext();
3582 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3583 setup_stack(&mut interpreter, operands);
3584
3585 evict_caches();
3586 let result;
3587 #[block]
3588 {
3589 result = evm::run_plain(&mut interpreter);
3590 }
3591
3592 let ControlFlow::Break(halt) = result;
3593 assert!(matches!(halt, Halt::Stop));
3594 assert_eq!(interpreter.stack.len(), 0);
3595 }
3596
3597 #[benchmark(pov_mode = Measured)]
3599 fn evm_sdiv_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3600 use one_limb_division_operands::DENOMINATORS;
3601
3602 let mut rng = Pcg64::seed_from_u64(1337);
3603 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3604
3605 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3606 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3607 let (mut ext, _) = setup.ext();
3608 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3609 setup_stack(&mut interpreter, operands);
3610
3611 evict_caches();
3612 let result;
3613 #[block]
3614 {
3615 result = evm::run_plain(&mut interpreter);
3616 }
3617
3618 let ControlFlow::Break(halt) = result;
3619 assert!(matches!(halt, Halt::Stop));
3620 assert_eq!(interpreter.stack.len(), 0);
3621 }
3622
3623 #[benchmark(pov_mode = Measured)]
3625 fn evm_sdiv_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3626 let mut rng = Pcg64::seed_from_u64(1337);
3627 let operands = (0..r).flat_map(|_| mixed_division_operands::signed_operands(&mut rng));
3628
3629 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3630 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3631 let (mut ext, _) = setup.ext();
3632 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3633 setup_stack(&mut interpreter, operands);
3634
3635 evict_caches();
3636 let result;
3637 #[block]
3638 {
3639 result = evm::run_plain(&mut interpreter);
3640 }
3641
3642 let ControlFlow::Break(halt) = result;
3643 assert!(matches!(halt, Halt::Stop));
3644 assert_eq!(interpreter.stack.len(), 0);
3645 }
3646
3647 #[benchmark(pov_mode = Measured)]
3649 fn evm_sdiv_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3650 use knuth_division_operands::{SDIV_FIXED_DIVISOR, fixed_signed_operands};
3651
3652 let mut rng = Pcg64::seed_from_u64(1337);
3653 let operands = (0..r).flat_map(|_| fixed_signed_operands(&mut rng, SDIV_FIXED_DIVISOR));
3654
3655 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3656 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3657 let (mut ext, _) = setup.ext();
3658 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3659 setup_stack(&mut interpreter, operands);
3660
3661 evict_caches();
3662 let result;
3663 #[block]
3664 {
3665 result = evm::run_plain(&mut interpreter);
3666 }
3667
3668 let ControlFlow::Break(halt) = result;
3669 assert!(matches!(halt, Halt::Stop));
3670 assert_eq!(interpreter.stack.len(), 0);
3671 }
3672
3673 #[benchmark(pov_mode = Measured)]
3681 fn evm_mod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3682 use knuth_division_operands::DENOMINATORS;
3683
3684 let mut rng = Pcg64::seed_from_u64(1337);
3685 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3686
3687 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3688 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3689 let (mut ext, _) = setup.ext();
3690 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3691 setup_stack(&mut interpreter, operands);
3692
3693 evict_caches();
3694 let result;
3695 #[block]
3696 {
3697 result = evm::run_plain(&mut interpreter);
3698 }
3699
3700 let ControlFlow::Break(halt) = result;
3701 assert!(matches!(halt, Halt::Stop));
3702 assert_eq!(interpreter.stack.len(), 0);
3703 }
3704
3705 #[benchmark(pov_mode = Measured)]
3707 fn evm_mod_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3708 use one_limb_division_operands::DENOMINATORS;
3709
3710 let mut rng = Pcg64::seed_from_u64(1337);
3711 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3712
3713 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3714 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3715 let (mut ext, _) = setup.ext();
3716 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3717 setup_stack(&mut interpreter, operands);
3718
3719 evict_caches();
3720 let result;
3721 #[block]
3722 {
3723 result = evm::run_plain(&mut interpreter);
3724 }
3725
3726 let ControlFlow::Break(halt) = result;
3727 assert!(matches!(halt, Halt::Stop));
3728 assert_eq!(interpreter.stack.len(), 0);
3729 }
3730
3731 #[benchmark(pov_mode = Measured)]
3733 fn evm_mod_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3734 let mut rng = Pcg64::seed_from_u64(1337);
3735 let operands = (0..r).flat_map(|_| mixed_division_operands::unsigned_operands(&mut rng));
3736
3737 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3738 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3739 let (mut ext, _) = setup.ext();
3740 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3741 setup_stack(&mut interpreter, operands);
3742
3743 evict_caches();
3744 let result;
3745 #[block]
3746 {
3747 result = evm::run_plain(&mut interpreter);
3748 }
3749
3750 let ControlFlow::Break(halt) = result;
3751 assert!(matches!(halt, Halt::Stop));
3752 assert_eq!(interpreter.stack.len(), 0);
3753 }
3754
3755 #[benchmark(pov_mode = Measured)]
3757 fn evm_mod_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3758 use knuth_division_operands::{MOD_FIXED_DIVISOR, NUMERATOR};
3759
3760 let operands = (0..r).flat_map(|_| [MOD_FIXED_DIVISOR, NUMERATOR]);
3761 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3762 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3763 let (mut ext, _) = setup.ext();
3764 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3765 setup_stack(&mut interpreter, operands);
3766
3767 evict_caches();
3768 let result;
3769 #[block]
3770 {
3771 result = evm::run_plain(&mut interpreter);
3772 }
3773
3774 let ControlFlow::Break(halt) = result;
3775 assert!(matches!(halt, Halt::Stop));
3776 assert_eq!(interpreter.stack.len(), 0);
3777 }
3778
3779 #[benchmark(pov_mode = Measured)]
3789 fn evm_smod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3790 use knuth_division_operands::DENOMINATORS;
3791
3792 let mut rng = Pcg64::seed_from_u64(1337);
3793 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3794
3795 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3796 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3797 let (mut ext, _) = setup.ext();
3798 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3799 setup_stack(&mut interpreter, operands);
3800
3801 evict_caches();
3802 let result;
3803 #[block]
3804 {
3805 result = evm::run_plain(&mut interpreter);
3806 }
3807
3808 let ControlFlow::Break(halt) = result;
3809 assert!(matches!(halt, Halt::Stop));
3810 assert_eq!(interpreter.stack.len(), 0);
3811 }
3812
3813 #[benchmark(pov_mode = Measured)]
3815 fn evm_smod_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3816 use one_limb_division_operands::DENOMINATORS;
3817
3818 let mut rng = Pcg64::seed_from_u64(1337);
3819 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3820
3821 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3822 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3823 let (mut ext, _) = setup.ext();
3824 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3825 setup_stack(&mut interpreter, operands);
3826
3827 evict_caches();
3828 let result;
3829 #[block]
3830 {
3831 result = evm::run_plain(&mut interpreter);
3832 }
3833
3834 let ControlFlow::Break(halt) = result;
3835 assert!(matches!(halt, Halt::Stop));
3836 assert_eq!(interpreter.stack.len(), 0);
3837 }
3838
3839 #[benchmark(pov_mode = Measured)]
3841 fn evm_smod_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3842 let mut rng = Pcg64::seed_from_u64(1337);
3843 let operands = (0..r).flat_map(|_| mixed_division_operands::signed_operands(&mut rng));
3844
3845 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3846 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3847 let (mut ext, _) = setup.ext();
3848 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3849 setup_stack(&mut interpreter, operands);
3850
3851 evict_caches();
3852 let result;
3853 #[block]
3854 {
3855 result = evm::run_plain(&mut interpreter);
3856 }
3857
3858 let ControlFlow::Break(halt) = result;
3859 assert!(matches!(halt, Halt::Stop));
3860 assert_eq!(interpreter.stack.len(), 0);
3861 }
3862
3863 #[benchmark(pov_mode = Measured)]
3865 fn evm_smod_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3866 use knuth_division_operands::{SMOD_FIXED_DIVISOR, fixed_signed_operands};
3867
3868 let mut rng = Pcg64::seed_from_u64(1337);
3869 let operands = (0..r).flat_map(|_| fixed_signed_operands(&mut rng, SMOD_FIXED_DIVISOR));
3870
3871 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3872 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3873 let (mut ext, _) = setup.ext();
3874 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3875 setup_stack(&mut interpreter, operands);
3876
3877 evict_caches();
3878 let result;
3879 #[block]
3880 {
3881 result = evm::run_plain(&mut interpreter);
3882 }
3883
3884 let ControlFlow::Break(halt) = result;
3885 assert!(matches!(halt, Halt::Stop));
3886 assert_eq!(interpreter.stack.len(), 0);
3887 }
3888
3889 #[benchmark(pov_mode = Measured)]
3900 fn evm_addmod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3901 use knuth_division_operands::DENOMINATORS;
3902
3903 let mut rng = Pcg64::seed_from_u64(1337);
3904 let operands = (0..r).flat_map(|_| {
3905 let [modulus, numerator] = unsigned_division_operands(&mut rng, &DENOMINATORS);
3906 [modulus, numerator, numerator]
3907 });
3908
3909 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3910 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3911 let (mut ext, _) = setup.ext();
3912 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3913 setup_stack(&mut interpreter, operands);
3914
3915 evict_caches();
3916 let result;
3917 #[block]
3918 {
3919 result = evm::run_plain(&mut interpreter);
3920 }
3921
3922 let ControlFlow::Break(halt) = result;
3923 assert!(matches!(halt, Halt::Stop));
3924 assert_eq!(interpreter.stack.len(), 0);
3925 }
3926
3927 #[benchmark(pov_mode = Measured)]
3929 fn evm_addmod_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3930 use one_limb_division_operands::DENOMINATORS;
3931
3932 let mut rng = Pcg64::seed_from_u64(1337);
3933 let operands = (0..r).flat_map(|_| {
3934 let [modulus, numerator] = unsigned_division_operands(&mut rng, &DENOMINATORS);
3935 [modulus, numerator, numerator]
3936 });
3937
3938 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3939 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3940 let (mut ext, _) = setup.ext();
3941 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3942 setup_stack(&mut interpreter, operands);
3943
3944 evict_caches();
3945 let result;
3946 #[block]
3947 {
3948 result = evm::run_plain(&mut interpreter);
3949 }
3950
3951 let ControlFlow::Break(halt) = result;
3952 assert!(matches!(halt, Halt::Stop));
3953 assert_eq!(interpreter.stack.len(), 0);
3954 }
3955
3956 #[benchmark(pov_mode = Measured)]
3958 fn evm_addmod_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3959 let mut rng = Pcg64::seed_from_u64(1337);
3960 let operands = (0..r).flat_map(|_| {
3961 let [modulus, numerator] = mixed_division_operands::unsigned_operands(&mut rng);
3962 [modulus, numerator, numerator]
3963 });
3964
3965 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3966 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3967 let (mut ext, _) = setup.ext();
3968 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3969 setup_stack(&mut interpreter, operands);
3970
3971 evict_caches();
3972 let result;
3973 #[block]
3974 {
3975 result = evm::run_plain(&mut interpreter);
3976 }
3977
3978 let ControlFlow::Break(halt) = result;
3979 assert!(matches!(halt, Halt::Stop));
3980 assert_eq!(interpreter.stack.len(), 0);
3981 }
3982
3983 #[benchmark(pov_mode = Measured)]
3985 fn evm_addmod_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3986 use knuth_division_operands::{ADDMOD_FIXED_DIVISOR, NUMERATOR};
3987
3988 let operands = (0..r).flat_map(|_| [ADDMOD_FIXED_DIVISOR, NUMERATOR, NUMERATOR]);
3989 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3990 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3991 let (mut ext, _) = setup.ext();
3992 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3993 setup_stack(&mut interpreter, operands);
3994
3995 evict_caches();
3996 let result;
3997 #[block]
3998 {
3999 result = evm::run_plain(&mut interpreter);
4000 }
4001
4002 let ControlFlow::Break(halt) = result;
4003 assert!(matches!(halt, Halt::Stop));
4004 assert_eq!(interpreter.stack.len(), 0);
4005 }
4006
4007 #[benchmark(pov_mode = Measured)]
4022 fn evm_mulmod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
4023 let top_bit = U256::one() << 255;
4024 let reciprocal_adjustments = |modulus: U256| {
4028 let top = (modulus << modulus.leading_zeros() as usize) >> 128;
4029 let (high, low) = (top.low_u128() >> 64, u128::from(top.low_u64()));
4030 [low > u128::MAX % high, (U256::MAX >> 64) / top < U256::from((u128::MAX - low) / high)]
4031 };
4032 let mut rng = Pcg64::seed_from_u64(1337);
4033 let operands = (0..r).flat_map(|_| {
4034 let limbs = if rng.gen_bool(0.5) { 3 } else { 4 };
4035 let adjustments = [rng.gen_bool(0.5), rng.gen_bool(0.5)];
4036 let modulus = loop {
4037 let modulus = (U256(rng.r#gen()) | top_bit) >> (2 + 64 * (4 - limbs));
4038 if reciprocal_adjustments(modulus) == adjustments {
4039 break modulus;
4040 }
4041 };
4042 let a = U256(rng.r#gen()) | top_bit | U256::one();
4043 let mut b = U256(rng.r#gen()) | top_bit | U256::one();
4044 for limb in 1..=2 {
4045 if rng.gen_bool(0.5) {
4046 b.0[limb] = 0;
4047 }
4048 }
4049 [modulus, b, a]
4050 });
4051
4052 let code = Bytecode::new_raw([MULMOD, POP].repeat(r as usize).into());
4053 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
4054 let (mut ext, _) = setup.ext();
4055 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
4056 setup_stack(&mut interpreter, operands);
4057
4058 evict_caches();
4059 let result;
4060 #[block]
4061 {
4062 result = evm::run_plain(&mut interpreter);
4063 }
4064
4065 let ControlFlow::Break(halt) = result;
4066 assert!(matches!(halt, Halt::Stop));
4067 assert_eq!(interpreter.stack.len(), 0);
4068 }
4069
4070 #[benchmark(pov_mode = Ignored)]
4075 fn instr(r: Linear<0, 10_000>) {
4076 use rand::{SeedableRng, seq::SliceRandom};
4077 use rand_pcg::Pcg64;
4078
4079 const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
4081
4082 const CACHE_LINE_SIZE: u64 = 64;
4084
4085 const MISALIGNMENT: u64 = 60;
4087
4088 const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
4091
4092 assert!(
4093 u64::from(r) <= NUM_ADDRESSES / 2,
4094 "If we do too many iterations we run into the risk of loading from warm cache lines",
4095 );
4096
4097 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
4098 let (mut ext, module) = setup.ext();
4099 let mut prepared =
4100 CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
4101
4102 assert!(
4103 u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
4104 "aux data base must be cache aligned"
4105 );
4106
4107 let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
4109
4110 let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
4114 for i in 1..NUM_ADDRESSES {
4115 let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
4116 addresses.push(addr);
4117 }
4118 let mut rng = Pcg64::seed_from_u64(1337);
4119 addresses.shuffle(&mut rng);
4120
4121 let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
4123 for address in addresses {
4124 memory.extend_from_slice(&address);
4125 memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
4126 }
4127
4128 prepared
4131 .setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
4132 .unwrap();
4133
4134 #[block]
4135 {
4136 prepared.call().unwrap();
4137 }
4138 }
4139
4140 #[benchmark(pov_mode = Ignored)]
4141 fn instr_empty_loop(r: Linear<0, 10_000>) {
4142 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
4143 let (mut ext, module) = setup.ext();
4144 let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
4145 prepared.setup_aux_data(&[], 0, r.into()).unwrap();
4146
4147 #[block]
4148 {
4149 prepared.call().unwrap();
4150 }
4151 }
4152
4153 #[benchmark(pov_mode = Measured)]
4154 fn extcodecopy(n: Linear<1_000, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
4155 let mut setup = CallSetup::<T>::new(VmBinaryModule::dummy());
4157 let target = Contract::<T>::with_index(1, VmBinaryModule::sized(n), vec![])?;
4159
4160 let (mut ext, _) = setup.ext();
4161 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
4162
4163 let _ = interpreter.stack.push(U256::from(n));
4165 let _ = interpreter.stack.push(U256::from(0u32));
4166 let _ = interpreter.stack.push(U256::from(0u32));
4167 let _ = interpreter.stack.push(target.address);
4168
4169 let result;
4170 #[block]
4171 {
4172 result = instructions::host::extcodecopy(&mut interpreter);
4173 }
4174
4175 assert!(result.is_continue());
4176 assert_eq!(
4177 *interpreter.memory.slice(0..n as usize),
4178 PristineCode::<T>::get(target.info()?.code_hash).unwrap()[0..n as usize],
4179 "Memory should contain the target contract's code after extcodecopy"
4180 );
4181
4182 Ok(())
4183 }
4184
4185 #[benchmark]
4186 fn v1_migration_step() {
4187 use crate::migrations::v1;
4188 let addr = H160::from([1u8; 20]);
4189 let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
4190
4191 v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
4192 let mut meter = WeightMeter::new();
4193 assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
4194
4195 #[block]
4196 {
4197 v1::Migration::<T>::step(None, &mut meter).unwrap();
4198 }
4199
4200 assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
4201 assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
4202
4203 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
4205 }
4206
4207 #[benchmark]
4208 fn v2_migration_step() {
4209 use crate::migrations::v2;
4210 let code_hash = H256::from([0; 32]);
4211 let old_code_info = v2::Migration::<T>::create_old_code_info(
4212 whitelisted_caller(),
4213 1000u32.into(),
4214 1,
4215 100,
4216 0,
4217 );
4218 v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
4219 let mut meter = WeightMeter::new();
4220
4221 #[block]
4222 {
4223 v2::Migration::<T>::step(None, &mut meter).unwrap();
4224 }
4225
4226 v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
4227
4228 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
4230 }
4231
4232 #[benchmark]
4233 fn v3_migration_step() {
4234 use crate::migrations::v3;
4235 let _ = frame_system::Account::<T>::clear(u32::MAX, None);
4237
4238 let account = account::<T::AccountId>("target", 0, 0);
4239 T::Currency::mint_into(&account, Pallet::<T>::min_balance())
4240 .expect("should mint into account");
4241
4242 let addr = T::AddressMapper::to_address(&account);
4244 crate::OriginalAccount::<T>::remove(addr);
4245
4246 assert!(!T::AddressMapper::is_mapped(&account));
4247 let mut meter = WeightMeter::new();
4248
4249 #[block]
4250 {
4251 v3::Migration::<T>::step(None, &mut meter).unwrap();
4252 }
4253
4254 assert!(T::AddressMapper::is_mapped(&account));
4255
4256 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v3_migration_step() * 2);
4258 }
4259
4260 #[benchmark]
4265 fn v4_code_upload_step() {
4266 use crate::migrations::v4;
4267
4268 let _ = CodeInfoOf::<T>::clear(u32::MAX, None);
4269
4270 let owner: T::AccountId = whitelisted_caller();
4271 let deposit: BalanceOf<T> = 1_000u32.into();
4272
4273 let pallet_account = Pallet::<T>::account_id();
4274 T::Currency::mint_into(&pallet_account, Pallet::<T>::min_balance()).unwrap();
4275 T::Currency::mint_into(&pallet_account, deposit).unwrap();
4276 T::Currency::hold(&HoldReason::CodeUploadDepositReserve.into(), &pallet_account, deposit)
4277 .unwrap();
4278
4279 CodeInfoOf::<T>::insert(
4280 H256::from([1u8; 32]),
4281 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
4282 );
4283 CodeInfoOf::<T>::insert(
4284 H256::from([2u8; 32]),
4285 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
4286 );
4287
4288 let first = match v4::Migration::<T>::step_once(None) {
4289 Some(v4::Cursor::CodeUpload(h)) => h,
4290 other => panic!("expected CodeUpload cursor, got {other:?}"),
4291 };
4292 let cursor = Some(v4::Cursor::CodeUpload(first));
4293
4294 #[block]
4295 {
4296 let _ = v4::Migration::<T>::step_once(cursor);
4297 }
4298
4299 assert_eq!(
4300 NativeDepositOf::<T>::get(&pallet_account, &owner),
4301 deposit + deposit,
4302 "both code uploads credited to owner",
4303 );
4304 }
4305
4306 #[benchmark]
4311 fn v4_contract_step() {
4312 use crate::migrations::v4;
4313
4314 let _ = AccountInfoOf::<T>::clear(u32::MAX, None);
4315
4316 let code_hash = H256::from([0u8; 32]);
4317 let deposit: BalanceOf<T> = 1_000u32.into();
4318
4319 for byte in [0x41u8, 0x42u8] {
4320 let addr = H160::from([byte; 20]);
4321 let contract_account = T::AddressMapper::to_account_id(&addr);
4322 let info =
4323 ContractInfo::<T>::new(&addr, 1u32.into(), code_hash).expect("fresh contract info");
4324 AccountInfoOf::<T>::insert(
4325 addr,
4326 crate::storage::AccountInfo::<T> {
4327 account_type: crate::storage::AccountType::Contract(info),
4328 dust: 0,
4329 },
4330 );
4331 T::Currency::mint_into(&contract_account, Pallet::<T>::min_balance()).unwrap();
4332 T::Currency::mint_into(&contract_account, deposit).unwrap();
4333 T::Currency::hold(
4334 &HoldReason::StorageDepositReserve.into(),
4335 &contract_account,
4336 deposit,
4337 )
4338 .unwrap();
4339 }
4340
4341 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::Contract(None))) {
4342 Some(v4::Cursor::Contract(Some(addr))) => addr,
4343 other => panic!("expected Contract cursor, got {other:?}"),
4344 };
4345 let cursor = Some(v4::Cursor::Contract(Some(first)));
4346
4347 #[block]
4348 {
4349 let _ = v4::Migration::<T>::step_once(cursor);
4350 }
4351
4352 if T::Deposit::SUPPORTS_PGAS {
4355 for byte in [0x41u8, 0x42u8] {
4356 let addr = H160::from([byte; 20]);
4357 let contract_account = T::AddressMapper::to_account_id(&addr);
4358 assert_eq!(
4359 T::Currency::balance_on_hold(
4360 &HoldReason::StorageDepositReserve.into(),
4361 &contract_account,
4362 ),
4363 0u32.into(),
4364 "native storage deposit burned for {addr:?}",
4365 );
4366 }
4367 }
4368 }
4369
4370 #[benchmark]
4376 fn v4_deletion_queue_step() {
4377 use crate::migrations::v4;
4378
4379 let _ = v4::old::DeletionQueue::<T>::clear(u32::MAX, None);
4380
4381 let trie_a: TrieId = vec![0xAAu8; 16].try_into().unwrap();
4382 let trie_b: TrieId = vec![0xBBu8; 24].try_into().unwrap();
4383 v4::old::DeletionQueue::<T>::insert(0u32, trie_a);
4384 v4::old::DeletionQueue::<T>::insert(1u32, trie_b);
4385
4386 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::DeletionQueue(None))) {
4387 Some(v4::Cursor::DeletionQueue(Some(key))) => key,
4388 other => panic!("expected DeletionQueue cursor, got {other:?}"),
4389 };
4390 let cursor = Some(v4::Cursor::DeletionQueue(Some(first)));
4391
4392 #[block]
4393 {
4394 let _ = v4::Migration::<T>::step_once(cursor);
4395 }
4396
4397 assert!(
4398 DeletionQueue::<T>::get(0u32).is_some() && DeletionQueue::<T>::get(1u32).is_some(),
4399 "both legacy entries rewritten into the new format",
4400 );
4401 }
4402
4403 fn create_test_signer<T: Config>() -> (T::AccountId, SigningKey, H160) {
4405 use hex_literal::hex;
4406 let signer_account_id = hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
4408 let signer_priv_key =
4409 hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133");
4410
4411 let signer_key = SigningKey::from_bytes(&signer_priv_key.into()).expect("valid key");
4412
4413 let signer_address = H160::from_slice(&signer_account_id);
4414 let signer_caller = T::AddressMapper::to_fallback_account_id(&signer_address);
4415
4416 (signer_caller, signer_key, signer_address)
4417 }
4418
4419 fn create_signed_transaction<T: Config>(
4421 signer_key: &SigningKey,
4422 target_address: H160,
4423 value: U256,
4424 input_data: Vec<u8>,
4425 ) -> Vec<u8> {
4426 let unsigned_tx: TransactionUnsigned = TransactionLegacyUnsigned {
4427 to: Some(target_address),
4428 value,
4429 chain_id: Some(T::ChainId::get().into()),
4430 input: input_data.into(),
4431 ..Default::default()
4432 }
4433 .into();
4434
4435 let hashed_payload = sp_io::hashing::keccak_256(&unsigned_tx.unsigned_payload());
4436 let (signature, recovery_id) =
4437 signer_key.sign_prehash_recoverable(&hashed_payload).expect("signing success");
4438
4439 let mut sig_bytes = [0u8; 65];
4440 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
4441 sig_bytes[64] = recovery_id.to_byte();
4442
4443 let signed_tx = unsigned_tx.with_signature(sig_bytes);
4444
4445 signed_tx.signed_payload()
4446 }
4447
4448 fn setup_finalize_block_benchmark<T>()
4450 -> Result<(Contract<T>, BalanceOf<T>, U256, SigningKey, BlockNumberFor<T>), BenchmarkError>
4451 where
4452 BalanceOf<T>: Into<U256> + TryFrom<U256>,
4453 T: Config,
4454 MomentOf<T>: Into<U256>,
4455 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
4456 {
4457 let (signer_caller, signer_key, _signer_address) = create_test_signer::<T>();
4459 whitelist_account!(signer_caller);
4460
4461 let instance =
4463 Contract::<T>::with_caller(signer_caller.clone(), VmBinaryModule::dummy(), vec![])?;
4464 let storage_deposit = default_deposit_limit::<T>();
4465 let value = Pallet::<T>::min_balance();
4466 let evm_value =
4467 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, 0));
4468
4469 let current_block = BlockNumberFor::<T>::from(1u32);
4471 frame_system::Pallet::<T>::set_block_number(current_block);
4472
4473 Pallet::<T>::clear_outside_frame_logs();
4477
4478 Ok((instance, storage_deposit, evm_value, signer_key, current_block))
4479 }
4480
4481 #[benchmark(pov_mode = Measured)]
4498 fn on_finalize_per_transaction(n: Linear<0, 200>) -> Result<(), BenchmarkError> {
4499 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4500 setup_finalize_block_benchmark::<T>()?;
4501
4502 let fixed_payload_size = 100usize;
4504
4505 if n > 0 {
4507 let _ = Pallet::<T>::on_initialize(current_block);
4509
4510 let input_data = vec![0x42u8; fixed_payload_size];
4512 let receipt_gas_info = ReceiptGasInfo {
4513 gas_used: U256::from(1_000_000),
4514 effective_gas_price: Pallet::<T>::evm_base_fee(),
4515 };
4516
4517 for _ in 0..n {
4518 let signed_transaction = create_signed_transaction::<T>(
4520 &signer_key,
4521 instance.address,
4522 evm_value,
4523 input_data.clone(),
4524 );
4525
4526 let _ = block_storage::bench_with_ethereum_context(|| {
4528 let (encoded_logs, bloom) =
4529 block_storage::get_receipt_details::<T>().unwrap_or_default();
4530
4531 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4532 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4533
4534 block_builder.process_transaction(
4535 signed_transaction,
4536 true,
4537 receipt_gas_info.clone(),
4538 encoded_logs,
4539 bloom,
4540 );
4541
4542 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4543 });
4544 }
4545 }
4546
4547 #[block]
4548 {
4549 let _ = Pallet::<T>::on_finalize(current_block);
4551 }
4552
4553 assert_eq!(Pallet::<T>::eth_block().transactions.len(), n as usize);
4555
4556 Ok(())
4557 }
4558
4559 #[benchmark(pov_mode = Measured)]
4576 fn on_finalize_per_transaction_data(d: Linear<0, 1000>) -> Result<(), BenchmarkError> {
4577 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4578 setup_finalize_block_benchmark::<T>()?;
4579
4580 let fixed_tx_count = 10u32;
4582
4583 let _ = Pallet::<T>::on_initialize(current_block);
4585
4586 let input_data = vec![0x42u8; d as usize];
4588 let receipt_gas_info = ReceiptGasInfo {
4589 gas_used: U256::from(1_000_000),
4590 effective_gas_price: Pallet::<T>::evm_base_fee(),
4591 };
4592
4593 for _ in 0..fixed_tx_count {
4594 let signed_transaction = create_signed_transaction::<T>(
4596 &signer_key,
4597 instance.address,
4598 evm_value,
4599 input_data.clone(),
4600 );
4601
4602 let _ = block_storage::bench_with_ethereum_context(|| {
4604 let (encoded_logs, bloom) =
4605 block_storage::get_receipt_details::<T>().unwrap_or_default();
4606
4607 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4608 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4609
4610 block_builder.process_transaction(
4611 signed_transaction,
4612 true,
4613 receipt_gas_info.clone(),
4614 encoded_logs,
4615 bloom,
4616 );
4617
4618 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4619 });
4620 }
4621
4622 #[block]
4623 {
4624 let _ = Pallet::<T>::on_finalize(current_block);
4626 }
4627
4628 assert_eq!(Pallet::<T>::eth_block().transactions.len(), fixed_tx_count as usize);
4630
4631 Ok(())
4632 }
4633
4634 #[benchmark(pov_mode = Measured)]
4652 fn on_finalize_per_event(e: Linear<0, 100>) -> Result<(), BenchmarkError> {
4653 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4654 setup_finalize_block_benchmark::<T>()?;
4655
4656 let input_data = vec![0x42u8; 100];
4658 let signed_transaction = create_signed_transaction::<T>(
4659 &signer_key,
4660 instance.address,
4661 evm_value,
4662 input_data.clone(),
4663 );
4664
4665 let receipt_gas_info = ReceiptGasInfo {
4666 gas_used: U256::from(1_000_000),
4667 effective_gas_price: Pallet::<T>::evm_base_fee(),
4668 };
4669
4670 let _ = block_storage::bench_with_ethereum_context(|| {
4672 for _ in 0..e {
4676 block_storage::capture_frame_log::<T>(&instance.address, &vec![], &vec![]);
4677 }
4678
4679 let (encoded_logs, bloom) =
4680 block_storage::get_receipt_details::<T>().unwrap_or_default();
4681
4682 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4683 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4684
4685 block_builder.process_transaction(
4686 signed_transaction,
4687 true,
4688 receipt_gas_info.clone(),
4689 encoded_logs,
4690 bloom,
4691 );
4692
4693 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4694 });
4695
4696 #[block]
4697 {
4698 let _ = Pallet::<T>::on_initialize(current_block);
4700
4701 let _ = Pallet::<T>::on_finalize(current_block);
4703 }
4704
4705 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
4708
4709 Ok(())
4710 }
4711
4712 #[benchmark(pov_mode = Measured)]
4721 fn on_finalize_per_event_data(d: Linear<0, 16384>) -> Result<(), BenchmarkError> {
4722 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4723 setup_finalize_block_benchmark::<T>()?;
4724
4725 let input_data = vec![0x42u8; 100];
4727 let signed_transaction = create_signed_transaction::<T>(
4728 &signer_key,
4729 instance.address,
4730 evm_value,
4731 input_data.clone(),
4732 );
4733
4734 let receipt_gas_info = ReceiptGasInfo {
4735 gas_used: U256::from(1_000_000),
4736 effective_gas_price: Pallet::<T>::evm_base_fee(),
4737 };
4738
4739 let (event_data, topics) = if d < 32 {
4741 (vec![0x42u8; d as usize], vec![])
4743 } else {
4744 let num_topics = core::cmp::min(limits::NUM_EVENT_TOPICS, d / 32);
4746 let topic_bytes_used = num_topics * 32;
4747 let data_bytes_remaining = d - topic_bytes_used;
4748
4749 let mut topics = Vec::new();
4751 for topic_index in 0..num_topics {
4752 let topic_data = [topic_index as u8; 32];
4753 topics.push(H256::from(topic_data));
4754 }
4755
4756 let event_data = vec![0x42u8; data_bytes_remaining as usize];
4758
4759 (event_data, topics)
4760 };
4761
4762 let _ = block_storage::bench_with_ethereum_context(|| {
4764 block_storage::capture_frame_log::<T>(&instance.address, &event_data, &topics);
4769
4770 let (encoded_logs, bloom) =
4771 block_storage::get_receipt_details::<T>().unwrap_or_default();
4772
4773 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4774 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4775
4776 block_builder.process_transaction(
4777 signed_transaction,
4778 true,
4779 receipt_gas_info,
4780 encoded_logs,
4781 bloom,
4782 );
4783
4784 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4785 });
4786
4787 #[block]
4788 {
4789 let _ = Pallet::<T>::on_initialize(current_block);
4791
4792 let _ = Pallet::<T>::on_finalize(current_block);
4794 }
4795
4796 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
4799
4800 Ok(())
4801 }
4802
4803 #[benchmark(pov_mode = Measured)]
4814 fn outside_frame_log(n: Linear<0, 100>) -> Result<(), BenchmarkError> {
4815 let (instance, _storage_deposit, _evm_value, _signer_key, current_block) =
4816 setup_finalize_block_benchmark::<T>()?;
4817
4818 let _ = Pallet::<T>::on_initialize(current_block);
4820
4821 let topics =
4823 vec![H256::repeat_byte(0x11), H256::repeat_byte(0x22), H256::repeat_byte(0x33)];
4824 let data = vec![0x44u8; 32];
4825
4826 for _ in 0..n {
4829 Pallet::<T>::bench_buffer_outside_frame_log(
4830 instance.address,
4831 topics.clone(),
4832 data.clone(),
4833 );
4834 }
4835
4836 #[block]
4837 {
4838 let _ = Pallet::<T>::on_finalize(current_block);
4839 }
4840
4841 assert_eq!(Pallet::<T>::eth_block().transactions.len(), (n > 0) as usize);
4843
4844 Ok(())
4845 }
4846
4847 #[benchmark(pov_mode = Measured)]
4856 fn outside_frame_log_data(d: Linear<0, { limits::EVENT_BYTES }>) -> Result<(), BenchmarkError> {
4857 let (instance, _storage_deposit, _evm_value, _signer_key, current_block) =
4858 setup_finalize_block_benchmark::<T>()?;
4859
4860 let _ = Pallet::<T>::on_initialize(current_block);
4861
4862 let topics =
4863 vec![H256::repeat_byte(0x11), H256::repeat_byte(0x22), H256::repeat_byte(0x33)];
4864 let data = vec![0x44u8; d as usize];
4865
4866 Pallet::<T>::bench_buffer_outside_frame_log(instance.address, topics, data);
4867
4868 #[block]
4869 {
4870 let _ = Pallet::<T>::on_finalize(current_block);
4871 }
4872
4873 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
4874
4875 Ok(())
4876 }
4877
4878 impl_benchmark_test_suite!(
4879 Contracts,
4880 crate::tests::ExtBuilder::default().build(),
4881 crate::tests::Test,
4882 );
4883}