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)]
1734 fn seal_terminate_logic() -> Result<(), BenchmarkError> {
1735 let caller = whitelisted_caller();
1736 let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1737 T::AddressMapper::map_no_deposit_unchecked(&beneficiary)?;
1738
1739 build_runtime!(_runtime, instance, _memory: [vec![0u8; 0], ]);
1740 let code_hash = instance.info()?.code_hash;
1741
1742 assert!(PristineCode::<T>::get(code_hash).is_some());
1743
1744 T::Currency::set_balance(&instance.account_id, Pallet::<T>::min_balance() * 10u32.into());
1745
1746 let storage_deposit = T::Currency::balance_on_hold(
1747 &HoldReason::StorageDepositReserve.into(),
1748 &instance.account_id,
1749 );
1750 NativeDepositOf::<T>::insert(&instance.account_id, &caller, storage_deposit);
1751
1752 let mut transaction_meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
1753 weight_limit: Default::default(),
1754 deposit_limit: BalanceOf::<T>::max_value(),
1755 })
1756 .unwrap();
1757 let exec_config = ExecConfig::new_substrate_tx();
1758 let contract_account = &instance.account_id;
1759 let origin = &ExecOrigin::from_account_id(caller);
1760 let beneficiary_clone = beneficiary.clone();
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 only_if_same_tx,
1773 );
1774 }
1775 result.unwrap();
1776
1777 assert!(PristineCode::<T>::get(code_hash).is_none());
1779
1780 let balance = <T as Config>::Currency::total_balance(&instance.account_id);
1782 assert_eq!(balance, 0u32.into());
1783
1784 let balance = <T as Config>::Currency::balance(&beneficiary);
1786 assert_eq!(balance, Pallet::<T>::min_balance() + Pallet::<T>::min_balance() * 9u32.into());
1787
1788 Ok(())
1789 }
1790
1791 #[benchmark(pov_mode = Measured)]
1795 fn seal_deposit_event(
1796 t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>,
1797 n: Linear<0, { limits::EVENT_BYTES }>,
1798 ) {
1799 let num_topic = t as u32;
1800 let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::<Vec<_>>();
1801 let topics_data =
1802 topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::<Vec<u8>>();
1803 let data = vec![42u8; n as _];
1804 build_runtime!(runtime, instance, memory: [ topics_data, data, ]);
1805
1806 let result;
1810 #[block]
1811 {
1812 result = block_storage::bench_with_ethereum_context(|| {
1813 runtime.bench_deposit_event(
1814 memory.as_mut_slice(),
1815 0, num_topic,
1817 topics_data.len() as u32, n, )
1820 });
1821 }
1822 assert_ok!(result);
1823
1824 let events = System::<T>::events();
1825 let record = &events[events.len() - 1];
1826
1827 assert_eq!(
1828 record.event,
1829 crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(),
1830 );
1831 }
1832
1833 enum TrieFill {
1834 Empty,
1835 Full,
1836 }
1837
1838 enum SlotAccess {
1839 Cold,
1840 Hot,
1841 }
1842
1843 fn build_storage_contract<T: Config>(
1844 op: StorageOp,
1845 fill: TrieFill,
1846 ) -> Result<(ContractInfo<T>, Vec<u8>, Vec<u8>), BenchmarkError> {
1847 let key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1848 let value = vec![1u8; limits::STORAGE_BYTES as usize];
1849 let initial_value = match op {
1850 StorageOp::Read => value.clone(),
1851 StorageOp::Write => vec![42u8; limits::STORAGE_BYTES as usize],
1852 };
1853
1854 let instance = match fill {
1855 TrieFill::Full => {
1856 Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?
1857 },
1858 TrieFill::Empty => Contract::<T>::new(VmBinaryModule::dummy(), vec![])?,
1859 };
1860 let info = instance.info()?;
1861 info.bench_write_raw(&key, Some(initial_value), false)
1862 .map_err(|_| "Failed to write to storage during setup.")?;
1863 Ok((info, key, value))
1864 }
1865
1866 enum StorageCall {
1867 Clear,
1868 Contains,
1869 Take,
1870 }
1871
1872 fn setup_precompile_bench<T: Config>(
1873 op: StorageCall,
1874 key_byte: u8,
1875 access: SlotAccess,
1876 ) -> Result<(CallSetup<T>, Key, Vec<u8>), BenchmarkError> {
1877 let max_key_len = limits::STORAGE_KEY_BYTES;
1878 let key = Key::try_from_var(vec![key_byte; max_key_len as usize])
1879 .map_err(|_| "Key has wrong length")?;
1880 let raw_key = vec![key_byte; max_key_len as usize].into();
1881 let input_bytes = match op {
1882 StorageCall::Clear => {
1883 IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
1884 flags: StorageFlags::empty().bits(),
1885 key: raw_key,
1886 isFixedKey: false,
1887 })
1888 },
1889 StorageCall::Contains => {
1890 IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
1891 flags: StorageFlags::empty().bits(),
1892 key: raw_key,
1893 isFixedKey: false,
1894 })
1895 },
1896 StorageCall::Take => IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
1897 flags: StorageFlags::empty().bits(),
1898 key: raw_key,
1899 isFixedKey: false,
1900 }),
1901 }
1902 .abi_encode();
1903
1904 let call_setup = CallSetup::<T>::default();
1905 if matches!(access, SlotAccess::Hot) {
1906 let info = call_setup.contract().info()?;
1907 frame_benchmarking::add_to_whitelist_child(
1908 info.child_trie_info().storage_key().to_vec(),
1909 key.hash(),
1910 );
1911 }
1912 Ok((call_setup, key, input_bytes))
1913 }
1914
1915 #[benchmark(skip_meta, pov_mode = Measured)]
1916 fn get_storage_empty() -> Result<(), BenchmarkError> {
1917 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Empty)?;
1918 let child_trie_info = info.child_trie_info();
1919
1920 let result;
1921 #[block]
1922 {
1923 result = child::get_raw(&child_trie_info, &key);
1924 }
1925
1926 assert_eq!(result, Some(value));
1927 Ok(())
1928 }
1929
1930 #[benchmark(skip_meta, pov_mode = Measured)]
1931 fn get_storage_full() -> Result<(), BenchmarkError> {
1932 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Full)?;
1933 let child_trie_info = info.child_trie_info();
1934
1935 let result;
1936 #[block]
1937 {
1938 result = child::get_raw(&child_trie_info, &key);
1939 }
1940
1941 assert_eq!(result, Some(value));
1942 Ok(())
1943 }
1944
1945 #[benchmark(skip_meta, pov_mode = Measured)]
1946 fn set_storage_empty() -> Result<(), BenchmarkError> {
1947 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Empty)?;
1948
1949 let val = Some(value.clone());
1950 let result;
1951 #[block]
1952 {
1953 result = info.bench_write_raw(&key, val, true);
1954 }
1955
1956 assert_ok!(result);
1957 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1958 Ok(())
1959 }
1960
1961 #[benchmark(skip_meta, pov_mode = Measured)]
1962 fn set_storage_full() -> Result<(), BenchmarkError> {
1963 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Full)?;
1964
1965 let val = Some(value.clone());
1966 let result;
1967 #[block]
1968 {
1969 result = info.bench_write_raw(&key, val, true);
1970 }
1971
1972 assert_ok!(result);
1973 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1974 Ok(())
1975 }
1976
1977 fn shared_prefix_keys(count: usize, suffix_of: impl Fn(u64) -> u64) -> Vec<Vec<u8>> {
1979 (0..count as u64)
1980 .map(|i| {
1981 let mut key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1982 key[limits::STORAGE_KEY_BYTES as usize - 8..]
1983 .copy_from_slice(&suffix_of(i).to_be_bytes());
1984 key
1985 })
1986 .collect()
1987 }
1988
1989 fn setup_stored_keys<T: Config>(
1992 count: usize,
1993 value_byte: u8,
1994 suffix_of: impl Fn(u64) -> u64,
1995 ) -> Result<(ContractInfo<T>, Vec<Vec<u8>>), BenchmarkError> {
1996 let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1997 let info = instance.info()?;
1998 let child_trie_info = info.child_trie_info();
1999 let value = vec![value_byte; limits::STORAGE_BYTES as usize];
2000 let stored_keys = shared_prefix_keys(count, suffix_of);
2001 for key in &stored_keys {
2002 info.bench_write_raw(key, Some(value.clone()), false)
2003 .map_err(|_| "Failed to write to storage during setup.")?;
2004 frame_benchmarking::add_to_whitelist_child(
2005 child_trie_info.storage_key().to_vec(),
2006 key.clone(),
2007 );
2008 }
2009 Ok((info, stored_keys))
2010 }
2011
2012 #[benchmark(skip_meta, pov_mode = Measured)]
2013 fn overlay_probe_full(
2014 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
2015 ) -> Result<(), BenchmarkError> {
2016 let value_byte = 42;
2017 let (info, stored_keys) =
2019 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
2020 let child_trie_info = info.child_trie_info();
2021 let fill_keys = shared_prefix_keys(MAX_ACCESS_LIST_ENTRIES, |i| (i + 1) * 2);
2023
2024 let mut result = None;
2025 #[block]
2026 {
2027 for key in &fill_keys {
2029 child::put_raw(&child_trie_info, key, &[0u8]);
2030 }
2031 for i in 0..n {
2032 let index = i as usize % stored_keys.len();
2033 result = child::get_raw(&child_trie_info, &stored_keys[index]);
2034 }
2035 }
2036
2037 if n > 0 {
2038 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
2039 assert_eq!(result, Some(expected), "the stored value must be read back");
2040 }
2041 Ok(())
2042 }
2043
2044 #[benchmark(skip_meta, pov_mode = Measured)]
2045 fn overlay_probe_empty(
2046 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
2047 ) -> Result<(), BenchmarkError> {
2048 let value_byte = 42;
2049 let (info, stored_keys) =
2051 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
2052 let child_trie_info = info.child_trie_info();
2053
2054 let mut result = None;
2055 #[block]
2056 {
2057 for i in 0..n {
2058 let index = i as usize % stored_keys.len();
2059 result = child::get_raw(&child_trie_info, &stored_keys[index]);
2060 }
2061 }
2062
2063 if n > 0 {
2064 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
2065 assert_eq!(result, Some(expected), "the stored value must be read back");
2066 }
2067 Ok(())
2068 }
2069
2070 #[benchmark(skip_meta, pov_mode = Measured)]
2073 fn seal_set_storage(
2074 n: Linear<0, { limits::STORAGE_BYTES }>,
2075 o: Linear<0, { limits::STORAGE_BYTES }>,
2076 ) -> Result<(), BenchmarkError> {
2077 let max_key_len = limits::STORAGE_KEY_BYTES;
2078 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2079 .map_err(|_| "Key has wrong length")?;
2080 let value = vec![1u8; n as usize];
2081
2082 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
2083 let info = instance.info()?;
2084
2085 info.write(&key, Some(vec![42u8; o as usize]), None, false)
2086 .map_err(|_| "Failed to write to storage during setup.")?;
2087
2088 let result;
2089 #[block]
2090 {
2091 result = runtime.bench_set_storage(
2092 memory.as_mut_slice(),
2093 StorageFlags::empty().bits(),
2094 0, max_key_len, max_key_len, n, );
2099 }
2100
2101 assert_ok!(result);
2102 assert_eq!(info.read(&key).unwrap(), value);
2103 Ok(())
2104 }
2105
2106 #[benchmark(skip_meta, pov_mode = Measured)]
2107 fn seal_set_storage_hot(
2108 n: Linear<0, { limits::STORAGE_BYTES }>,
2109 o: Linear<0, { limits::STORAGE_BYTES }>,
2110 ) -> Result<(), BenchmarkError> {
2111 let max_key_len = limits::STORAGE_KEY_BYTES;
2112 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2113 .map_err(|_| "Key has wrong length")?;
2114 let value = vec![1u8; n as usize];
2115
2116 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
2117 let info = instance.info()?;
2118
2119 info.write(&key, Some(vec![42u8; o as usize]), None, false)
2120 .map_err(|_| "Failed to write to storage during setup.")?;
2121
2122 frame_benchmarking::add_to_whitelist_child(
2123 info.child_trie_info().storage_key().to_vec(),
2124 key.hash(),
2125 );
2126
2127 runtime.ext().touch_storage_access(&key, StorageOp::Write);
2129
2130 let result;
2131 #[block]
2132 {
2133 result = runtime.bench_set_storage(
2134 memory.as_mut_slice(),
2135 StorageFlags::empty().bits(),
2136 0, max_key_len, max_key_len, n, );
2141 }
2142
2143 assert_ok!(result);
2144 assert_eq!(info.read(&key).unwrap(), value);
2145 Ok(())
2146 }
2147
2148 #[benchmark(skip_meta, pov_mode = Measured)]
2149 fn clear_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2150 let key_byte = 0;
2151 let (mut call_setup, key, input_bytes) =
2152 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Cold)?;
2153 let (mut ext, _) = call_setup.ext();
2154 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2155 .map_err(|_| "Failed to write to storage during setup.")?;
2156
2157 let result;
2158 #[block]
2159 {
2160 result = run_builtin_precompile(
2161 &mut ext,
2162 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2163 input_bytes,
2164 );
2165 }
2166 assert_ok!(result);
2167 assert!(ext.get_storage(&key).is_none());
2168
2169 Ok(())
2170 }
2171
2172 #[benchmark(skip_meta, pov_mode = Measured)]
2173 fn clear_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2174 let key_byte = 0;
2175 let (mut call_setup, key, input_bytes) =
2176 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Hot)?;
2177 let (mut ext, _) = call_setup.ext();
2178 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2179 .map_err(|_| "Failed to write to storage during setup.")?;
2180
2181 ext.touch_storage_access(&key, StorageOp::Write);
2182
2183 let result;
2184 #[block]
2185 {
2186 result = run_builtin_precompile(
2187 &mut ext,
2188 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2189 input_bytes,
2190 );
2191 }
2192 assert_ok!(result);
2193 assert!(ext.get_storage(&key).is_none());
2194
2195 Ok(())
2196 }
2197
2198 #[benchmark(skip_meta, pov_mode = Measured)]
2199 fn seal_get_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2200 let max_key_len = limits::STORAGE_KEY_BYTES;
2201 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2202 .map_err(|_| "Key has wrong length")?;
2203 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2204 let info = instance.info()?;
2205
2206 info.write(&key, Some(vec![42u8; n as usize]), None, false)
2207 .map_err(|_| "Failed to write to storage during setup.")?;
2208
2209 let out_ptr = max_key_len + 4;
2210 let result;
2211 #[block]
2212 {
2213 result = runtime.bench_get_storage(
2214 memory.as_mut_slice(),
2215 StorageFlags::empty().bits(),
2216 0, max_key_len, out_ptr, max_key_len, );
2221 }
2222
2223 assert_ok!(result);
2224 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
2225 Ok(())
2226 }
2227
2228 #[benchmark(skip_meta, pov_mode = Measured)]
2229 fn seal_get_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2230 let max_key_len = limits::STORAGE_KEY_BYTES;
2231 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2232 .map_err(|_| "Key has wrong length")?;
2233 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2234 let info = instance.info()?;
2235
2236 info.write(&key, Some(vec![42u8; n as usize]), None, false)
2237 .map_err(|_| "Failed to write to storage during setup.")?;
2238
2239 frame_benchmarking::add_to_whitelist_child(
2240 info.child_trie_info().storage_key().to_vec(),
2241 key.hash(),
2242 );
2243
2244 runtime.ext().touch_storage_access(&key, StorageOp::Read);
2245
2246 let out_ptr = max_key_len + 4;
2247 let result;
2248 #[block]
2249 {
2250 result = runtime.bench_get_storage(
2251 memory.as_mut_slice(),
2252 StorageFlags::empty().bits(),
2253 0, max_key_len, out_ptr, max_key_len, );
2258 }
2259
2260 assert_ok!(result);
2261 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
2262 Ok(())
2263 }
2264
2265 #[benchmark(skip_meta, pov_mode = Measured)]
2266 fn contains_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2267 let key_byte = 0;
2268 let (mut call_setup, key, input_bytes) =
2269 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Cold)?;
2270 let (mut ext, _) = call_setup.ext();
2271 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2272 .map_err(|_| "Failed to write to storage during setup.")?;
2273
2274 let result;
2275 #[block]
2276 {
2277 result = run_builtin_precompile(
2278 &mut ext,
2279 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2280 input_bytes,
2281 );
2282 }
2283 assert_ok!(result);
2284 assert!(ext.get_storage(&key).is_some());
2285
2286 Ok(())
2287 }
2288
2289 #[benchmark(skip_meta, pov_mode = Measured)]
2290 fn contains_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2291 let key_byte = 0;
2292 let (mut call_setup, key, input_bytes) =
2293 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Hot)?;
2294 let (mut ext, _) = call_setup.ext();
2295 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2296 .map_err(|_| "Failed to write to storage during setup.")?;
2297
2298 ext.touch_storage_access(&key, StorageOp::Read);
2299
2300 let result;
2301 #[block]
2302 {
2303 result = run_builtin_precompile(
2304 &mut ext,
2305 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2306 input_bytes,
2307 );
2308 }
2309 assert_ok!(result);
2310 assert!(ext.get_storage(&key).is_some());
2311
2312 Ok(())
2313 }
2314
2315 #[benchmark(skip_meta, pov_mode = Measured)]
2316 fn take_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2317 let key_byte = 3;
2318 let (mut call_setup, key, input_bytes) =
2319 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Cold)?;
2320 let (mut ext, _) = call_setup.ext();
2321 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2322 .map_err(|_| "Failed to write to storage during setup.")?;
2323
2324 let result;
2325 #[block]
2326 {
2327 result = run_builtin_precompile(
2328 &mut ext,
2329 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2330 input_bytes,
2331 );
2332 }
2333 assert_ok!(result);
2334 assert!(ext.get_storage(&key).is_none());
2335
2336 Ok(())
2337 }
2338
2339 #[benchmark(skip_meta, pov_mode = Measured)]
2340 fn take_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2341 let key_byte = 3;
2342 let (mut call_setup, key, input_bytes) =
2343 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Hot)?;
2344 let (mut ext, _) = call_setup.ext();
2345 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2346 .map_err(|_| "Failed to write to storage during setup.")?;
2347
2348 ext.touch_storage_access(&key, StorageOp::Write);
2349
2350 let result;
2351 #[block]
2352 {
2353 result = run_builtin_precompile(
2354 &mut ext,
2355 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2356 input_bytes,
2357 );
2358 }
2359 assert_ok!(result);
2360 assert!(ext.get_storage(&key).is_none());
2361
2362 Ok(())
2363 }
2364
2365 fn worst_case_slot() -> crate::access_list::Slot {
2366 let key = Key::try_from_var(vec![0xFFu8; limits::STORAGE_KEY_BYTES as usize])
2367 .expect("key fits STORAGE_KEY_BYTES bound; qed");
2368 crate::access_list::Slot::from(&key)
2369 }
2370
2371 fn near_full_access_list() -> crate::access_list::AccessList {
2372 let mut al = AccessList::new();
2373 for i in 0..(MAX_ACCESS_LIST_ENTRIES - 1) {
2374 al.touch(
2375 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(i as u64) },
2376 StorageOp::Read,
2377 );
2378 }
2379 al
2380 }
2381
2382 #[benchmark(pov_mode = Ignored)]
2383 fn access_list_touch_cold_full() -> Result<(), BenchmarkError> {
2384 let mut al = near_full_access_list();
2385 let entry =
2387 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2388 let outcome;
2389 #[block]
2390 {
2391 outcome = al.touch(entry, StorageOp::Read);
2392 }
2393 assert!(outcome.is_cold());
2394 Ok(())
2395 }
2396
2397 #[benchmark(pov_mode = Ignored)]
2398 fn access_list_touch_hot_full() -> Result<(), BenchmarkError> {
2399 let mut al = near_full_access_list();
2400 let entry = AccessEntry {
2402 slot: worst_case_slot(),
2403 address: H160::from_low_u64_be(MAX_ACCESS_LIST_ENTRIES as u64 - 2),
2404 };
2405
2406 let touched = entry.clone();
2407 let outcome;
2408 #[block]
2409 {
2410 outcome = al.touch(touched, StorageOp::Write);
2411 }
2412 assert_eq!(
2413 outcome,
2414 Warmth::Hot { charged: StorageOp::Read },
2415 "the fill seeded this entry read-paid"
2416 );
2417 assert_eq!(
2418 al.peek(&entry),
2419 Warmth::Hot { charged: StorageOp::Write },
2420 "the write upgraded the entry"
2421 );
2422 Ok(())
2423 }
2424
2425 #[benchmark(pov_mode = Ignored)]
2426 fn access_list_touch_cold_empty() -> Result<(), BenchmarkError> {
2427 let mut al = AccessList::new();
2428 let entry =
2429 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2430 let outcome;
2431 #[block]
2432 {
2433 outcome = al.touch(entry, StorageOp::Read);
2434 }
2435 assert!(outcome.is_cold());
2436 Ok(())
2437 }
2438
2439 #[benchmark(pov_mode = Ignored)]
2440 fn access_list_touch_hot_single_element() -> Result<(), BenchmarkError> {
2441 let mut al = AccessList::new();
2442 let entry =
2443 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2444 al.touch(entry.clone(), StorageOp::Read);
2445 let outcome;
2446 #[block]
2447 {
2448 outcome = al.touch(entry, StorageOp::Read);
2449 }
2450 assert!(!outcome.is_cold());
2451 Ok(())
2452 }
2453
2454 #[benchmark(pov_mode = Ignored)]
2458 fn access_list_rollback_amortization() -> Result<(), BenchmarkError> {
2459 let mut al = near_full_access_list();
2460 al.enter_frame();
2461 al.touch(
2462 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) },
2463 StorageOp::Read,
2464 );
2465 #[block]
2466 {
2467 al.rollback_frame();
2468 }
2469 Ok(())
2470 }
2471
2472 #[benchmark(pov_mode = Ignored)]
2477 fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
2478 let max_value_len = limits::STORAGE_BYTES;
2479 let max_key_len = limits::STORAGE_KEY_BYTES;
2480 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2481 .map_err(|_| "Key has wrong length")?;
2482 let value = Some(vec![42u8; max_value_len as _]);
2483 let mut setup = CallSetup::<T>::default();
2484 let (mut ext, _) = setup.ext();
2485 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2486 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2487 let result;
2488 #[block]
2489 {
2490 result = runtime.ext().set_transient_storage(&key, value, false);
2491 }
2492
2493 assert_eq!(result, Ok(WriteOutcome::New));
2494 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2495 Ok(())
2496 }
2497
2498 #[benchmark(pov_mode = Ignored)]
2499 fn set_transient_storage_full() -> Result<(), BenchmarkError> {
2500 let max_value_len = limits::STORAGE_BYTES;
2501 let max_key_len = limits::STORAGE_KEY_BYTES;
2502 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2503 .map_err(|_| "Key has wrong length")?;
2504 let value = Some(vec![42u8; max_value_len as _]);
2505 let mut setup = CallSetup::<T>::default();
2506 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2507 let (mut ext, _) = setup.ext();
2508 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2509 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2510 let result;
2511 #[block]
2512 {
2513 result = runtime.ext().set_transient_storage(&key, value, false);
2514 }
2515
2516 assert_eq!(result, Ok(WriteOutcome::New));
2517 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2518 Ok(())
2519 }
2520
2521 #[benchmark(pov_mode = Ignored)]
2522 fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
2523 let max_value_len = limits::STORAGE_BYTES;
2524 let max_key_len = limits::STORAGE_KEY_BYTES;
2525 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2526 .map_err(|_| "Key has wrong length")?;
2527
2528 let mut setup = CallSetup::<T>::default();
2529 let (mut ext, _) = setup.ext();
2530 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2531 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2532 runtime
2533 .ext()
2534 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2535 .map_err(|_| "Failed to write to transient storage during setup.")?;
2536 let result;
2537 #[block]
2538 {
2539 result = runtime.ext().get_transient_storage(&key);
2540 }
2541
2542 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2543 Ok(())
2544 }
2545
2546 #[benchmark(pov_mode = Ignored)]
2547 fn get_transient_storage_full() -> Result<(), BenchmarkError> {
2548 let max_value_len = limits::STORAGE_BYTES;
2549 let max_key_len = limits::STORAGE_KEY_BYTES;
2550 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2551 .map_err(|_| "Key has wrong length")?;
2552
2553 let mut setup = CallSetup::<T>::default();
2554 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2555 let (mut ext, _) = setup.ext();
2556 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2557 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2558 runtime
2559 .ext()
2560 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2561 .map_err(|_| "Failed to write to transient storage during setup.")?;
2562 let result;
2563 #[block]
2564 {
2565 result = runtime.ext().get_transient_storage(&key);
2566 }
2567
2568 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2569 Ok(())
2570 }
2571
2572 #[benchmark(pov_mode = Ignored)]
2574 fn rollback_transient_storage() -> Result<(), BenchmarkError> {
2575 let max_value_len = limits::STORAGE_BYTES;
2576 let max_key_len = limits::STORAGE_KEY_BYTES;
2577 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2578 .map_err(|_| "Key has wrong length")?;
2579
2580 let mut setup = CallSetup::<T>::default();
2581 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2582 let (mut ext, _) = setup.ext();
2583 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2584 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2585 runtime.ext().transient_storage().start_transaction();
2586 runtime
2587 .ext()
2588 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2589 .map_err(|_| "Failed to write to transient storage during setup.")?;
2590 #[block]
2591 {
2592 runtime.ext().transient_storage().rollback_transaction();
2593 }
2594
2595 assert_eq!(runtime.ext().get_transient_storage(&key), None);
2596 Ok(())
2597 }
2598
2599 #[benchmark(pov_mode = Measured)]
2602 fn seal_set_transient_storage(
2603 n: Linear<0, { limits::STORAGE_BYTES }>,
2604 o: Linear<0, { limits::STORAGE_BYTES }>,
2605 ) -> Result<(), BenchmarkError> {
2606 let max_key_len = limits::STORAGE_KEY_BYTES;
2607 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2608 .map_err(|_| "Key has wrong length")?;
2609 let value = vec![1u8; n as usize];
2610 build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]);
2611 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2612 runtime
2613 .ext()
2614 .set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
2615 .map_err(|_| "Failed to write to transient storage during setup.")?;
2616
2617 let result;
2618 #[block]
2619 {
2620 result = runtime.bench_set_storage(
2621 memory.as_mut_slice(),
2622 StorageFlags::TRANSIENT.bits(),
2623 0, max_key_len, max_key_len, n, );
2628 }
2629
2630 assert_ok!(result);
2631 assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
2632 Ok(())
2633 }
2634
2635 #[benchmark(pov_mode = Measured)]
2636 fn seal_clear_transient_storage(
2637 n: Linear<0, { limits::STORAGE_BYTES }>,
2638 ) -> Result<(), BenchmarkError> {
2639 let max_key_len = limits::STORAGE_KEY_BYTES;
2640 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2641 .map_err(|_| "Key has wrong length")?;
2642 let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
2643 flags: StorageFlags::TRANSIENT.bits(),
2644 key: vec![0u8; max_key_len as usize].into(),
2645 isFixedKey: false,
2646 })
2647 .abi_encode();
2648
2649 let mut call_setup = CallSetup::<T>::default();
2650 let (mut ext, _) = call_setup.ext();
2651 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2652 .map_err(|_| "Failed to write to transient storage during setup.")?;
2653
2654 let result;
2655 #[block]
2656 {
2657 result = run_builtin_precompile(
2658 &mut ext,
2659 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2660 input_bytes,
2661 );
2662 }
2663 assert_ok!(result);
2664 assert!(ext.get_transient_storage(&key).is_none());
2665
2666 Ok(())
2667 }
2668
2669 #[benchmark(pov_mode = Measured)]
2670 fn seal_get_transient_storage(
2671 n: Linear<0, { limits::STORAGE_BYTES }>,
2672 ) -> Result<(), BenchmarkError> {
2673 let max_key_len = limits::STORAGE_KEY_BYTES;
2674 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2675 .map_err(|_| "Key has wrong length")?;
2676 build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2677 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2678 runtime
2679 .ext()
2680 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2681 .map_err(|_| "Failed to write to transient storage during setup.")?;
2682
2683 let out_ptr = max_key_len + 4;
2684 let result;
2685 #[block]
2686 {
2687 result = runtime.bench_get_storage(
2688 memory.as_mut_slice(),
2689 StorageFlags::TRANSIENT.bits(),
2690 0, max_key_len, out_ptr, max_key_len, );
2695 }
2696
2697 assert_ok!(result);
2698 assert_eq!(
2699 &runtime.ext().get_transient_storage(&key).unwrap(),
2700 &memory[out_ptr as usize..]
2701 );
2702 Ok(())
2703 }
2704
2705 #[benchmark(pov_mode = Measured)]
2706 fn seal_contains_transient_storage(
2707 n: Linear<0, { limits::STORAGE_BYTES }>,
2708 ) -> Result<(), BenchmarkError> {
2709 let max_key_len = limits::STORAGE_KEY_BYTES;
2710 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2711 .map_err(|_| "Key has wrong length")?;
2712
2713 let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
2714 flags: StorageFlags::TRANSIENT.bits(),
2715 key: vec![0u8; max_key_len as usize].into(),
2716 isFixedKey: false,
2717 })
2718 .abi_encode();
2719
2720 let mut call_setup = CallSetup::<T>::default();
2721 let (mut ext, _) = call_setup.ext();
2722 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2723 .map_err(|_| "Failed to write to transient storage during setup.")?;
2724
2725 let result;
2726 #[block]
2727 {
2728 result = run_builtin_precompile(
2729 &mut ext,
2730 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2731 input_bytes,
2732 );
2733 }
2734 assert!(result.is_ok());
2735 assert!(ext.get_transient_storage(&key).is_some());
2736
2737 Ok(())
2738 }
2739
2740 #[benchmark(pov_mode = Measured)]
2741 fn seal_take_transient_storage(
2742 n: Linear<0, { limits::STORAGE_BYTES }>,
2743 ) -> Result<(), BenchmarkError> {
2744 let n = limits::STORAGE_BYTES;
2745 let value = vec![42u8; n as usize];
2746 let max_key_len = limits::STORAGE_KEY_BYTES;
2747 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2748 .map_err(|_| "Key has wrong length")?;
2749
2750 let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
2751 flags: StorageFlags::TRANSIENT.bits(),
2752 key: vec![0u8; max_key_len as usize].into(),
2753 isFixedKey: false,
2754 })
2755 .abi_encode();
2756
2757 let mut call_setup = CallSetup::<T>::default();
2758 let (mut ext, _) = call_setup.ext();
2759 ext.set_transient_storage(&key, Some(value), false)
2760 .map_err(|_| "Failed to write to transient storage during setup.")?;
2761
2762 let result;
2763 #[block]
2764 {
2765 result = run_builtin_precompile(
2766 &mut ext,
2767 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2768 input_bytes,
2769 );
2770 }
2771 assert!(result.is_ok());
2772 assert!(ext.get_transient_storage(&key).is_none());
2773
2774 Ok(())
2775 }
2776
2777 #[benchmark(pov_mode = Measured)]
2781 fn seal_call(
2782 t: Linear<0, 1>,
2783 d: Linear<0, 1>,
2784 i: Linear<0, { limits::code::BLOB_BYTES }>,
2785 ) -> Result<(), BenchmarkError> {
2786 let target = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?;
2792 let callee_addr = H160([0x42; 20]);
2793 let callee = delegated_eoa::<T>(callee_addr, target.address)?;
2794 T::Currency::set_balance(&callee, Pallet::<T>::min_balance());
2799
2800 let callee_bytes = callee.encode();
2801 let callee_len = callee_bytes.len() as u32;
2802
2803 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2804 let dust = 100u32 * d;
2805 let evm_value =
2806 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2807 let value_bytes = evm_value.encode();
2808
2809 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2810 let deposit_bytes = Into::<U256>::into(deposit).encode();
2811 let deposit_len = deposit_bytes.len() as u32;
2812
2813 let mut setup = CallSetup::<T>::default();
2814 setup.set_storage_deposit_limit(deposit);
2815 setup.set_data(vec![42; i as usize]);
2818 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2819 setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
2820
2821 let (mut ext, _) = setup.ext();
2822 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2823 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
2824 let before = Pallet::<T>::evm_balance(&callee_addr);
2825
2826 let result;
2827 #[block]
2828 {
2829 result = runtime.bench_call(
2830 memory.as_mut_slice(),
2831 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), );
2838 }
2839
2840 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2841 assert_eq!(
2842 Pallet::<T>::evm_balance(&callee_addr),
2843 before + evm_value,
2844 "{callee_addr:?} balance should have grown by {evm_value:?}"
2845 );
2846
2847 Ok(())
2848 }
2849
2850 #[benchmark(pov_mode = Measured)]
2853 fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
2854 use alloy_core::sol_types::SolInterface;
2855 use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
2856
2857 let callee_bytes = if d == 1 {
2858 BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
2859 } else {
2860 BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
2861 };
2862 let callee_len = callee_bytes.len() as u32;
2863
2864 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2865 let deposit_bytes = Into::<U256>::into(deposit).encode();
2866 let deposit_len = deposit_bytes.len() as u32;
2867
2868 let value: BalanceOf<T> = Zero::zero();
2869 let value_bytes = Into::<U256>::into(value).encode();
2870 let value_len = value_bytes.len() as u32;
2871
2872 let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
2873 input: vec![42_u8; i as usize].into(),
2874 })
2875 .abi_encode();
2876 let input_len = input_bytes.len() as u32;
2877
2878 let mut setup = CallSetup::<T>::default();
2879 setup.set_storage_deposit_limit(deposit);
2880
2881 let (mut ext, _) = setup.ext();
2882 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2883 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
2884
2885 let mut do_benchmark = || {
2886 runtime.bench_call(
2887 memory.as_mut_slice(),
2888 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), )
2897 };
2898
2899 assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
2902
2903 let result;
2904 #[block]
2905 {
2906 result = do_benchmark();
2907 }
2908
2909 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2910 }
2911
2912 #[benchmark(pov_mode = Measured)]
2913 fn seal_delegate_call() -> Result<(), BenchmarkError> {
2914 let target = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?;
2917 let address = delegated_eoa::<T>(H160([0x43; 20]), target.address)?;
2918
2919 let address_bytes = address.encode();
2920 let address_len = address_bytes.len() as u32;
2921
2922 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2923 let deposit_bytes = Into::<U256>::into(deposit).encode();
2924
2925 let mut setup = CallSetup::<T>::default();
2926 setup.set_storage_deposit_limit(deposit);
2927 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2928
2929 let (mut ext, _) = setup.ext();
2930 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2931 let mut memory = memory!(address_bytes, deposit_bytes,);
2932
2933 let result;
2934 #[block]
2935 {
2936 result = runtime.bench_delegate_call(
2937 memory.as_mut_slice(),
2938 pack_hi_lo(0, 0), u64::MAX, u64::MAX, address_len, pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
2945 }
2946
2947 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2948 Ok(())
2949 }
2950
2951 #[benchmark(pov_mode = Measured)]
2955 fn seal_instantiate(
2956 t: Linear<0, 1>,
2957 d: Linear<0, 1>,
2958 i: Linear<0, { limits::CALLDATA_BYTES }>,
2959 ) -> Result<(), BenchmarkError> {
2960 let code = VmBinaryModule::dummy();
2961 let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
2962 let hash_bytes = hash.encode();
2963
2964 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2965 let dust = 100u32 * d;
2966 let evm_value =
2967 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2968 let value_bytes = evm_value.encode();
2969 let value_len = value_bytes.len() as u32;
2970
2971 let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
2972 let deposit_bytes = Into::<U256>::into(deposit).encode();
2973 let deposit_len = deposit_bytes.len() as u32;
2974
2975 let mut setup = CallSetup::<T>::default();
2976 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2977 setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
2978
2979 let account_id = &setup.contract().account_id.clone();
2980 let (mut ext, _) = setup.ext();
2981 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2982
2983 let input = vec![42u8; i as _];
2984 let input_len = hash_bytes.len() as u32 + input.len() as u32;
2985 let salt = [42u8; 32];
2986 let deployer = T::AddressMapper::to_address(&account_id);
2987 let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
2988 let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
2989
2990 let mut offset = {
2991 let mut current = 0u32;
2992 move |after: u32| {
2993 current += after;
2994 current
2995 }
2996 };
2997
2998 assert!(AccountInfoOf::<T>::get(&addr).is_none());
2999
3000 let result;
3001 #[block]
3002 {
3003 result = runtime.bench_instantiate(
3004 memory.as_mut_slice(),
3005 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)), );
3012 }
3013
3014 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
3015 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
3016
3017 assert_eq!(
3018 Pallet::<T>::evm_balance(&addr),
3019 evm_value,
3020 "{addr:?} balance should hold {evm_value:?}"
3021 );
3022 Ok(())
3023 }
3024
3025 #[benchmark(pov_mode = Measured)]
3029 fn evm_instantiate(
3030 t: Linear<0, 1>,
3031 d: Linear<0, 1>,
3032 i: Linear<{ 10 * 1024 }, { 48 * 1024 }>,
3033 ) -> Result<(), BenchmarkError> {
3034 use crate::vm::evm::instructions::BENCH_INIT_CODE;
3035 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_init_code_for_runtime_size(0));
3036 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
3037 setup.set_balance(caller_funding::<T>());
3038
3039 let (mut ext, _) = setup.ext();
3040 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
3041
3042 let value = {
3043 let value: BalanceOf<T> = (1_000_000u32 * t).into();
3044 let dust = 100u32 * d;
3045 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust))
3046 };
3047
3048 let init_code = vec![BENCH_INIT_CODE; i as usize];
3049 let _ = interpreter.memory.resize(0, init_code.len());
3050 let salt = U256::from(42u64);
3051 interpreter.memory.set_data(0, 0, init_code.len(), &init_code);
3052
3053 let _ = interpreter.stack.push(salt);
3055 let _ = interpreter.stack.push(U256::from(init_code.len()));
3056 let _ = interpreter.stack.push(U256::zero());
3057 let _ = interpreter.stack.push(value);
3058
3059 let result;
3060 #[block]
3061 {
3062 result = instructions::contract::create::<true, _>(&mut interpreter);
3063 }
3064
3065 assert!(result.is_continue());
3066 let addr = interpreter.stack.top().unwrap().into_address();
3067 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
3068 assert_eq!(Pallet::<T>::code(&addr).len(), revm::primitives::eip170::MAX_CODE_SIZE);
3069 assert_eq!(Pallet::<T>::evm_balance(&addr), value, "balance should hold {value:?}");
3070 Ok(())
3071 }
3072
3073 #[benchmark(pov_mode = Measured)]
3075 fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3076 let input = vec![0u8; n as usize];
3077 let mut call_setup = CallSetup::<T>::default();
3078 let (mut ext, _) = call_setup.ext();
3079
3080 let result;
3081 #[block]
3082 {
3083 result = run_builtin_precompile(
3084 &mut ext,
3085 H160::from_low_u64_be(2).as_fixed_bytes(),
3086 input.clone(),
3087 );
3088 }
3089 assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
3090 }
3091
3092 #[benchmark(pov_mode = Measured)]
3093 fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3094 let input = vec![0u8; n as usize];
3095 let mut call_setup = CallSetup::<T>::default();
3096 let (mut ext, _) = call_setup.ext();
3097
3098 let result;
3099 #[block]
3100 {
3101 result = run_builtin_precompile(
3102 &mut ext,
3103 H160::from_low_u64_be(4).as_fixed_bytes(),
3104 input.clone(),
3105 );
3106 }
3107 assert_eq!(input, result.unwrap().data);
3108 }
3109
3110 #[benchmark(pov_mode = Measured)]
3112 fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3113 use ripemd::Digest;
3114 let input = vec![0u8; n as usize];
3115 let mut call_setup = CallSetup::<T>::default();
3116 let (mut ext, _) = call_setup.ext();
3117
3118 let result;
3119 #[block]
3120 {
3121 result = run_builtin_precompile(
3122 &mut ext,
3123 H160::from_low_u64_be(3).as_fixed_bytes(),
3124 input.clone(),
3125 );
3126 }
3127 let mut expected = [0u8; 32];
3128 expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
3129
3130 assert_eq!(expected.to_vec(), result.unwrap().data);
3131 }
3132
3133 #[benchmark(pov_mode = Measured)]
3135 fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3136 build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
3137
3138 let result;
3139 #[block]
3140 {
3141 result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
3142 }
3143 assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
3144 assert_ok!(result);
3145 }
3146
3147 #[benchmark(pov_mode = Measured)]
3149 fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3150 let input = vec![0u8; n as usize];
3151 let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
3152 input: input.clone().into(),
3153 })
3154 .abi_encode();
3155
3156 let mut call_setup = CallSetup::<T>::default();
3157 let (mut ext, _) = call_setup.ext();
3158
3159 let result;
3160 #[block]
3161 {
3162 result = run_builtin_precompile(
3163 &mut ext,
3164 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
3165 input_bytes,
3166 );
3167 }
3168 let truth: [u8; 32] = sp_io::hashing::blake2_256(&input);
3169 let truth = FixedBytes::<32>::abi_encode(&truth);
3170 let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed");
3171
3172 let raw_data = result.unwrap().data;
3173 let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed");
3174 assert_eq!(truth, ret_hash);
3175 }
3176
3177 #[benchmark(pov_mode = Measured)]
3179 fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
3180 let input = vec![0u8; n as usize];
3181 let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
3182 input: input.clone().into(),
3183 })
3184 .abi_encode();
3185
3186 let mut call_setup = CallSetup::<T>::default();
3187 let (mut ext, _) = call_setup.ext();
3188
3189 let result;
3190 #[block]
3191 {
3192 result = run_builtin_precompile(
3193 &mut ext,
3194 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
3195 input_bytes,
3196 );
3197 }
3198 let truth: [u8; 16] = sp_io::hashing::blake2_128(&input);
3199 let truth = FixedBytes::<16>::abi_encode(&truth);
3200 let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed");
3201
3202 let raw_data = result.unwrap().data;
3203 let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed");
3204 assert_eq!(truth, ret_hash);
3205 }
3206
3207 #[benchmark(pov_mode = Measured)]
3210 fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) {
3211 let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
3212 let message_len = message.len() as u32;
3213
3214 let key_type = sp_core::crypto::KeyTypeId(*b"code");
3215 let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
3216 let sig =
3217 sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
3218 let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
3219 let sig_len = sig.len() as u32;
3220
3221 build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
3222
3223 let result;
3224 #[block]
3225 {
3226 result = runtime.bench_sr25519_verify(
3227 memory.as_mut_slice(),
3228 0, sig_len, message_len, sig_len + pub_key.len() as u32, );
3233 }
3234
3235 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
3236 }
3237
3238 #[benchmark(pov_mode = Measured)]
3239 fn ecdsa_recover() {
3240 use hex_literal::hex;
3241 let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
3242 let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
3243 let mut call_setup = CallSetup::<T>::default();
3244 let (mut ext, _) = call_setup.ext();
3245
3246 let result;
3247
3248 #[block]
3249 {
3250 result =
3251 run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
3252 }
3253
3254 assert_eq!(result.unwrap().data, expected);
3255 }
3256
3257 #[benchmark(pov_mode = Measured)]
3258 fn p256_verify() {
3259 use hex_literal::hex;
3260 let input = hex!("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").to_vec();
3261 let expected = U256::one().to_big_endian();
3262 let mut call_setup = CallSetup::<T>::default();
3263 let (mut ext, _) = call_setup.ext();
3264
3265 let result;
3266
3267 #[block]
3268 {
3269 result = run_builtin_precompile(
3270 &mut ext,
3271 H160::from_low_u64_be(0x100).as_fixed_bytes(),
3272 input,
3273 );
3274 }
3275
3276 assert_eq!(result.unwrap().data, expected);
3277 }
3278
3279 #[benchmark(pov_mode = Measured)]
3280 fn bn128_add() {
3281 use hex_literal::hex;
3282 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
3283 let expected = hex!(
3284 "0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
3285 );
3286 let mut call_setup = CallSetup::<T>::default();
3287 let (mut ext, _) = call_setup.ext();
3288
3289 let result;
3290 #[block]
3291 {
3292 result =
3293 run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
3294 }
3295
3296 assert_eq!(result.unwrap().data, expected);
3297 }
3298
3299 #[benchmark(pov_mode = Measured)]
3300 fn bn128_mul() {
3301 use hex_literal::hex;
3302 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
3303 let expected = hex!(
3304 "0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
3305 );
3306 let mut call_setup = CallSetup::<T>::default();
3307 let (mut ext, _) = call_setup.ext();
3308
3309 let result;
3310 #[block]
3311 {
3312 result =
3313 run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
3314 }
3315
3316 assert_eq!(result.unwrap().data, expected);
3317 }
3318
3319 #[benchmark(pov_mode = Measured)]
3321 fn bn128_pairing(n: Linear<0, { 20 }>) {
3322 fn generate_random_ecpairs(n: usize) -> Vec<u8> {
3323 use bn::{AffineG1, AffineG2, Fr, G1, G2, Group};
3324 use rand::SeedableRng;
3325 use rand_pcg::Pcg64;
3326 let mut rng = Pcg64::seed_from_u64(1);
3327
3328 let mut buffer = vec![0u8; n * 192];
3329
3330 let mut write = |element: &bn::Fq, offset: &mut usize| {
3331 element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
3332 *offset += 32
3333 };
3334
3335 for i in 0..n {
3336 let mut offset = i * 192;
3337 let scalar = Fr::random(&mut rng);
3338
3339 let g1 = G1::one() * scalar;
3340 let g2 = G2::one() * scalar;
3341 let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
3342 let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
3343
3344 write(&a.x(), &mut offset);
3345 write(&a.y(), &mut offset);
3346 write(&b.x().imaginary(), &mut offset);
3347 write(&b.x().real(), &mut offset);
3348 write(&b.y().imaginary(), &mut offset);
3349 write(&b.y().real(), &mut offset);
3350 }
3351
3352 buffer
3353 }
3354
3355 let input = generate_random_ecpairs(n as usize);
3356 let mut call_setup = CallSetup::<T>::default();
3357 let (mut ext, _) = call_setup.ext();
3358
3359 let result;
3360 #[block]
3361 {
3362 result =
3363 run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
3364 }
3365 assert_ok!(result);
3366 }
3367
3368 #[benchmark(pov_mode = Measured)]
3370 fn blake2f(n: Linear<0, 1200>) {
3371 use hex_literal::hex;
3372 let input = hex!(
3373 "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
3374 );
3375 let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
3376 let mut call_setup = CallSetup::<T>::default();
3377 let (mut ext, _) = call_setup.ext();
3378
3379 let result;
3380 #[block]
3381 {
3382 result =
3383 run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
3384 }
3385 assert_ok!(result);
3386 }
3387
3388 #[benchmark(pov_mode = Measured)]
3392 fn seal_ecdsa_to_eth_address() {
3393 let key_type = sp_core::crypto::KeyTypeId(*b"code");
3394 let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
3395 build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
3396
3397 let result;
3398 #[block]
3399 {
3400 result = runtime.bench_ecdsa_to_eth_address(
3401 memory.as_mut_slice(),
3402 20, 0, );
3405 }
3406
3407 assert_ok!(result);
3408 assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
3409 }
3410
3411 #[benchmark(pov_mode = Measured)]
3413 fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
3414 let module = VmBinaryModule::evm_noop(r);
3415 let inputs = vec![];
3416
3417 let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
3418 let mut setup = CallSetup::<T>::new(module);
3419 let (mut ext, _) = setup.ext();
3420
3421 let result;
3422 #[block]
3423 {
3424 result = evm::call(code, &mut ext, inputs);
3425 }
3426
3427 assert!(result.is_ok());
3428 Ok(())
3429 }
3430
3431 #[benchmark(pov_mode = Measured)]
3433 fn evm_pop_opcode(r: Linear<0, EVM_STACK_LIMIT>) {
3434 let code = Bytecode::new_raw(vec![POP; r as usize].into());
3435 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3436 let (mut ext, _) = setup.ext();
3437 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3438 setup_stack(&mut interpreter, vec![U256::MAX; r as usize]);
3439
3440 evict_caches();
3441 let result;
3442 #[block]
3443 {
3444 result = evm::run_plain(&mut interpreter);
3445 }
3446
3447 let ControlFlow::Break(halt) = result;
3448 assert!(matches!(halt, Halt::Stop));
3449 assert_eq!(interpreter.stack.len(), 0);
3450 }
3451
3452 #[benchmark(pov_mode = Measured)]
3460 fn evm_div_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3461 use knuth_division_operands::DENOMINATORS;
3462
3463 let mut rng = Pcg64::seed_from_u64(1337);
3464 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3465
3466 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3467 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3468 let (mut ext, _) = setup.ext();
3469 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3470 setup_stack(&mut interpreter, operands);
3471
3472 evict_caches();
3473 let result;
3474 #[block]
3475 {
3476 result = evm::run_plain(&mut interpreter);
3477 }
3478
3479 let ControlFlow::Break(halt) = result;
3480 assert!(matches!(halt, Halt::Stop));
3481 assert_eq!(interpreter.stack.len(), 0);
3482 }
3483
3484 #[benchmark(pov_mode = Measured)]
3486 fn evm_div_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3487 use one_limb_division_operands::DENOMINATORS;
3488
3489 let mut rng = Pcg64::seed_from_u64(1337);
3490 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3491
3492 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3493 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3494 let (mut ext, _) = setup.ext();
3495 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3496 setup_stack(&mut interpreter, operands);
3497
3498 evict_caches();
3499 let result;
3500 #[block]
3501 {
3502 result = evm::run_plain(&mut interpreter);
3503 }
3504
3505 let ControlFlow::Break(halt) = result;
3506 assert!(matches!(halt, Halt::Stop));
3507 assert_eq!(interpreter.stack.len(), 0);
3508 }
3509
3510 #[benchmark(pov_mode = Measured)]
3512 fn evm_div_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3513 let mut rng = Pcg64::seed_from_u64(1337);
3514 let operands = (0..r).flat_map(|_| mixed_division_operands::unsigned_operands(&mut rng));
3515
3516 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3517 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3518 let (mut ext, _) = setup.ext();
3519 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3520 setup_stack(&mut interpreter, operands);
3521
3522 evict_caches();
3523 let result;
3524 #[block]
3525 {
3526 result = evm::run_plain(&mut interpreter);
3527 }
3528
3529 let ControlFlow::Break(halt) = result;
3530 assert!(matches!(halt, Halt::Stop));
3531 assert_eq!(interpreter.stack.len(), 0);
3532 }
3533
3534 #[benchmark(pov_mode = Measured)]
3536 fn evm_div_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3537 use knuth_division_operands::{DIV_FIXED_DIVISOR, NUMERATOR};
3538
3539 let operands = (0..r).flat_map(|_| [DIV_FIXED_DIVISOR, NUMERATOR]);
3540 let code = Bytecode::new_raw([DIV, POP].repeat(r as usize).into());
3541 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3542 let (mut ext, _) = setup.ext();
3543 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3544 setup_stack(&mut interpreter, operands);
3545
3546 evict_caches();
3547 let result;
3548 #[block]
3549 {
3550 result = evm::run_plain(&mut interpreter);
3551 }
3552
3553 let ControlFlow::Break(halt) = result;
3554 assert!(matches!(halt, Halt::Stop));
3555 assert_eq!(interpreter.stack.len(), 0);
3556 }
3557
3558 #[benchmark(pov_mode = Measured)]
3571 fn evm_sdiv_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3572 use knuth_division_operands::DENOMINATORS;
3573
3574 let mut rng = Pcg64::seed_from_u64(1337);
3575 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3576
3577 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3578 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3579 let (mut ext, _) = setup.ext();
3580 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3581 setup_stack(&mut interpreter, operands);
3582
3583 evict_caches();
3584 let result;
3585 #[block]
3586 {
3587 result = evm::run_plain(&mut interpreter);
3588 }
3589
3590 let ControlFlow::Break(halt) = result;
3591 assert!(matches!(halt, Halt::Stop));
3592 assert_eq!(interpreter.stack.len(), 0);
3593 }
3594
3595 #[benchmark(pov_mode = Measured)]
3597 fn evm_sdiv_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3598 use one_limb_division_operands::DENOMINATORS;
3599
3600 let mut rng = Pcg64::seed_from_u64(1337);
3601 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3602
3603 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3604 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3605 let (mut ext, _) = setup.ext();
3606 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3607 setup_stack(&mut interpreter, operands);
3608
3609 evict_caches();
3610 let result;
3611 #[block]
3612 {
3613 result = evm::run_plain(&mut interpreter);
3614 }
3615
3616 let ControlFlow::Break(halt) = result;
3617 assert!(matches!(halt, Halt::Stop));
3618 assert_eq!(interpreter.stack.len(), 0);
3619 }
3620
3621 #[benchmark(pov_mode = Measured)]
3623 fn evm_sdiv_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3624 let mut rng = Pcg64::seed_from_u64(1337);
3625 let operands = (0..r).flat_map(|_| mixed_division_operands::signed_operands(&mut rng));
3626
3627 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3628 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3629 let (mut ext, _) = setup.ext();
3630 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3631 setup_stack(&mut interpreter, operands);
3632
3633 evict_caches();
3634 let result;
3635 #[block]
3636 {
3637 result = evm::run_plain(&mut interpreter);
3638 }
3639
3640 let ControlFlow::Break(halt) = result;
3641 assert!(matches!(halt, Halt::Stop));
3642 assert_eq!(interpreter.stack.len(), 0);
3643 }
3644
3645 #[benchmark(pov_mode = Measured)]
3647 fn evm_sdiv_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3648 use knuth_division_operands::{SDIV_FIXED_DIVISOR, fixed_signed_operands};
3649
3650 let mut rng = Pcg64::seed_from_u64(1337);
3651 let operands = (0..r).flat_map(|_| fixed_signed_operands(&mut rng, SDIV_FIXED_DIVISOR));
3652
3653 let code = Bytecode::new_raw([SDIV, POP].repeat(r as usize).into());
3654 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3655 let (mut ext, _) = setup.ext();
3656 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3657 setup_stack(&mut interpreter, operands);
3658
3659 evict_caches();
3660 let result;
3661 #[block]
3662 {
3663 result = evm::run_plain(&mut interpreter);
3664 }
3665
3666 let ControlFlow::Break(halt) = result;
3667 assert!(matches!(halt, Halt::Stop));
3668 assert_eq!(interpreter.stack.len(), 0);
3669 }
3670
3671 #[benchmark(pov_mode = Measured)]
3679 fn evm_mod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3680 use knuth_division_operands::DENOMINATORS;
3681
3682 let mut rng = Pcg64::seed_from_u64(1337);
3683 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3684
3685 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3686 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3687 let (mut ext, _) = setup.ext();
3688 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3689 setup_stack(&mut interpreter, operands);
3690
3691 evict_caches();
3692 let result;
3693 #[block]
3694 {
3695 result = evm::run_plain(&mut interpreter);
3696 }
3697
3698 let ControlFlow::Break(halt) = result;
3699 assert!(matches!(halt, Halt::Stop));
3700 assert_eq!(interpreter.stack.len(), 0);
3701 }
3702
3703 #[benchmark(pov_mode = Measured)]
3705 fn evm_mod_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3706 use one_limb_division_operands::DENOMINATORS;
3707
3708 let mut rng = Pcg64::seed_from_u64(1337);
3709 let operands = (0..r).flat_map(|_| unsigned_division_operands(&mut rng, &DENOMINATORS));
3710
3711 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3712 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3713 let (mut ext, _) = setup.ext();
3714 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3715 setup_stack(&mut interpreter, operands);
3716
3717 evict_caches();
3718 let result;
3719 #[block]
3720 {
3721 result = evm::run_plain(&mut interpreter);
3722 }
3723
3724 let ControlFlow::Break(halt) = result;
3725 assert!(matches!(halt, Halt::Stop));
3726 assert_eq!(interpreter.stack.len(), 0);
3727 }
3728
3729 #[benchmark(pov_mode = Measured)]
3731 fn evm_mod_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3732 let mut rng = Pcg64::seed_from_u64(1337);
3733 let operands = (0..r).flat_map(|_| mixed_division_operands::unsigned_operands(&mut rng));
3734
3735 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3736 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3737 let (mut ext, _) = setup.ext();
3738 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3739 setup_stack(&mut interpreter, operands);
3740
3741 evict_caches();
3742 let result;
3743 #[block]
3744 {
3745 result = evm::run_plain(&mut interpreter);
3746 }
3747
3748 let ControlFlow::Break(halt) = result;
3749 assert!(matches!(halt, Halt::Stop));
3750 assert_eq!(interpreter.stack.len(), 0);
3751 }
3752
3753 #[benchmark(pov_mode = Measured)]
3755 fn evm_mod_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3756 use knuth_division_operands::{MOD_FIXED_DIVISOR, NUMERATOR};
3757
3758 let operands = (0..r).flat_map(|_| [MOD_FIXED_DIVISOR, NUMERATOR]);
3759 let code = Bytecode::new_raw([MOD, POP].repeat(r as usize).into());
3760 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3761 let (mut ext, _) = setup.ext();
3762 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3763 setup_stack(&mut interpreter, operands);
3764
3765 evict_caches();
3766 let result;
3767 #[block]
3768 {
3769 result = evm::run_plain(&mut interpreter);
3770 }
3771
3772 let ControlFlow::Break(halt) = result;
3773 assert!(matches!(halt, Halt::Stop));
3774 assert_eq!(interpreter.stack.len(), 0);
3775 }
3776
3777 #[benchmark(pov_mode = Measured)]
3787 fn evm_smod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3788 use knuth_division_operands::DENOMINATORS;
3789
3790 let mut rng = Pcg64::seed_from_u64(1337);
3791 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3792
3793 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3794 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3795 let (mut ext, _) = setup.ext();
3796 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3797 setup_stack(&mut interpreter, operands);
3798
3799 evict_caches();
3800 let result;
3801 #[block]
3802 {
3803 result = evm::run_plain(&mut interpreter);
3804 }
3805
3806 let ControlFlow::Break(halt) = result;
3807 assert!(matches!(halt, Halt::Stop));
3808 assert_eq!(interpreter.stack.len(), 0);
3809 }
3810
3811 #[benchmark(pov_mode = Measured)]
3813 fn evm_smod_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3814 use one_limb_division_operands::DENOMINATORS;
3815
3816 let mut rng = Pcg64::seed_from_u64(1337);
3817 let operands = (0..r).flat_map(|_| signed_division_operands(&mut rng, &DENOMINATORS));
3818
3819 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3820 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3821 let (mut ext, _) = setup.ext();
3822 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3823 setup_stack(&mut interpreter, operands);
3824
3825 evict_caches();
3826 let result;
3827 #[block]
3828 {
3829 result = evm::run_plain(&mut interpreter);
3830 }
3831
3832 let ControlFlow::Break(halt) = result;
3833 assert!(matches!(halt, Halt::Stop));
3834 assert_eq!(interpreter.stack.len(), 0);
3835 }
3836
3837 #[benchmark(pov_mode = Measured)]
3839 fn evm_smod_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3840 let mut rng = Pcg64::seed_from_u64(1337);
3841 let operands = (0..r).flat_map(|_| mixed_division_operands::signed_operands(&mut rng));
3842
3843 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3844 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3845 let (mut ext, _) = setup.ext();
3846 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3847 setup_stack(&mut interpreter, operands);
3848
3849 evict_caches();
3850 let result;
3851 #[block]
3852 {
3853 result = evm::run_plain(&mut interpreter);
3854 }
3855
3856 let ControlFlow::Break(halt) = result;
3857 assert!(matches!(halt, Halt::Stop));
3858 assert_eq!(interpreter.stack.len(), 0);
3859 }
3860
3861 #[benchmark(pov_mode = Measured)]
3863 fn evm_smod_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 2 }>) {
3864 use knuth_division_operands::{SMOD_FIXED_DIVISOR, fixed_signed_operands};
3865
3866 let mut rng = Pcg64::seed_from_u64(1337);
3867 let operands = (0..r).flat_map(|_| fixed_signed_operands(&mut rng, SMOD_FIXED_DIVISOR));
3868
3869 let code = Bytecode::new_raw([SMOD, POP].repeat(r as usize).into());
3870 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3871 let (mut ext, _) = setup.ext();
3872 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3873 setup_stack(&mut interpreter, operands);
3874
3875 evict_caches();
3876 let result;
3877 #[block]
3878 {
3879 result = evm::run_plain(&mut interpreter);
3880 }
3881
3882 let ControlFlow::Break(halt) = result;
3883 assert!(matches!(halt, Halt::Stop));
3884 assert_eq!(interpreter.stack.len(), 0);
3885 }
3886
3887 #[benchmark(pov_mode = Measured)]
3898 fn evm_addmod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3899 use knuth_division_operands::DENOMINATORS;
3900
3901 let mut rng = Pcg64::seed_from_u64(1337);
3902 let operands = (0..r).flat_map(|_| {
3903 let [modulus, numerator] = unsigned_division_operands(&mut rng, &DENOMINATORS);
3904 [modulus, numerator, numerator]
3905 });
3906
3907 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3908 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3909 let (mut ext, _) = setup.ext();
3910 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3911 setup_stack(&mut interpreter, operands);
3912
3913 evict_caches();
3914 let result;
3915 #[block]
3916 {
3917 result = evm::run_plain(&mut interpreter);
3918 }
3919
3920 let ControlFlow::Break(halt) = result;
3921 assert!(matches!(halt, Halt::Stop));
3922 assert_eq!(interpreter.stack.len(), 0);
3923 }
3924
3925 #[benchmark(pov_mode = Measured)]
3927 fn evm_addmod_opcode_one_limb_variant(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3928 use one_limb_division_operands::DENOMINATORS;
3929
3930 let mut rng = Pcg64::seed_from_u64(1337);
3931 let operands = (0..r).flat_map(|_| {
3932 let [modulus, numerator] = unsigned_division_operands(&mut rng, &DENOMINATORS);
3933 [modulus, numerator, numerator]
3934 });
3935
3936 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3937 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3938 let (mut ext, _) = setup.ext();
3939 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3940 setup_stack(&mut interpreter, operands);
3941
3942 evict_caches();
3943 let result;
3944 #[block]
3945 {
3946 result = evm::run_plain(&mut interpreter);
3947 }
3948
3949 let ControlFlow::Break(halt) = result;
3950 assert!(matches!(halt, Halt::Stop));
3951 assert_eq!(interpreter.stack.len(), 0);
3952 }
3953
3954 #[benchmark(pov_mode = Measured)]
3956 fn evm_addmod_opcode_mixed_variant(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3957 let mut rng = Pcg64::seed_from_u64(1337);
3958 let operands = (0..r).flat_map(|_| {
3959 let [modulus, numerator] = mixed_division_operands::unsigned_operands(&mut rng);
3960 [modulus, numerator, numerator]
3961 });
3962
3963 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3964 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3965 let (mut ext, _) = setup.ext();
3966 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3967 setup_stack(&mut interpreter, operands);
3968
3969 evict_caches();
3970 let result;
3971 #[block]
3972 {
3973 result = evm::run_plain(&mut interpreter);
3974 }
3975
3976 let ControlFlow::Break(halt) = result;
3977 assert!(matches!(halt, Halt::Stop));
3978 assert_eq!(interpreter.stack.len(), 0);
3979 }
3980
3981 #[benchmark(pov_mode = Measured)]
3983 fn evm_addmod_opcode_fixed_knuth_variant(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
3984 use knuth_division_operands::{ADDMOD_FIXED_DIVISOR, NUMERATOR};
3985
3986 let operands = (0..r).flat_map(|_| [ADDMOD_FIXED_DIVISOR, NUMERATOR, NUMERATOR]);
3987 let code = Bytecode::new_raw([ADDMOD, POP].repeat(r as usize).into());
3988 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
3989 let (mut ext, _) = setup.ext();
3990 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
3991 setup_stack(&mut interpreter, operands);
3992
3993 evict_caches();
3994 let result;
3995 #[block]
3996 {
3997 result = evm::run_plain(&mut interpreter);
3998 }
3999
4000 let ControlFlow::Break(halt) = result;
4001 assert!(matches!(halt, Halt::Stop));
4002 assert_eq!(interpreter.stack.len(), 0);
4003 }
4004
4005 #[benchmark(pov_mode = Measured)]
4020 fn evm_mulmod_opcode(r: Linear<0, { EVM_STACK_LIMIT / 3 }>) {
4021 let top_bit = U256::one() << 255;
4022 let reciprocal_adjustments = |modulus: U256| {
4026 let top = (modulus << modulus.leading_zeros() as usize) >> 128;
4027 let (high, low) = (top.low_u128() >> 64, u128::from(top.low_u64()));
4028 [low > u128::MAX % high, (U256::MAX >> 64) / top < U256::from((u128::MAX - low) / high)]
4029 };
4030 let mut rng = Pcg64::seed_from_u64(1337);
4031 let operands = (0..r).flat_map(|_| {
4032 let limbs = if rng.gen_bool(0.5) { 3 } else { 4 };
4033 let adjustments = [rng.gen_bool(0.5), rng.gen_bool(0.5)];
4034 let modulus = loop {
4035 let modulus = (U256(rng.r#gen()) | top_bit) >> (2 + 64 * (4 - limbs));
4036 if reciprocal_adjustments(modulus) == adjustments {
4037 break modulus;
4038 }
4039 };
4040 let a = U256(rng.r#gen()) | top_bit | U256::one();
4041 let mut b = U256(rng.r#gen()) | top_bit | U256::one();
4042 for limb in 1..=2 {
4043 if rng.gen_bool(0.5) {
4044 b.0[limb] = 0;
4045 }
4046 }
4047 [modulus, b, a]
4048 });
4049
4050 let code = Bytecode::new_raw([MULMOD, POP].repeat(r as usize).into());
4051 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_noop(0));
4052 let (mut ext, _) = setup.ext();
4053 let mut interpreter = Interpreter::new(ExtBytecode::new(code), Vec::new(), &mut ext);
4054 setup_stack(&mut interpreter, operands);
4055
4056 evict_caches();
4057 let result;
4058 #[block]
4059 {
4060 result = evm::run_plain(&mut interpreter);
4061 }
4062
4063 let ControlFlow::Break(halt) = result;
4064 assert!(matches!(halt, Halt::Stop));
4065 assert_eq!(interpreter.stack.len(), 0);
4066 }
4067
4068 #[benchmark(pov_mode = Ignored)]
4073 fn instr(r: Linear<0, 10_000>) {
4074 use rand::{SeedableRng, seq::SliceRandom};
4075 use rand_pcg::Pcg64;
4076
4077 const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
4079
4080 const CACHE_LINE_SIZE: u64 = 64;
4082
4083 const MISALIGNMENT: u64 = 60;
4085
4086 const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
4089
4090 assert!(
4091 u64::from(r) <= NUM_ADDRESSES / 2,
4092 "If we do too many iterations we run into the risk of loading from warm cache lines",
4093 );
4094
4095 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
4096 let (mut ext, module) = setup.ext();
4097 let mut prepared =
4098 CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
4099
4100 assert!(
4101 u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
4102 "aux data base must be cache aligned"
4103 );
4104
4105 let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
4107
4108 let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
4112 for i in 1..NUM_ADDRESSES {
4113 let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
4114 addresses.push(addr);
4115 }
4116 let mut rng = Pcg64::seed_from_u64(1337);
4117 addresses.shuffle(&mut rng);
4118
4119 let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
4121 for address in addresses {
4122 memory.extend_from_slice(&address);
4123 memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
4124 }
4125
4126 prepared
4129 .setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
4130 .unwrap();
4131
4132 #[block]
4133 {
4134 prepared.call().unwrap();
4135 }
4136 }
4137
4138 #[benchmark(pov_mode = Ignored)]
4139 fn instr_empty_loop(r: Linear<0, 10_000>) {
4140 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
4141 let (mut ext, module) = setup.ext();
4142 let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
4143 prepared.setup_aux_data(&[], 0, r.into()).unwrap();
4144
4145 #[block]
4146 {
4147 prepared.call().unwrap();
4148 }
4149 }
4150
4151 #[benchmark(pov_mode = Measured)]
4152 fn extcodecopy(n: Linear<1_000, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
4153 let mut setup = CallSetup::<T>::new(VmBinaryModule::dummy());
4155 let target = Contract::<T>::with_index(1, VmBinaryModule::sized(n), vec![])?;
4157
4158 let (mut ext, _) = setup.ext();
4159 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
4160
4161 let _ = interpreter.stack.push(U256::from(n));
4163 let _ = interpreter.stack.push(U256::from(0u32));
4164 let _ = interpreter.stack.push(U256::from(0u32));
4165 let _ = interpreter.stack.push(target.address);
4166
4167 let result;
4168 #[block]
4169 {
4170 result = instructions::host::extcodecopy(&mut interpreter);
4171 }
4172
4173 assert!(result.is_continue());
4174 assert_eq!(
4175 *interpreter.memory.slice(0..n as usize),
4176 PristineCode::<T>::get(target.info()?.code_hash).unwrap()[0..n as usize],
4177 "Memory should contain the target contract's code after extcodecopy"
4178 );
4179
4180 Ok(())
4181 }
4182
4183 #[benchmark]
4184 fn v1_migration_step() {
4185 use crate::migrations::v1;
4186 let addr = H160::from([1u8; 20]);
4187 let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
4188
4189 v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
4190 let mut meter = WeightMeter::new();
4191 assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
4192
4193 #[block]
4194 {
4195 v1::Migration::<T>::step(None, &mut meter).unwrap();
4196 }
4197
4198 assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
4199 assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
4200
4201 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
4203 }
4204
4205 #[benchmark]
4206 fn v2_migration_step() {
4207 use crate::migrations::v2;
4208 let code_hash = H256::from([0; 32]);
4209 let old_code_info = v2::Migration::<T>::create_old_code_info(
4210 whitelisted_caller(),
4211 1000u32.into(),
4212 1,
4213 100,
4214 0,
4215 );
4216 v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
4217 let mut meter = WeightMeter::new();
4218
4219 #[block]
4220 {
4221 v2::Migration::<T>::step(None, &mut meter).unwrap();
4222 }
4223
4224 v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
4225
4226 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
4228 }
4229
4230 #[benchmark]
4231 fn v3_migration_step() {
4232 use crate::migrations::v3;
4233 let _ = frame_system::Account::<T>::clear(u32::MAX, None);
4235
4236 let account = account::<T::AccountId>("target", 0, 0);
4237 T::Currency::mint_into(&account, Pallet::<T>::min_balance())
4238 .expect("should mint into account");
4239
4240 let addr = T::AddressMapper::to_address(&account);
4242 crate::OriginalAccount::<T>::remove(addr);
4243
4244 assert!(!T::AddressMapper::is_mapped(&account));
4245 let mut meter = WeightMeter::new();
4246
4247 #[block]
4248 {
4249 v3::Migration::<T>::step(None, &mut meter).unwrap();
4250 }
4251
4252 assert!(T::AddressMapper::is_mapped(&account));
4253
4254 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v3_migration_step() * 2);
4256 }
4257
4258 #[benchmark]
4263 fn v4_code_upload_step() {
4264 use crate::migrations::v4;
4265
4266 let _ = CodeInfoOf::<T>::clear(u32::MAX, None);
4267
4268 let owner: T::AccountId = whitelisted_caller();
4269 let deposit: BalanceOf<T> = 1_000u32.into();
4270
4271 let pallet_account = Pallet::<T>::account_id();
4272 T::Currency::mint_into(&pallet_account, Pallet::<T>::min_balance()).unwrap();
4273 T::Currency::mint_into(&pallet_account, deposit).unwrap();
4274 T::Currency::hold(&HoldReason::CodeUploadDepositReserve.into(), &pallet_account, deposit)
4275 .unwrap();
4276
4277 CodeInfoOf::<T>::insert(
4278 H256::from([1u8; 32]),
4279 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
4280 );
4281 CodeInfoOf::<T>::insert(
4282 H256::from([2u8; 32]),
4283 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
4284 );
4285
4286 let first = match v4::Migration::<T>::step_once(None) {
4287 Some(v4::Cursor::CodeUpload(h)) => h,
4288 other => panic!("expected CodeUpload cursor, got {other:?}"),
4289 };
4290 let cursor = Some(v4::Cursor::CodeUpload(first));
4291
4292 #[block]
4293 {
4294 let _ = v4::Migration::<T>::step_once(cursor);
4295 }
4296
4297 assert_eq!(
4298 NativeDepositOf::<T>::get(&pallet_account, &owner),
4299 deposit + deposit,
4300 "both code uploads credited to owner",
4301 );
4302 }
4303
4304 #[benchmark]
4309 fn v4_contract_step() {
4310 use crate::migrations::v4;
4311
4312 let _ = AccountInfoOf::<T>::clear(u32::MAX, None);
4313
4314 let code_hash = H256::from([0u8; 32]);
4315 let deposit: BalanceOf<T> = 1_000u32.into();
4316
4317 for byte in [0x41u8, 0x42u8] {
4318 let addr = H160::from([byte; 20]);
4319 let contract_account = T::AddressMapper::to_account_id(&addr);
4320 let info =
4321 ContractInfo::<T>::new(&addr, 1u32.into(), code_hash).expect("fresh contract info");
4322 AccountInfoOf::<T>::insert(
4323 addr,
4324 crate::storage::AccountInfo::<T> {
4325 account_type: crate::storage::AccountType::Contract(info),
4326 dust: 0,
4327 },
4328 );
4329 T::Currency::mint_into(&contract_account, Pallet::<T>::min_balance()).unwrap();
4330 T::Currency::mint_into(&contract_account, deposit).unwrap();
4331 T::Currency::hold(
4332 &HoldReason::StorageDepositReserve.into(),
4333 &contract_account,
4334 deposit,
4335 )
4336 .unwrap();
4337 }
4338
4339 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::Contract(None))) {
4340 Some(v4::Cursor::Contract(Some(addr))) => addr,
4341 other => panic!("expected Contract cursor, got {other:?}"),
4342 };
4343 let cursor = Some(v4::Cursor::Contract(Some(first)));
4344
4345 #[block]
4346 {
4347 let _ = v4::Migration::<T>::step_once(cursor);
4348 }
4349
4350 if T::Deposit::SUPPORTS_PGAS {
4353 for byte in [0x41u8, 0x42u8] {
4354 let addr = H160::from([byte; 20]);
4355 let contract_account = T::AddressMapper::to_account_id(&addr);
4356 assert_eq!(
4357 T::Currency::balance_on_hold(
4358 &HoldReason::StorageDepositReserve.into(),
4359 &contract_account,
4360 ),
4361 0u32.into(),
4362 "native storage deposit burned for {addr:?}",
4363 );
4364 }
4365 }
4366 }
4367
4368 #[benchmark]
4374 fn v4_deletion_queue_step() {
4375 use crate::migrations::v4;
4376
4377 let _ = v4::old::DeletionQueue::<T>::clear(u32::MAX, None);
4378
4379 let trie_a: TrieId = vec![0xAAu8; 16].try_into().unwrap();
4380 let trie_b: TrieId = vec![0xBBu8; 24].try_into().unwrap();
4381 v4::old::DeletionQueue::<T>::insert(0u32, trie_a);
4382 v4::old::DeletionQueue::<T>::insert(1u32, trie_b);
4383
4384 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::DeletionQueue(None))) {
4385 Some(v4::Cursor::DeletionQueue(Some(key))) => key,
4386 other => panic!("expected DeletionQueue cursor, got {other:?}"),
4387 };
4388 let cursor = Some(v4::Cursor::DeletionQueue(Some(first)));
4389
4390 #[block]
4391 {
4392 let _ = v4::Migration::<T>::step_once(cursor);
4393 }
4394
4395 assert!(
4396 DeletionQueue::<T>::get(0u32).is_some() && DeletionQueue::<T>::get(1u32).is_some(),
4397 "both legacy entries rewritten into the new format",
4398 );
4399 }
4400
4401 fn create_test_signer<T: Config>() -> (T::AccountId, SigningKey, H160) {
4403 use hex_literal::hex;
4404 let signer_account_id = hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
4406 let signer_priv_key =
4407 hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133");
4408
4409 let signer_key = SigningKey::from_bytes(&signer_priv_key.into()).expect("valid key");
4410
4411 let signer_address = H160::from_slice(&signer_account_id);
4412 let signer_caller = T::AddressMapper::to_fallback_account_id(&signer_address);
4413
4414 (signer_caller, signer_key, signer_address)
4415 }
4416
4417 fn create_signed_transaction<T: Config>(
4419 signer_key: &SigningKey,
4420 target_address: H160,
4421 value: U256,
4422 input_data: Vec<u8>,
4423 ) -> Vec<u8> {
4424 let unsigned_tx: TransactionUnsigned = TransactionLegacyUnsigned {
4425 to: Some(target_address),
4426 value,
4427 chain_id: Some(T::ChainId::get().into()),
4428 input: input_data.into(),
4429 ..Default::default()
4430 }
4431 .into();
4432
4433 let hashed_payload = sp_io::hashing::keccak_256(&unsigned_tx.unsigned_payload());
4434 let (signature, recovery_id) =
4435 signer_key.sign_prehash_recoverable(&hashed_payload).expect("signing success");
4436
4437 let mut sig_bytes = [0u8; 65];
4438 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
4439 sig_bytes[64] = recovery_id.to_byte();
4440
4441 let signed_tx = unsigned_tx.with_signature(sig_bytes);
4442
4443 signed_tx.signed_payload()
4444 }
4445
4446 fn setup_finalize_block_benchmark<T>()
4448 -> Result<(Contract<T>, BalanceOf<T>, U256, SigningKey, BlockNumberFor<T>), BenchmarkError>
4449 where
4450 BalanceOf<T>: Into<U256> + TryFrom<U256>,
4451 T: Config,
4452 MomentOf<T>: Into<U256>,
4453 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
4454 {
4455 let (signer_caller, signer_key, _signer_address) = create_test_signer::<T>();
4457 whitelist_account!(signer_caller);
4458
4459 let instance =
4461 Contract::<T>::with_caller(signer_caller.clone(), VmBinaryModule::dummy(), vec![])?;
4462 let storage_deposit = default_deposit_limit::<T>();
4463 let value = Pallet::<T>::min_balance();
4464 let evm_value =
4465 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, 0));
4466
4467 let current_block = BlockNumberFor::<T>::from(1u32);
4469 frame_system::Pallet::<T>::set_block_number(current_block);
4470
4471 Pallet::<T>::clear_outside_frame_logs();
4475
4476 Ok((instance, storage_deposit, evm_value, signer_key, current_block))
4477 }
4478
4479 #[benchmark(pov_mode = Measured)]
4496 fn on_finalize_per_transaction(n: Linear<0, 200>) -> Result<(), BenchmarkError> {
4497 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4498 setup_finalize_block_benchmark::<T>()?;
4499
4500 let fixed_payload_size = 100usize;
4502
4503 if n > 0 {
4505 let _ = Pallet::<T>::on_initialize(current_block);
4507
4508 let input_data = vec![0x42u8; fixed_payload_size];
4510 let receipt_gas_info = ReceiptGasInfo {
4511 gas_used: U256::from(1_000_000),
4512 effective_gas_price: Pallet::<T>::evm_base_fee(),
4513 };
4514
4515 for _ in 0..n {
4516 let signed_transaction = create_signed_transaction::<T>(
4518 &signer_key,
4519 instance.address,
4520 evm_value,
4521 input_data.clone(),
4522 );
4523
4524 let _ = block_storage::bench_with_ethereum_context(|| {
4526 let (encoded_logs, bloom) =
4527 block_storage::get_receipt_details::<T>().unwrap_or_default();
4528
4529 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4530 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4531
4532 block_builder.process_transaction(
4533 signed_transaction,
4534 true,
4535 receipt_gas_info.clone(),
4536 encoded_logs,
4537 bloom,
4538 );
4539
4540 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4541 });
4542 }
4543 }
4544
4545 #[block]
4546 {
4547 let _ = Pallet::<T>::on_finalize(current_block);
4549 }
4550
4551 assert_eq!(Pallet::<T>::eth_block().transactions.len(), n as usize);
4553
4554 Ok(())
4555 }
4556
4557 #[benchmark(pov_mode = Measured)]
4574 fn on_finalize_per_transaction_data(d: Linear<0, 1000>) -> Result<(), BenchmarkError> {
4575 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4576 setup_finalize_block_benchmark::<T>()?;
4577
4578 let fixed_tx_count = 10u32;
4580
4581 let _ = Pallet::<T>::on_initialize(current_block);
4583
4584 let input_data = vec![0x42u8; d as usize];
4586 let receipt_gas_info = ReceiptGasInfo {
4587 gas_used: U256::from(1_000_000),
4588 effective_gas_price: Pallet::<T>::evm_base_fee(),
4589 };
4590
4591 for _ in 0..fixed_tx_count {
4592 let signed_transaction = create_signed_transaction::<T>(
4594 &signer_key,
4595 instance.address,
4596 evm_value,
4597 input_data.clone(),
4598 );
4599
4600 let _ = block_storage::bench_with_ethereum_context(|| {
4602 let (encoded_logs, bloom) =
4603 block_storage::get_receipt_details::<T>().unwrap_or_default();
4604
4605 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4606 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4607
4608 block_builder.process_transaction(
4609 signed_transaction,
4610 true,
4611 receipt_gas_info.clone(),
4612 encoded_logs,
4613 bloom,
4614 );
4615
4616 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4617 });
4618 }
4619
4620 #[block]
4621 {
4622 let _ = Pallet::<T>::on_finalize(current_block);
4624 }
4625
4626 assert_eq!(Pallet::<T>::eth_block().transactions.len(), fixed_tx_count as usize);
4628
4629 Ok(())
4630 }
4631
4632 #[benchmark(pov_mode = Measured)]
4650 fn on_finalize_per_event(e: Linear<0, 100>) -> Result<(), BenchmarkError> {
4651 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4652 setup_finalize_block_benchmark::<T>()?;
4653
4654 let input_data = vec![0x42u8; 100];
4656 let signed_transaction = create_signed_transaction::<T>(
4657 &signer_key,
4658 instance.address,
4659 evm_value,
4660 input_data.clone(),
4661 );
4662
4663 let receipt_gas_info = ReceiptGasInfo {
4664 gas_used: U256::from(1_000_000),
4665 effective_gas_price: Pallet::<T>::evm_base_fee(),
4666 };
4667
4668 let _ = block_storage::bench_with_ethereum_context(|| {
4670 for _ in 0..e {
4674 block_storage::capture_frame_log::<T>(&instance.address, &vec![], &vec![]);
4675 }
4676
4677 let (encoded_logs, bloom) =
4678 block_storage::get_receipt_details::<T>().unwrap_or_default();
4679
4680 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4681 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4682
4683 block_builder.process_transaction(
4684 signed_transaction,
4685 true,
4686 receipt_gas_info.clone(),
4687 encoded_logs,
4688 bloom,
4689 );
4690
4691 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4692 });
4693
4694 #[block]
4695 {
4696 let _ = Pallet::<T>::on_initialize(current_block);
4698
4699 let _ = Pallet::<T>::on_finalize(current_block);
4701 }
4702
4703 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
4706
4707 Ok(())
4708 }
4709
4710 #[benchmark(pov_mode = Measured)]
4719 fn on_finalize_per_event_data(d: Linear<0, 16384>) -> Result<(), BenchmarkError> {
4720 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
4721 setup_finalize_block_benchmark::<T>()?;
4722
4723 let input_data = vec![0x42u8; 100];
4725 let signed_transaction = create_signed_transaction::<T>(
4726 &signer_key,
4727 instance.address,
4728 evm_value,
4729 input_data.clone(),
4730 );
4731
4732 let receipt_gas_info = ReceiptGasInfo {
4733 gas_used: U256::from(1_000_000),
4734 effective_gas_price: Pallet::<T>::evm_base_fee(),
4735 };
4736
4737 let (event_data, topics) = if d < 32 {
4739 (vec![0x42u8; d as usize], vec![])
4741 } else {
4742 let num_topics = core::cmp::min(limits::NUM_EVENT_TOPICS, d / 32);
4744 let topic_bytes_used = num_topics * 32;
4745 let data_bytes_remaining = d - topic_bytes_used;
4746
4747 let mut topics = Vec::new();
4749 for topic_index in 0..num_topics {
4750 let topic_data = [topic_index as u8; 32];
4751 topics.push(H256::from(topic_data));
4752 }
4753
4754 let event_data = vec![0x42u8; data_bytes_remaining as usize];
4756
4757 (event_data, topics)
4758 };
4759
4760 let _ = block_storage::bench_with_ethereum_context(|| {
4762 block_storage::capture_frame_log::<T>(&instance.address, &event_data, &topics);
4767
4768 let (encoded_logs, bloom) =
4769 block_storage::get_receipt_details::<T>().unwrap_or_default();
4770
4771 let block_builder_ir = EthBlockBuilderIR::<T>::get();
4772 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
4773
4774 block_builder.process_transaction(
4775 signed_transaction,
4776 true,
4777 receipt_gas_info,
4778 encoded_logs,
4779 bloom,
4780 );
4781
4782 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
4783 });
4784
4785 #[block]
4786 {
4787 let _ = Pallet::<T>::on_initialize(current_block);
4789
4790 let _ = Pallet::<T>::on_finalize(current_block);
4792 }
4793
4794 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
4797
4798 Ok(())
4799 }
4800
4801 #[benchmark(pov_mode = Measured)]
4812 fn outside_frame_log(n: Linear<0, 100>) -> Result<(), BenchmarkError> {
4813 let (instance, _storage_deposit, _evm_value, _signer_key, current_block) =
4814 setup_finalize_block_benchmark::<T>()?;
4815
4816 let _ = Pallet::<T>::on_initialize(current_block);
4818
4819 let topics =
4821 vec![H256::repeat_byte(0x11), H256::repeat_byte(0x22), H256::repeat_byte(0x33)];
4822 let data = vec![0x44u8; 32];
4823
4824 for _ in 0..n {
4827 Pallet::<T>::bench_buffer_outside_frame_log(
4828 instance.address,
4829 topics.clone(),
4830 data.clone(),
4831 );
4832 }
4833
4834 #[block]
4835 {
4836 let _ = Pallet::<T>::on_finalize(current_block);
4837 }
4838
4839 assert_eq!(Pallet::<T>::eth_block().transactions.len(), (n > 0) as usize);
4841
4842 Ok(())
4843 }
4844
4845 #[benchmark(pov_mode = Measured)]
4854 fn outside_frame_log_data(d: Linear<0, { limits::EVENT_BYTES }>) -> Result<(), BenchmarkError> {
4855 let (instance, _storage_deposit, _evm_value, _signer_key, current_block) =
4856 setup_finalize_block_benchmark::<T>()?;
4857
4858 let _ = Pallet::<T>::on_initialize(current_block);
4859
4860 let topics =
4861 vec![H256::repeat_byte(0x11), H256::repeat_byte(0x22), H256::repeat_byte(0x33)];
4862 let data = vec![0x44u8; d as usize];
4863
4864 Pallet::<T>::bench_buffer_outside_frame_log(instance.address, topics, data);
4865
4866 #[block]
4867 {
4868 let _ = Pallet::<T>::on_finalize(current_block);
4869 }
4870
4871 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
4872
4873 Ok(())
4874 }
4875
4876 impl_benchmark_test_suite!(
4877 Contracts,
4878 crate::tests::ExtBuilder::default().build(),
4879 crate::tests::Test,
4880 );
4881}