1#![cfg(feature = "runtime-benchmarks")]
21use crate::{
22 Pallet as Contracts,
23 access_list::{AccessEntry, AccessList, MAX_ACCESS_LIST_ENTRIES},
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::{Key, Origin as ExecOrigin, PrecompileExt},
30 limits,
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::{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 frame_benchmarking::v2::*;
51use frame_support::{
52 self, assert_ok,
53 migrations::SteppedMigration,
54 storage::child,
55 traits::{Hooks, fungible::InspectHold},
56 weights::{Weight, WeightMeter},
57};
58use frame_system::RawOrigin;
59use k256::ecdsa::SigningKey;
60use pallet_revive_uapi::{
61 CallFlags, ReturnErrorCode, StorageFlags, pack_hi_lo,
62 precompiles::{storage::IStorage, system::ISystem},
63};
64use revm::bytecode::Bytecode;
65use sp_consensus_aura::AURA_ENGINE_ID;
66use sp_consensus_babe::{
67 BABE_ENGINE_ID,
68 digests::{PreDigest, PrimaryPreDigest},
69};
70use sp_consensus_slots::Slot;
71use sp_runtime::{generic::DigestItem, traits::Zero};
72
73const API_BENCHMARK_RUNS: u32 = 1600;
79
80macro_rules! memory(
81 ($($bytes:expr,)*) => {{
82 vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::<Vec<_>>()
83 }};
84);
85
86macro_rules! build_runtime(
87 ($runtime:ident, $memory:ident: [$($segment:expr,)*]) => {
88 build_runtime!($runtime, _contract, $memory: [$($segment,)*]);
89 };
90 ($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => {
91 build_runtime!($runtime, $contract);
92 let mut $memory = memory!($($bytes,)*);
93 };
94 ($runtime:ident, $contract:ident) => {
95 let mut setup = CallSetup::<T>::default();
96 let $contract = setup.contract();
97 let input = setup.data();
98 let (mut ext, _) = setup.ext();
99 let mut $runtime = $crate::vm::pvm::Runtime::<_, [u8]>::new(&mut ext, input);
100 };
101);
102
103fn whitelisted_pallet_account<T: Config>() -> T::AccountId {
106 let pallet_account = Pallet::<T>::account_id();
107 whitelist_account!(pallet_account);
108 pallet_account
109}
110
111#[benchmarks(
112 where
113 T: Config,
114 <T as Config>::RuntimeCall: From<frame_system::Call<T>>,
115 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
116 OriginFor<T>: From<Origin<T>>,
117)]
118mod benchmarks {
119 use super::*;
120
121 #[benchmark(pov_mode = Measured)]
123 fn deletion_queue_batch() {
124 #[block]
125 {
126 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
127 }
128 }
129
130 #[benchmark(pov_mode = Measured)]
133 fn deletion_queue_per_entry() -> Result<(), BenchmarkError> {
134 let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
135 ContractInfo::<T>::queue_for_deletion(
136 instance.info()?.trie_id,
137 instance.account_id.clone(),
138 );
139
140 #[block]
141 {
142 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
143 }
144
145 assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
146 Ok(())
147 }
148
149 #[benchmark(skip_meta, pov_mode = Measured)]
150 fn deletion_queue_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
151 let instance =
152 Contract::<T>::with_storage(VmBinaryModule::dummy(), k, limits::STORAGE_BYTES)?;
153 ContractInfo::<T>::queue_for_deletion(
154 instance.info()?.trie_id,
155 instance.account_id.clone(),
156 );
157
158 #[block]
159 {
160 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
161 }
162
163 assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
164 Ok(())
165 }
166
167 #[benchmark(skip_meta, pov_mode = Measured)]
172 fn deletion_queue_per_native_deposit_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
173 use frame_benchmarking::v2::account;
174
175 let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
177 for i in 0..k {
178 let payer: T::AccountId = account("payer", i, 0);
179 NativeDepositOf::<T>::insert(&instance.account_id, &payer, BalanceOf::<T>::default());
180 }
181 ContractInfo::<T>::queue_for_deletion(
182 instance.info()?.trie_id,
183 instance.account_id.clone(),
184 );
185
186 #[block]
187 {
188 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
189 }
190
191 assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
192 Ok(())
193 }
194
195 #[benchmark(pov_mode = Measured)]
207 fn call_with_pvm_code_per_byte(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
208 let instance =
209 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::sized(c), vec![])?;
210 let value = Pallet::<T>::min_balance();
211 let storage_deposit = default_deposit_limit::<T>();
212
213 #[extrinsic_call]
214 call(
215 RawOrigin::Signed(instance.caller.clone()),
216 instance.address,
217 value,
218 Weight::MAX,
219 storage_deposit,
220 vec![],
221 );
222
223 Ok(())
224 }
225
226 #[benchmark(pov_mode = Measured)]
230 fn call_with_evm_code_per_byte(c: Linear<1, { 10 * 1024 }>) -> Result<(), BenchmarkError> {
231 let instance = Contract::<T>::with_caller(
232 whitelisted_caller(),
233 VmBinaryModule::evm_init_code_for_runtime_size(c),
234 vec![],
235 )?;
236 let value = Pallet::<T>::min_balance();
237 let storage_deposit = default_deposit_limit::<T>();
238
239 let code_len = PristineCode::<T>::get(instance.info()?.code_hash)
240 .expect("code should be stored")
241 .len();
242 assert_eq!(
243 code_len, c as usize,
244 "runtime bytecode should be exactly {c} bytes, got {code_len}"
245 );
246
247 #[extrinsic_call]
248 call(
249 RawOrigin::Signed(instance.caller.clone()),
250 instance.address,
251 value,
252 Weight::MAX,
253 storage_deposit,
254 vec![],
255 );
256
257 Ok(())
258 }
259
260 #[benchmark(pov_mode = Measured)]
271 fn basic_block_compilation(b: Linear<0, 1>) -> Result<(), BenchmarkError> {
272 let instance = Contract::<T>::with_caller(
273 whitelisted_caller(),
274 VmBinaryModule::with_num_instructions(limits::code::BASIC_BLOCK_SIZE),
275 vec![],
276 )?;
277 let value = Pallet::<T>::min_balance();
278 let storage_deposit = default_deposit_limit::<T>();
279
280 #[block]
281 {
282 Pallet::<T>::call(
283 RawOrigin::Signed(instance.caller.clone()).into(),
284 instance.address,
285 value,
286 Weight::MAX,
287 storage_deposit,
288 vec![],
289 )?;
290 }
291
292 Ok(())
293 }
294
295 #[benchmark(pov_mode = Measured)]
298 fn instantiate_with_code(
299 c: Linear<0, { 100 * 1024 }>,
300 i: Linear<0, { limits::CALLDATA_BYTES }>,
301 ) {
302 let pallet_account = whitelisted_pallet_account::<T>();
303 let input = vec![42u8; i as usize];
304 let salt = [42u8; 32];
305 let value = Pallet::<T>::min_balance();
306 let caller = whitelisted_caller();
307 T::Currency::set_balance(&caller, caller_funding::<T>());
308 let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
309 let origin = RawOrigin::Signed(caller.clone());
310 if !T::AddressMapper::is_mapped(&caller) {
311 T::AddressMapper::map(&caller).unwrap();
312 }
313 let deployer = T::AddressMapper::to_address(&caller);
314 let addr = crate::address::create2(&deployer, &code, &input, &salt);
315 let account_id = T::AddressMapper::to_fallback_account_id(&addr);
316 let storage_deposit = default_deposit_limit::<T>();
317 #[extrinsic_call]
318 _(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt));
319
320 let deposit =
321 T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
322 let code_deposit = T::Currency::balance_on_hold(
324 &HoldReason::CodeUploadDepositReserve.into(),
325 &pallet_account,
326 );
327 let mapping_deposit =
328 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller);
329 assert_eq!(
330 T::Currency::balance(&caller),
331 caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
332 );
333 assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
335 }
336
337 #[benchmark(pov_mode = Measured)]
341 fn eth_instantiate_with_code(
342 c: Linear<0, { 100 * 1024 }>,
343 i: Linear<0, { limits::CALLDATA_BYTES }>,
344 d: Linear<0, 1>,
345 ) -> Result<(), BenchmarkError> {
346 let input = vec![42u8; i as usize];
347
348 let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
352 let value = Pallet::<T>::min_balance();
353 let dust = 42u32 * d;
354 let evm_value =
355 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
356 let caller = whitelisted_caller();
357 T::Currency::set_balance(&caller, caller_funding::<T>());
358 let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
359 let origin = Origin::EthTransaction(caller.clone());
360 if !T::AddressMapper::is_mapped(&caller) {
361 T::AddressMapper::map(&caller).unwrap();
362 }
363 let deployer = T::AddressMapper::to_address(&caller);
364 let nonce = System::<T>::account_nonce(&caller).try_into().unwrap_or_default();
365 let addr = crate::address::create1(&deployer, nonce);
366
367 assert!(AccountInfoOf::<T>::get(&deployer).is_none());
368
369 <T as Config>::FeeInfo::deposit_txfee(
370 <T as Config>::Currency::issue(caller_funding::<T>()),
371 );
372
373 #[extrinsic_call]
374 _(
375 origin,
376 evm_value,
377 Weight::MAX,
378 U256::MAX,
379 code,
380 input,
381 TransactionSigned::default().signed_payload(),
382 effective_gas_price,
383 0,
384 );
385
386 assert_eq!(Pallet::<T>::evm_balance(&addr), evm_value);
388 Ok(())
389 }
390
391 #[benchmark(pov_mode = Measured)]
392 fn deposit_eth_extrinsic_revert_event() {
393 #[block]
394 {
395 Pallet::<T>::deposit_event(Event::<T>::EthExtrinsicRevert {
396 dispatch_error: crate::Error::<T>::BenchmarkingError.into(),
397 });
398 }
399 }
400
401 #[benchmark(pov_mode = Measured)]
404 fn instantiate(i: Linear<0, { limits::CALLDATA_BYTES }>) -> Result<(), BenchmarkError> {
405 let pallet_account = whitelisted_pallet_account::<T>();
406 let input = vec![42u8; i as usize];
407 let salt = [42u8; 32];
408 let value = Pallet::<T>::min_balance();
409 let caller = whitelisted_caller();
410 T::Currency::set_balance(&caller, caller_funding::<T>());
411 let origin = RawOrigin::Signed(caller.clone());
412 if !T::AddressMapper::is_mapped(&caller) {
413 T::AddressMapper::map(&caller).unwrap();
414 }
415 let VmBinaryModule { code, .. } = VmBinaryModule::dummy();
416 let storage_deposit = default_deposit_limit::<T>();
417 let deployer = T::AddressMapper::to_address(&caller);
418 let addr = crate::address::create2(&deployer, &code, &input, &salt);
419 let hash = Contracts::<T>::bare_upload_code(origin.clone().into(), code, storage_deposit)?
420 .code_hash;
421 let account_id = T::AddressMapper::to_fallback_account_id(&addr);
422
423 #[extrinsic_call]
424 _(origin, value, Weight::MAX, storage_deposit, hash, input, Some(salt));
425
426 let deposit =
427 T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
428 let code_deposit = T::Currency::balance_on_hold(
429 &HoldReason::CodeUploadDepositReserve.into(),
430 &pallet_account,
431 );
432 let mapping_deposit =
433 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &account_id);
434 assert_eq!(
436 T::Currency::total_balance(&caller),
437 caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
438 );
439 assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
441
442 Ok(())
443 }
444
445 #[benchmark(pov_mode = Measured)]
453 fn call() -> Result<(), BenchmarkError> {
454 let pallet_account = whitelisted_pallet_account::<T>();
455 let data = vec![42u8; 1024];
456 let instance =
457 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
458 let value = Pallet::<T>::min_balance();
459 let origin = RawOrigin::Signed(instance.caller.clone());
460 let before = T::Currency::balance(&instance.account_id);
461 let storage_deposit = default_deposit_limit::<T>();
462 #[extrinsic_call]
463 _(origin, instance.address, value, Weight::MAX, storage_deposit, data);
464 let deposit = T::Currency::balance_on_hold(
465 &HoldReason::StorageDepositReserve.into(),
466 &instance.account_id,
467 );
468 let code_deposit = T::Currency::balance_on_hold(
469 &HoldReason::CodeUploadDepositReserve.into(),
470 &pallet_account,
471 );
472 let mapping_deposit =
473 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller);
474 assert_eq!(
476 T::Currency::balance(&instance.caller),
477 caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
478 );
479 assert_eq!(T::Currency::balance(&instance.account_id), before + value);
481 instance.info()?;
483
484 Ok(())
485 }
486
487 #[benchmark(pov_mode = Measured)]
489 fn eth_call(d: Linear<0, 1>) -> Result<(), BenchmarkError> {
490 let data = vec![42u8; 1024];
491 let instance =
492 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
493
494 let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
498 let value = Pallet::<T>::min_balance();
499 let dust = 42u32 * d;
500 let evm_value =
501 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
502
503 <T as Config>::FeeInfo::deposit_txfee(
505 <T as Config>::Currency::issue(caller_funding::<T>()),
506 );
507
508 let origin = Origin::EthTransaction(instance.caller.clone());
509 let before = Pallet::<T>::evm_balance(&instance.address);
510
511 #[extrinsic_call]
512 _(
513 origin,
514 instance.address,
515 evm_value,
516 Weight::MAX,
517 U256::MAX,
518 data,
519 TransactionSigned::default().signed_payload(),
520 effective_gas_price,
521 0,
522 );
523
524 assert_eq!(Pallet::<T>::evm_balance(&instance.address), before + evm_value);
526 instance.info()?;
528
529 Ok(())
530 }
531
532 #[benchmark(pov_mode = Measured)]
534 fn eth_substrate_call(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
535 let caller = whitelisted_caller();
536 T::Currency::set_balance(&caller, caller_funding::<T>());
537 let origin = Origin::EthTransaction(caller);
538 let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
539 #[extrinsic_call]
540 _(origin, Box::new(dispatchable), vec![42u8; c as usize]);
541 Ok(())
542 }
543
544 #[benchmark(pov_mode = Measured)]
548 fn upload_code(c: Linear<0, { 100 * 1024 }>) {
549 let caller = whitelisted_caller();
550 let pallet_account = whitelisted_pallet_account::<T>();
551 T::Currency::set_balance(&caller, caller_funding::<T>());
552 let VmBinaryModule { code, hash, .. } = VmBinaryModule::sized(c);
553 let origin = RawOrigin::Signed(caller.clone());
554 let storage_deposit = default_deposit_limit::<T>();
555 #[extrinsic_call]
556 _(origin, code, storage_deposit);
557 assert!(T::Currency::total_balance_on_hold(&pallet_account) > 0u32.into());
559 assert!(<Contract<T>>::code_exists(&hash));
560 }
561
562 #[benchmark(pov_mode = Measured)]
566 fn remove_code() -> Result<(), BenchmarkError> {
567 let caller = whitelisted_caller();
568 let pallet_account = whitelisted_pallet_account::<T>();
569 T::Currency::set_balance(&caller, caller_funding::<T>());
570 let VmBinaryModule { code, hash, .. } = VmBinaryModule::dummy();
571 let origin = RawOrigin::Signed(caller.clone());
572 let storage_deposit = default_deposit_limit::<T>();
573 let uploaded =
574 <Contracts<T>>::bare_upload_code(origin.clone().into(), code, storage_deposit)?;
575 assert_eq!(uploaded.code_hash, hash);
576 assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&pallet_account));
577 assert!(<Contract<T>>::code_exists(&hash));
578 #[extrinsic_call]
579 _(origin, hash);
580 assert_eq!(T::Currency::total_balance_on_hold(&pallet_account), 0u32.into());
582 assert!(<Contract<T>>::code_removed(&hash));
583 Ok(())
584 }
585
586 #[benchmark(pov_mode = Measured)]
587 fn set_code() -> Result<(), BenchmarkError> {
588 let instance =
589 <Contract<T>>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
590 let VmBinaryModule { code, .. } = VmBinaryModule::dummy_unique(128);
592 let origin = RawOrigin::Signed(instance.caller.clone());
593 let storage_deposit = default_deposit_limit::<T>();
594 let hash =
595 <Contracts<T>>::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash;
596 assert_ne!(instance.info()?.code_hash, hash);
597 #[extrinsic_call]
598 _(RawOrigin::Root, instance.address, hash);
599 assert_eq!(instance.info()?.code_hash, hash);
600 Ok(())
601 }
602
603 #[benchmark(pov_mode = Measured)]
604 fn map_account() {
605 let caller = whitelisted_caller();
606 T::Currency::set_balance(&caller, caller_funding::<T>());
607 let origin = RawOrigin::Signed(caller.clone());
608 if T::AddressMapper::is_mapped(&caller) {
609 T::AddressMapper::unmap(&caller).unwrap();
610 }
611 assert!(!T::AddressMapper::is_mapped(&caller));
612 #[extrinsic_call]
613 _(origin);
614 assert!(T::AddressMapper::is_mapped(&caller));
615 }
616
617 #[benchmark(pov_mode = Measured)]
618 fn unmap_account() {
619 let caller = whitelisted_caller();
620 T::Currency::set_balance(&caller, caller_funding::<T>());
621 let origin = RawOrigin::Signed(caller.clone());
622 if !T::AddressMapper::is_mapped(&caller) {
623 T::AddressMapper::map(&caller).unwrap();
624 }
625 assert!(T::AddressMapper::is_mapped(&caller));
626 #[extrinsic_call]
627 _(origin);
628 assert!(!T::AddressMapper::is_mapped(&caller));
629 }
630
631 #[benchmark(pov_mode = Measured)]
636 fn batch_map_accounts(a: Linear<0, 1024>) -> Result<(), BenchmarkError> {
637 use frame_benchmarking::v2::account;
638
639 let caller: T::AccountId = whitelisted_caller();
640 T::Currency::set_balance(&caller, caller_funding::<T>());
641
642 let deposit = T::DepositPerByte::get()
644 .saturating_mul(52u32.into())
645 .saturating_add(T::DepositPerItem::get());
646
647 let mut accounts = Vec::with_capacity(a as usize);
648 for i in 0..a {
649 let account_id: T::AccountId = account("to_map", i, 0);
650 T::Currency::set_balance(&account_id, caller_funding::<T>());
651 T::Currency::hold(&HoldReason::AddressMapping.into(), &account_id, deposit)?;
652 accounts.push(account_id);
653 }
654
655 #[extrinsic_call]
656 _(RawOrigin::Signed(caller), accounts.clone());
657
658 for account_id in &accounts {
659 assert!(T::AddressMapper::is_mapped(account_id));
660 assert_eq!(
661 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), account_id),
662 0u32.into(),
663 );
664 }
665
666 Ok(())
667 }
668
669 #[benchmark(pov_mode = Measured)]
670 fn dispatch_as_fallback_account() {
671 let caller = whitelisted_caller();
672 T::Currency::set_balance(&caller, caller_funding::<T>());
673 let origin = RawOrigin::Signed(caller.clone());
674 let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
675 #[extrinsic_call]
676 _(origin, Box::new(dispatchable));
677 }
678
679 #[benchmark(pov_mode = Measured)]
680 fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) {
681 let mut setup = CallSetup::<T>::new(VmBinaryModule::noop());
682 let (mut ext, module) = setup.ext();
683 let prepared = CallSetup::<T>::prepare_call(&mut ext, module, r.encode(), 0);
684 #[block]
685 {
686 prepared.call().unwrap();
687 }
688 }
689
690 #[benchmark(pov_mode = Measured)]
691 fn seal_caller() {
692 let len = H160::len_bytes();
693 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
694
695 let result;
696 #[block]
697 {
698 result = runtime.bench_caller(memory.as_mut_slice(), 0);
699 }
700
701 assert_ok!(result);
702 assert_eq!(
703 <H160 as Decode>::decode(&mut &memory[..]).unwrap(),
704 T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap())
705 );
706 }
707
708 #[benchmark(pov_mode = Measured)]
709 fn seal_origin() {
710 let len = H160::len_bytes();
711 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
712
713 let result;
714 #[block]
715 {
716 result = runtime.bench_origin(memory.as_mut_slice(), 0);
717 }
718
719 assert_ok!(result);
720 assert_eq!(
721 <H160 as Decode>::decode(&mut &memory[..]).unwrap(),
722 T::AddressMapper::to_address(&runtime.ext().origin().account_id().unwrap())
723 );
724 }
725
726 #[benchmark(pov_mode = Measured)]
727 fn to_account_id() {
728 let account_id = account("precompile_to_account_id", 0, 0);
731 let address = {
732 T::Currency::set_balance(&account_id, caller_funding::<T>());
733 if !T::AddressMapper::is_mapped(&account_id) {
734 T::AddressMapper::map(&account_id).unwrap();
735 }
736 T::AddressMapper::to_address(&account_id)
737 };
738
739 let input_bytes = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
740 input: address.0.into(),
741 })
742 .abi_encode();
743
744 let mut call_setup = CallSetup::<T>::default();
745 let (mut ext, _) = call_setup.ext();
746
747 let result;
748 #[block]
749 {
750 result = run_builtin_precompile(
751 &mut ext,
752 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
753 input_bytes,
754 );
755 }
756 let raw_data = result.unwrap().data;
757 let data = Bytes::abi_decode(&raw_data).expect("decoding failed");
758 assert_ne!(
759 data.0.as_ref()[20..32],
760 [0xEE; 12],
761 "fallback suffix found where none should be"
762 );
763 assert_eq!(T::AccountId::decode(&mut data.as_ref()), Ok(account_id),);
764 }
765
766 #[benchmark(pov_mode = Measured)]
767 fn seal_code_hash() {
768 let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
769 let len = <sp_core::H256 as MaxEncodedLen>::max_encoded_len() as u32;
770 build_runtime!(runtime, memory: [vec![0u8; len as _], contract.account_id.encode(), ]);
771
772 let result;
773 #[block]
774 {
775 result = runtime.bench_code_hash(memory.as_mut_slice(), len, 0);
776 }
777
778 assert_ok!(result);
779 assert_eq!(
780 <sp_core::H256 as Decode>::decode(&mut &memory[..]).unwrap(),
781 contract.info().unwrap().code_hash
782 );
783 }
784
785 #[benchmark(pov_mode = Measured)]
786 fn own_code_hash() {
787 let input_bytes =
788 ISystem::ISystemCalls::ownCodeHash(ISystem::ownCodeHashCall {}).abi_encode();
789 let mut call_setup = CallSetup::<T>::default();
790 let contract_acc = call_setup.contract().account_id.clone();
791 let caller = call_setup.contract().address;
792 call_setup.set_origin(ExecOrigin::from_account_id(contract_acc));
793 let (mut ext, _) = call_setup.ext();
794
795 let result;
796 #[block]
797 {
798 result = run_builtin_precompile(
799 &mut ext,
800 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
801 input_bytes,
802 );
803 }
804 assert!(result.is_ok());
805 let caller_code_hash = ext.code_hash(&caller);
806 assert_eq!(caller_code_hash.0.to_vec(), result.unwrap().data);
807 }
808
809 #[benchmark(pov_mode = Measured)]
810 fn seal_code_size() {
811 let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
812 build_runtime!(runtime, memory: [contract.address.encode(),]);
813
814 let result;
815 #[block]
816 {
817 result = runtime.bench_code_size(memory.as_mut_slice(), 0);
818 }
819
820 assert_eq!(result.unwrap(), VmBinaryModule::dummy().code.len() as u64);
821 }
822
823 #[benchmark(pov_mode = Measured)]
824 fn caller_is_origin() {
825 let input_bytes =
826 ISystem::ISystemCalls::callerIsOrigin(ISystem::callerIsOriginCall {}).abi_encode();
827
828 let mut call_setup = CallSetup::<T>::default();
829 let (mut ext, _) = call_setup.ext();
830
831 let result;
832 #[block]
833 {
834 result = run_builtin_precompile(
835 &mut ext,
836 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
837 input_bytes,
838 );
839 }
840 let raw_data = result.unwrap().data;
841 let is_origin = Bool::abi_decode(&raw_data[..]).expect("decoding failed");
842 assert!(is_origin);
843 }
844
845 #[benchmark(pov_mode = Measured)]
846 fn caller_is_root() {
847 let input_bytes =
848 ISystem::ISystemCalls::callerIsRoot(ISystem::callerIsRootCall {}).abi_encode();
849
850 let mut setup = CallSetup::<T>::default();
851 setup.set_origin(ExecOrigin::Root);
852 let (mut ext, _) = setup.ext();
853
854 let result;
855 #[block]
856 {
857 result = run_builtin_precompile(
858 &mut ext,
859 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
860 input_bytes,
861 );
862 }
863 let raw_data = result.unwrap().data;
864 let is_root = Bool::abi_decode(&raw_data).expect("decoding failed");
865 assert!(is_root);
866 }
867
868 #[benchmark(pov_mode = Measured)]
869 fn seal_address() {
870 let len = H160::len_bytes();
871 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
872
873 let result;
874 #[block]
875 {
876 result = runtime.bench_address(memory.as_mut_slice(), 0);
877 }
878 assert_ok!(result);
879 assert_eq!(<H160 as Decode>::decode(&mut &memory[..]).unwrap(), runtime.ext().address());
880 }
881
882 #[benchmark(pov_mode = Measured)]
883 fn weight_left() {
884 let input_bytes =
885 ISystem::ISystemCalls::weightLeft(ISystem::weightLeftCall {}).abi_encode();
886
887 let mut call_setup = CallSetup::<T>::default();
888 let (mut ext, _) = call_setup.ext();
889
890 let weight_left_before = ext.frame_meter().weight_left().unwrap();
891 let result;
892 #[block]
893 {
894 result = run_builtin_precompile(
895 &mut ext,
896 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
897 input_bytes,
898 );
899 }
900 let weight_left_after = ext.frame_meter().weight_left().unwrap();
901 assert_ne!(weight_left_after.ref_time(), 0);
902 assert!(weight_left_before.ref_time() > weight_left_after.ref_time());
903
904 let raw_data = result.unwrap().data;
905 type MyTy = (Uint<64>, Uint<64>);
906 let foo = MyTy::abi_decode(&raw_data[..]).unwrap();
907 assert_eq!(weight_left_after.ref_time(), foo.0);
908 }
909
910 #[benchmark(pov_mode = Measured)]
911 fn seal_ref_time_left() {
912 build_runtime!(runtime, memory: [vec![], ]);
913
914 let result;
915 #[block]
916 {
917 result = runtime.bench_ref_time_left(memory.as_mut_slice());
918 }
919 assert_eq!(result.unwrap(), runtime.ext().gas_left());
920 }
921
922 #[benchmark(pov_mode = Measured)]
923 fn seal_balance() {
924 build_runtime!(runtime, contract, memory: [[0u8;32], ]);
925 contract.set_balance(BalanceWithDust::new_unchecked::<T>(
926 Pallet::<T>::min_balance() * 2u32.into(),
927 42u32,
928 ));
929
930 let result;
931 #[block]
932 {
933 result = runtime.bench_balance(memory.as_mut_slice(), 0);
934 }
935 assert_ok!(result);
936 assert_eq!(
937 U256::from_little_endian(&memory[..]),
938 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
939 Pallet::<T>::min_balance(),
940 42
941 ))
942 );
943 }
944
945 #[benchmark(pov_mode = Measured)]
946 fn seal_balance_of() {
947 let len = <sp_core::U256 as MaxEncodedLen>::max_encoded_len();
948 let account = account::<T::AccountId>("target", 0, 0);
949 <T as Config>::AddressMapper::map_no_deposit_unchecked(&account).unwrap();
950
951 let address = T::AddressMapper::to_address(&account);
952 let balance = Pallet::<T>::min_balance() * 2u32.into();
953 T::Currency::set_balance(&account, balance);
954 AccountInfoOf::<T>::insert(&address, AccountInfo { dust: 42, ..Default::default() });
955
956 build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]);
957
958 let result;
959 #[block]
960 {
961 result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0);
962 }
963
964 assert_ok!(result);
965 assert_eq!(
966 U256::from_little_endian(&memory[..len]),
967 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
968 Pallet::<T>::min_balance(),
969 42
970 ))
971 );
972 }
973
974 #[benchmark(pov_mode = Measured)]
975 fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
976 let len = n as usize;
977 let immutable_data = vec![1u8; len];
978
979 build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]);
980
981 <ImmutableDataOf<T>>::insert::<_, BoundedVec<_, _>>(
982 contract.address,
983 immutable_data.clone().try_into().unwrap(),
984 );
985
986 let result;
987 #[block]
988 {
989 result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32);
990 }
991
992 assert_ok!(result);
993 assert_eq!(&memory[0..4], (len as u32).encode());
994 assert_eq!(&memory[4..len + 4], &immutable_data);
995 }
996
997 #[benchmark(pov_mode = Measured)]
998 fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
999 let len = n as usize;
1000 let mut memory = vec![1u8; len];
1001 let mut setup = CallSetup::<T>::default();
1002 let input = setup.data();
1003 let (mut ext, _) = setup.ext();
1004 ext.override_export(crate::exec::ExportedFunction::Constructor);
1005
1006 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
1007
1008 let result;
1009 #[block]
1010 {
1011 result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n);
1012 }
1013
1014 assert_ok!(result);
1015 assert_eq!(&memory[..], &<ImmutableDataOf<T>>::get(setup.contract().address).unwrap()[..]);
1016 }
1017
1018 #[benchmark(pov_mode = Measured)]
1019 fn seal_value_transferred() {
1020 build_runtime!(runtime, memory: [[0u8;32], ]);
1021 let result;
1022 #[block]
1023 {
1024 result = runtime.bench_value_transferred(memory.as_mut_slice(), 0);
1025 }
1026 assert_ok!(result);
1027 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().value_transferred());
1028 }
1029
1030 #[benchmark(pov_mode = Measured)]
1031 fn minimum_balance() {
1032 let input_bytes =
1033 ISystem::ISystemCalls::minimumBalance(ISystem::minimumBalanceCall {}).abi_encode();
1034
1035 let mut call_setup = CallSetup::<T>::default();
1036 let (mut ext, _) = call_setup.ext();
1037
1038 let result;
1039 #[block]
1040 {
1041 result = run_builtin_precompile(
1042 &mut ext,
1043 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1044 input_bytes,
1045 );
1046 }
1047 let min: U256 = crate::Pallet::<T>::convert_native_to_evm(T::Currency::minimum_balance());
1048 let min =
1049 crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&min.to_big_endian())
1050 .unwrap();
1051
1052 let raw_data = result.unwrap().data;
1053 let returned_min =
1054 crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&raw_data)
1055 .expect("decoding failed");
1056 assert_eq!(returned_min, min);
1057 }
1058
1059 #[benchmark(pov_mode = Measured)]
1060 fn seal_return_data_size() {
1061 let mut setup = CallSetup::<T>::default();
1062 let (mut ext, _) = setup.ext();
1063 let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
1064 let mut memory = memory!(vec![],);
1065 *runtime.ext().last_frame_output_mut() =
1066 ExecReturnValue { data: vec![42; 256], ..Default::default() };
1067 let result;
1068 #[block]
1069 {
1070 result = runtime.bench_return_data_size(memory.as_mut_slice());
1071 }
1072 assert_eq!(result.unwrap(), 256);
1073 }
1074
1075 #[benchmark(pov_mode = Measured)]
1076 fn seal_call_data_size() {
1077 let mut setup = CallSetup::<T>::default();
1078 let (mut ext, _) = setup.ext();
1079 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 128 as usize]);
1080 let mut memory = memory!(vec![0u8; 4],);
1081 let result;
1082 #[block]
1083 {
1084 result = runtime.bench_call_data_size(memory.as_mut_slice());
1085 }
1086 assert_eq!(result.unwrap(), 128);
1087 }
1088
1089 #[benchmark(pov_mode = Measured)]
1090 fn seal_gas_limit() {
1091 build_runtime!(runtime, memory: []);
1092 let result;
1093 #[block]
1094 {
1095 result = runtime.bench_gas_limit(&mut memory);
1096 }
1097 assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_block_gas_limit());
1098 }
1099
1100 #[benchmark(pov_mode = Measured)]
1101 fn seal_gas_price() {
1102 build_runtime!(runtime, memory: []);
1103 let result;
1104 #[block]
1105 {
1106 result = runtime.bench_gas_price(memory.as_mut_slice());
1107 }
1108 assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_base_fee());
1109 }
1110
1111 #[benchmark(pov_mode = Measured)]
1112 fn seal_base_fee() {
1113 build_runtime!(runtime, memory: [[1u8;32], ]);
1114 let result;
1115 #[block]
1116 {
1117 result = runtime.bench_base_fee(memory.as_mut_slice(), 0);
1118 }
1119 assert_ok!(result);
1120 assert_eq!(U256::from_little_endian(&memory[..]), <crate::Pallet<T>>::evm_base_fee());
1121 }
1122
1123 #[benchmark(pov_mode = Measured)]
1124 fn seal_block_number() {
1125 build_runtime!(runtime, memory: [[0u8;32], ]);
1126 let result;
1127 #[block]
1128 {
1129 result = runtime.bench_block_number(memory.as_mut_slice(), 0);
1130 }
1131 assert_ok!(result);
1132 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number());
1133 }
1134
1135 #[benchmark(pov_mode = Measured)]
1136 fn seal_block_author() {
1137 build_runtime!(runtime, memory: [[123u8; 20], ]);
1138
1139 for i in 0..16 {
1143 frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1144 [i, i, i, i],
1145 vec![i; 128],
1146 ));
1147 frame_system::Pallet::<T>::deposit_log(DigestItem::Consensus(
1148 [i, i, i, i],
1149 vec![i; 128],
1150 ));
1151 frame_system::Pallet::<T>::deposit_log(DigestItem::Seal([i, i, i, i], vec![i; 128]));
1152 frame_system::Pallet::<T>::deposit_log(DigestItem::Other(vec![i; 128]));
1153 }
1154
1155 let primary_pre_digest = vec![0; <PrimaryPreDigest as MaxEncodedLen>::max_encoded_len()];
1161 let pre_digest =
1162 PreDigest::Primary(PrimaryPreDigest::decode(&mut &primary_pre_digest[..]).unwrap());
1163 frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1164 BABE_ENGINE_ID,
1165 pre_digest.encode(),
1166 ));
1167 frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(
1168 BABE_ENGINE_ID,
1169 pre_digest.encode(),
1170 ));
1171
1172 let slot = Slot::default();
1174 frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1175 AURA_ENGINE_ID,
1176 slot.encode(),
1177 ));
1178 frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(AURA_ENGINE_ID, slot.encode()));
1179
1180 let result;
1181 #[block]
1182 {
1183 result = runtime.bench_block_author(memory.as_mut_slice(), 0);
1184 }
1185 assert_ok!(result);
1186
1187 let block_author = runtime.ext().block_author();
1188 assert_eq!(&memory[..], block_author.as_bytes());
1189 }
1190
1191 #[benchmark(pov_mode = Measured)]
1192 fn seal_block_hash() {
1193 let mut memory = vec![0u8; 64];
1194 let mut setup = CallSetup::<T>::default();
1195 let input = setup.data();
1196 let (mut ext, _) = setup.ext();
1197 ext.set_block_number(BlockNumberFor::<T>::from(1u32));
1198
1199 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
1200
1201 let block_hash = H256::from([1; 32]);
1202
1203 crate::BlockHash::<T>::insert(crate::BlockNumberFor::<T>::from(0u32), block_hash);
1205
1206 let result;
1207 #[block]
1208 {
1209 result = runtime.bench_block_hash(memory.as_mut_slice(), 32, 0);
1210 }
1211 assert_ok!(result);
1212 assert_eq!(&memory[..32], &block_hash.0);
1213 }
1214
1215 #[benchmark(pov_mode = Measured)]
1216 fn seal_now() {
1217 build_runtime!(runtime, memory: [[0u8;32], ]);
1218 let result;
1219 #[block]
1220 {
1221 result = runtime.bench_now(memory.as_mut_slice(), 0);
1222 }
1223 assert_ok!(result);
1224 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now());
1225 }
1226
1227 #[benchmark(pov_mode = Measured)]
1228 fn seal_copy_to_contract(n: Linear<0, { limits::code::BLOB_BYTES - 4 }>) {
1229 let mut setup = CallSetup::<T>::default();
1230 let (mut ext, _) = setup.ext();
1231 let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
1232 let mut memory = memory!(n.encode(), vec![0u8; n as usize],);
1233 let result;
1234 #[block]
1235 {
1236 result = runtime.write_sandbox_output(
1237 memory.as_mut_slice(),
1238 4,
1239 0,
1240 &vec![42u8; n as usize],
1241 false,
1242 |_| None,
1243 );
1244 }
1245 assert_ok!(result);
1246 assert_eq!(&memory[..4], &n.encode());
1247 assert_eq!(&memory[4..], &vec![42u8; n as usize]);
1248 }
1249
1250 #[benchmark(pov_mode = Measured)]
1251 fn seal_call_data_load() {
1252 let mut setup = CallSetup::<T>::default();
1253 let (mut ext, _) = setup.ext();
1254 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 32]);
1255 let mut memory = memory!(vec![0u8; 32],);
1256 let result;
1257 #[block]
1258 {
1259 result = runtime.bench_call_data_load(memory.as_mut_slice(), 0, 0);
1260 }
1261 assert_ok!(result);
1262 assert_eq!(&memory[..], &vec![42u8; 32]);
1263 }
1264
1265 #[benchmark(pov_mode = Measured)]
1266 fn seal_call_data_copy(n: Linear<0, { limits::code::BLOB_BYTES }>) {
1267 let mut setup = CallSetup::<T>::default();
1268 let (mut ext, _) = setup.ext();
1269 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; n as usize]);
1270 let mut memory = memory!(vec![0u8; n as usize],);
1271 let result;
1272 #[block]
1273 {
1274 result = runtime.bench_call_data_copy(memory.as_mut_slice(), 0, n, 0);
1275 }
1276 assert_ok!(result);
1277 assert_eq!(&memory[..], &vec![42u8; n as usize]);
1278 }
1279
1280 #[benchmark(pov_mode = Measured)]
1281 fn seal_return(n: Linear<0, { limits::CALLDATA_BYTES }>) {
1282 build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]);
1283
1284 let result;
1285 #[block]
1286 {
1287 result = runtime.bench_seal_return(memory.as_mut_slice(), 0, 0, n);
1288 }
1289
1290 assert!(matches!(
1291 result,
1292 Err(crate::vm::pvm::TrapReason::Return(crate::vm::pvm::ReturnData { .. }))
1293 ));
1294 }
1295
1296 #[benchmark(pov_mode = Measured)]
1300 fn seal_terminate(r: Linear<0, 1>) -> Result<(), BenchmarkError> {
1301 let delete_code = r == 1;
1302 let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1303
1304 build_runtime!(runtime, instance, memory: [beneficiary.encode(),]);
1305 let code_hash = instance.info()?.code_hash;
1306
1307 if !delete_code {
1309 <CodeInfo<T>>::increment_refcount(code_hash).unwrap();
1310 }
1311
1312 let result;
1313 #[block]
1314 {
1315 result = runtime.bench_terminate(memory.as_mut_slice(), 0);
1316 }
1317
1318 assert!(matches!(result, Err(crate::vm::pvm::TrapReason::Termination)));
1319
1320 Ok(())
1321 }
1322
1323 #[benchmark(pov_mode = Measured)]
1324 fn seal_terminate_logic() -> Result<(), BenchmarkError> {
1325 let caller = whitelisted_caller();
1326 let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1327 T::AddressMapper::map_no_deposit_unchecked(&beneficiary)?;
1328
1329 build_runtime!(_runtime, instance, _memory: [vec![0u8; 0], ]);
1330 let code_hash = instance.info()?.code_hash;
1331
1332 assert!(PristineCode::<T>::get(code_hash).is_some());
1333
1334 T::Currency::set_balance(&instance.account_id, Pallet::<T>::min_balance() * 10u32.into());
1335
1336 let storage_deposit = T::Currency::balance_on_hold(
1337 &HoldReason::StorageDepositReserve.into(),
1338 &instance.account_id,
1339 );
1340 NativeDepositOf::<T>::insert(&instance.account_id, &caller, storage_deposit);
1341
1342 let mut transaction_meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
1343 weight_limit: Default::default(),
1344 deposit_limit: BalanceOf::<T>::max_value(),
1345 })
1346 .unwrap();
1347 let exec_config = ExecConfig::new_substrate_tx();
1348 let contract_account = &instance.account_id;
1349 let origin = &ExecOrigin::from_account_id(caller);
1350 let beneficiary_clone = beneficiary.clone();
1351 let trie_id = instance.info()?.trie_id.clone();
1352 let code_hash = instance.info()?.code_hash;
1353 let only_if_same_tx = false;
1354
1355 let result;
1356 #[block]
1357 {
1358 result = crate::exec::bench_do_terminate::<T>(
1359 &mut transaction_meter,
1360 &exec_config,
1361 contract_account,
1362 &origin,
1363 beneficiary_clone,
1364 trie_id,
1365 code_hash,
1366 only_if_same_tx,
1367 );
1368 }
1369 result.unwrap();
1370
1371 assert!(PristineCode::<T>::get(code_hash).is_none());
1373
1374 let balance = <T as Config>::Currency::total_balance(&instance.account_id);
1376 assert_eq!(balance, 0u32.into());
1377
1378 let balance = <T as Config>::Currency::balance(&beneficiary);
1380 assert_eq!(balance, Pallet::<T>::min_balance() + Pallet::<T>::min_balance() * 9u32.into());
1381
1382 Ok(())
1383 }
1384
1385 #[benchmark(pov_mode = Measured)]
1389 fn seal_deposit_event(
1390 t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>,
1391 n: Linear<0, { limits::EVENT_BYTES }>,
1392 ) {
1393 let num_topic = t as u32;
1394 let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::<Vec<_>>();
1395 let topics_data =
1396 topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::<Vec<u8>>();
1397 let data = vec![42u8; n as _];
1398 build_runtime!(runtime, instance, memory: [ topics_data, data, ]);
1399
1400 let result;
1401 #[block]
1402 {
1403 result = runtime.bench_deposit_event(
1404 memory.as_mut_slice(),
1405 0, num_topic,
1407 topics_data.len() as u32, n, );
1410 }
1411 assert_ok!(result);
1412
1413 let events = System::<T>::events();
1414 let record = &events[events.len() - 1];
1415
1416 assert_eq!(
1417 record.event,
1418 crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(),
1419 );
1420 }
1421
1422 enum TrieFill {
1423 Empty,
1424 Full,
1425 }
1426
1427 enum SlotAccess {
1428 Cold,
1429 Hot,
1430 }
1431
1432 enum StorageOp {
1433 Read,
1434 Write,
1435 }
1436
1437 fn build_storage_contract<T: Config>(
1438 op: StorageOp,
1439 fill: TrieFill,
1440 ) -> Result<(ContractInfo<T>, Vec<u8>, Vec<u8>), BenchmarkError> {
1441 let key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1442 let value = vec![1u8; limits::STORAGE_BYTES as usize];
1443 let initial_value = match op {
1444 StorageOp::Read => value.clone(),
1445 StorageOp::Write => vec![42u8; limits::STORAGE_BYTES as usize],
1446 };
1447
1448 let instance = match fill {
1449 TrieFill::Full => {
1450 Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?
1451 },
1452 TrieFill::Empty => Contract::<T>::new(VmBinaryModule::dummy(), vec![])?,
1453 };
1454 let info = instance.info()?;
1455 info.bench_write_raw(&key, Some(initial_value), false)
1456 .map_err(|_| "Failed to write to storage during setup.")?;
1457 Ok((info, key, value))
1458 }
1459
1460 enum StorageCall {
1461 Clear,
1462 Contains,
1463 Take,
1464 }
1465
1466 fn setup_precompile_bench<T: Config>(
1467 op: StorageCall,
1468 key_byte: u8,
1469 access: SlotAccess,
1470 ) -> Result<(CallSetup<T>, Key, Vec<u8>), BenchmarkError> {
1471 let max_key_len = limits::STORAGE_KEY_BYTES;
1472 let key = Key::try_from_var(vec![key_byte; max_key_len as usize])
1473 .map_err(|_| "Key has wrong length")?;
1474 let raw_key = vec![key_byte; max_key_len as usize].into();
1475 let input_bytes = match op {
1476 StorageCall::Clear => {
1477 IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
1478 flags: StorageFlags::empty().bits(),
1479 key: raw_key,
1480 isFixedKey: false,
1481 })
1482 },
1483 StorageCall::Contains => {
1484 IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
1485 flags: StorageFlags::empty().bits(),
1486 key: raw_key,
1487 isFixedKey: false,
1488 })
1489 },
1490 StorageCall::Take => IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
1491 flags: StorageFlags::empty().bits(),
1492 key: raw_key,
1493 isFixedKey: false,
1494 }),
1495 }
1496 .abi_encode();
1497
1498 let call_setup = CallSetup::<T>::default();
1499 if matches!(access, SlotAccess::Hot) {
1500 let info = call_setup.contract().info()?;
1501 frame_benchmarking::add_to_whitelist_child(
1502 info.child_trie_info().storage_key().to_vec(),
1503 key.hash(),
1504 );
1505 }
1506 Ok((call_setup, key, input_bytes))
1507 }
1508
1509 #[benchmark(skip_meta, pov_mode = Measured)]
1510 fn get_storage_empty() -> Result<(), BenchmarkError> {
1511 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Empty)?;
1512 let child_trie_info = info.child_trie_info();
1513
1514 let result;
1515 #[block]
1516 {
1517 result = child::get_raw(&child_trie_info, &key);
1518 }
1519
1520 assert_eq!(result, Some(value));
1521 Ok(())
1522 }
1523
1524 #[benchmark(skip_meta, pov_mode = Measured)]
1525 fn get_storage_full() -> Result<(), BenchmarkError> {
1526 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Full)?;
1527 let child_trie_info = info.child_trie_info();
1528
1529 let result;
1530 #[block]
1531 {
1532 result = child::get_raw(&child_trie_info, &key);
1533 }
1534
1535 assert_eq!(result, Some(value));
1536 Ok(())
1537 }
1538
1539 #[benchmark(skip_meta, pov_mode = Measured)]
1540 fn set_storage_empty() -> Result<(), BenchmarkError> {
1541 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Empty)?;
1542
1543 let val = Some(value.clone());
1544 let result;
1545 #[block]
1546 {
1547 result = info.bench_write_raw(&key, val, true);
1548 }
1549
1550 assert_ok!(result);
1551 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1552 Ok(())
1553 }
1554
1555 #[benchmark(skip_meta, pov_mode = Measured)]
1556 fn set_storage_full() -> Result<(), BenchmarkError> {
1557 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Full)?;
1558
1559 let val = Some(value.clone());
1560 let result;
1561 #[block]
1562 {
1563 result = info.bench_write_raw(&key, val, true);
1564 }
1565
1566 assert_ok!(result);
1567 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1568 Ok(())
1569 }
1570
1571 #[benchmark(skip_meta, pov_mode = Measured)]
1574 fn seal_set_storage(
1575 n: Linear<0, { limits::STORAGE_BYTES }>,
1576 o: Linear<0, { limits::STORAGE_BYTES }>,
1577 ) -> Result<(), BenchmarkError> {
1578 let max_key_len = limits::STORAGE_KEY_BYTES;
1579 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1580 .map_err(|_| "Key has wrong length")?;
1581 let value = vec![1u8; n as usize];
1582
1583 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1584 let info = instance.info()?;
1585
1586 info.write(&key, Some(vec![42u8; o as usize]), None, false)
1587 .map_err(|_| "Failed to write to storage during setup.")?;
1588
1589 let result;
1590 #[block]
1591 {
1592 result = runtime.bench_set_storage(
1593 memory.as_mut_slice(),
1594 StorageFlags::empty().bits(),
1595 0, max_key_len, max_key_len, n, );
1600 }
1601
1602 assert_ok!(result);
1603 assert_eq!(info.read(&key).unwrap(), value);
1604 Ok(())
1605 }
1606
1607 #[benchmark(skip_meta, pov_mode = Measured)]
1608 fn seal_set_storage_hot(
1609 n: Linear<0, { limits::STORAGE_BYTES }>,
1610 o: Linear<0, { limits::STORAGE_BYTES }>,
1611 ) -> Result<(), BenchmarkError> {
1612 let max_key_len = limits::STORAGE_KEY_BYTES;
1613 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1614 .map_err(|_| "Key has wrong length")?;
1615 let value = vec![1u8; n as usize];
1616
1617 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1618 let info = instance.info()?;
1619
1620 info.write(&key, Some(vec![42u8; o as usize]), None, false)
1621 .map_err(|_| "Failed to write to storage during setup.")?;
1622
1623 frame_benchmarking::add_to_whitelist_child(
1624 info.child_trie_info().storage_key().to_vec(),
1625 key.hash(),
1626 );
1627
1628 runtime.ext().touch_storage_access(false, &key);
1630
1631 let result;
1632 #[block]
1633 {
1634 result = runtime.bench_set_storage(
1635 memory.as_mut_slice(),
1636 StorageFlags::empty().bits(),
1637 0, max_key_len, max_key_len, n, );
1642 }
1643
1644 assert_ok!(result);
1645 assert_eq!(info.read(&key).unwrap(), value);
1646 Ok(())
1647 }
1648
1649 #[benchmark(skip_meta, pov_mode = Measured)]
1650 fn clear_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1651 let key_byte = 0;
1652 let (mut call_setup, key, input_bytes) =
1653 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Cold)?;
1654 let (mut ext, _) = call_setup.ext();
1655 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1656 .map_err(|_| "Failed to write to storage during setup.")?;
1657
1658 let result;
1659 #[block]
1660 {
1661 result = run_builtin_precompile(
1662 &mut ext,
1663 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1664 input_bytes,
1665 );
1666 }
1667 assert_ok!(result);
1668 assert!(ext.get_storage(&key).is_none());
1669
1670 Ok(())
1671 }
1672
1673 #[benchmark(skip_meta, pov_mode = Measured)]
1674 fn clear_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1675 let key_byte = 0;
1676 let (mut call_setup, key, input_bytes) =
1677 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Hot)?;
1678 let (mut ext, _) = call_setup.ext();
1679 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1680 .map_err(|_| "Failed to write to storage during setup.")?;
1681
1682 ext.touch_storage_access(false, &key);
1683
1684 let result;
1685 #[block]
1686 {
1687 result = run_builtin_precompile(
1688 &mut ext,
1689 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1690 input_bytes,
1691 );
1692 }
1693 assert_ok!(result);
1694 assert!(ext.get_storage(&key).is_none());
1695
1696 Ok(())
1697 }
1698
1699 #[benchmark(skip_meta, pov_mode = Measured)]
1700 fn seal_get_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1701 let max_key_len = limits::STORAGE_KEY_BYTES;
1702 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1703 .map_err(|_| "Key has wrong length")?;
1704 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1705 let info = instance.info()?;
1706
1707 info.write(&key, Some(vec![42u8; n as usize]), None, false)
1708 .map_err(|_| "Failed to write to storage during setup.")?;
1709
1710 let out_ptr = max_key_len + 4;
1711 let result;
1712 #[block]
1713 {
1714 result = runtime.bench_get_storage(
1715 memory.as_mut_slice(),
1716 StorageFlags::empty().bits(),
1717 0, max_key_len, out_ptr, max_key_len, );
1722 }
1723
1724 assert_ok!(result);
1725 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
1726 Ok(())
1727 }
1728
1729 #[benchmark(skip_meta, pov_mode = Measured)]
1730 fn seal_get_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1731 let max_key_len = limits::STORAGE_KEY_BYTES;
1732 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1733 .map_err(|_| "Key has wrong length")?;
1734 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1735 let info = instance.info()?;
1736
1737 info.write(&key, Some(vec![42u8; n as usize]), None, false)
1738 .map_err(|_| "Failed to write to storage during setup.")?;
1739
1740 frame_benchmarking::add_to_whitelist_child(
1741 info.child_trie_info().storage_key().to_vec(),
1742 key.hash(),
1743 );
1744
1745 runtime.ext().touch_storage_access(false, &key);
1746
1747 let out_ptr = max_key_len + 4;
1748 let result;
1749 #[block]
1750 {
1751 result = runtime.bench_get_storage(
1752 memory.as_mut_slice(),
1753 StorageFlags::empty().bits(),
1754 0, max_key_len, out_ptr, max_key_len, );
1759 }
1760
1761 assert_ok!(result);
1762 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
1763 Ok(())
1764 }
1765
1766 #[benchmark(skip_meta, pov_mode = Measured)]
1767 fn contains_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1768 let key_byte = 0;
1769 let (mut call_setup, key, input_bytes) =
1770 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Cold)?;
1771 let (mut ext, _) = call_setup.ext();
1772 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1773 .map_err(|_| "Failed to write to storage during setup.")?;
1774
1775 let result;
1776 #[block]
1777 {
1778 result = run_builtin_precompile(
1779 &mut ext,
1780 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1781 input_bytes,
1782 );
1783 }
1784 assert_ok!(result);
1785 assert!(ext.get_storage(&key).is_some());
1786
1787 Ok(())
1788 }
1789
1790 #[benchmark(skip_meta, pov_mode = Measured)]
1791 fn contains_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1792 let key_byte = 0;
1793 let (mut call_setup, key, input_bytes) =
1794 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Hot)?;
1795 let (mut ext, _) = call_setup.ext();
1796 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1797 .map_err(|_| "Failed to write to storage during setup.")?;
1798
1799 ext.touch_storage_access(false, &key);
1800
1801 let result;
1802 #[block]
1803 {
1804 result = run_builtin_precompile(
1805 &mut ext,
1806 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1807 input_bytes,
1808 );
1809 }
1810 assert_ok!(result);
1811 assert!(ext.get_storage(&key).is_some());
1812
1813 Ok(())
1814 }
1815
1816 #[benchmark(skip_meta, pov_mode = Measured)]
1817 fn take_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1818 let key_byte = 3;
1819 let (mut call_setup, key, input_bytes) =
1820 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Cold)?;
1821 let (mut ext, _) = call_setup.ext();
1822 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1823 .map_err(|_| "Failed to write to storage during setup.")?;
1824
1825 let result;
1826 #[block]
1827 {
1828 result = run_builtin_precompile(
1829 &mut ext,
1830 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1831 input_bytes,
1832 );
1833 }
1834 assert_ok!(result);
1835 assert!(ext.get_storage(&key).is_none());
1836
1837 Ok(())
1838 }
1839
1840 #[benchmark(skip_meta, pov_mode = Measured)]
1841 fn take_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1842 let key_byte = 3;
1843 let (mut call_setup, key, input_bytes) =
1844 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Hot)?;
1845 let (mut ext, _) = call_setup.ext();
1846 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1847 .map_err(|_| "Failed to write to storage during setup.")?;
1848
1849 ext.touch_storage_access(false, &key);
1850
1851 let result;
1852 #[block]
1853 {
1854 result = run_builtin_precompile(
1855 &mut ext,
1856 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1857 input_bytes,
1858 );
1859 }
1860 assert_ok!(result);
1861 assert!(ext.get_storage(&key).is_none());
1862
1863 Ok(())
1864 }
1865
1866 fn worst_case_slot() -> crate::access_list::Slot {
1867 let key = Key::try_from_var(vec![0xFFu8; limits::STORAGE_KEY_BYTES as usize])
1868 .expect("key fits STORAGE_KEY_BYTES bound; qed");
1869 crate::access_list::Slot::from(&key)
1870 }
1871
1872 fn near_full_access_list() -> crate::access_list::AccessList {
1873 let mut al = AccessList::new();
1874 for i in 0..(MAX_ACCESS_LIST_ENTRIES - 1) {
1875 al.touch(AccessEntry {
1876 slot: worst_case_slot(),
1877 address: H160::from_low_u64_be(i as u64),
1878 });
1879 }
1880 al
1881 }
1882
1883 #[benchmark(pov_mode = Ignored)]
1884 fn access_list_touch_cold_full() -> Result<(), BenchmarkError> {
1885 let mut al = near_full_access_list();
1886 let entry =
1888 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
1889 let outcome;
1890 #[block]
1891 {
1892 outcome = al.touch(entry);
1893 }
1894 assert!(outcome.is_cold());
1895 Ok(())
1896 }
1897
1898 #[benchmark(pov_mode = Ignored)]
1899 fn access_list_touch_hot_full() -> Result<(), BenchmarkError> {
1900 let mut al = near_full_access_list();
1901 let entry = AccessEntry { slot: worst_case_slot(), address: H160::zero() };
1903 let outcome;
1904 #[block]
1905 {
1906 outcome = al.touch(entry);
1907 }
1908 assert!(!outcome.is_cold());
1909 Ok(())
1910 }
1911
1912 #[benchmark(pov_mode = Ignored)]
1913 fn access_list_touch_cold_empty() -> Result<(), BenchmarkError> {
1914 let mut al = AccessList::new();
1915 let entry =
1916 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
1917 let outcome;
1918 #[block]
1919 {
1920 outcome = al.touch(entry);
1921 }
1922 assert!(outcome.is_cold());
1923 Ok(())
1924 }
1925
1926 #[benchmark(pov_mode = Ignored)]
1927 fn access_list_touch_hot_single_element() -> Result<(), BenchmarkError> {
1928 let mut al = AccessList::new();
1929 let entry =
1930 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
1931 al.touch(entry.clone());
1932 let outcome;
1933 #[block]
1934 {
1935 outcome = al.touch(entry);
1936 }
1937 assert!(!outcome.is_cold());
1938 Ok(())
1939 }
1940
1941 #[benchmark(pov_mode = Ignored)]
1945 fn access_list_rollback_amortization() -> Result<(), BenchmarkError> {
1946 let mut al = near_full_access_list();
1947 al.enter_frame();
1948 al.touch(AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) });
1949 #[block]
1950 {
1951 al.rollback_frame();
1952 }
1953 Ok(())
1954 }
1955
1956 #[benchmark(pov_mode = Ignored)]
1961 fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
1962 let max_value_len = limits::STORAGE_BYTES;
1963 let max_key_len = limits::STORAGE_KEY_BYTES;
1964 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1965 .map_err(|_| "Key has wrong length")?;
1966 let value = Some(vec![42u8; max_value_len as _]);
1967 let mut setup = CallSetup::<T>::default();
1968 let (mut ext, _) = setup.ext();
1969 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1970 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1971 let result;
1972 #[block]
1973 {
1974 result = runtime.ext().set_transient_storage(&key, value, false);
1975 }
1976
1977 assert_eq!(result, Ok(WriteOutcome::New));
1978 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
1979 Ok(())
1980 }
1981
1982 #[benchmark(pov_mode = Ignored)]
1983 fn set_transient_storage_full() -> Result<(), BenchmarkError> {
1984 let max_value_len = limits::STORAGE_BYTES;
1985 let max_key_len = limits::STORAGE_KEY_BYTES;
1986 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1987 .map_err(|_| "Key has wrong length")?;
1988 let value = Some(vec![42u8; max_value_len as _]);
1989 let mut setup = CallSetup::<T>::default();
1990 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
1991 let (mut ext, _) = setup.ext();
1992 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1993 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1994 let result;
1995 #[block]
1996 {
1997 result = runtime.ext().set_transient_storage(&key, value, false);
1998 }
1999
2000 assert_eq!(result, Ok(WriteOutcome::New));
2001 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2002 Ok(())
2003 }
2004
2005 #[benchmark(pov_mode = Ignored)]
2006 fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
2007 let max_value_len = limits::STORAGE_BYTES;
2008 let max_key_len = limits::STORAGE_KEY_BYTES;
2009 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2010 .map_err(|_| "Key has wrong length")?;
2011
2012 let mut setup = CallSetup::<T>::default();
2013 let (mut ext, _) = setup.ext();
2014 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2015 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2016 runtime
2017 .ext()
2018 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2019 .map_err(|_| "Failed to write to transient storage during setup.")?;
2020 let result;
2021 #[block]
2022 {
2023 result = runtime.ext().get_transient_storage(&key);
2024 }
2025
2026 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2027 Ok(())
2028 }
2029
2030 #[benchmark(pov_mode = Ignored)]
2031 fn get_transient_storage_full() -> Result<(), BenchmarkError> {
2032 let max_value_len = limits::STORAGE_BYTES;
2033 let max_key_len = limits::STORAGE_KEY_BYTES;
2034 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2035 .map_err(|_| "Key has wrong length")?;
2036
2037 let mut setup = CallSetup::<T>::default();
2038 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2039 let (mut ext, _) = setup.ext();
2040 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2041 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2042 runtime
2043 .ext()
2044 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2045 .map_err(|_| "Failed to write to transient storage during setup.")?;
2046 let result;
2047 #[block]
2048 {
2049 result = runtime.ext().get_transient_storage(&key);
2050 }
2051
2052 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2053 Ok(())
2054 }
2055
2056 #[benchmark(pov_mode = Ignored)]
2058 fn rollback_transient_storage() -> Result<(), BenchmarkError> {
2059 let max_value_len = limits::STORAGE_BYTES;
2060 let max_key_len = limits::STORAGE_KEY_BYTES;
2061 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2062 .map_err(|_| "Key has wrong length")?;
2063
2064 let mut setup = CallSetup::<T>::default();
2065 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2066 let (mut ext, _) = setup.ext();
2067 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2068 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2069 runtime.ext().transient_storage().start_transaction();
2070 runtime
2071 .ext()
2072 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2073 .map_err(|_| "Failed to write to transient storage during setup.")?;
2074 #[block]
2075 {
2076 runtime.ext().transient_storage().rollback_transaction();
2077 }
2078
2079 assert_eq!(runtime.ext().get_transient_storage(&key), None);
2080 Ok(())
2081 }
2082
2083 #[benchmark(pov_mode = Measured)]
2086 fn seal_set_transient_storage(
2087 n: Linear<0, { limits::STORAGE_BYTES }>,
2088 o: Linear<0, { limits::STORAGE_BYTES }>,
2089 ) -> Result<(), BenchmarkError> {
2090 let max_key_len = limits::STORAGE_KEY_BYTES;
2091 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2092 .map_err(|_| "Key has wrong length")?;
2093 let value = vec![1u8; n as usize];
2094 build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]);
2095 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2096 runtime
2097 .ext()
2098 .set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
2099 .map_err(|_| "Failed to write to transient storage during setup.")?;
2100
2101 let result;
2102 #[block]
2103 {
2104 result = runtime.bench_set_storage(
2105 memory.as_mut_slice(),
2106 StorageFlags::TRANSIENT.bits(),
2107 0, max_key_len, max_key_len, n, );
2112 }
2113
2114 assert_ok!(result);
2115 assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
2116 Ok(())
2117 }
2118
2119 #[benchmark(pov_mode = Measured)]
2120 fn seal_clear_transient_storage(
2121 n: Linear<0, { limits::STORAGE_BYTES }>,
2122 ) -> Result<(), BenchmarkError> {
2123 let max_key_len = limits::STORAGE_KEY_BYTES;
2124 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2125 .map_err(|_| "Key has wrong length")?;
2126 let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
2127 flags: StorageFlags::TRANSIENT.bits(),
2128 key: vec![0u8; max_key_len as usize].into(),
2129 isFixedKey: false,
2130 })
2131 .abi_encode();
2132
2133 let mut call_setup = CallSetup::<T>::default();
2134 let (mut ext, _) = call_setup.ext();
2135 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2136 .map_err(|_| "Failed to write to transient storage during setup.")?;
2137
2138 let result;
2139 #[block]
2140 {
2141 result = run_builtin_precompile(
2142 &mut ext,
2143 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2144 input_bytes,
2145 );
2146 }
2147 assert_ok!(result);
2148 assert!(ext.get_transient_storage(&key).is_none());
2149
2150 Ok(())
2151 }
2152
2153 #[benchmark(pov_mode = Measured)]
2154 fn seal_get_transient_storage(
2155 n: Linear<0, { limits::STORAGE_BYTES }>,
2156 ) -> Result<(), BenchmarkError> {
2157 let max_key_len = limits::STORAGE_KEY_BYTES;
2158 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2159 .map_err(|_| "Key has wrong length")?;
2160 build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2161 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2162 runtime
2163 .ext()
2164 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2165 .map_err(|_| "Failed to write to transient storage during setup.")?;
2166
2167 let out_ptr = max_key_len + 4;
2168 let result;
2169 #[block]
2170 {
2171 result = runtime.bench_get_storage(
2172 memory.as_mut_slice(),
2173 StorageFlags::TRANSIENT.bits(),
2174 0, max_key_len, out_ptr, max_key_len, );
2179 }
2180
2181 assert_ok!(result);
2182 assert_eq!(
2183 &runtime.ext().get_transient_storage(&key).unwrap(),
2184 &memory[out_ptr as usize..]
2185 );
2186 Ok(())
2187 }
2188
2189 #[benchmark(pov_mode = Measured)]
2190 fn seal_contains_transient_storage(
2191 n: Linear<0, { limits::STORAGE_BYTES }>,
2192 ) -> Result<(), BenchmarkError> {
2193 let max_key_len = limits::STORAGE_KEY_BYTES;
2194 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2195 .map_err(|_| "Key has wrong length")?;
2196
2197 let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
2198 flags: StorageFlags::TRANSIENT.bits(),
2199 key: vec![0u8; max_key_len as usize].into(),
2200 isFixedKey: false,
2201 })
2202 .abi_encode();
2203
2204 let mut call_setup = CallSetup::<T>::default();
2205 let (mut ext, _) = call_setup.ext();
2206 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2207 .map_err(|_| "Failed to write to transient storage during setup.")?;
2208
2209 let result;
2210 #[block]
2211 {
2212 result = run_builtin_precompile(
2213 &mut ext,
2214 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2215 input_bytes,
2216 );
2217 }
2218 assert!(result.is_ok());
2219 assert!(ext.get_transient_storage(&key).is_some());
2220
2221 Ok(())
2222 }
2223
2224 #[benchmark(pov_mode = Measured)]
2225 fn seal_take_transient_storage(
2226 n: Linear<0, { limits::STORAGE_BYTES }>,
2227 ) -> Result<(), BenchmarkError> {
2228 let n = limits::STORAGE_BYTES;
2229 let value = vec![42u8; n as usize];
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
2234 let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
2235 flags: StorageFlags::TRANSIENT.bits(),
2236 key: vec![0u8; max_key_len as usize].into(),
2237 isFixedKey: false,
2238 })
2239 .abi_encode();
2240
2241 let mut call_setup = CallSetup::<T>::default();
2242 let (mut ext, _) = call_setup.ext();
2243 ext.set_transient_storage(&key, Some(value), false)
2244 .map_err(|_| "Failed to write to transient storage during setup.")?;
2245
2246 let result;
2247 #[block]
2248 {
2249 result = run_builtin_precompile(
2250 &mut ext,
2251 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2252 input_bytes,
2253 );
2254 }
2255 assert!(result.is_ok());
2256 assert!(ext.get_transient_storage(&key).is_none());
2257
2258 Ok(())
2259 }
2260
2261 #[benchmark(pov_mode = Measured)]
2265 fn seal_call(t: Linear<0, 1>, d: Linear<0, 1>, i: Linear<0, { limits::code::BLOB_BYTES }>) {
2266 let Contract { account_id: callee, address: callee_addr, .. } =
2267 Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
2268
2269 let callee_bytes = callee.encode();
2270 let callee_len = callee_bytes.len() as u32;
2271
2272 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2273 let dust = 100u32 * d;
2274 let evm_value =
2275 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2276 let value_bytes = evm_value.encode();
2277
2278 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2279 let deposit_bytes = Into::<U256>::into(deposit).encode();
2280 let deposit_len = deposit_bytes.len() as u32;
2281
2282 let mut setup = CallSetup::<T>::default();
2283 setup.set_storage_deposit_limit(deposit);
2284 setup.set_data(vec![42; i as usize]);
2287 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2288 setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
2289
2290 let (mut ext, _) = setup.ext();
2291 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2292 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
2293
2294 let result;
2295 #[block]
2296 {
2297 result = runtime.bench_call(
2298 memory.as_mut_slice(),
2299 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), );
2306 }
2307
2308 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2309 assert_eq!(
2310 Pallet::<T>::evm_balance(&callee_addr),
2311 evm_value,
2312 "{callee_addr:?} balance should hold {evm_value:?}"
2313 );
2314 }
2315
2316 #[benchmark(pov_mode = Measured)]
2319 fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
2320 use alloy_core::sol_types::SolInterface;
2321 use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
2322
2323 let callee_bytes = if d == 1 {
2324 BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
2325 } else {
2326 BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
2327 };
2328 let callee_len = callee_bytes.len() as u32;
2329
2330 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2331 let deposit_bytes = Into::<U256>::into(deposit).encode();
2332 let deposit_len = deposit_bytes.len() as u32;
2333
2334 let value: BalanceOf<T> = Zero::zero();
2335 let value_bytes = Into::<U256>::into(value).encode();
2336 let value_len = value_bytes.len() as u32;
2337
2338 let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
2339 input: vec![42_u8; i as usize].into(),
2340 })
2341 .abi_encode();
2342 let input_len = input_bytes.len() as u32;
2343
2344 let mut setup = CallSetup::<T>::default();
2345 setup.set_storage_deposit_limit(deposit);
2346
2347 let (mut ext, _) = setup.ext();
2348 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2349 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
2350
2351 let mut do_benchmark = || {
2352 runtime.bench_call(
2353 memory.as_mut_slice(),
2354 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), )
2363 };
2364
2365 assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
2368
2369 let result;
2370 #[block]
2371 {
2372 result = do_benchmark();
2373 }
2374
2375 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2376 }
2377
2378 #[benchmark(pov_mode = Measured)]
2379 fn seal_delegate_call() -> Result<(), BenchmarkError> {
2380 let Contract { account_id: address, .. } =
2381 Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
2382
2383 let address_bytes = address.encode();
2384 let address_len = address_bytes.len() as u32;
2385
2386 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2387 let deposit_bytes = Into::<U256>::into(deposit).encode();
2388
2389 let mut setup = CallSetup::<T>::default();
2390 setup.set_storage_deposit_limit(deposit);
2391 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2392
2393 let (mut ext, _) = setup.ext();
2394 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2395 let mut memory = memory!(address_bytes, deposit_bytes,);
2396
2397 let result;
2398 #[block]
2399 {
2400 result = runtime.bench_delegate_call(
2401 memory.as_mut_slice(),
2402 pack_hi_lo(0, 0), u64::MAX, u64::MAX, address_len, pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
2409 }
2410
2411 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2412 Ok(())
2413 }
2414
2415 #[benchmark(pov_mode = Measured)]
2419 fn seal_instantiate(
2420 t: Linear<0, 1>,
2421 d: Linear<0, 1>,
2422 i: Linear<0, { limits::CALLDATA_BYTES }>,
2423 ) -> Result<(), BenchmarkError> {
2424 let code = VmBinaryModule::dummy();
2425 let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
2426 let hash_bytes = hash.encode();
2427
2428 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2429 let dust = 100u32 * d;
2430 let evm_value =
2431 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2432 let value_bytes = evm_value.encode();
2433 let value_len = value_bytes.len() as u32;
2434
2435 let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
2436 let deposit_bytes = Into::<U256>::into(deposit).encode();
2437 let deposit_len = deposit_bytes.len() as u32;
2438
2439 let mut setup = CallSetup::<T>::default();
2440 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2441 setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
2442
2443 let account_id = &setup.contract().account_id.clone();
2444 let (mut ext, _) = setup.ext();
2445 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2446
2447 let input = vec![42u8; i as _];
2448 let input_len = hash_bytes.len() as u32 + input.len() as u32;
2449 let salt = [42u8; 32];
2450 let deployer = T::AddressMapper::to_address(&account_id);
2451 let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
2452 let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
2453
2454 let mut offset = {
2455 let mut current = 0u32;
2456 move |after: u32| {
2457 current += after;
2458 current
2459 }
2460 };
2461
2462 assert!(AccountInfoOf::<T>::get(&addr).is_none());
2463
2464 let result;
2465 #[block]
2466 {
2467 result = runtime.bench_instantiate(
2468 memory.as_mut_slice(),
2469 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)), );
2476 }
2477
2478 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2479 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2480
2481 assert_eq!(
2482 Pallet::<T>::evm_balance(&addr),
2483 evm_value,
2484 "{addr:?} balance should hold {evm_value:?}"
2485 );
2486 Ok(())
2487 }
2488
2489 #[benchmark(pov_mode = Measured)]
2493 fn evm_instantiate(
2494 t: Linear<0, 1>,
2495 d: Linear<0, 1>,
2496 i: Linear<{ 10 * 1024 }, { 48 * 1024 }>,
2497 ) -> Result<(), BenchmarkError> {
2498 use crate::vm::evm::instructions::BENCH_INIT_CODE;
2499 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_init_code_for_runtime_size(0));
2500 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2501 setup.set_balance(caller_funding::<T>());
2502
2503 let (mut ext, _) = setup.ext();
2504 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
2505
2506 let value = {
2507 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2508 let dust = 100u32 * d;
2509 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust))
2510 };
2511
2512 let init_code = vec![BENCH_INIT_CODE; i as usize];
2513 let _ = interpreter.memory.resize(0, init_code.len());
2514 let salt = U256::from(42u64);
2515 interpreter.memory.set_data(0, 0, init_code.len(), &init_code);
2516
2517 let _ = interpreter.stack.push(salt);
2519 let _ = interpreter.stack.push(U256::from(init_code.len()));
2520 let _ = interpreter.stack.push(U256::zero());
2521 let _ = interpreter.stack.push(value);
2522
2523 let result;
2524 #[block]
2525 {
2526 result = instructions::contract::create::<true, _>(&mut interpreter);
2527 }
2528
2529 assert!(result.is_continue());
2530 let addr = interpreter.stack.top().unwrap().into_address();
2531 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2532 assert_eq!(Pallet::<T>::code(&addr).len(), revm::primitives::eip170::MAX_CODE_SIZE);
2533 assert_eq!(Pallet::<T>::evm_balance(&addr), value, "balance should hold {value:?}");
2534 Ok(())
2535 }
2536
2537 #[benchmark(pov_mode = Measured)]
2539 fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2540 let input = vec![0u8; n as usize];
2541 let mut call_setup = CallSetup::<T>::default();
2542 let (mut ext, _) = call_setup.ext();
2543
2544 let result;
2545 #[block]
2546 {
2547 result = run_builtin_precompile(
2548 &mut ext,
2549 H160::from_low_u64_be(2).as_fixed_bytes(),
2550 input.clone(),
2551 );
2552 }
2553 assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
2554 }
2555
2556 #[benchmark(pov_mode = Measured)]
2557 fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2558 let input = vec![0u8; n as usize];
2559 let mut call_setup = CallSetup::<T>::default();
2560 let (mut ext, _) = call_setup.ext();
2561
2562 let result;
2563 #[block]
2564 {
2565 result = run_builtin_precompile(
2566 &mut ext,
2567 H160::from_low_u64_be(4).as_fixed_bytes(),
2568 input.clone(),
2569 );
2570 }
2571 assert_eq!(input, result.unwrap().data);
2572 }
2573
2574 #[benchmark(pov_mode = Measured)]
2576 fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2577 use ripemd::Digest;
2578 let input = vec![0u8; n as usize];
2579 let mut call_setup = CallSetup::<T>::default();
2580 let (mut ext, _) = call_setup.ext();
2581
2582 let result;
2583 #[block]
2584 {
2585 result = run_builtin_precompile(
2586 &mut ext,
2587 H160::from_low_u64_be(3).as_fixed_bytes(),
2588 input.clone(),
2589 );
2590 }
2591 let mut expected = [0u8; 32];
2592 expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
2593
2594 assert_eq!(expected.to_vec(), result.unwrap().data);
2595 }
2596
2597 #[benchmark(pov_mode = Measured)]
2599 fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2600 build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
2601
2602 let result;
2603 #[block]
2604 {
2605 result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
2606 }
2607 assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
2608 assert_ok!(result);
2609 }
2610
2611 #[benchmark(pov_mode = Measured)]
2613 fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2614 let input = vec![0u8; n as usize];
2615 let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
2616 input: input.clone().into(),
2617 })
2618 .abi_encode();
2619
2620 let mut call_setup = CallSetup::<T>::default();
2621 let (mut ext, _) = call_setup.ext();
2622
2623 let result;
2624 #[block]
2625 {
2626 result = run_builtin_precompile(
2627 &mut ext,
2628 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2629 input_bytes,
2630 );
2631 }
2632 let truth: [u8; 32] = sp_io::hashing::blake2_256(&input);
2633 let truth = FixedBytes::<32>::abi_encode(&truth);
2634 let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed");
2635
2636 let raw_data = result.unwrap().data;
2637 let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed");
2638 assert_eq!(truth, ret_hash);
2639 }
2640
2641 #[benchmark(pov_mode = Measured)]
2643 fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2644 let input = vec![0u8; n as usize];
2645 let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
2646 input: input.clone().into(),
2647 })
2648 .abi_encode();
2649
2650 let mut call_setup = CallSetup::<T>::default();
2651 let (mut ext, _) = call_setup.ext();
2652
2653 let result;
2654 #[block]
2655 {
2656 result = run_builtin_precompile(
2657 &mut ext,
2658 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2659 input_bytes,
2660 );
2661 }
2662 let truth: [u8; 16] = sp_io::hashing::blake2_128(&input);
2663 let truth = FixedBytes::<16>::abi_encode(&truth);
2664 let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed");
2665
2666 let raw_data = result.unwrap().data;
2667 let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed");
2668 assert_eq!(truth, ret_hash);
2669 }
2670
2671 #[benchmark(pov_mode = Measured)]
2674 fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) {
2675 let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
2676 let message_len = message.len() as u32;
2677
2678 let key_type = sp_core::crypto::KeyTypeId(*b"code");
2679 let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
2680 let sig =
2681 sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
2682 let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
2683 let sig_len = sig.len() as u32;
2684
2685 build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
2686
2687 let result;
2688 #[block]
2689 {
2690 result = runtime.bench_sr25519_verify(
2691 memory.as_mut_slice(),
2692 0, sig_len, message_len, sig_len + pub_key.len() as u32, );
2697 }
2698
2699 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2700 }
2701
2702 #[benchmark(pov_mode = Measured)]
2703 fn ecdsa_recover() {
2704 use hex_literal::hex;
2705 let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
2706 let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
2707 let mut call_setup = CallSetup::<T>::default();
2708 let (mut ext, _) = call_setup.ext();
2709
2710 let result;
2711
2712 #[block]
2713 {
2714 result =
2715 run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
2716 }
2717
2718 assert_eq!(result.unwrap().data, expected);
2719 }
2720
2721 #[benchmark(pov_mode = Measured)]
2722 fn p256_verify() {
2723 use hex_literal::hex;
2724 let input = hex!("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").to_vec();
2725 let expected = U256::one().to_big_endian();
2726 let mut call_setup = CallSetup::<T>::default();
2727 let (mut ext, _) = call_setup.ext();
2728
2729 let result;
2730
2731 #[block]
2732 {
2733 result = run_builtin_precompile(
2734 &mut ext,
2735 H160::from_low_u64_be(0x100).as_fixed_bytes(),
2736 input,
2737 );
2738 }
2739
2740 assert_eq!(result.unwrap().data, expected);
2741 }
2742
2743 #[benchmark(pov_mode = Measured)]
2744 fn bn128_add() {
2745 use hex_literal::hex;
2746 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
2747 let expected = hex!(
2748 "0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
2749 );
2750 let mut call_setup = CallSetup::<T>::default();
2751 let (mut ext, _) = call_setup.ext();
2752
2753 let result;
2754 #[block]
2755 {
2756 result =
2757 run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
2758 }
2759
2760 assert_eq!(result.unwrap().data, expected);
2761 }
2762
2763 #[benchmark(pov_mode = Measured)]
2764 fn bn128_mul() {
2765 use hex_literal::hex;
2766 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
2767 let expected = hex!(
2768 "0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
2769 );
2770 let mut call_setup = CallSetup::<T>::default();
2771 let (mut ext, _) = call_setup.ext();
2772
2773 let result;
2774 #[block]
2775 {
2776 result =
2777 run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
2778 }
2779
2780 assert_eq!(result.unwrap().data, expected);
2781 }
2782
2783 #[benchmark(pov_mode = Measured)]
2785 fn bn128_pairing(n: Linear<0, { 20 }>) {
2786 fn generate_random_ecpairs(n: usize) -> Vec<u8> {
2787 use bn::{AffineG1, AffineG2, Fr, G1, G2, Group};
2788 use rand::SeedableRng;
2789 use rand_pcg::Pcg64;
2790 let mut rng = Pcg64::seed_from_u64(1);
2791
2792 let mut buffer = vec![0u8; n * 192];
2793
2794 let mut write = |element: &bn::Fq, offset: &mut usize| {
2795 element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
2796 *offset += 32
2797 };
2798
2799 for i in 0..n {
2800 let mut offset = i * 192;
2801 let scalar = Fr::random(&mut rng);
2802
2803 let g1 = G1::one() * scalar;
2804 let g2 = G2::one() * scalar;
2805 let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
2806 let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
2807
2808 write(&a.x(), &mut offset);
2809 write(&a.y(), &mut offset);
2810 write(&b.x().imaginary(), &mut offset);
2811 write(&b.x().real(), &mut offset);
2812 write(&b.y().imaginary(), &mut offset);
2813 write(&b.y().real(), &mut offset);
2814 }
2815
2816 buffer
2817 }
2818
2819 let input = generate_random_ecpairs(n as usize);
2820 let mut call_setup = CallSetup::<T>::default();
2821 let (mut ext, _) = call_setup.ext();
2822
2823 let result;
2824 #[block]
2825 {
2826 result =
2827 run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
2828 }
2829 assert_ok!(result);
2830 }
2831
2832 #[benchmark(pov_mode = Measured)]
2834 fn blake2f(n: Linear<0, 1200>) {
2835 use hex_literal::hex;
2836 let input = hex!(
2837 "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
2838 );
2839 let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
2840 let mut call_setup = CallSetup::<T>::default();
2841 let (mut ext, _) = call_setup.ext();
2842
2843 let result;
2844 #[block]
2845 {
2846 result =
2847 run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
2848 }
2849 assert_ok!(result);
2850 }
2851
2852 #[benchmark(pov_mode = Measured)]
2856 fn seal_ecdsa_to_eth_address() {
2857 let key_type = sp_core::crypto::KeyTypeId(*b"code");
2858 let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
2859 build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
2860
2861 let result;
2862 #[block]
2863 {
2864 result = runtime.bench_ecdsa_to_eth_address(
2865 memory.as_mut_slice(),
2866 20, 0, );
2869 }
2870
2871 assert_ok!(result);
2872 assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
2873 }
2874
2875 #[benchmark(pov_mode = Measured)]
2877 fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
2878 let module = VmBinaryModule::evm_noop(r);
2879 let inputs = vec![];
2880
2881 let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
2882 let mut setup = CallSetup::<T>::new(module);
2883 let (mut ext, _) = setup.ext();
2884
2885 let result;
2886 #[block]
2887 {
2888 result = evm::call(code, &mut ext, inputs);
2889 }
2890
2891 assert!(result.is_ok());
2892 Ok(())
2893 }
2894
2895 #[benchmark(pov_mode = Ignored)]
2900 fn instr(r: Linear<0, 10_000>) {
2901 use rand::{SeedableRng, seq::SliceRandom};
2902 use rand_pcg::Pcg64;
2903
2904 const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
2906
2907 const CACHE_LINE_SIZE: u64 = 64;
2909
2910 const MISALIGNMENT: u64 = 60;
2912
2913 const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
2916
2917 assert!(
2918 u64::from(r) <= NUM_ADDRESSES / 2,
2919 "If we do too many iterations we run into the risk of loading from warm cache lines",
2920 );
2921
2922 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
2923 let (mut ext, module) = setup.ext();
2924 let mut prepared =
2925 CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
2926
2927 assert!(
2928 u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
2929 "aux data base must be cache aligned"
2930 );
2931
2932 let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
2934
2935 let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
2939 for i in 1..NUM_ADDRESSES {
2940 let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
2941 addresses.push(addr);
2942 }
2943 let mut rng = Pcg64::seed_from_u64(1337);
2944 addresses.shuffle(&mut rng);
2945
2946 let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
2948 for address in addresses {
2949 memory.extend_from_slice(&address);
2950 memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
2951 }
2952
2953 prepared
2956 .setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
2957 .unwrap();
2958
2959 #[block]
2960 {
2961 prepared.call().unwrap();
2962 }
2963 }
2964
2965 #[benchmark(pov_mode = Ignored)]
2966 fn instr_empty_loop(r: Linear<0, 10_000>) {
2967 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
2968 let (mut ext, module) = setup.ext();
2969 let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
2970 prepared.setup_aux_data(&[], 0, r.into()).unwrap();
2971
2972 #[block]
2973 {
2974 prepared.call().unwrap();
2975 }
2976 }
2977
2978 #[benchmark(pov_mode = Measured)]
2979 fn extcodecopy(n: Linear<1_000, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
2980 let mut setup = CallSetup::<T>::new(VmBinaryModule::dummy());
2982 let target = Contract::<T>::with_index(1, VmBinaryModule::sized(n), vec![])?;
2984
2985 let (mut ext, _) = setup.ext();
2986 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
2987
2988 let _ = interpreter.stack.push(U256::from(n));
2990 let _ = interpreter.stack.push(U256::from(0u32));
2991 let _ = interpreter.stack.push(U256::from(0u32));
2992 let _ = interpreter.stack.push(target.address);
2993
2994 let result;
2995 #[block]
2996 {
2997 result = instructions::host::extcodecopy(&mut interpreter);
2998 }
2999
3000 assert!(result.is_continue());
3001 assert_eq!(
3002 *interpreter.memory.slice(0..n as usize),
3003 PristineCode::<T>::get(target.info()?.code_hash).unwrap()[0..n as usize],
3004 "Memory should contain the target contract's code after extcodecopy"
3005 );
3006
3007 Ok(())
3008 }
3009
3010 #[benchmark]
3011 fn v1_migration_step() {
3012 use crate::migrations::v1;
3013 let addr = H160::from([1u8; 20]);
3014 let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
3015
3016 v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
3017 let mut meter = WeightMeter::new();
3018 assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
3019
3020 #[block]
3021 {
3022 v1::Migration::<T>::step(None, &mut meter).unwrap();
3023 }
3024
3025 assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
3026 assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
3027
3028 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
3030 }
3031
3032 #[benchmark]
3033 fn v2_migration_step() {
3034 use crate::migrations::v2;
3035 let code_hash = H256::from([0; 32]);
3036 let old_code_info = v2::Migration::<T>::create_old_code_info(
3037 whitelisted_caller(),
3038 1000u32.into(),
3039 1,
3040 100,
3041 0,
3042 );
3043 v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
3044 let mut meter = WeightMeter::new();
3045
3046 #[block]
3047 {
3048 v2::Migration::<T>::step(None, &mut meter).unwrap();
3049 }
3050
3051 v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
3052
3053 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
3055 }
3056
3057 #[benchmark]
3058 fn v3_migration_step() {
3059 use crate::migrations::v3;
3060 let _ = frame_system::Account::<T>::clear(u32::MAX, None);
3062
3063 let account = account::<T::AccountId>("target", 0, 0);
3064 T::Currency::mint_into(&account, Pallet::<T>::min_balance())
3065 .expect("should mint into account");
3066
3067 let addr = T::AddressMapper::to_address(&account);
3069 crate::OriginalAccount::<T>::remove(addr);
3070
3071 assert!(!T::AddressMapper::is_mapped(&account));
3072 let mut meter = WeightMeter::new();
3073
3074 #[block]
3075 {
3076 v3::Migration::<T>::step(None, &mut meter).unwrap();
3077 }
3078
3079 assert!(T::AddressMapper::is_mapped(&account));
3080
3081 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v3_migration_step() * 2);
3083 }
3084
3085 #[benchmark]
3090 fn v4_code_upload_step() {
3091 use crate::migrations::v4;
3092
3093 let _ = CodeInfoOf::<T>::clear(u32::MAX, None);
3094
3095 let owner: T::AccountId = whitelisted_caller();
3096 let deposit: BalanceOf<T> = 1_000u32.into();
3097
3098 let pallet_account = Pallet::<T>::account_id();
3099 T::Currency::mint_into(&pallet_account, Pallet::<T>::min_balance()).unwrap();
3100 T::Currency::mint_into(&pallet_account, deposit).unwrap();
3101 T::Currency::hold(&HoldReason::CodeUploadDepositReserve.into(), &pallet_account, deposit)
3102 .unwrap();
3103
3104 CodeInfoOf::<T>::insert(
3105 H256::from([1u8; 32]),
3106 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
3107 );
3108 CodeInfoOf::<T>::insert(
3109 H256::from([2u8; 32]),
3110 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
3111 );
3112
3113 let first = match v4::Migration::<T>::step_once(None) {
3114 Some(v4::Cursor::CodeUpload(h)) => h,
3115 other => panic!("expected CodeUpload cursor, got {other:?}"),
3116 };
3117 let cursor = Some(v4::Cursor::CodeUpload(first));
3118
3119 #[block]
3120 {
3121 let _ = v4::Migration::<T>::step_once(cursor);
3122 }
3123
3124 assert_eq!(
3125 NativeDepositOf::<T>::get(&pallet_account, &owner),
3126 deposit + deposit,
3127 "both code uploads credited to owner",
3128 );
3129 }
3130
3131 #[benchmark]
3136 fn v4_contract_step() {
3137 use crate::migrations::v4;
3138
3139 let _ = AccountInfoOf::<T>::clear(u32::MAX, None);
3140
3141 let code_hash = H256::from([0u8; 32]);
3142 let deposit: BalanceOf<T> = 1_000u32.into();
3143
3144 for byte in [0x41u8, 0x42u8] {
3145 let addr = H160::from([byte; 20]);
3146 let contract_account = T::AddressMapper::to_account_id(&addr);
3147 let info =
3148 ContractInfo::<T>::new(&addr, 1u32.into(), code_hash).expect("fresh contract info");
3149 AccountInfoOf::<T>::insert(
3150 addr,
3151 crate::storage::AccountInfo::<T> {
3152 account_type: crate::storage::AccountType::Contract(info),
3153 dust: 0,
3154 },
3155 );
3156 T::Currency::mint_into(&contract_account, Pallet::<T>::min_balance()).unwrap();
3157 T::Currency::mint_into(&contract_account, deposit).unwrap();
3158 T::Currency::hold(
3159 &HoldReason::StorageDepositReserve.into(),
3160 &contract_account,
3161 deposit,
3162 )
3163 .unwrap();
3164 }
3165
3166 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::Contract(None))) {
3167 Some(v4::Cursor::Contract(Some(addr))) => addr,
3168 other => panic!("expected Contract cursor, got {other:?}"),
3169 };
3170 let cursor = Some(v4::Cursor::Contract(Some(first)));
3171
3172 #[block]
3173 {
3174 let _ = v4::Migration::<T>::step_once(cursor);
3175 }
3176
3177 if T::Deposit::SUPPORTS_PGAS {
3180 for byte in [0x41u8, 0x42u8] {
3181 let addr = H160::from([byte; 20]);
3182 let contract_account = T::AddressMapper::to_account_id(&addr);
3183 assert_eq!(
3184 T::Currency::balance_on_hold(
3185 &HoldReason::StorageDepositReserve.into(),
3186 &contract_account,
3187 ),
3188 0u32.into(),
3189 "native storage deposit burned for {addr:?}",
3190 );
3191 }
3192 }
3193 }
3194
3195 #[benchmark]
3201 fn v4_deletion_queue_step() {
3202 use crate::migrations::v4;
3203
3204 let _ = v4::old::DeletionQueue::<T>::clear(u32::MAX, None);
3205
3206 let trie_a: TrieId = vec![0xAAu8; 16].try_into().unwrap();
3207 let trie_b: TrieId = vec![0xBBu8; 24].try_into().unwrap();
3208 v4::old::DeletionQueue::<T>::insert(0u32, trie_a);
3209 v4::old::DeletionQueue::<T>::insert(1u32, trie_b);
3210
3211 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::DeletionQueue(None))) {
3212 Some(v4::Cursor::DeletionQueue(Some(key))) => key,
3213 other => panic!("expected DeletionQueue cursor, got {other:?}"),
3214 };
3215 let cursor = Some(v4::Cursor::DeletionQueue(Some(first)));
3216
3217 #[block]
3218 {
3219 let _ = v4::Migration::<T>::step_once(cursor);
3220 }
3221
3222 assert!(
3223 DeletionQueue::<T>::get(0u32).is_some() && DeletionQueue::<T>::get(1u32).is_some(),
3224 "both legacy entries rewritten into the new format",
3225 );
3226 }
3227
3228 fn create_test_signer<T: Config>() -> (T::AccountId, SigningKey, H160) {
3230 use hex_literal::hex;
3231 let signer_account_id = hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
3233 let signer_priv_key =
3234 hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133");
3235
3236 let signer_key = SigningKey::from_bytes(&signer_priv_key.into()).expect("valid key");
3237
3238 let signer_address = H160::from_slice(&signer_account_id);
3239 let signer_caller = T::AddressMapper::to_fallback_account_id(&signer_address);
3240
3241 (signer_caller, signer_key, signer_address)
3242 }
3243
3244 fn create_signed_transaction<T: Config>(
3246 signer_key: &SigningKey,
3247 target_address: H160,
3248 value: U256,
3249 input_data: Vec<u8>,
3250 ) -> Vec<u8> {
3251 let unsigned_tx: TransactionUnsigned = TransactionLegacyUnsigned {
3252 to: Some(target_address),
3253 value,
3254 chain_id: Some(T::ChainId::get().into()),
3255 input: input_data.into(),
3256 ..Default::default()
3257 }
3258 .into();
3259
3260 let hashed_payload = sp_io::hashing::keccak_256(&unsigned_tx.unsigned_payload());
3261 let (signature, recovery_id) =
3262 signer_key.sign_prehash_recoverable(&hashed_payload).expect("signing success");
3263
3264 let mut sig_bytes = [0u8; 65];
3265 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
3266 sig_bytes[64] = recovery_id.to_byte();
3267
3268 let signed_tx = unsigned_tx.with_signature(sig_bytes);
3269
3270 signed_tx.signed_payload()
3271 }
3272
3273 fn setup_finalize_block_benchmark<T>()
3275 -> Result<(Contract<T>, BalanceOf<T>, U256, SigningKey, BlockNumberFor<T>), BenchmarkError>
3276 where
3277 BalanceOf<T>: Into<U256> + TryFrom<U256>,
3278 T: Config,
3279 MomentOf<T>: Into<U256>,
3280 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
3281 {
3282 let (signer_caller, signer_key, _signer_address) = create_test_signer::<T>();
3284 whitelist_account!(signer_caller);
3285
3286 let instance =
3288 Contract::<T>::with_caller(signer_caller.clone(), VmBinaryModule::dummy(), vec![])?;
3289 let storage_deposit = default_deposit_limit::<T>();
3290 let value = Pallet::<T>::min_balance();
3291 let evm_value =
3292 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, 0));
3293
3294 let current_block = BlockNumberFor::<T>::from(1u32);
3296 frame_system::Pallet::<T>::set_block_number(current_block);
3297
3298 Ok((instance, storage_deposit, evm_value, signer_key, current_block))
3299 }
3300
3301 #[benchmark(pov_mode = Measured)]
3318 fn on_finalize_per_transaction(n: Linear<0, 200>) -> Result<(), BenchmarkError> {
3319 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3320 setup_finalize_block_benchmark::<T>()?;
3321
3322 let fixed_payload_size = 100usize;
3324
3325 if n > 0 {
3327 let _ = Pallet::<T>::on_initialize(current_block);
3329
3330 let input_data = vec![0x42u8; fixed_payload_size];
3332 let receipt_gas_info = ReceiptGasInfo {
3333 gas_used: U256::from(1_000_000),
3334 effective_gas_price: Pallet::<T>::evm_base_fee(),
3335 };
3336
3337 for _ in 0..n {
3338 let signed_transaction = create_signed_transaction::<T>(
3340 &signer_key,
3341 instance.address,
3342 evm_value,
3343 input_data.clone(),
3344 );
3345
3346 let _ = block_storage::bench_with_ethereum_context(|| {
3348 let (encoded_logs, bloom) =
3349 block_storage::get_receipt_details().unwrap_or_default();
3350
3351 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3352 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3353
3354 block_builder.process_transaction(
3355 signed_transaction,
3356 true,
3357 receipt_gas_info.clone(),
3358 encoded_logs,
3359 bloom,
3360 );
3361
3362 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3363 });
3364 }
3365 }
3366
3367 #[block]
3368 {
3369 let _ = Pallet::<T>::on_finalize(current_block);
3371 }
3372
3373 assert_eq!(Pallet::<T>::eth_block().transactions.len(), n as usize);
3375
3376 Ok(())
3377 }
3378
3379 #[benchmark(pov_mode = Measured)]
3396 fn on_finalize_per_transaction_data(d: Linear<0, 1000>) -> Result<(), BenchmarkError> {
3397 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3398 setup_finalize_block_benchmark::<T>()?;
3399
3400 let fixed_tx_count = 10u32;
3402
3403 let _ = Pallet::<T>::on_initialize(current_block);
3405
3406 let input_data = vec![0x42u8; d as usize];
3408 let receipt_gas_info = ReceiptGasInfo {
3409 gas_used: U256::from(1_000_000),
3410 effective_gas_price: Pallet::<T>::evm_base_fee(),
3411 };
3412
3413 for _ in 0..fixed_tx_count {
3414 let signed_transaction = create_signed_transaction::<T>(
3416 &signer_key,
3417 instance.address,
3418 evm_value,
3419 input_data.clone(),
3420 );
3421
3422 let _ = block_storage::bench_with_ethereum_context(|| {
3424 let (encoded_logs, bloom) =
3425 block_storage::get_receipt_details().unwrap_or_default();
3426
3427 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3428 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3429
3430 block_builder.process_transaction(
3431 signed_transaction,
3432 true,
3433 receipt_gas_info.clone(),
3434 encoded_logs,
3435 bloom,
3436 );
3437
3438 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3439 });
3440 }
3441
3442 #[block]
3443 {
3444 let _ = Pallet::<T>::on_finalize(current_block);
3446 }
3447
3448 assert_eq!(Pallet::<T>::eth_block().transactions.len(), fixed_tx_count as usize);
3450
3451 Ok(())
3452 }
3453
3454 #[benchmark(pov_mode = Measured)]
3472 fn on_finalize_per_event(e: Linear<0, 100>) -> Result<(), BenchmarkError> {
3473 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3474 setup_finalize_block_benchmark::<T>()?;
3475
3476 let input_data = vec![0x42u8; 100];
3478 let signed_transaction = create_signed_transaction::<T>(
3479 &signer_key,
3480 instance.address,
3481 evm_value,
3482 input_data.clone(),
3483 );
3484
3485 let receipt_gas_info = ReceiptGasInfo {
3486 gas_used: U256::from(1_000_000),
3487 effective_gas_price: Pallet::<T>::evm_base_fee(),
3488 };
3489
3490 let _ = block_storage::bench_with_ethereum_context(|| {
3492 let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
3493
3494 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3495 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3496
3497 block_builder.process_transaction(
3498 signed_transaction,
3499 true,
3500 receipt_gas_info.clone(),
3501 encoded_logs,
3502 bloom,
3503 );
3504
3505 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3506 });
3507
3508 for _ in 0..e {
3510 block_storage::capture_ethereum_log(&instance.address, &vec![], &vec![]);
3511 }
3512
3513 #[block]
3514 {
3515 let _ = Pallet::<T>::on_initialize(current_block);
3517
3518 let _ = Pallet::<T>::on_finalize(current_block);
3520 }
3521
3522 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3524
3525 Ok(())
3526 }
3527
3528 #[benchmark(pov_mode = Measured)]
3537 fn on_finalize_per_event_data(d: Linear<0, 16384>) -> Result<(), BenchmarkError> {
3538 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3539 setup_finalize_block_benchmark::<T>()?;
3540
3541 let input_data = vec![0x42u8; 100];
3543 let signed_transaction = create_signed_transaction::<T>(
3544 &signer_key,
3545 instance.address,
3546 evm_value,
3547 input_data.clone(),
3548 );
3549
3550 let receipt_gas_info = ReceiptGasInfo {
3551 gas_used: U256::from(1_000_000),
3552 effective_gas_price: Pallet::<T>::evm_base_fee(),
3553 };
3554
3555 let _ = block_storage::bench_with_ethereum_context(|| {
3557 let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
3558
3559 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3560 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3561
3562 block_builder.process_transaction(
3563 signed_transaction,
3564 true,
3565 receipt_gas_info,
3566 encoded_logs,
3567 bloom,
3568 );
3569
3570 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3571 });
3572
3573 let (event_data, topics) = if d < 32 {
3575 (vec![0x42u8; d as usize], vec![])
3577 } else {
3578 let num_topics = core::cmp::min(limits::NUM_EVENT_TOPICS, d / 32);
3580 let topic_bytes_used = num_topics * 32;
3581 let data_bytes_remaining = d - topic_bytes_used;
3582
3583 let mut topics = Vec::new();
3585 for topic_index in 0..num_topics {
3586 let topic_data = [topic_index as u8; 32];
3587 topics.push(H256::from(topic_data));
3588 }
3589
3590 let event_data = vec![0x42u8; data_bytes_remaining as usize];
3592
3593 (event_data, topics)
3594 };
3595
3596 block_storage::capture_ethereum_log(&instance.address, &event_data, &topics);
3597
3598 #[block]
3599 {
3600 let _ = Pallet::<T>::on_initialize(current_block);
3602
3603 let _ = Pallet::<T>::on_finalize(current_block);
3605 }
3606
3607 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3609
3610 Ok(())
3611 }
3612
3613 impl_benchmark_test_suite!(
3614 Contracts,
3615 crate::tests::ExtBuilder::default().build(),
3616 crate::tests::Test,
3617 );
3618}