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