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 fn shared_prefix_keys(count: usize, suffix_of: impl Fn(u64) -> u64) -> Vec<Vec<u8>> {
1573 (0..count as u64)
1574 .map(|i| {
1575 let mut key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1576 key[limits::STORAGE_KEY_BYTES as usize - 8..]
1577 .copy_from_slice(&suffix_of(i).to_be_bytes());
1578 key
1579 })
1580 .collect()
1581 }
1582
1583 fn setup_stored_keys<T: Config>(
1586 count: usize,
1587 value_byte: u8,
1588 suffix_of: impl Fn(u64) -> u64,
1589 ) -> Result<(ContractInfo<T>, Vec<Vec<u8>>), BenchmarkError> {
1590 let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1591 let info = instance.info()?;
1592 let child_trie_info = info.child_trie_info();
1593 let value = vec![value_byte; limits::STORAGE_BYTES as usize];
1594 let stored_keys = shared_prefix_keys(count, suffix_of);
1595 for key in &stored_keys {
1596 info.bench_write_raw(key, Some(value.clone()), false)
1597 .map_err(|_| "Failed to write to storage during setup.")?;
1598 frame_benchmarking::add_to_whitelist_child(
1599 child_trie_info.storage_key().to_vec(),
1600 key.clone(),
1601 );
1602 }
1603 Ok((info, stored_keys))
1604 }
1605
1606 #[benchmark(skip_meta, pov_mode = Measured)]
1607 fn overlay_probe_full(
1608 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
1609 ) -> Result<(), BenchmarkError> {
1610 let value_byte = 42;
1611 let (info, stored_keys) =
1613 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
1614 let child_trie_info = info.child_trie_info();
1615 let fill_keys = shared_prefix_keys(MAX_ACCESS_LIST_ENTRIES, |i| (i + 1) * 2);
1617
1618 let mut result = None;
1619 #[block]
1620 {
1621 for key in &fill_keys {
1623 child::put_raw(&child_trie_info, key, &[0u8]);
1624 }
1625 for i in 0..n {
1626 let index = i as usize % stored_keys.len();
1627 result = child::get_raw(&child_trie_info, &stored_keys[index]);
1628 }
1629 }
1630
1631 if n > 0 {
1632 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
1633 assert_eq!(result, Some(expected), "the stored value must be read back");
1634 }
1635 Ok(())
1636 }
1637
1638 #[benchmark(skip_meta, pov_mode = Measured)]
1639 fn overlay_probe_empty(
1640 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
1641 ) -> Result<(), BenchmarkError> {
1642 let value_byte = 42;
1643 let (info, stored_keys) =
1645 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
1646 let child_trie_info = info.child_trie_info();
1647
1648 let mut result = None;
1649 #[block]
1650 {
1651 for i in 0..n {
1652 let index = i as usize % stored_keys.len();
1653 result = child::get_raw(&child_trie_info, &stored_keys[index]);
1654 }
1655 }
1656
1657 if n > 0 {
1658 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
1659 assert_eq!(result, Some(expected), "the stored value must be read back");
1660 }
1661 Ok(())
1662 }
1663
1664 #[benchmark(skip_meta, pov_mode = Measured)]
1667 fn seal_set_storage(
1668 n: Linear<0, { limits::STORAGE_BYTES }>,
1669 o: Linear<0, { limits::STORAGE_BYTES }>,
1670 ) -> Result<(), BenchmarkError> {
1671 let max_key_len = limits::STORAGE_KEY_BYTES;
1672 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1673 .map_err(|_| "Key has wrong length")?;
1674 let value = vec![1u8; n as usize];
1675
1676 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1677 let info = instance.info()?;
1678
1679 info.write(&key, Some(vec![42u8; o as usize]), None, false)
1680 .map_err(|_| "Failed to write to storage during setup.")?;
1681
1682 let result;
1683 #[block]
1684 {
1685 result = runtime.bench_set_storage(
1686 memory.as_mut_slice(),
1687 StorageFlags::empty().bits(),
1688 0, max_key_len, max_key_len, n, );
1693 }
1694
1695 assert_ok!(result);
1696 assert_eq!(info.read(&key).unwrap(), value);
1697 Ok(())
1698 }
1699
1700 #[benchmark(skip_meta, pov_mode = Measured)]
1701 fn seal_set_storage_hot(
1702 n: Linear<0, { limits::STORAGE_BYTES }>,
1703 o: Linear<0, { limits::STORAGE_BYTES }>,
1704 ) -> Result<(), BenchmarkError> {
1705 let max_key_len = limits::STORAGE_KEY_BYTES;
1706 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1707 .map_err(|_| "Key has wrong length")?;
1708 let value = vec![1u8; n as usize];
1709
1710 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1711 let info = instance.info()?;
1712
1713 info.write(&key, Some(vec![42u8; o as usize]), None, false)
1714 .map_err(|_| "Failed to write to storage during setup.")?;
1715
1716 frame_benchmarking::add_to_whitelist_child(
1717 info.child_trie_info().storage_key().to_vec(),
1718 key.hash(),
1719 );
1720
1721 runtime.ext().touch_storage_access(false, &key);
1723
1724 let result;
1725 #[block]
1726 {
1727 result = runtime.bench_set_storage(
1728 memory.as_mut_slice(),
1729 StorageFlags::empty().bits(),
1730 0, max_key_len, max_key_len, n, );
1735 }
1736
1737 assert_ok!(result);
1738 assert_eq!(info.read(&key).unwrap(), value);
1739 Ok(())
1740 }
1741
1742 #[benchmark(skip_meta, pov_mode = Measured)]
1743 fn clear_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1744 let key_byte = 0;
1745 let (mut call_setup, key, input_bytes) =
1746 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Cold)?;
1747 let (mut ext, _) = call_setup.ext();
1748 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1749 .map_err(|_| "Failed to write to storage during setup.")?;
1750
1751 let result;
1752 #[block]
1753 {
1754 result = run_builtin_precompile(
1755 &mut ext,
1756 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1757 input_bytes,
1758 );
1759 }
1760 assert_ok!(result);
1761 assert!(ext.get_storage(&key).is_none());
1762
1763 Ok(())
1764 }
1765
1766 #[benchmark(skip_meta, pov_mode = Measured)]
1767 fn clear_storage_hot(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::Clear, key_byte, SlotAccess::Hot)?;
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 ext.touch_storage_access(false, &key);
1776
1777 let result;
1778 #[block]
1779 {
1780 result = run_builtin_precompile(
1781 &mut ext,
1782 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1783 input_bytes,
1784 );
1785 }
1786 assert_ok!(result);
1787 assert!(ext.get_storage(&key).is_none());
1788
1789 Ok(())
1790 }
1791
1792 #[benchmark(skip_meta, pov_mode = Measured)]
1793 fn seal_get_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1794 let max_key_len = limits::STORAGE_KEY_BYTES;
1795 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1796 .map_err(|_| "Key has wrong length")?;
1797 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1798 let info = instance.info()?;
1799
1800 info.write(&key, Some(vec![42u8; n as usize]), None, false)
1801 .map_err(|_| "Failed to write to storage during setup.")?;
1802
1803 let out_ptr = max_key_len + 4;
1804 let result;
1805 #[block]
1806 {
1807 result = runtime.bench_get_storage(
1808 memory.as_mut_slice(),
1809 StorageFlags::empty().bits(),
1810 0, max_key_len, out_ptr, max_key_len, );
1815 }
1816
1817 assert_ok!(result);
1818 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
1819 Ok(())
1820 }
1821
1822 #[benchmark(skip_meta, pov_mode = Measured)]
1823 fn seal_get_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1824 let max_key_len = limits::STORAGE_KEY_BYTES;
1825 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1826 .map_err(|_| "Key has wrong length")?;
1827 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1828 let info = instance.info()?;
1829
1830 info.write(&key, Some(vec![42u8; n as usize]), None, false)
1831 .map_err(|_| "Failed to write to storage during setup.")?;
1832
1833 frame_benchmarking::add_to_whitelist_child(
1834 info.child_trie_info().storage_key().to_vec(),
1835 key.hash(),
1836 );
1837
1838 runtime.ext().touch_storage_access(false, &key);
1839
1840 let out_ptr = max_key_len + 4;
1841 let result;
1842 #[block]
1843 {
1844 result = runtime.bench_get_storage(
1845 memory.as_mut_slice(),
1846 StorageFlags::empty().bits(),
1847 0, max_key_len, out_ptr, max_key_len, );
1852 }
1853
1854 assert_ok!(result);
1855 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
1856 Ok(())
1857 }
1858
1859 #[benchmark(skip_meta, pov_mode = Measured)]
1860 fn contains_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1861 let key_byte = 0;
1862 let (mut call_setup, key, input_bytes) =
1863 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Cold)?;
1864 let (mut ext, _) = call_setup.ext();
1865 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1866 .map_err(|_| "Failed to write to storage during setup.")?;
1867
1868 let result;
1869 #[block]
1870 {
1871 result = run_builtin_precompile(
1872 &mut ext,
1873 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1874 input_bytes,
1875 );
1876 }
1877 assert_ok!(result);
1878 assert!(ext.get_storage(&key).is_some());
1879
1880 Ok(())
1881 }
1882
1883 #[benchmark(skip_meta, pov_mode = Measured)]
1884 fn contains_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1885 let key_byte = 0;
1886 let (mut call_setup, key, input_bytes) =
1887 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Hot)?;
1888 let (mut ext, _) = call_setup.ext();
1889 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1890 .map_err(|_| "Failed to write to storage during setup.")?;
1891
1892 ext.touch_storage_access(false, &key);
1893
1894 let result;
1895 #[block]
1896 {
1897 result = run_builtin_precompile(
1898 &mut ext,
1899 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1900 input_bytes,
1901 );
1902 }
1903 assert_ok!(result);
1904 assert!(ext.get_storage(&key).is_some());
1905
1906 Ok(())
1907 }
1908
1909 #[benchmark(skip_meta, pov_mode = Measured)]
1910 fn take_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1911 let key_byte = 3;
1912 let (mut call_setup, key, input_bytes) =
1913 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Cold)?;
1914 let (mut ext, _) = call_setup.ext();
1915 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1916 .map_err(|_| "Failed to write to storage during setup.")?;
1917
1918 let result;
1919 #[block]
1920 {
1921 result = run_builtin_precompile(
1922 &mut ext,
1923 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1924 input_bytes,
1925 );
1926 }
1927 assert_ok!(result);
1928 assert!(ext.get_storage(&key).is_none());
1929
1930 Ok(())
1931 }
1932
1933 #[benchmark(skip_meta, pov_mode = Measured)]
1934 fn take_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1935 let key_byte = 3;
1936 let (mut call_setup, key, input_bytes) =
1937 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Hot)?;
1938 let (mut ext, _) = call_setup.ext();
1939 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1940 .map_err(|_| "Failed to write to storage during setup.")?;
1941
1942 ext.touch_storage_access(false, &key);
1943
1944 let result;
1945 #[block]
1946 {
1947 result = run_builtin_precompile(
1948 &mut ext,
1949 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1950 input_bytes,
1951 );
1952 }
1953 assert_ok!(result);
1954 assert!(ext.get_storage(&key).is_none());
1955
1956 Ok(())
1957 }
1958
1959 fn worst_case_slot() -> crate::access_list::Slot {
1960 let key = Key::try_from_var(vec![0xFFu8; limits::STORAGE_KEY_BYTES as usize])
1961 .expect("key fits STORAGE_KEY_BYTES bound; qed");
1962 crate::access_list::Slot::from(&key)
1963 }
1964
1965 fn near_full_access_list() -> crate::access_list::AccessList {
1966 let mut al = AccessList::new();
1967 for i in 0..(MAX_ACCESS_LIST_ENTRIES - 1) {
1968 al.touch(AccessEntry {
1969 slot: worst_case_slot(),
1970 address: H160::from_low_u64_be(i as u64),
1971 });
1972 }
1973 al
1974 }
1975
1976 #[benchmark(pov_mode = Ignored)]
1977 fn access_list_touch_cold_full() -> Result<(), BenchmarkError> {
1978 let mut al = near_full_access_list();
1979 let entry =
1981 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
1982 let outcome;
1983 #[block]
1984 {
1985 outcome = al.touch(entry);
1986 }
1987 assert!(outcome.is_cold());
1988 Ok(())
1989 }
1990
1991 #[benchmark(pov_mode = Ignored)]
1992 fn access_list_touch_hot_full() -> Result<(), BenchmarkError> {
1993 let mut al = near_full_access_list();
1994 let entry = AccessEntry { slot: worst_case_slot(), address: H160::zero() };
1996 let outcome;
1997 #[block]
1998 {
1999 outcome = al.touch(entry);
2000 }
2001 assert!(!outcome.is_cold());
2002 Ok(())
2003 }
2004
2005 #[benchmark(pov_mode = Ignored)]
2006 fn access_list_touch_cold_empty() -> Result<(), BenchmarkError> {
2007 let mut al = AccessList::new();
2008 let entry =
2009 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2010 let outcome;
2011 #[block]
2012 {
2013 outcome = al.touch(entry);
2014 }
2015 assert!(outcome.is_cold());
2016 Ok(())
2017 }
2018
2019 #[benchmark(pov_mode = Ignored)]
2020 fn access_list_touch_hot_single_element() -> Result<(), BenchmarkError> {
2021 let mut al = AccessList::new();
2022 let entry =
2023 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2024 al.touch(entry.clone());
2025 let outcome;
2026 #[block]
2027 {
2028 outcome = al.touch(entry);
2029 }
2030 assert!(!outcome.is_cold());
2031 Ok(())
2032 }
2033
2034 #[benchmark(pov_mode = Ignored)]
2038 fn access_list_rollback_amortization() -> Result<(), BenchmarkError> {
2039 let mut al = near_full_access_list();
2040 al.enter_frame();
2041 al.touch(AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) });
2042 #[block]
2043 {
2044 al.rollback_frame();
2045 }
2046 Ok(())
2047 }
2048
2049 #[benchmark(pov_mode = Ignored)]
2054 fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
2055 let max_value_len = limits::STORAGE_BYTES;
2056 let max_key_len = limits::STORAGE_KEY_BYTES;
2057 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2058 .map_err(|_| "Key has wrong length")?;
2059 let value = Some(vec![42u8; max_value_len as _]);
2060 let mut setup = CallSetup::<T>::default();
2061 let (mut ext, _) = setup.ext();
2062 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2063 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2064 let result;
2065 #[block]
2066 {
2067 result = runtime.ext().set_transient_storage(&key, value, false);
2068 }
2069
2070 assert_eq!(result, Ok(WriteOutcome::New));
2071 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2072 Ok(())
2073 }
2074
2075 #[benchmark(pov_mode = Ignored)]
2076 fn set_transient_storage_full() -> Result<(), BenchmarkError> {
2077 let max_value_len = limits::STORAGE_BYTES;
2078 let max_key_len = limits::STORAGE_KEY_BYTES;
2079 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2080 .map_err(|_| "Key has wrong length")?;
2081 let value = Some(vec![42u8; max_value_len as _]);
2082 let mut setup = CallSetup::<T>::default();
2083 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2084 let (mut ext, _) = setup.ext();
2085 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2086 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2087 let result;
2088 #[block]
2089 {
2090 result = runtime.ext().set_transient_storage(&key, value, false);
2091 }
2092
2093 assert_eq!(result, Ok(WriteOutcome::New));
2094 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2095 Ok(())
2096 }
2097
2098 #[benchmark(pov_mode = Ignored)]
2099 fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
2100 let max_value_len = limits::STORAGE_BYTES;
2101 let max_key_len = limits::STORAGE_KEY_BYTES;
2102 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2103 .map_err(|_| "Key has wrong length")?;
2104
2105 let mut setup = CallSetup::<T>::default();
2106 let (mut ext, _) = setup.ext();
2107 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2108 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2109 runtime
2110 .ext()
2111 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2112 .map_err(|_| "Failed to write to transient storage during setup.")?;
2113 let result;
2114 #[block]
2115 {
2116 result = runtime.ext().get_transient_storage(&key);
2117 }
2118
2119 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2120 Ok(())
2121 }
2122
2123 #[benchmark(pov_mode = Ignored)]
2124 fn get_transient_storage_full() -> Result<(), BenchmarkError> {
2125 let max_value_len = limits::STORAGE_BYTES;
2126 let max_key_len = limits::STORAGE_KEY_BYTES;
2127 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2128 .map_err(|_| "Key has wrong length")?;
2129
2130 let mut setup = CallSetup::<T>::default();
2131 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2132 let (mut ext, _) = setup.ext();
2133 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2134 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2135 runtime
2136 .ext()
2137 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2138 .map_err(|_| "Failed to write to transient storage during setup.")?;
2139 let result;
2140 #[block]
2141 {
2142 result = runtime.ext().get_transient_storage(&key);
2143 }
2144
2145 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2146 Ok(())
2147 }
2148
2149 #[benchmark(pov_mode = Ignored)]
2151 fn rollback_transient_storage() -> Result<(), BenchmarkError> {
2152 let max_value_len = limits::STORAGE_BYTES;
2153 let max_key_len = limits::STORAGE_KEY_BYTES;
2154 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2155 .map_err(|_| "Key has wrong length")?;
2156
2157 let mut setup = CallSetup::<T>::default();
2158 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2159 let (mut ext, _) = setup.ext();
2160 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2161 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2162 runtime.ext().transient_storage().start_transaction();
2163 runtime
2164 .ext()
2165 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2166 .map_err(|_| "Failed to write to transient storage during setup.")?;
2167 #[block]
2168 {
2169 runtime.ext().transient_storage().rollback_transaction();
2170 }
2171
2172 assert_eq!(runtime.ext().get_transient_storage(&key), None);
2173 Ok(())
2174 }
2175
2176 #[benchmark(pov_mode = Measured)]
2179 fn seal_set_transient_storage(
2180 n: Linear<0, { limits::STORAGE_BYTES }>,
2181 o: Linear<0, { limits::STORAGE_BYTES }>,
2182 ) -> Result<(), BenchmarkError> {
2183 let max_key_len = limits::STORAGE_KEY_BYTES;
2184 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2185 .map_err(|_| "Key has wrong length")?;
2186 let value = vec![1u8; n as usize];
2187 build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]);
2188 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2189 runtime
2190 .ext()
2191 .set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
2192 .map_err(|_| "Failed to write to transient storage during setup.")?;
2193
2194 let result;
2195 #[block]
2196 {
2197 result = runtime.bench_set_storage(
2198 memory.as_mut_slice(),
2199 StorageFlags::TRANSIENT.bits(),
2200 0, max_key_len, max_key_len, n, );
2205 }
2206
2207 assert_ok!(result);
2208 assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
2209 Ok(())
2210 }
2211
2212 #[benchmark(pov_mode = Measured)]
2213 fn seal_clear_transient_storage(
2214 n: Linear<0, { limits::STORAGE_BYTES }>,
2215 ) -> Result<(), BenchmarkError> {
2216 let max_key_len = limits::STORAGE_KEY_BYTES;
2217 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2218 .map_err(|_| "Key has wrong length")?;
2219 let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
2220 flags: StorageFlags::TRANSIENT.bits(),
2221 key: vec![0u8; max_key_len as usize].into(),
2222 isFixedKey: false,
2223 })
2224 .abi_encode();
2225
2226 let mut call_setup = CallSetup::<T>::default();
2227 let (mut ext, _) = call_setup.ext();
2228 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2229 .map_err(|_| "Failed to write to transient storage during setup.")?;
2230
2231 let result;
2232 #[block]
2233 {
2234 result = run_builtin_precompile(
2235 &mut ext,
2236 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2237 input_bytes,
2238 );
2239 }
2240 assert_ok!(result);
2241 assert!(ext.get_transient_storage(&key).is_none());
2242
2243 Ok(())
2244 }
2245
2246 #[benchmark(pov_mode = Measured)]
2247 fn seal_get_transient_storage(
2248 n: Linear<0, { limits::STORAGE_BYTES }>,
2249 ) -> Result<(), BenchmarkError> {
2250 let max_key_len = limits::STORAGE_KEY_BYTES;
2251 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2252 .map_err(|_| "Key has wrong length")?;
2253 build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2254 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2255 runtime
2256 .ext()
2257 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2258 .map_err(|_| "Failed to write to transient storage during setup.")?;
2259
2260 let out_ptr = max_key_len + 4;
2261 let result;
2262 #[block]
2263 {
2264 result = runtime.bench_get_storage(
2265 memory.as_mut_slice(),
2266 StorageFlags::TRANSIENT.bits(),
2267 0, max_key_len, out_ptr, max_key_len, );
2272 }
2273
2274 assert_ok!(result);
2275 assert_eq!(
2276 &runtime.ext().get_transient_storage(&key).unwrap(),
2277 &memory[out_ptr as usize..]
2278 );
2279 Ok(())
2280 }
2281
2282 #[benchmark(pov_mode = Measured)]
2283 fn seal_contains_transient_storage(
2284 n: Linear<0, { limits::STORAGE_BYTES }>,
2285 ) -> Result<(), BenchmarkError> {
2286 let max_key_len = limits::STORAGE_KEY_BYTES;
2287 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2288 .map_err(|_| "Key has wrong length")?;
2289
2290 let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
2291 flags: StorageFlags::TRANSIENT.bits(),
2292 key: vec![0u8; max_key_len as usize].into(),
2293 isFixedKey: false,
2294 })
2295 .abi_encode();
2296
2297 let mut call_setup = CallSetup::<T>::default();
2298 let (mut ext, _) = call_setup.ext();
2299 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2300 .map_err(|_| "Failed to write to transient storage during setup.")?;
2301
2302 let result;
2303 #[block]
2304 {
2305 result = run_builtin_precompile(
2306 &mut ext,
2307 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2308 input_bytes,
2309 );
2310 }
2311 assert!(result.is_ok());
2312 assert!(ext.get_transient_storage(&key).is_some());
2313
2314 Ok(())
2315 }
2316
2317 #[benchmark(pov_mode = Measured)]
2318 fn seal_take_transient_storage(
2319 n: Linear<0, { limits::STORAGE_BYTES }>,
2320 ) -> Result<(), BenchmarkError> {
2321 let n = limits::STORAGE_BYTES;
2322 let value = vec![42u8; n as usize];
2323 let max_key_len = limits::STORAGE_KEY_BYTES;
2324 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2325 .map_err(|_| "Key has wrong length")?;
2326
2327 let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
2328 flags: StorageFlags::TRANSIENT.bits(),
2329 key: vec![0u8; max_key_len as usize].into(),
2330 isFixedKey: false,
2331 })
2332 .abi_encode();
2333
2334 let mut call_setup = CallSetup::<T>::default();
2335 let (mut ext, _) = call_setup.ext();
2336 ext.set_transient_storage(&key, Some(value), false)
2337 .map_err(|_| "Failed to write to transient storage during setup.")?;
2338
2339 let result;
2340 #[block]
2341 {
2342 result = run_builtin_precompile(
2343 &mut ext,
2344 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2345 input_bytes,
2346 );
2347 }
2348 assert!(result.is_ok());
2349 assert!(ext.get_transient_storage(&key).is_none());
2350
2351 Ok(())
2352 }
2353
2354 #[benchmark(pov_mode = Measured)]
2358 fn seal_call(t: Linear<0, 1>, d: Linear<0, 1>, i: Linear<0, { limits::code::BLOB_BYTES }>) {
2359 let Contract { account_id: callee, address: callee_addr, .. } =
2360 Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
2361
2362 let callee_bytes = callee.encode();
2363 let callee_len = callee_bytes.len() as u32;
2364
2365 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2366 let dust = 100u32 * d;
2367 let evm_value =
2368 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2369 let value_bytes = evm_value.encode();
2370
2371 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2372 let deposit_bytes = Into::<U256>::into(deposit).encode();
2373 let deposit_len = deposit_bytes.len() as u32;
2374
2375 let mut setup = CallSetup::<T>::default();
2376 setup.set_storage_deposit_limit(deposit);
2377 setup.set_data(vec![42; i as usize]);
2380 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2381 setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
2382
2383 let (mut ext, _) = setup.ext();
2384 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2385 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
2386
2387 let result;
2388 #[block]
2389 {
2390 result = runtime.bench_call(
2391 memory.as_mut_slice(),
2392 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), );
2399 }
2400
2401 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2402 assert_eq!(
2403 Pallet::<T>::evm_balance(&callee_addr),
2404 evm_value,
2405 "{callee_addr:?} balance should hold {evm_value:?}"
2406 );
2407 }
2408
2409 #[benchmark(pov_mode = Measured)]
2412 fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
2413 use alloy_core::sol_types::SolInterface;
2414 use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
2415
2416 let callee_bytes = if d == 1 {
2417 BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
2418 } else {
2419 BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
2420 };
2421 let callee_len = callee_bytes.len() as u32;
2422
2423 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2424 let deposit_bytes = Into::<U256>::into(deposit).encode();
2425 let deposit_len = deposit_bytes.len() as u32;
2426
2427 let value: BalanceOf<T> = Zero::zero();
2428 let value_bytes = Into::<U256>::into(value).encode();
2429 let value_len = value_bytes.len() as u32;
2430
2431 let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
2432 input: vec![42_u8; i as usize].into(),
2433 })
2434 .abi_encode();
2435 let input_len = input_bytes.len() as u32;
2436
2437 let mut setup = CallSetup::<T>::default();
2438 setup.set_storage_deposit_limit(deposit);
2439
2440 let (mut ext, _) = setup.ext();
2441 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2442 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
2443
2444 let mut do_benchmark = || {
2445 runtime.bench_call(
2446 memory.as_mut_slice(),
2447 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), )
2456 };
2457
2458 assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
2461
2462 let result;
2463 #[block]
2464 {
2465 result = do_benchmark();
2466 }
2467
2468 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2469 }
2470
2471 #[benchmark(pov_mode = Measured)]
2472 fn seal_delegate_call() -> Result<(), BenchmarkError> {
2473 let Contract { account_id: address, .. } =
2474 Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
2475
2476 let address_bytes = address.encode();
2477 let address_len = address_bytes.len() as u32;
2478
2479 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2480 let deposit_bytes = Into::<U256>::into(deposit).encode();
2481
2482 let mut setup = CallSetup::<T>::default();
2483 setup.set_storage_deposit_limit(deposit);
2484 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2485
2486 let (mut ext, _) = setup.ext();
2487 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2488 let mut memory = memory!(address_bytes, deposit_bytes,);
2489
2490 let result;
2491 #[block]
2492 {
2493 result = runtime.bench_delegate_call(
2494 memory.as_mut_slice(),
2495 pack_hi_lo(0, 0), u64::MAX, u64::MAX, address_len, pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
2502 }
2503
2504 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2505 Ok(())
2506 }
2507
2508 #[benchmark(pov_mode = Measured)]
2512 fn seal_instantiate(
2513 t: Linear<0, 1>,
2514 d: Linear<0, 1>,
2515 i: Linear<0, { limits::CALLDATA_BYTES }>,
2516 ) -> Result<(), BenchmarkError> {
2517 let code = VmBinaryModule::dummy();
2518 let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
2519 let hash_bytes = hash.encode();
2520
2521 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2522 let dust = 100u32 * d;
2523 let evm_value =
2524 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2525 let value_bytes = evm_value.encode();
2526 let value_len = value_bytes.len() as u32;
2527
2528 let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
2529 let deposit_bytes = Into::<U256>::into(deposit).encode();
2530 let deposit_len = deposit_bytes.len() as u32;
2531
2532 let mut setup = CallSetup::<T>::default();
2533 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2534 setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
2535
2536 let account_id = &setup.contract().account_id.clone();
2537 let (mut ext, _) = setup.ext();
2538 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2539
2540 let input = vec![42u8; i as _];
2541 let input_len = hash_bytes.len() as u32 + input.len() as u32;
2542 let salt = [42u8; 32];
2543 let deployer = T::AddressMapper::to_address(&account_id);
2544 let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
2545 let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
2546
2547 let mut offset = {
2548 let mut current = 0u32;
2549 move |after: u32| {
2550 current += after;
2551 current
2552 }
2553 };
2554
2555 assert!(AccountInfoOf::<T>::get(&addr).is_none());
2556
2557 let result;
2558 #[block]
2559 {
2560 result = runtime.bench_instantiate(
2561 memory.as_mut_slice(),
2562 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)), );
2569 }
2570
2571 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2572 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2573
2574 assert_eq!(
2575 Pallet::<T>::evm_balance(&addr),
2576 evm_value,
2577 "{addr:?} balance should hold {evm_value:?}"
2578 );
2579 Ok(())
2580 }
2581
2582 #[benchmark(pov_mode = Measured)]
2586 fn evm_instantiate(
2587 t: Linear<0, 1>,
2588 d: Linear<0, 1>,
2589 i: Linear<{ 10 * 1024 }, { 48 * 1024 }>,
2590 ) -> Result<(), BenchmarkError> {
2591 use crate::vm::evm::instructions::BENCH_INIT_CODE;
2592 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_init_code_for_runtime_size(0));
2593 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2594 setup.set_balance(caller_funding::<T>());
2595
2596 let (mut ext, _) = setup.ext();
2597 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
2598
2599 let value = {
2600 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2601 let dust = 100u32 * d;
2602 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust))
2603 };
2604
2605 let init_code = vec![BENCH_INIT_CODE; i as usize];
2606 let _ = interpreter.memory.resize(0, init_code.len());
2607 let salt = U256::from(42u64);
2608 interpreter.memory.set_data(0, 0, init_code.len(), &init_code);
2609
2610 let _ = interpreter.stack.push(salt);
2612 let _ = interpreter.stack.push(U256::from(init_code.len()));
2613 let _ = interpreter.stack.push(U256::zero());
2614 let _ = interpreter.stack.push(value);
2615
2616 let result;
2617 #[block]
2618 {
2619 result = instructions::contract::create::<true, _>(&mut interpreter);
2620 }
2621
2622 assert!(result.is_continue());
2623 let addr = interpreter.stack.top().unwrap().into_address();
2624 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2625 assert_eq!(Pallet::<T>::code(&addr).len(), revm::primitives::eip170::MAX_CODE_SIZE);
2626 assert_eq!(Pallet::<T>::evm_balance(&addr), value, "balance should hold {value:?}");
2627 Ok(())
2628 }
2629
2630 #[benchmark(pov_mode = Measured)]
2632 fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2633 let input = vec![0u8; n as usize];
2634 let mut call_setup = CallSetup::<T>::default();
2635 let (mut ext, _) = call_setup.ext();
2636
2637 let result;
2638 #[block]
2639 {
2640 result = run_builtin_precompile(
2641 &mut ext,
2642 H160::from_low_u64_be(2).as_fixed_bytes(),
2643 input.clone(),
2644 );
2645 }
2646 assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
2647 }
2648
2649 #[benchmark(pov_mode = Measured)]
2650 fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2651 let input = vec![0u8; n as usize];
2652 let mut call_setup = CallSetup::<T>::default();
2653 let (mut ext, _) = call_setup.ext();
2654
2655 let result;
2656 #[block]
2657 {
2658 result = run_builtin_precompile(
2659 &mut ext,
2660 H160::from_low_u64_be(4).as_fixed_bytes(),
2661 input.clone(),
2662 );
2663 }
2664 assert_eq!(input, result.unwrap().data);
2665 }
2666
2667 #[benchmark(pov_mode = Measured)]
2669 fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2670 use ripemd::Digest;
2671 let input = vec![0u8; n as usize];
2672 let mut call_setup = CallSetup::<T>::default();
2673 let (mut ext, _) = call_setup.ext();
2674
2675 let result;
2676 #[block]
2677 {
2678 result = run_builtin_precompile(
2679 &mut ext,
2680 H160::from_low_u64_be(3).as_fixed_bytes(),
2681 input.clone(),
2682 );
2683 }
2684 let mut expected = [0u8; 32];
2685 expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
2686
2687 assert_eq!(expected.to_vec(), result.unwrap().data);
2688 }
2689
2690 #[benchmark(pov_mode = Measured)]
2692 fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2693 build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
2694
2695 let result;
2696 #[block]
2697 {
2698 result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
2699 }
2700 assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
2701 assert_ok!(result);
2702 }
2703
2704 #[benchmark(pov_mode = Measured)]
2706 fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2707 let input = vec![0u8; n as usize];
2708 let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
2709 input: input.clone().into(),
2710 })
2711 .abi_encode();
2712
2713 let mut call_setup = CallSetup::<T>::default();
2714 let (mut ext, _) = call_setup.ext();
2715
2716 let result;
2717 #[block]
2718 {
2719 result = run_builtin_precompile(
2720 &mut ext,
2721 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2722 input_bytes,
2723 );
2724 }
2725 let truth: [u8; 32] = sp_io::hashing::blake2_256(&input);
2726 let truth = FixedBytes::<32>::abi_encode(&truth);
2727 let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed");
2728
2729 let raw_data = result.unwrap().data;
2730 let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed");
2731 assert_eq!(truth, ret_hash);
2732 }
2733
2734 #[benchmark(pov_mode = Measured)]
2736 fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2737 let input = vec![0u8; n as usize];
2738 let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
2739 input: input.clone().into(),
2740 })
2741 .abi_encode();
2742
2743 let mut call_setup = CallSetup::<T>::default();
2744 let (mut ext, _) = call_setup.ext();
2745
2746 let result;
2747 #[block]
2748 {
2749 result = run_builtin_precompile(
2750 &mut ext,
2751 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2752 input_bytes,
2753 );
2754 }
2755 let truth: [u8; 16] = sp_io::hashing::blake2_128(&input);
2756 let truth = FixedBytes::<16>::abi_encode(&truth);
2757 let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed");
2758
2759 let raw_data = result.unwrap().data;
2760 let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed");
2761 assert_eq!(truth, ret_hash);
2762 }
2763
2764 #[benchmark(pov_mode = Measured)]
2767 fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) {
2768 let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
2769 let message_len = message.len() as u32;
2770
2771 let key_type = sp_core::crypto::KeyTypeId(*b"code");
2772 let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
2773 let sig =
2774 sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
2775 let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
2776 let sig_len = sig.len() as u32;
2777
2778 build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
2779
2780 let result;
2781 #[block]
2782 {
2783 result = runtime.bench_sr25519_verify(
2784 memory.as_mut_slice(),
2785 0, sig_len, message_len, sig_len + pub_key.len() as u32, );
2790 }
2791
2792 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2793 }
2794
2795 #[benchmark(pov_mode = Measured)]
2796 fn ecdsa_recover() {
2797 use hex_literal::hex;
2798 let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
2799 let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
2800 let mut call_setup = CallSetup::<T>::default();
2801 let (mut ext, _) = call_setup.ext();
2802
2803 let result;
2804
2805 #[block]
2806 {
2807 result =
2808 run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
2809 }
2810
2811 assert_eq!(result.unwrap().data, expected);
2812 }
2813
2814 #[benchmark(pov_mode = Measured)]
2815 fn p256_verify() {
2816 use hex_literal::hex;
2817 let input = hex!("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").to_vec();
2818 let expected = U256::one().to_big_endian();
2819 let mut call_setup = CallSetup::<T>::default();
2820 let (mut ext, _) = call_setup.ext();
2821
2822 let result;
2823
2824 #[block]
2825 {
2826 result = run_builtin_precompile(
2827 &mut ext,
2828 H160::from_low_u64_be(0x100).as_fixed_bytes(),
2829 input,
2830 );
2831 }
2832
2833 assert_eq!(result.unwrap().data, expected);
2834 }
2835
2836 #[benchmark(pov_mode = Measured)]
2837 fn bn128_add() {
2838 use hex_literal::hex;
2839 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
2840 let expected = hex!(
2841 "0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
2842 );
2843 let mut call_setup = CallSetup::<T>::default();
2844 let (mut ext, _) = call_setup.ext();
2845
2846 let result;
2847 #[block]
2848 {
2849 result =
2850 run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
2851 }
2852
2853 assert_eq!(result.unwrap().data, expected);
2854 }
2855
2856 #[benchmark(pov_mode = Measured)]
2857 fn bn128_mul() {
2858 use hex_literal::hex;
2859 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
2860 let expected = hex!(
2861 "0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
2862 );
2863 let mut call_setup = CallSetup::<T>::default();
2864 let (mut ext, _) = call_setup.ext();
2865
2866 let result;
2867 #[block]
2868 {
2869 result =
2870 run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
2871 }
2872
2873 assert_eq!(result.unwrap().data, expected);
2874 }
2875
2876 #[benchmark(pov_mode = Measured)]
2878 fn bn128_pairing(n: Linear<0, { 20 }>) {
2879 fn generate_random_ecpairs(n: usize) -> Vec<u8> {
2880 use bn::{AffineG1, AffineG2, Fr, G1, G2, Group};
2881 use rand::SeedableRng;
2882 use rand_pcg::Pcg64;
2883 let mut rng = Pcg64::seed_from_u64(1);
2884
2885 let mut buffer = vec![0u8; n * 192];
2886
2887 let mut write = |element: &bn::Fq, offset: &mut usize| {
2888 element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
2889 *offset += 32
2890 };
2891
2892 for i in 0..n {
2893 let mut offset = i * 192;
2894 let scalar = Fr::random(&mut rng);
2895
2896 let g1 = G1::one() * scalar;
2897 let g2 = G2::one() * scalar;
2898 let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
2899 let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
2900
2901 write(&a.x(), &mut offset);
2902 write(&a.y(), &mut offset);
2903 write(&b.x().imaginary(), &mut offset);
2904 write(&b.x().real(), &mut offset);
2905 write(&b.y().imaginary(), &mut offset);
2906 write(&b.y().real(), &mut offset);
2907 }
2908
2909 buffer
2910 }
2911
2912 let input = generate_random_ecpairs(n as usize);
2913 let mut call_setup = CallSetup::<T>::default();
2914 let (mut ext, _) = call_setup.ext();
2915
2916 let result;
2917 #[block]
2918 {
2919 result =
2920 run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
2921 }
2922 assert_ok!(result);
2923 }
2924
2925 #[benchmark(pov_mode = Measured)]
2927 fn blake2f(n: Linear<0, 1200>) {
2928 use hex_literal::hex;
2929 let input = hex!(
2930 "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
2931 );
2932 let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
2933 let mut call_setup = CallSetup::<T>::default();
2934 let (mut ext, _) = call_setup.ext();
2935
2936 let result;
2937 #[block]
2938 {
2939 result =
2940 run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
2941 }
2942 assert_ok!(result);
2943 }
2944
2945 #[benchmark(pov_mode = Measured)]
2949 fn seal_ecdsa_to_eth_address() {
2950 let key_type = sp_core::crypto::KeyTypeId(*b"code");
2951 let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
2952 build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
2953
2954 let result;
2955 #[block]
2956 {
2957 result = runtime.bench_ecdsa_to_eth_address(
2958 memory.as_mut_slice(),
2959 20, 0, );
2962 }
2963
2964 assert_ok!(result);
2965 assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
2966 }
2967
2968 #[benchmark(pov_mode = Measured)]
2970 fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
2971 let module = VmBinaryModule::evm_noop(r);
2972 let inputs = vec![];
2973
2974 let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
2975 let mut setup = CallSetup::<T>::new(module);
2976 let (mut ext, _) = setup.ext();
2977
2978 let result;
2979 #[block]
2980 {
2981 result = evm::call(code, &mut ext, inputs);
2982 }
2983
2984 assert!(result.is_ok());
2985 Ok(())
2986 }
2987
2988 #[benchmark(pov_mode = Ignored)]
2993 fn instr(r: Linear<0, 10_000>) {
2994 use rand::{SeedableRng, seq::SliceRandom};
2995 use rand_pcg::Pcg64;
2996
2997 const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
2999
3000 const CACHE_LINE_SIZE: u64 = 64;
3002
3003 const MISALIGNMENT: u64 = 60;
3005
3006 const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
3009
3010 assert!(
3011 u64::from(r) <= NUM_ADDRESSES / 2,
3012 "If we do too many iterations we run into the risk of loading from warm cache lines",
3013 );
3014
3015 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
3016 let (mut ext, module) = setup.ext();
3017 let mut prepared =
3018 CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
3019
3020 assert!(
3021 u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
3022 "aux data base must be cache aligned"
3023 );
3024
3025 let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
3027
3028 let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
3032 for i in 1..NUM_ADDRESSES {
3033 let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
3034 addresses.push(addr);
3035 }
3036 let mut rng = Pcg64::seed_from_u64(1337);
3037 addresses.shuffle(&mut rng);
3038
3039 let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
3041 for address in addresses {
3042 memory.extend_from_slice(&address);
3043 memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
3044 }
3045
3046 prepared
3049 .setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
3050 .unwrap();
3051
3052 #[block]
3053 {
3054 prepared.call().unwrap();
3055 }
3056 }
3057
3058 #[benchmark(pov_mode = Ignored)]
3059 fn instr_empty_loop(r: Linear<0, 10_000>) {
3060 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
3061 let (mut ext, module) = setup.ext();
3062 let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
3063 prepared.setup_aux_data(&[], 0, r.into()).unwrap();
3064
3065 #[block]
3066 {
3067 prepared.call().unwrap();
3068 }
3069 }
3070
3071 #[benchmark(pov_mode = Measured)]
3072 fn extcodecopy(n: Linear<1_000, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
3073 let mut setup = CallSetup::<T>::new(VmBinaryModule::dummy());
3075 let target = Contract::<T>::with_index(1, VmBinaryModule::sized(n), vec![])?;
3077
3078 let (mut ext, _) = setup.ext();
3079 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
3080
3081 let _ = interpreter.stack.push(U256::from(n));
3083 let _ = interpreter.stack.push(U256::from(0u32));
3084 let _ = interpreter.stack.push(U256::from(0u32));
3085 let _ = interpreter.stack.push(target.address);
3086
3087 let result;
3088 #[block]
3089 {
3090 result = instructions::host::extcodecopy(&mut interpreter);
3091 }
3092
3093 assert!(result.is_continue());
3094 assert_eq!(
3095 *interpreter.memory.slice(0..n as usize),
3096 PristineCode::<T>::get(target.info()?.code_hash).unwrap()[0..n as usize],
3097 "Memory should contain the target contract's code after extcodecopy"
3098 );
3099
3100 Ok(())
3101 }
3102
3103 #[benchmark]
3104 fn v1_migration_step() {
3105 use crate::migrations::v1;
3106 let addr = H160::from([1u8; 20]);
3107 let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
3108
3109 v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
3110 let mut meter = WeightMeter::new();
3111 assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
3112
3113 #[block]
3114 {
3115 v1::Migration::<T>::step(None, &mut meter).unwrap();
3116 }
3117
3118 assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
3119 assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
3120
3121 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
3123 }
3124
3125 #[benchmark]
3126 fn v2_migration_step() {
3127 use crate::migrations::v2;
3128 let code_hash = H256::from([0; 32]);
3129 let old_code_info = v2::Migration::<T>::create_old_code_info(
3130 whitelisted_caller(),
3131 1000u32.into(),
3132 1,
3133 100,
3134 0,
3135 );
3136 v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
3137 let mut meter = WeightMeter::new();
3138
3139 #[block]
3140 {
3141 v2::Migration::<T>::step(None, &mut meter).unwrap();
3142 }
3143
3144 v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
3145
3146 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
3148 }
3149
3150 #[benchmark]
3151 fn v3_migration_step() {
3152 use crate::migrations::v3;
3153 let _ = frame_system::Account::<T>::clear(u32::MAX, None);
3155
3156 let account = account::<T::AccountId>("target", 0, 0);
3157 T::Currency::mint_into(&account, Pallet::<T>::min_balance())
3158 .expect("should mint into account");
3159
3160 let addr = T::AddressMapper::to_address(&account);
3162 crate::OriginalAccount::<T>::remove(addr);
3163
3164 assert!(!T::AddressMapper::is_mapped(&account));
3165 let mut meter = WeightMeter::new();
3166
3167 #[block]
3168 {
3169 v3::Migration::<T>::step(None, &mut meter).unwrap();
3170 }
3171
3172 assert!(T::AddressMapper::is_mapped(&account));
3173
3174 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v3_migration_step() * 2);
3176 }
3177
3178 #[benchmark]
3183 fn v4_code_upload_step() {
3184 use crate::migrations::v4;
3185
3186 let _ = CodeInfoOf::<T>::clear(u32::MAX, None);
3187
3188 let owner: T::AccountId = whitelisted_caller();
3189 let deposit: BalanceOf<T> = 1_000u32.into();
3190
3191 let pallet_account = Pallet::<T>::account_id();
3192 T::Currency::mint_into(&pallet_account, Pallet::<T>::min_balance()).unwrap();
3193 T::Currency::mint_into(&pallet_account, deposit).unwrap();
3194 T::Currency::hold(&HoldReason::CodeUploadDepositReserve.into(), &pallet_account, deposit)
3195 .unwrap();
3196
3197 CodeInfoOf::<T>::insert(
3198 H256::from([1u8; 32]),
3199 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
3200 );
3201 CodeInfoOf::<T>::insert(
3202 H256::from([2u8; 32]),
3203 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
3204 );
3205
3206 let first = match v4::Migration::<T>::step_once(None) {
3207 Some(v4::Cursor::CodeUpload(h)) => h,
3208 other => panic!("expected CodeUpload cursor, got {other:?}"),
3209 };
3210 let cursor = Some(v4::Cursor::CodeUpload(first));
3211
3212 #[block]
3213 {
3214 let _ = v4::Migration::<T>::step_once(cursor);
3215 }
3216
3217 assert_eq!(
3218 NativeDepositOf::<T>::get(&pallet_account, &owner),
3219 deposit + deposit,
3220 "both code uploads credited to owner",
3221 );
3222 }
3223
3224 #[benchmark]
3229 fn v4_contract_step() {
3230 use crate::migrations::v4;
3231
3232 let _ = AccountInfoOf::<T>::clear(u32::MAX, None);
3233
3234 let code_hash = H256::from([0u8; 32]);
3235 let deposit: BalanceOf<T> = 1_000u32.into();
3236
3237 for byte in [0x41u8, 0x42u8] {
3238 let addr = H160::from([byte; 20]);
3239 let contract_account = T::AddressMapper::to_account_id(&addr);
3240 let info =
3241 ContractInfo::<T>::new(&addr, 1u32.into(), code_hash).expect("fresh contract info");
3242 AccountInfoOf::<T>::insert(
3243 addr,
3244 crate::storage::AccountInfo::<T> {
3245 account_type: crate::storage::AccountType::Contract(info),
3246 dust: 0,
3247 },
3248 );
3249 T::Currency::mint_into(&contract_account, Pallet::<T>::min_balance()).unwrap();
3250 T::Currency::mint_into(&contract_account, deposit).unwrap();
3251 T::Currency::hold(
3252 &HoldReason::StorageDepositReserve.into(),
3253 &contract_account,
3254 deposit,
3255 )
3256 .unwrap();
3257 }
3258
3259 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::Contract(None))) {
3260 Some(v4::Cursor::Contract(Some(addr))) => addr,
3261 other => panic!("expected Contract cursor, got {other:?}"),
3262 };
3263 let cursor = Some(v4::Cursor::Contract(Some(first)));
3264
3265 #[block]
3266 {
3267 let _ = v4::Migration::<T>::step_once(cursor);
3268 }
3269
3270 if T::Deposit::SUPPORTS_PGAS {
3273 for byte in [0x41u8, 0x42u8] {
3274 let addr = H160::from([byte; 20]);
3275 let contract_account = T::AddressMapper::to_account_id(&addr);
3276 assert_eq!(
3277 T::Currency::balance_on_hold(
3278 &HoldReason::StorageDepositReserve.into(),
3279 &contract_account,
3280 ),
3281 0u32.into(),
3282 "native storage deposit burned for {addr:?}",
3283 );
3284 }
3285 }
3286 }
3287
3288 #[benchmark]
3294 fn v4_deletion_queue_step() {
3295 use crate::migrations::v4;
3296
3297 let _ = v4::old::DeletionQueue::<T>::clear(u32::MAX, None);
3298
3299 let trie_a: TrieId = vec![0xAAu8; 16].try_into().unwrap();
3300 let trie_b: TrieId = vec![0xBBu8; 24].try_into().unwrap();
3301 v4::old::DeletionQueue::<T>::insert(0u32, trie_a);
3302 v4::old::DeletionQueue::<T>::insert(1u32, trie_b);
3303
3304 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::DeletionQueue(None))) {
3305 Some(v4::Cursor::DeletionQueue(Some(key))) => key,
3306 other => panic!("expected DeletionQueue cursor, got {other:?}"),
3307 };
3308 let cursor = Some(v4::Cursor::DeletionQueue(Some(first)));
3309
3310 #[block]
3311 {
3312 let _ = v4::Migration::<T>::step_once(cursor);
3313 }
3314
3315 assert!(
3316 DeletionQueue::<T>::get(0u32).is_some() && DeletionQueue::<T>::get(1u32).is_some(),
3317 "both legacy entries rewritten into the new format",
3318 );
3319 }
3320
3321 fn create_test_signer<T: Config>() -> (T::AccountId, SigningKey, H160) {
3323 use hex_literal::hex;
3324 let signer_account_id = hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
3326 let signer_priv_key =
3327 hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133");
3328
3329 let signer_key = SigningKey::from_bytes(&signer_priv_key.into()).expect("valid key");
3330
3331 let signer_address = H160::from_slice(&signer_account_id);
3332 let signer_caller = T::AddressMapper::to_fallback_account_id(&signer_address);
3333
3334 (signer_caller, signer_key, signer_address)
3335 }
3336
3337 fn create_signed_transaction<T: Config>(
3339 signer_key: &SigningKey,
3340 target_address: H160,
3341 value: U256,
3342 input_data: Vec<u8>,
3343 ) -> Vec<u8> {
3344 let unsigned_tx: TransactionUnsigned = TransactionLegacyUnsigned {
3345 to: Some(target_address),
3346 value,
3347 chain_id: Some(T::ChainId::get().into()),
3348 input: input_data.into(),
3349 ..Default::default()
3350 }
3351 .into();
3352
3353 let hashed_payload = sp_io::hashing::keccak_256(&unsigned_tx.unsigned_payload());
3354 let (signature, recovery_id) =
3355 signer_key.sign_prehash_recoverable(&hashed_payload).expect("signing success");
3356
3357 let mut sig_bytes = [0u8; 65];
3358 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
3359 sig_bytes[64] = recovery_id.to_byte();
3360
3361 let signed_tx = unsigned_tx.with_signature(sig_bytes);
3362
3363 signed_tx.signed_payload()
3364 }
3365
3366 fn setup_finalize_block_benchmark<T>()
3368 -> Result<(Contract<T>, BalanceOf<T>, U256, SigningKey, BlockNumberFor<T>), BenchmarkError>
3369 where
3370 BalanceOf<T>: Into<U256> + TryFrom<U256>,
3371 T: Config,
3372 MomentOf<T>: Into<U256>,
3373 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
3374 {
3375 let (signer_caller, signer_key, _signer_address) = create_test_signer::<T>();
3377 whitelist_account!(signer_caller);
3378
3379 let instance =
3381 Contract::<T>::with_caller(signer_caller.clone(), VmBinaryModule::dummy(), vec![])?;
3382 let storage_deposit = default_deposit_limit::<T>();
3383 let value = Pallet::<T>::min_balance();
3384 let evm_value =
3385 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, 0));
3386
3387 let current_block = BlockNumberFor::<T>::from(1u32);
3389 frame_system::Pallet::<T>::set_block_number(current_block);
3390
3391 Ok((instance, storage_deposit, evm_value, signer_key, current_block))
3392 }
3393
3394 #[benchmark(pov_mode = Measured)]
3411 fn on_finalize_per_transaction(n: Linear<0, 200>) -> Result<(), BenchmarkError> {
3412 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3413 setup_finalize_block_benchmark::<T>()?;
3414
3415 let fixed_payload_size = 100usize;
3417
3418 if n > 0 {
3420 let _ = Pallet::<T>::on_initialize(current_block);
3422
3423 let input_data = vec![0x42u8; fixed_payload_size];
3425 let receipt_gas_info = ReceiptGasInfo {
3426 gas_used: U256::from(1_000_000),
3427 effective_gas_price: Pallet::<T>::evm_base_fee(),
3428 };
3429
3430 for _ in 0..n {
3431 let signed_transaction = create_signed_transaction::<T>(
3433 &signer_key,
3434 instance.address,
3435 evm_value,
3436 input_data.clone(),
3437 );
3438
3439 let _ = block_storage::bench_with_ethereum_context(|| {
3441 let (encoded_logs, bloom) =
3442 block_storage::get_receipt_details().unwrap_or_default();
3443
3444 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3445 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3446
3447 block_builder.process_transaction(
3448 signed_transaction,
3449 true,
3450 receipt_gas_info.clone(),
3451 encoded_logs,
3452 bloom,
3453 );
3454
3455 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3456 });
3457 }
3458 }
3459
3460 #[block]
3461 {
3462 let _ = Pallet::<T>::on_finalize(current_block);
3464 }
3465
3466 assert_eq!(Pallet::<T>::eth_block().transactions.len(), n as usize);
3468
3469 Ok(())
3470 }
3471
3472 #[benchmark(pov_mode = Measured)]
3489 fn on_finalize_per_transaction_data(d: Linear<0, 1000>) -> Result<(), BenchmarkError> {
3490 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3491 setup_finalize_block_benchmark::<T>()?;
3492
3493 let fixed_tx_count = 10u32;
3495
3496 let _ = Pallet::<T>::on_initialize(current_block);
3498
3499 let input_data = vec![0x42u8; d as usize];
3501 let receipt_gas_info = ReceiptGasInfo {
3502 gas_used: U256::from(1_000_000),
3503 effective_gas_price: Pallet::<T>::evm_base_fee(),
3504 };
3505
3506 for _ in 0..fixed_tx_count {
3507 let signed_transaction = create_signed_transaction::<T>(
3509 &signer_key,
3510 instance.address,
3511 evm_value,
3512 input_data.clone(),
3513 );
3514
3515 let _ = block_storage::bench_with_ethereum_context(|| {
3517 let (encoded_logs, bloom) =
3518 block_storage::get_receipt_details().unwrap_or_default();
3519
3520 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3521 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3522
3523 block_builder.process_transaction(
3524 signed_transaction,
3525 true,
3526 receipt_gas_info.clone(),
3527 encoded_logs,
3528 bloom,
3529 );
3530
3531 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3532 });
3533 }
3534
3535 #[block]
3536 {
3537 let _ = Pallet::<T>::on_finalize(current_block);
3539 }
3540
3541 assert_eq!(Pallet::<T>::eth_block().transactions.len(), fixed_tx_count as usize);
3543
3544 Ok(())
3545 }
3546
3547 #[benchmark(pov_mode = Measured)]
3565 fn on_finalize_per_event(e: Linear<0, 100>) -> Result<(), BenchmarkError> {
3566 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3567 setup_finalize_block_benchmark::<T>()?;
3568
3569 let input_data = vec![0x42u8; 100];
3571 let signed_transaction = create_signed_transaction::<T>(
3572 &signer_key,
3573 instance.address,
3574 evm_value,
3575 input_data.clone(),
3576 );
3577
3578 let receipt_gas_info = ReceiptGasInfo {
3579 gas_used: U256::from(1_000_000),
3580 effective_gas_price: Pallet::<T>::evm_base_fee(),
3581 };
3582
3583 let _ = block_storage::bench_with_ethereum_context(|| {
3585 let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
3586
3587 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3588 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3589
3590 block_builder.process_transaction(
3591 signed_transaction,
3592 true,
3593 receipt_gas_info.clone(),
3594 encoded_logs,
3595 bloom,
3596 );
3597
3598 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3599 });
3600
3601 for _ in 0..e {
3603 block_storage::capture_ethereum_log(&instance.address, &vec![], &vec![]);
3604 }
3605
3606 #[block]
3607 {
3608 let _ = Pallet::<T>::on_initialize(current_block);
3610
3611 let _ = Pallet::<T>::on_finalize(current_block);
3613 }
3614
3615 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3617
3618 Ok(())
3619 }
3620
3621 #[benchmark(pov_mode = Measured)]
3630 fn on_finalize_per_event_data(d: Linear<0, 16384>) -> Result<(), BenchmarkError> {
3631 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3632 setup_finalize_block_benchmark::<T>()?;
3633
3634 let input_data = vec![0x42u8; 100];
3636 let signed_transaction = create_signed_transaction::<T>(
3637 &signer_key,
3638 instance.address,
3639 evm_value,
3640 input_data.clone(),
3641 );
3642
3643 let receipt_gas_info = ReceiptGasInfo {
3644 gas_used: U256::from(1_000_000),
3645 effective_gas_price: Pallet::<T>::evm_base_fee(),
3646 };
3647
3648 let _ = block_storage::bench_with_ethereum_context(|| {
3650 let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
3651
3652 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3653 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3654
3655 block_builder.process_transaction(
3656 signed_transaction,
3657 true,
3658 receipt_gas_info,
3659 encoded_logs,
3660 bloom,
3661 );
3662
3663 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3664 });
3665
3666 let (event_data, topics) = if d < 32 {
3668 (vec![0x42u8; d as usize], vec![])
3670 } else {
3671 let num_topics = core::cmp::min(limits::NUM_EVENT_TOPICS, d / 32);
3673 let topic_bytes_used = num_topics * 32;
3674 let data_bytes_remaining = d - topic_bytes_used;
3675
3676 let mut topics = Vec::new();
3678 for topic_index in 0..num_topics {
3679 let topic_data = [topic_index as u8; 32];
3680 topics.push(H256::from(topic_data));
3681 }
3682
3683 let event_data = vec![0x42u8; data_bytes_remaining as usize];
3685
3686 (event_data, topics)
3687 };
3688
3689 block_storage::capture_ethereum_log(&instance.address, &event_data, &topics);
3690
3691 #[block]
3692 {
3693 let _ = Pallet::<T>::on_initialize(current_block);
3695
3696 let _ = Pallet::<T>::on_finalize(current_block);
3698 }
3699
3700 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3702
3703 Ok(())
3704 }
3705
3706 impl_benchmark_test_suite!(
3707 Contracts,
3708 crate::tests::ExtBuilder::default().build(),
3709 crate::tests::Test,
3710 );
3711}