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;
1612 #[block]
1613 {
1614 result = block_storage::bench_with_ethereum_context(|| {
1615 runtime.bench_deposit_event(
1616 memory.as_mut_slice(),
1617 0, num_topic,
1619 topics_data.len() as u32, n, )
1622 });
1623 }
1624 assert_ok!(result);
1625
1626 let events = System::<T>::events();
1627 let record = &events[events.len() - 1];
1628
1629 assert_eq!(
1630 record.event,
1631 crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(),
1632 );
1633 }
1634
1635 enum TrieFill {
1636 Empty,
1637 Full,
1638 }
1639
1640 enum SlotAccess {
1641 Cold,
1642 Hot,
1643 }
1644
1645 fn build_storage_contract<T: Config>(
1646 op: StorageOp,
1647 fill: TrieFill,
1648 ) -> Result<(ContractInfo<T>, Vec<u8>, Vec<u8>), BenchmarkError> {
1649 let key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1650 let value = vec![1u8; limits::STORAGE_BYTES as usize];
1651 let initial_value = match op {
1652 StorageOp::Read => value.clone(),
1653 StorageOp::Write => vec![42u8; limits::STORAGE_BYTES as usize],
1654 };
1655
1656 let instance = match fill {
1657 TrieFill::Full => {
1658 Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?
1659 },
1660 TrieFill::Empty => Contract::<T>::new(VmBinaryModule::dummy(), vec![])?,
1661 };
1662 let info = instance.info()?;
1663 info.bench_write_raw(&key, Some(initial_value), false)
1664 .map_err(|_| "Failed to write to storage during setup.")?;
1665 Ok((info, key, value))
1666 }
1667
1668 enum StorageCall {
1669 Clear,
1670 Contains,
1671 Take,
1672 }
1673
1674 fn setup_precompile_bench<T: Config>(
1675 op: StorageCall,
1676 key_byte: u8,
1677 access: SlotAccess,
1678 ) -> Result<(CallSetup<T>, Key, Vec<u8>), BenchmarkError> {
1679 let max_key_len = limits::STORAGE_KEY_BYTES;
1680 let key = Key::try_from_var(vec![key_byte; max_key_len as usize])
1681 .map_err(|_| "Key has wrong length")?;
1682 let raw_key = vec![key_byte; max_key_len as usize].into();
1683 let input_bytes = match op {
1684 StorageCall::Clear => {
1685 IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
1686 flags: StorageFlags::empty().bits(),
1687 key: raw_key,
1688 isFixedKey: false,
1689 })
1690 },
1691 StorageCall::Contains => {
1692 IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
1693 flags: StorageFlags::empty().bits(),
1694 key: raw_key,
1695 isFixedKey: false,
1696 })
1697 },
1698 StorageCall::Take => IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
1699 flags: StorageFlags::empty().bits(),
1700 key: raw_key,
1701 isFixedKey: false,
1702 }),
1703 }
1704 .abi_encode();
1705
1706 let call_setup = CallSetup::<T>::default();
1707 if matches!(access, SlotAccess::Hot) {
1708 let info = call_setup.contract().info()?;
1709 frame_benchmarking::add_to_whitelist_child(
1710 info.child_trie_info().storage_key().to_vec(),
1711 key.hash(),
1712 );
1713 }
1714 Ok((call_setup, key, input_bytes))
1715 }
1716
1717 #[benchmark(skip_meta, pov_mode = Measured)]
1718 fn get_storage_empty() -> Result<(), BenchmarkError> {
1719 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Empty)?;
1720 let child_trie_info = info.child_trie_info();
1721
1722 let result;
1723 #[block]
1724 {
1725 result = child::get_raw(&child_trie_info, &key);
1726 }
1727
1728 assert_eq!(result, Some(value));
1729 Ok(())
1730 }
1731
1732 #[benchmark(skip_meta, pov_mode = Measured)]
1733 fn get_storage_full() -> Result<(), BenchmarkError> {
1734 let (info, key, value) = build_storage_contract::<T>(StorageOp::Read, TrieFill::Full)?;
1735 let child_trie_info = info.child_trie_info();
1736
1737 let result;
1738 #[block]
1739 {
1740 result = child::get_raw(&child_trie_info, &key);
1741 }
1742
1743 assert_eq!(result, Some(value));
1744 Ok(())
1745 }
1746
1747 #[benchmark(skip_meta, pov_mode = Measured)]
1748 fn set_storage_empty() -> Result<(), BenchmarkError> {
1749 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Empty)?;
1750
1751 let val = Some(value.clone());
1752 let result;
1753 #[block]
1754 {
1755 result = info.bench_write_raw(&key, val, true);
1756 }
1757
1758 assert_ok!(result);
1759 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1760 Ok(())
1761 }
1762
1763 #[benchmark(skip_meta, pov_mode = Measured)]
1764 fn set_storage_full() -> Result<(), BenchmarkError> {
1765 let (info, key, value) = build_storage_contract::<T>(StorageOp::Write, TrieFill::Full)?;
1766
1767 let val = Some(value.clone());
1768 let result;
1769 #[block]
1770 {
1771 result = info.bench_write_raw(&key, val, true);
1772 }
1773
1774 assert_ok!(result);
1775 assert_eq!(child::get_raw(&info.child_trie_info(), &key).unwrap(), value);
1776 Ok(())
1777 }
1778
1779 fn shared_prefix_keys(count: usize, suffix_of: impl Fn(u64) -> u64) -> Vec<Vec<u8>> {
1781 (0..count as u64)
1782 .map(|i| {
1783 let mut key = vec![0u8; limits::STORAGE_KEY_BYTES as usize];
1784 key[limits::STORAGE_KEY_BYTES as usize - 8..]
1785 .copy_from_slice(&suffix_of(i).to_be_bytes());
1786 key
1787 })
1788 .collect()
1789 }
1790
1791 fn setup_stored_keys<T: Config>(
1794 count: usize,
1795 value_byte: u8,
1796 suffix_of: impl Fn(u64) -> u64,
1797 ) -> Result<(ContractInfo<T>, Vec<Vec<u8>>), BenchmarkError> {
1798 let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1799 let info = instance.info()?;
1800 let child_trie_info = info.child_trie_info();
1801 let value = vec![value_byte; limits::STORAGE_BYTES as usize];
1802 let stored_keys = shared_prefix_keys(count, suffix_of);
1803 for key in &stored_keys {
1804 info.bench_write_raw(key, Some(value.clone()), false)
1805 .map_err(|_| "Failed to write to storage during setup.")?;
1806 frame_benchmarking::add_to_whitelist_child(
1807 child_trie_info.storage_key().to_vec(),
1808 key.clone(),
1809 );
1810 }
1811 Ok((info, stored_keys))
1812 }
1813
1814 #[benchmark(skip_meta, pov_mode = Measured)]
1815 fn overlay_probe_full(
1816 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
1817 ) -> Result<(), BenchmarkError> {
1818 let value_byte = 42;
1819 let (info, stored_keys) =
1821 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
1822 let child_trie_info = info.child_trie_info();
1823 let fill_keys = shared_prefix_keys(MAX_ACCESS_LIST_ENTRIES, |i| (i + 1) * 2);
1825
1826 let mut result = None;
1827 #[block]
1828 {
1829 for key in &fill_keys {
1831 child::put_raw(&child_trie_info, key, &[0u8]);
1832 }
1833 for i in 0..n {
1834 let index = i as usize % stored_keys.len();
1835 result = child::get_raw(&child_trie_info, &stored_keys[index]);
1836 }
1837 }
1838
1839 if n > 0 {
1840 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
1841 assert_eq!(result, Some(expected), "the stored value must be read back");
1842 }
1843 Ok(())
1844 }
1845
1846 #[benchmark(skip_meta, pov_mode = Measured)]
1847 fn overlay_probe_empty(
1848 n: Linear<0, { MAX_ACCESS_LIST_ENTRIES as u32 }>,
1849 ) -> Result<(), BenchmarkError> {
1850 let value_byte = 42;
1851 let (info, stored_keys) =
1853 setup_stored_keys::<T>(MAX_ACCESS_LIST_ENTRIES, value_byte, |i| i * 2 + 1)?;
1854 let child_trie_info = info.child_trie_info();
1855
1856 let mut result = None;
1857 #[block]
1858 {
1859 for i in 0..n {
1860 let index = i as usize % stored_keys.len();
1861 result = child::get_raw(&child_trie_info, &stored_keys[index]);
1862 }
1863 }
1864
1865 if n > 0 {
1866 let expected = vec![value_byte; limits::STORAGE_BYTES as usize];
1867 assert_eq!(result, Some(expected), "the stored value must be read back");
1868 }
1869 Ok(())
1870 }
1871
1872 #[benchmark(skip_meta, pov_mode = Measured)]
1875 fn seal_set_storage(
1876 n: Linear<0, { limits::STORAGE_BYTES }>,
1877 o: Linear<0, { limits::STORAGE_BYTES }>,
1878 ) -> Result<(), BenchmarkError> {
1879 let max_key_len = limits::STORAGE_KEY_BYTES;
1880 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1881 .map_err(|_| "Key has wrong length")?;
1882 let value = vec![1u8; n as usize];
1883
1884 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1885 let info = instance.info()?;
1886
1887 info.write(&key, Some(vec![42u8; o as usize]), None, false)
1888 .map_err(|_| "Failed to write to storage during setup.")?;
1889
1890 let result;
1891 #[block]
1892 {
1893 result = runtime.bench_set_storage(
1894 memory.as_mut_slice(),
1895 StorageFlags::empty().bits(),
1896 0, max_key_len, max_key_len, n, );
1901 }
1902
1903 assert_ok!(result);
1904 assert_eq!(info.read(&key).unwrap(), value);
1905 Ok(())
1906 }
1907
1908 #[benchmark(skip_meta, pov_mode = Measured)]
1909 fn seal_set_storage_hot(
1910 n: Linear<0, { limits::STORAGE_BYTES }>,
1911 o: Linear<0, { limits::STORAGE_BYTES }>,
1912 ) -> Result<(), BenchmarkError> {
1913 let max_key_len = limits::STORAGE_KEY_BYTES;
1914 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1915 .map_err(|_| "Key has wrong length")?;
1916 let value = vec![1u8; n as usize];
1917
1918 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1919 let info = instance.info()?;
1920
1921 info.write(&key, Some(vec![42u8; o as usize]), None, false)
1922 .map_err(|_| "Failed to write to storage during setup.")?;
1923
1924 frame_benchmarking::add_to_whitelist_child(
1925 info.child_trie_info().storage_key().to_vec(),
1926 key.hash(),
1927 );
1928
1929 runtime.ext().touch_storage_access(&key, StorageOp::Write);
1931
1932 let result;
1933 #[block]
1934 {
1935 result = runtime.bench_set_storage(
1936 memory.as_mut_slice(),
1937 StorageFlags::empty().bits(),
1938 0, max_key_len, max_key_len, n, );
1943 }
1944
1945 assert_ok!(result);
1946 assert_eq!(info.read(&key).unwrap(), value);
1947 Ok(())
1948 }
1949
1950 #[benchmark(skip_meta, pov_mode = Measured)]
1951 fn clear_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1952 let key_byte = 0;
1953 let (mut call_setup, key, input_bytes) =
1954 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Cold)?;
1955 let (mut ext, _) = call_setup.ext();
1956 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1957 .map_err(|_| "Failed to write to storage during setup.")?;
1958
1959 let result;
1960 #[block]
1961 {
1962 result = run_builtin_precompile(
1963 &mut ext,
1964 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1965 input_bytes,
1966 );
1967 }
1968 assert_ok!(result);
1969 assert!(ext.get_storage(&key).is_none());
1970
1971 Ok(())
1972 }
1973
1974 #[benchmark(skip_meta, pov_mode = Measured)]
1975 fn clear_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1976 let key_byte = 0;
1977 let (mut call_setup, key, input_bytes) =
1978 setup_precompile_bench::<T>(StorageCall::Clear, key_byte, SlotAccess::Hot)?;
1979 let (mut ext, _) = call_setup.ext();
1980 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1981 .map_err(|_| "Failed to write to storage during setup.")?;
1982
1983 ext.touch_storage_access(&key, StorageOp::Write);
1984
1985 let result;
1986 #[block]
1987 {
1988 result = run_builtin_precompile(
1989 &mut ext,
1990 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1991 input_bytes,
1992 );
1993 }
1994 assert_ok!(result);
1995 assert!(ext.get_storage(&key).is_none());
1996
1997 Ok(())
1998 }
1999
2000 #[benchmark(skip_meta, pov_mode = Measured)]
2001 fn seal_get_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2002 let max_key_len = limits::STORAGE_KEY_BYTES;
2003 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2004 .map_err(|_| "Key has wrong length")?;
2005 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2006 let info = instance.info()?;
2007
2008 info.write(&key, Some(vec![42u8; n as usize]), None, false)
2009 .map_err(|_| "Failed to write to storage during setup.")?;
2010
2011 let out_ptr = max_key_len + 4;
2012 let result;
2013 #[block]
2014 {
2015 result = runtime.bench_get_storage(
2016 memory.as_mut_slice(),
2017 StorageFlags::empty().bits(),
2018 0, max_key_len, out_ptr, max_key_len, );
2023 }
2024
2025 assert_ok!(result);
2026 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
2027 Ok(())
2028 }
2029
2030 #[benchmark(skip_meta, pov_mode = Measured)]
2031 fn seal_get_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2032 let max_key_len = limits::STORAGE_KEY_BYTES;
2033 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2034 .map_err(|_| "Key has wrong length")?;
2035 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2036 let info = instance.info()?;
2037
2038 info.write(&key, Some(vec![42u8; n as usize]), None, false)
2039 .map_err(|_| "Failed to write to storage during setup.")?;
2040
2041 frame_benchmarking::add_to_whitelist_child(
2042 info.child_trie_info().storage_key().to_vec(),
2043 key.hash(),
2044 );
2045
2046 runtime.ext().touch_storage_access(&key, StorageOp::Read);
2047
2048 let out_ptr = max_key_len + 4;
2049 let result;
2050 #[block]
2051 {
2052 result = runtime.bench_get_storage(
2053 memory.as_mut_slice(),
2054 StorageFlags::empty().bits(),
2055 0, max_key_len, out_ptr, max_key_len, );
2060 }
2061
2062 assert_ok!(result);
2063 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
2064 Ok(())
2065 }
2066
2067 #[benchmark(skip_meta, pov_mode = Measured)]
2068 fn contains_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2069 let key_byte = 0;
2070 let (mut call_setup, key, input_bytes) =
2071 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Cold)?;
2072 let (mut ext, _) = call_setup.ext();
2073 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2074 .map_err(|_| "Failed to write to storage during setup.")?;
2075
2076 let result;
2077 #[block]
2078 {
2079 result = run_builtin_precompile(
2080 &mut ext,
2081 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2082 input_bytes,
2083 );
2084 }
2085 assert_ok!(result);
2086 assert!(ext.get_storage(&key).is_some());
2087
2088 Ok(())
2089 }
2090
2091 #[benchmark(skip_meta, pov_mode = Measured)]
2092 fn contains_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2093 let key_byte = 0;
2094 let (mut call_setup, key, input_bytes) =
2095 setup_precompile_bench::<T>(StorageCall::Contains, key_byte, SlotAccess::Hot)?;
2096 let (mut ext, _) = call_setup.ext();
2097 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2098 .map_err(|_| "Failed to write to storage during setup.")?;
2099
2100 ext.touch_storage_access(&key, StorageOp::Read);
2101
2102 let result;
2103 #[block]
2104 {
2105 result = run_builtin_precompile(
2106 &mut ext,
2107 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2108 input_bytes,
2109 );
2110 }
2111 assert_ok!(result);
2112 assert!(ext.get_storage(&key).is_some());
2113
2114 Ok(())
2115 }
2116
2117 #[benchmark(skip_meta, pov_mode = Measured)]
2118 fn take_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2119 let key_byte = 3;
2120 let (mut call_setup, key, input_bytes) =
2121 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Cold)?;
2122 let (mut ext, _) = call_setup.ext();
2123 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2124 .map_err(|_| "Failed to write to storage during setup.")?;
2125
2126 let result;
2127 #[block]
2128 {
2129 result = run_builtin_precompile(
2130 &mut ext,
2131 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2132 input_bytes,
2133 );
2134 }
2135 assert_ok!(result);
2136 assert!(ext.get_storage(&key).is_none());
2137
2138 Ok(())
2139 }
2140
2141 #[benchmark(skip_meta, pov_mode = Measured)]
2142 fn take_storage_hot(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
2143 let key_byte = 3;
2144 let (mut call_setup, key, input_bytes) =
2145 setup_precompile_bench::<T>(StorageCall::Take, key_byte, SlotAccess::Hot)?;
2146 let (mut ext, _) = call_setup.ext();
2147 ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
2148 .map_err(|_| "Failed to write to storage during setup.")?;
2149
2150 ext.touch_storage_access(&key, StorageOp::Write);
2151
2152 let result;
2153 #[block]
2154 {
2155 result = run_builtin_precompile(
2156 &mut ext,
2157 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2158 input_bytes,
2159 );
2160 }
2161 assert_ok!(result);
2162 assert!(ext.get_storage(&key).is_none());
2163
2164 Ok(())
2165 }
2166
2167 fn worst_case_slot() -> crate::access_list::Slot {
2168 let key = Key::try_from_var(vec![0xFFu8; limits::STORAGE_KEY_BYTES as usize])
2169 .expect("key fits STORAGE_KEY_BYTES bound; qed");
2170 crate::access_list::Slot::from(&key)
2171 }
2172
2173 fn near_full_access_list() -> crate::access_list::AccessList {
2174 let mut al = AccessList::new();
2175 for i in 0..(MAX_ACCESS_LIST_ENTRIES - 1) {
2176 al.touch(
2177 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(i as u64) },
2178 StorageOp::Read,
2179 );
2180 }
2181 al
2182 }
2183
2184 #[benchmark(pov_mode = Ignored)]
2185 fn access_list_touch_cold_full() -> Result<(), BenchmarkError> {
2186 let mut al = near_full_access_list();
2187 let entry =
2189 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2190 let outcome;
2191 #[block]
2192 {
2193 outcome = al.touch(entry, StorageOp::Read);
2194 }
2195 assert!(outcome.is_cold());
2196 Ok(())
2197 }
2198
2199 #[benchmark(pov_mode = Ignored)]
2200 fn access_list_touch_hot_full() -> Result<(), BenchmarkError> {
2201 let mut al = near_full_access_list();
2202 let entry = AccessEntry {
2204 slot: worst_case_slot(),
2205 address: H160::from_low_u64_be(MAX_ACCESS_LIST_ENTRIES as u64 - 2),
2206 };
2207
2208 let touched = entry.clone();
2209 let outcome;
2210 #[block]
2211 {
2212 outcome = al.touch(touched, StorageOp::Write);
2213 }
2214 assert_eq!(
2215 outcome,
2216 Warmth::Hot { charged: StorageOp::Read },
2217 "the fill seeded this entry read-paid"
2218 );
2219 assert_eq!(
2220 al.peek(&entry),
2221 Warmth::Hot { charged: StorageOp::Write },
2222 "the write upgraded the entry"
2223 );
2224 Ok(())
2225 }
2226
2227 #[benchmark(pov_mode = Ignored)]
2228 fn access_list_touch_cold_empty() -> Result<(), BenchmarkError> {
2229 let mut al = AccessList::new();
2230 let entry =
2231 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2232 let outcome;
2233 #[block]
2234 {
2235 outcome = al.touch(entry, StorageOp::Read);
2236 }
2237 assert!(outcome.is_cold());
2238 Ok(())
2239 }
2240
2241 #[benchmark(pov_mode = Ignored)]
2242 fn access_list_touch_hot_single_element() -> Result<(), BenchmarkError> {
2243 let mut al = AccessList::new();
2244 let entry =
2245 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) };
2246 al.touch(entry.clone(), StorageOp::Read);
2247 let outcome;
2248 #[block]
2249 {
2250 outcome = al.touch(entry, StorageOp::Read);
2251 }
2252 assert!(!outcome.is_cold());
2253 Ok(())
2254 }
2255
2256 #[benchmark(pov_mode = Ignored)]
2260 fn access_list_rollback_amortization() -> Result<(), BenchmarkError> {
2261 let mut al = near_full_access_list();
2262 al.enter_frame();
2263 al.touch(
2264 AccessEntry { slot: worst_case_slot(), address: H160::from_low_u64_be(u64::MAX) },
2265 StorageOp::Read,
2266 );
2267 #[block]
2268 {
2269 al.rollback_frame();
2270 }
2271 Ok(())
2272 }
2273
2274 #[benchmark(pov_mode = Ignored)]
2279 fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
2280 let max_value_len = limits::STORAGE_BYTES;
2281 let max_key_len = limits::STORAGE_KEY_BYTES;
2282 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2283 .map_err(|_| "Key has wrong length")?;
2284 let value = Some(vec![42u8; max_value_len as _]);
2285 let mut setup = CallSetup::<T>::default();
2286 let (mut ext, _) = setup.ext();
2287 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2288 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2289 let result;
2290 #[block]
2291 {
2292 result = runtime.ext().set_transient_storage(&key, value, false);
2293 }
2294
2295 assert_eq!(result, Ok(WriteOutcome::New));
2296 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2297 Ok(())
2298 }
2299
2300 #[benchmark(pov_mode = Ignored)]
2301 fn set_transient_storage_full() -> Result<(), BenchmarkError> {
2302 let max_value_len = limits::STORAGE_BYTES;
2303 let max_key_len = limits::STORAGE_KEY_BYTES;
2304 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2305 .map_err(|_| "Key has wrong length")?;
2306 let value = Some(vec![42u8; max_value_len as _]);
2307 let mut setup = CallSetup::<T>::default();
2308 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2309 let (mut ext, _) = setup.ext();
2310 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2311 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2312 let result;
2313 #[block]
2314 {
2315 result = runtime.ext().set_transient_storage(&key, value, false);
2316 }
2317
2318 assert_eq!(result, Ok(WriteOutcome::New));
2319 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
2320 Ok(())
2321 }
2322
2323 #[benchmark(pov_mode = Ignored)]
2324 fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
2325 let max_value_len = limits::STORAGE_BYTES;
2326 let max_key_len = limits::STORAGE_KEY_BYTES;
2327 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2328 .map_err(|_| "Key has wrong length")?;
2329
2330 let mut setup = CallSetup::<T>::default();
2331 let (mut ext, _) = setup.ext();
2332 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2333 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2334 runtime
2335 .ext()
2336 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2337 .map_err(|_| "Failed to write to transient storage during setup.")?;
2338 let result;
2339 #[block]
2340 {
2341 result = runtime.ext().get_transient_storage(&key);
2342 }
2343
2344 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2345 Ok(())
2346 }
2347
2348 #[benchmark(pov_mode = Ignored)]
2349 fn get_transient_storage_full() -> Result<(), BenchmarkError> {
2350 let max_value_len = limits::STORAGE_BYTES;
2351 let max_key_len = limits::STORAGE_KEY_BYTES;
2352 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2353 .map_err(|_| "Key has wrong length")?;
2354
2355 let mut setup = CallSetup::<T>::default();
2356 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2357 let (mut ext, _) = setup.ext();
2358 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2359 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2360 runtime
2361 .ext()
2362 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2363 .map_err(|_| "Failed to write to transient storage during setup.")?;
2364 let result;
2365 #[block]
2366 {
2367 result = runtime.ext().get_transient_storage(&key);
2368 }
2369
2370 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
2371 Ok(())
2372 }
2373
2374 #[benchmark(pov_mode = Ignored)]
2376 fn rollback_transient_storage() -> Result<(), BenchmarkError> {
2377 let max_value_len = limits::STORAGE_BYTES;
2378 let max_key_len = limits::STORAGE_KEY_BYTES;
2379 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2380 .map_err(|_| "Key has wrong length")?;
2381
2382 let mut setup = CallSetup::<T>::default();
2383 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
2384 let (mut ext, _) = setup.ext();
2385 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2386 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2387 runtime.ext().transient_storage().start_transaction();
2388 runtime
2389 .ext()
2390 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
2391 .map_err(|_| "Failed to write to transient storage during setup.")?;
2392 #[block]
2393 {
2394 runtime.ext().transient_storage().rollback_transaction();
2395 }
2396
2397 assert_eq!(runtime.ext().get_transient_storage(&key), None);
2398 Ok(())
2399 }
2400
2401 #[benchmark(pov_mode = Measured)]
2404 fn seal_set_transient_storage(
2405 n: Linear<0, { limits::STORAGE_BYTES }>,
2406 o: Linear<0, { limits::STORAGE_BYTES }>,
2407 ) -> Result<(), BenchmarkError> {
2408 let max_key_len = limits::STORAGE_KEY_BYTES;
2409 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2410 .map_err(|_| "Key has wrong length")?;
2411 let value = vec![1u8; n as usize];
2412 build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]);
2413 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2414 runtime
2415 .ext()
2416 .set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
2417 .map_err(|_| "Failed to write to transient storage during setup.")?;
2418
2419 let result;
2420 #[block]
2421 {
2422 result = runtime.bench_set_storage(
2423 memory.as_mut_slice(),
2424 StorageFlags::TRANSIENT.bits(),
2425 0, max_key_len, max_key_len, n, );
2430 }
2431
2432 assert_ok!(result);
2433 assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
2434 Ok(())
2435 }
2436
2437 #[benchmark(pov_mode = Measured)]
2438 fn seal_clear_transient_storage(
2439 n: Linear<0, { limits::STORAGE_BYTES }>,
2440 ) -> Result<(), BenchmarkError> {
2441 let max_key_len = limits::STORAGE_KEY_BYTES;
2442 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2443 .map_err(|_| "Key has wrong length")?;
2444 let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
2445 flags: StorageFlags::TRANSIENT.bits(),
2446 key: vec![0u8; max_key_len as usize].into(),
2447 isFixedKey: false,
2448 })
2449 .abi_encode();
2450
2451 let mut call_setup = CallSetup::<T>::default();
2452 let (mut ext, _) = call_setup.ext();
2453 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2454 .map_err(|_| "Failed to write to transient storage during setup.")?;
2455
2456 let result;
2457 #[block]
2458 {
2459 result = run_builtin_precompile(
2460 &mut ext,
2461 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2462 input_bytes,
2463 );
2464 }
2465 assert_ok!(result);
2466 assert!(ext.get_transient_storage(&key).is_none());
2467
2468 Ok(())
2469 }
2470
2471 #[benchmark(pov_mode = Measured)]
2472 fn seal_get_transient_storage(
2473 n: Linear<0, { limits::STORAGE_BYTES }>,
2474 ) -> Result<(), BenchmarkError> {
2475 let max_key_len = limits::STORAGE_KEY_BYTES;
2476 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2477 .map_err(|_| "Key has wrong length")?;
2478 build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
2479 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
2480 runtime
2481 .ext()
2482 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2483 .map_err(|_| "Failed to write to transient storage during setup.")?;
2484
2485 let out_ptr = max_key_len + 4;
2486 let result;
2487 #[block]
2488 {
2489 result = runtime.bench_get_storage(
2490 memory.as_mut_slice(),
2491 StorageFlags::TRANSIENT.bits(),
2492 0, max_key_len, out_ptr, max_key_len, );
2497 }
2498
2499 assert_ok!(result);
2500 assert_eq!(
2501 &runtime.ext().get_transient_storage(&key).unwrap(),
2502 &memory[out_ptr as usize..]
2503 );
2504 Ok(())
2505 }
2506
2507 #[benchmark(pov_mode = Measured)]
2508 fn seal_contains_transient_storage(
2509 n: Linear<0, { limits::STORAGE_BYTES }>,
2510 ) -> Result<(), BenchmarkError> {
2511 let max_key_len = limits::STORAGE_KEY_BYTES;
2512 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2513 .map_err(|_| "Key has wrong length")?;
2514
2515 let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
2516 flags: StorageFlags::TRANSIENT.bits(),
2517 key: vec![0u8; max_key_len as usize].into(),
2518 isFixedKey: false,
2519 })
2520 .abi_encode();
2521
2522 let mut call_setup = CallSetup::<T>::default();
2523 let (mut ext, _) = call_setup.ext();
2524 ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
2525 .map_err(|_| "Failed to write to transient storage during setup.")?;
2526
2527 let result;
2528 #[block]
2529 {
2530 result = run_builtin_precompile(
2531 &mut ext,
2532 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2533 input_bytes,
2534 );
2535 }
2536 assert!(result.is_ok());
2537 assert!(ext.get_transient_storage(&key).is_some());
2538
2539 Ok(())
2540 }
2541
2542 #[benchmark(pov_mode = Measured)]
2543 fn seal_take_transient_storage(
2544 n: Linear<0, { limits::STORAGE_BYTES }>,
2545 ) -> Result<(), BenchmarkError> {
2546 let n = limits::STORAGE_BYTES;
2547 let value = vec![42u8; n as usize];
2548 let max_key_len = limits::STORAGE_KEY_BYTES;
2549 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
2550 .map_err(|_| "Key has wrong length")?;
2551
2552 let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
2553 flags: StorageFlags::TRANSIENT.bits(),
2554 key: vec![0u8; max_key_len as usize].into(),
2555 isFixedKey: false,
2556 })
2557 .abi_encode();
2558
2559 let mut call_setup = CallSetup::<T>::default();
2560 let (mut ext, _) = call_setup.ext();
2561 ext.set_transient_storage(&key, Some(value), false)
2562 .map_err(|_| "Failed to write to transient storage during setup.")?;
2563
2564 let result;
2565 #[block]
2566 {
2567 result = run_builtin_precompile(
2568 &mut ext,
2569 H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
2570 input_bytes,
2571 );
2572 }
2573 assert!(result.is_ok());
2574 assert!(ext.get_transient_storage(&key).is_none());
2575
2576 Ok(())
2577 }
2578
2579 #[benchmark(pov_mode = Measured)]
2583 fn seal_call(
2584 t: Linear<0, 1>,
2585 d: Linear<0, 1>,
2586 i: Linear<0, { limits::code::BLOB_BYTES }>,
2587 ) -> Result<(), BenchmarkError> {
2588 let target = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?;
2594 let callee_addr = H160([0x42; 20]);
2595 let callee = delegated_eoa::<T>(callee_addr, target.address)?;
2596 T::Currency::set_balance(&callee, Pallet::<T>::min_balance());
2601
2602 let callee_bytes = callee.encode();
2603 let callee_len = callee_bytes.len() as u32;
2604
2605 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2606 let dust = 100u32 * d;
2607 let evm_value =
2608 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2609 let value_bytes = evm_value.encode();
2610
2611 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2612 let deposit_bytes = Into::<U256>::into(deposit).encode();
2613 let deposit_len = deposit_bytes.len() as u32;
2614
2615 let mut setup = CallSetup::<T>::default();
2616 setup.set_storage_deposit_limit(deposit);
2617 setup.set_data(vec![42; i as usize]);
2620 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2621 setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
2622
2623 let (mut ext, _) = setup.ext();
2624 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2625 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
2626 let before = Pallet::<T>::evm_balance(&callee_addr);
2627
2628 let result;
2629 #[block]
2630 {
2631 result = runtime.bench_call(
2632 memory.as_mut_slice(),
2633 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), );
2640 }
2641
2642 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2643 assert_eq!(
2644 Pallet::<T>::evm_balance(&callee_addr),
2645 before + evm_value,
2646 "{callee_addr:?} balance should have grown by {evm_value:?}"
2647 );
2648
2649 Ok(())
2650 }
2651
2652 #[benchmark(pov_mode = Measured)]
2655 fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
2656 use alloy_core::sol_types::SolInterface;
2657 use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
2658
2659 let callee_bytes = if d == 1 {
2660 BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
2661 } else {
2662 BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
2663 };
2664 let callee_len = callee_bytes.len() as u32;
2665
2666 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2667 let deposit_bytes = Into::<U256>::into(deposit).encode();
2668 let deposit_len = deposit_bytes.len() as u32;
2669
2670 let value: BalanceOf<T> = Zero::zero();
2671 let value_bytes = Into::<U256>::into(value).encode();
2672 let value_len = value_bytes.len() as u32;
2673
2674 let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
2675 input: vec![42_u8; i as usize].into(),
2676 })
2677 .abi_encode();
2678 let input_len = input_bytes.len() as u32;
2679
2680 let mut setup = CallSetup::<T>::default();
2681 setup.set_storage_deposit_limit(deposit);
2682
2683 let (mut ext, _) = setup.ext();
2684 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2685 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
2686
2687 let mut do_benchmark = || {
2688 runtime.bench_call(
2689 memory.as_mut_slice(),
2690 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), )
2699 };
2700
2701 assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
2704
2705 let result;
2706 #[block]
2707 {
2708 result = do_benchmark();
2709 }
2710
2711 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2712 }
2713
2714 #[benchmark(pov_mode = Measured)]
2715 fn seal_delegate_call() -> Result<(), BenchmarkError> {
2716 let target = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?;
2719 let address = delegated_eoa::<T>(H160([0x43; 20]), target.address)?;
2720
2721 let address_bytes = address.encode();
2722 let address_len = address_bytes.len() as u32;
2723
2724 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2725 let deposit_bytes = Into::<U256>::into(deposit).encode();
2726
2727 let mut setup = CallSetup::<T>::default();
2728 setup.set_storage_deposit_limit(deposit);
2729 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2730
2731 let (mut ext, _) = setup.ext();
2732 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2733 let mut memory = memory!(address_bytes, deposit_bytes,);
2734
2735 let result;
2736 #[block]
2737 {
2738 result = runtime.bench_delegate_call(
2739 memory.as_mut_slice(),
2740 pack_hi_lo(0, 0), u64::MAX, u64::MAX, address_len, pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
2747 }
2748
2749 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2750 Ok(())
2751 }
2752
2753 #[benchmark(pov_mode = Measured)]
2757 fn seal_instantiate(
2758 t: Linear<0, 1>,
2759 d: Linear<0, 1>,
2760 i: Linear<0, { limits::CALLDATA_BYTES }>,
2761 ) -> Result<(), BenchmarkError> {
2762 let code = VmBinaryModule::dummy();
2763 let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
2764 let hash_bytes = hash.encode();
2765
2766 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2767 let dust = 100u32 * d;
2768 let evm_value =
2769 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2770 let value_bytes = evm_value.encode();
2771 let value_len = value_bytes.len() as u32;
2772
2773 let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
2774 let deposit_bytes = Into::<U256>::into(deposit).encode();
2775 let deposit_len = deposit_bytes.len() as u32;
2776
2777 let mut setup = CallSetup::<T>::default();
2778 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2779 setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
2780
2781 let account_id = &setup.contract().account_id.clone();
2782 let (mut ext, _) = setup.ext();
2783 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2784
2785 let input = vec![42u8; i as _];
2786 let input_len = hash_bytes.len() as u32 + input.len() as u32;
2787 let salt = [42u8; 32];
2788 let deployer = T::AddressMapper::to_address(&account_id);
2789 let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
2790 let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
2791
2792 let mut offset = {
2793 let mut current = 0u32;
2794 move |after: u32| {
2795 current += after;
2796 current
2797 }
2798 };
2799
2800 assert!(AccountInfoOf::<T>::get(&addr).is_none());
2801
2802 let result;
2803 #[block]
2804 {
2805 result = runtime.bench_instantiate(
2806 memory.as_mut_slice(),
2807 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)), );
2814 }
2815
2816 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2817 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2818
2819 assert_eq!(
2820 Pallet::<T>::evm_balance(&addr),
2821 evm_value,
2822 "{addr:?} balance should hold {evm_value:?}"
2823 );
2824 Ok(())
2825 }
2826
2827 #[benchmark(pov_mode = Measured)]
2831 fn evm_instantiate(
2832 t: Linear<0, 1>,
2833 d: Linear<0, 1>,
2834 i: Linear<{ 10 * 1024 }, { 48 * 1024 }>,
2835 ) -> Result<(), BenchmarkError> {
2836 use crate::vm::evm::instructions::BENCH_INIT_CODE;
2837 let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_init_code_for_runtime_size(0));
2838 setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2839 setup.set_balance(caller_funding::<T>());
2840
2841 let (mut ext, _) = setup.ext();
2842 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
2843
2844 let value = {
2845 let value: BalanceOf<T> = (1_000_000u32 * t).into();
2846 let dust = 100u32 * d;
2847 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust))
2848 };
2849
2850 let init_code = vec![BENCH_INIT_CODE; i as usize];
2851 let _ = interpreter.memory.resize(0, init_code.len());
2852 let salt = U256::from(42u64);
2853 interpreter.memory.set_data(0, 0, init_code.len(), &init_code);
2854
2855 let _ = interpreter.stack.push(salt);
2857 let _ = interpreter.stack.push(U256::from(init_code.len()));
2858 let _ = interpreter.stack.push(U256::zero());
2859 let _ = interpreter.stack.push(value);
2860
2861 let result;
2862 #[block]
2863 {
2864 result = instructions::contract::create::<true, _>(&mut interpreter);
2865 }
2866
2867 assert!(result.is_continue());
2868 let addr = interpreter.stack.top().unwrap().into_address();
2869 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2870 assert_eq!(Pallet::<T>::code(&addr).len(), revm::primitives::eip170::MAX_CODE_SIZE);
2871 assert_eq!(Pallet::<T>::evm_balance(&addr), value, "balance should hold {value:?}");
2872 Ok(())
2873 }
2874
2875 #[benchmark(pov_mode = Measured)]
2877 fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2878 let input = vec![0u8; n as usize];
2879 let mut call_setup = CallSetup::<T>::default();
2880 let (mut ext, _) = call_setup.ext();
2881
2882 let result;
2883 #[block]
2884 {
2885 result = run_builtin_precompile(
2886 &mut ext,
2887 H160::from_low_u64_be(2).as_fixed_bytes(),
2888 input.clone(),
2889 );
2890 }
2891 assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
2892 }
2893
2894 #[benchmark(pov_mode = Measured)]
2895 fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2896 let input = vec![0u8; n as usize];
2897 let mut call_setup = CallSetup::<T>::default();
2898 let (mut ext, _) = call_setup.ext();
2899
2900 let result;
2901 #[block]
2902 {
2903 result = run_builtin_precompile(
2904 &mut ext,
2905 H160::from_low_u64_be(4).as_fixed_bytes(),
2906 input.clone(),
2907 );
2908 }
2909 assert_eq!(input, result.unwrap().data);
2910 }
2911
2912 #[benchmark(pov_mode = Measured)]
2914 fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2915 use ripemd::Digest;
2916 let input = vec![0u8; n as usize];
2917 let mut call_setup = CallSetup::<T>::default();
2918 let (mut ext, _) = call_setup.ext();
2919
2920 let result;
2921 #[block]
2922 {
2923 result = run_builtin_precompile(
2924 &mut ext,
2925 H160::from_low_u64_be(3).as_fixed_bytes(),
2926 input.clone(),
2927 );
2928 }
2929 let mut expected = [0u8; 32];
2930 expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
2931
2932 assert_eq!(expected.to_vec(), result.unwrap().data);
2933 }
2934
2935 #[benchmark(pov_mode = Measured)]
2937 fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2938 build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
2939
2940 let result;
2941 #[block]
2942 {
2943 result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
2944 }
2945 assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
2946 assert_ok!(result);
2947 }
2948
2949 #[benchmark(pov_mode = Measured)]
2951 fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2952 let input = vec![0u8; n as usize];
2953 let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
2954 input: input.clone().into(),
2955 })
2956 .abi_encode();
2957
2958 let mut call_setup = CallSetup::<T>::default();
2959 let (mut ext, _) = call_setup.ext();
2960
2961 let result;
2962 #[block]
2963 {
2964 result = run_builtin_precompile(
2965 &mut ext,
2966 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2967 input_bytes,
2968 );
2969 }
2970 let truth: [u8; 32] = sp_io::hashing::blake2_256(&input);
2971 let truth = FixedBytes::<32>::abi_encode(&truth);
2972 let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed");
2973
2974 let raw_data = result.unwrap().data;
2975 let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed");
2976 assert_eq!(truth, ret_hash);
2977 }
2978
2979 #[benchmark(pov_mode = Measured)]
2981 fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2982 let input = vec![0u8; n as usize];
2983 let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
2984 input: input.clone().into(),
2985 })
2986 .abi_encode();
2987
2988 let mut call_setup = CallSetup::<T>::default();
2989 let (mut ext, _) = call_setup.ext();
2990
2991 let result;
2992 #[block]
2993 {
2994 result = run_builtin_precompile(
2995 &mut ext,
2996 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2997 input_bytes,
2998 );
2999 }
3000 let truth: [u8; 16] = sp_io::hashing::blake2_128(&input);
3001 let truth = FixedBytes::<16>::abi_encode(&truth);
3002 let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed");
3003
3004 let raw_data = result.unwrap().data;
3005 let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed");
3006 assert_eq!(truth, ret_hash);
3007 }
3008
3009 #[benchmark(pov_mode = Measured)]
3012 fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) {
3013 let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
3014 let message_len = message.len() as u32;
3015
3016 let key_type = sp_core::crypto::KeyTypeId(*b"code");
3017 let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
3018 let sig =
3019 sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
3020 let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
3021 let sig_len = sig.len() as u32;
3022
3023 build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
3024
3025 let result;
3026 #[block]
3027 {
3028 result = runtime.bench_sr25519_verify(
3029 memory.as_mut_slice(),
3030 0, sig_len, message_len, sig_len + pub_key.len() as u32, );
3035 }
3036
3037 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
3038 }
3039
3040 #[benchmark(pov_mode = Measured)]
3041 fn ecdsa_recover() {
3042 use hex_literal::hex;
3043 let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
3044 let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
3045 let mut call_setup = CallSetup::<T>::default();
3046 let (mut ext, _) = call_setup.ext();
3047
3048 let result;
3049
3050 #[block]
3051 {
3052 result =
3053 run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
3054 }
3055
3056 assert_eq!(result.unwrap().data, expected);
3057 }
3058
3059 #[benchmark(pov_mode = Measured)]
3060 fn p256_verify() {
3061 use hex_literal::hex;
3062 let input = hex!("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").to_vec();
3063 let expected = U256::one().to_big_endian();
3064 let mut call_setup = CallSetup::<T>::default();
3065 let (mut ext, _) = call_setup.ext();
3066
3067 let result;
3068
3069 #[block]
3070 {
3071 result = run_builtin_precompile(
3072 &mut ext,
3073 H160::from_low_u64_be(0x100).as_fixed_bytes(),
3074 input,
3075 );
3076 }
3077
3078 assert_eq!(result.unwrap().data, expected);
3079 }
3080
3081 #[benchmark(pov_mode = Measured)]
3082 fn bn128_add() {
3083 use hex_literal::hex;
3084 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
3085 let expected = hex!(
3086 "0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
3087 );
3088 let mut call_setup = CallSetup::<T>::default();
3089 let (mut ext, _) = call_setup.ext();
3090
3091 let result;
3092 #[block]
3093 {
3094 result =
3095 run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
3096 }
3097
3098 assert_eq!(result.unwrap().data, expected);
3099 }
3100
3101 #[benchmark(pov_mode = Measured)]
3102 fn bn128_mul() {
3103 use hex_literal::hex;
3104 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
3105 let expected = hex!(
3106 "0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
3107 );
3108 let mut call_setup = CallSetup::<T>::default();
3109 let (mut ext, _) = call_setup.ext();
3110
3111 let result;
3112 #[block]
3113 {
3114 result =
3115 run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
3116 }
3117
3118 assert_eq!(result.unwrap().data, expected);
3119 }
3120
3121 #[benchmark(pov_mode = Measured)]
3123 fn bn128_pairing(n: Linear<0, { 20 }>) {
3124 fn generate_random_ecpairs(n: usize) -> Vec<u8> {
3125 use bn::{AffineG1, AffineG2, Fr, G1, G2, Group};
3126 use rand::SeedableRng;
3127 use rand_pcg::Pcg64;
3128 let mut rng = Pcg64::seed_from_u64(1);
3129
3130 let mut buffer = vec![0u8; n * 192];
3131
3132 let mut write = |element: &bn::Fq, offset: &mut usize| {
3133 element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
3134 *offset += 32
3135 };
3136
3137 for i in 0..n {
3138 let mut offset = i * 192;
3139 let scalar = Fr::random(&mut rng);
3140
3141 let g1 = G1::one() * scalar;
3142 let g2 = G2::one() * scalar;
3143 let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
3144 let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
3145
3146 write(&a.x(), &mut offset);
3147 write(&a.y(), &mut offset);
3148 write(&b.x().imaginary(), &mut offset);
3149 write(&b.x().real(), &mut offset);
3150 write(&b.y().imaginary(), &mut offset);
3151 write(&b.y().real(), &mut offset);
3152 }
3153
3154 buffer
3155 }
3156
3157 let input = generate_random_ecpairs(n as usize);
3158 let mut call_setup = CallSetup::<T>::default();
3159 let (mut ext, _) = call_setup.ext();
3160
3161 let result;
3162 #[block]
3163 {
3164 result =
3165 run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
3166 }
3167 assert_ok!(result);
3168 }
3169
3170 #[benchmark(pov_mode = Measured)]
3172 fn blake2f(n: Linear<0, 1200>) {
3173 use hex_literal::hex;
3174 let input = hex!(
3175 "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
3176 );
3177 let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
3178 let mut call_setup = CallSetup::<T>::default();
3179 let (mut ext, _) = call_setup.ext();
3180
3181 let result;
3182 #[block]
3183 {
3184 result =
3185 run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
3186 }
3187 assert_ok!(result);
3188 }
3189
3190 #[benchmark(pov_mode = Measured)]
3194 fn seal_ecdsa_to_eth_address() {
3195 let key_type = sp_core::crypto::KeyTypeId(*b"code");
3196 let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
3197 build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
3198
3199 let result;
3200 #[block]
3201 {
3202 result = runtime.bench_ecdsa_to_eth_address(
3203 memory.as_mut_slice(),
3204 20, 0, );
3207 }
3208
3209 assert_ok!(result);
3210 assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
3211 }
3212
3213 #[benchmark(pov_mode = Measured)]
3215 fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
3216 let module = VmBinaryModule::evm_noop(r);
3217 let inputs = vec![];
3218
3219 let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
3220 let mut setup = CallSetup::<T>::new(module);
3221 let (mut ext, _) = setup.ext();
3222
3223 let result;
3224 #[block]
3225 {
3226 result = evm::call(code, &mut ext, inputs);
3227 }
3228
3229 assert!(result.is_ok());
3230 Ok(())
3231 }
3232
3233 #[benchmark(pov_mode = Ignored)]
3238 fn instr(r: Linear<0, 10_000>) {
3239 use rand::{SeedableRng, seq::SliceRandom};
3240 use rand_pcg::Pcg64;
3241
3242 const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
3244
3245 const CACHE_LINE_SIZE: u64 = 64;
3247
3248 const MISALIGNMENT: u64 = 60;
3250
3251 const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
3254
3255 assert!(
3256 u64::from(r) <= NUM_ADDRESSES / 2,
3257 "If we do too many iterations we run into the risk of loading from warm cache lines",
3258 );
3259
3260 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
3261 let (mut ext, module) = setup.ext();
3262 let mut prepared =
3263 CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
3264
3265 assert!(
3266 u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
3267 "aux data base must be cache aligned"
3268 );
3269
3270 let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
3272
3273 let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
3277 for i in 1..NUM_ADDRESSES {
3278 let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
3279 addresses.push(addr);
3280 }
3281 let mut rng = Pcg64::seed_from_u64(1337);
3282 addresses.shuffle(&mut rng);
3283
3284 let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
3286 for address in addresses {
3287 memory.extend_from_slice(&address);
3288 memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
3289 }
3290
3291 prepared
3294 .setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
3295 .unwrap();
3296
3297 #[block]
3298 {
3299 prepared.call().unwrap();
3300 }
3301 }
3302
3303 #[benchmark(pov_mode = Ignored)]
3304 fn instr_empty_loop(r: Linear<0, 10_000>) {
3305 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
3306 let (mut ext, module) = setup.ext();
3307 let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
3308 prepared.setup_aux_data(&[], 0, r.into()).unwrap();
3309
3310 #[block]
3311 {
3312 prepared.call().unwrap();
3313 }
3314 }
3315
3316 #[benchmark(pov_mode = Measured)]
3317 fn extcodecopy(n: Linear<1_000, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
3318 let mut setup = CallSetup::<T>::new(VmBinaryModule::dummy());
3320 let target = Contract::<T>::with_index(1, VmBinaryModule::sized(n), vec![])?;
3322
3323 let (mut ext, _) = setup.ext();
3324 let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
3325
3326 let _ = interpreter.stack.push(U256::from(n));
3328 let _ = interpreter.stack.push(U256::from(0u32));
3329 let _ = interpreter.stack.push(U256::from(0u32));
3330 let _ = interpreter.stack.push(target.address);
3331
3332 let result;
3333 #[block]
3334 {
3335 result = instructions::host::extcodecopy(&mut interpreter);
3336 }
3337
3338 assert!(result.is_continue());
3339 assert_eq!(
3340 *interpreter.memory.slice(0..n as usize),
3341 PristineCode::<T>::get(target.info()?.code_hash).unwrap()[0..n as usize],
3342 "Memory should contain the target contract's code after extcodecopy"
3343 );
3344
3345 Ok(())
3346 }
3347
3348 #[benchmark]
3349 fn v1_migration_step() {
3350 use crate::migrations::v1;
3351 let addr = H160::from([1u8; 20]);
3352 let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
3353
3354 v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
3355 let mut meter = WeightMeter::new();
3356 assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
3357
3358 #[block]
3359 {
3360 v1::Migration::<T>::step(None, &mut meter).unwrap();
3361 }
3362
3363 assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
3364 assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
3365
3366 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
3368 }
3369
3370 #[benchmark]
3371 fn v2_migration_step() {
3372 use crate::migrations::v2;
3373 let code_hash = H256::from([0; 32]);
3374 let old_code_info = v2::Migration::<T>::create_old_code_info(
3375 whitelisted_caller(),
3376 1000u32.into(),
3377 1,
3378 100,
3379 0,
3380 );
3381 v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
3382 let mut meter = WeightMeter::new();
3383
3384 #[block]
3385 {
3386 v2::Migration::<T>::step(None, &mut meter).unwrap();
3387 }
3388
3389 v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
3390
3391 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
3393 }
3394
3395 #[benchmark]
3396 fn v3_migration_step() {
3397 use crate::migrations::v3;
3398 let _ = frame_system::Account::<T>::clear(u32::MAX, None);
3400
3401 let account = account::<T::AccountId>("target", 0, 0);
3402 T::Currency::mint_into(&account, Pallet::<T>::min_balance())
3403 .expect("should mint into account");
3404
3405 let addr = T::AddressMapper::to_address(&account);
3407 crate::OriginalAccount::<T>::remove(addr);
3408
3409 assert!(!T::AddressMapper::is_mapped(&account));
3410 let mut meter = WeightMeter::new();
3411
3412 #[block]
3413 {
3414 v3::Migration::<T>::step(None, &mut meter).unwrap();
3415 }
3416
3417 assert!(T::AddressMapper::is_mapped(&account));
3418
3419 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v3_migration_step() * 2);
3421 }
3422
3423 #[benchmark]
3428 fn v4_code_upload_step() {
3429 use crate::migrations::v4;
3430
3431 let _ = CodeInfoOf::<T>::clear(u32::MAX, None);
3432
3433 let owner: T::AccountId = whitelisted_caller();
3434 let deposit: BalanceOf<T> = 1_000u32.into();
3435
3436 let pallet_account = Pallet::<T>::account_id();
3437 T::Currency::mint_into(&pallet_account, Pallet::<T>::min_balance()).unwrap();
3438 T::Currency::mint_into(&pallet_account, deposit).unwrap();
3439 T::Currency::hold(&HoldReason::CodeUploadDepositReserve.into(), &pallet_account, deposit)
3440 .unwrap();
3441
3442 CodeInfoOf::<T>::insert(
3443 H256::from([1u8; 32]),
3444 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
3445 );
3446 CodeInfoOf::<T>::insert(
3447 H256::from([2u8; 32]),
3448 CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
3449 );
3450
3451 let first = match v4::Migration::<T>::step_once(None) {
3452 Some(v4::Cursor::CodeUpload(h)) => h,
3453 other => panic!("expected CodeUpload cursor, got {other:?}"),
3454 };
3455 let cursor = Some(v4::Cursor::CodeUpload(first));
3456
3457 #[block]
3458 {
3459 let _ = v4::Migration::<T>::step_once(cursor);
3460 }
3461
3462 assert_eq!(
3463 NativeDepositOf::<T>::get(&pallet_account, &owner),
3464 deposit + deposit,
3465 "both code uploads credited to owner",
3466 );
3467 }
3468
3469 #[benchmark]
3474 fn v4_contract_step() {
3475 use crate::migrations::v4;
3476
3477 let _ = AccountInfoOf::<T>::clear(u32::MAX, None);
3478
3479 let code_hash = H256::from([0u8; 32]);
3480 let deposit: BalanceOf<T> = 1_000u32.into();
3481
3482 for byte in [0x41u8, 0x42u8] {
3483 let addr = H160::from([byte; 20]);
3484 let contract_account = T::AddressMapper::to_account_id(&addr);
3485 let info =
3486 ContractInfo::<T>::new(&addr, 1u32.into(), code_hash).expect("fresh contract info");
3487 AccountInfoOf::<T>::insert(
3488 addr,
3489 crate::storage::AccountInfo::<T> {
3490 account_type: crate::storage::AccountType::Contract(info),
3491 dust: 0,
3492 },
3493 );
3494 T::Currency::mint_into(&contract_account, Pallet::<T>::min_balance()).unwrap();
3495 T::Currency::mint_into(&contract_account, deposit).unwrap();
3496 T::Currency::hold(
3497 &HoldReason::StorageDepositReserve.into(),
3498 &contract_account,
3499 deposit,
3500 )
3501 .unwrap();
3502 }
3503
3504 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::Contract(None))) {
3505 Some(v4::Cursor::Contract(Some(addr))) => addr,
3506 other => panic!("expected Contract cursor, got {other:?}"),
3507 };
3508 let cursor = Some(v4::Cursor::Contract(Some(first)));
3509
3510 #[block]
3511 {
3512 let _ = v4::Migration::<T>::step_once(cursor);
3513 }
3514
3515 if T::Deposit::SUPPORTS_PGAS {
3518 for byte in [0x41u8, 0x42u8] {
3519 let addr = H160::from([byte; 20]);
3520 let contract_account = T::AddressMapper::to_account_id(&addr);
3521 assert_eq!(
3522 T::Currency::balance_on_hold(
3523 &HoldReason::StorageDepositReserve.into(),
3524 &contract_account,
3525 ),
3526 0u32.into(),
3527 "native storage deposit burned for {addr:?}",
3528 );
3529 }
3530 }
3531 }
3532
3533 #[benchmark]
3539 fn v4_deletion_queue_step() {
3540 use crate::migrations::v4;
3541
3542 let _ = v4::old::DeletionQueue::<T>::clear(u32::MAX, None);
3543
3544 let trie_a: TrieId = vec![0xAAu8; 16].try_into().unwrap();
3545 let trie_b: TrieId = vec![0xBBu8; 24].try_into().unwrap();
3546 v4::old::DeletionQueue::<T>::insert(0u32, trie_a);
3547 v4::old::DeletionQueue::<T>::insert(1u32, trie_b);
3548
3549 let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::DeletionQueue(None))) {
3550 Some(v4::Cursor::DeletionQueue(Some(key))) => key,
3551 other => panic!("expected DeletionQueue cursor, got {other:?}"),
3552 };
3553 let cursor = Some(v4::Cursor::DeletionQueue(Some(first)));
3554
3555 #[block]
3556 {
3557 let _ = v4::Migration::<T>::step_once(cursor);
3558 }
3559
3560 assert!(
3561 DeletionQueue::<T>::get(0u32).is_some() && DeletionQueue::<T>::get(1u32).is_some(),
3562 "both legacy entries rewritten into the new format",
3563 );
3564 }
3565
3566 fn create_test_signer<T: Config>() -> (T::AccountId, SigningKey, H160) {
3568 use hex_literal::hex;
3569 let signer_account_id = hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
3571 let signer_priv_key =
3572 hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133");
3573
3574 let signer_key = SigningKey::from_bytes(&signer_priv_key.into()).expect("valid key");
3575
3576 let signer_address = H160::from_slice(&signer_account_id);
3577 let signer_caller = T::AddressMapper::to_fallback_account_id(&signer_address);
3578
3579 (signer_caller, signer_key, signer_address)
3580 }
3581
3582 fn create_signed_transaction<T: Config>(
3584 signer_key: &SigningKey,
3585 target_address: H160,
3586 value: U256,
3587 input_data: Vec<u8>,
3588 ) -> Vec<u8> {
3589 let unsigned_tx: TransactionUnsigned = TransactionLegacyUnsigned {
3590 to: Some(target_address),
3591 value,
3592 chain_id: Some(T::ChainId::get().into()),
3593 input: input_data.into(),
3594 ..Default::default()
3595 }
3596 .into();
3597
3598 let hashed_payload = sp_io::hashing::keccak_256(&unsigned_tx.unsigned_payload());
3599 let (signature, recovery_id) =
3600 signer_key.sign_prehash_recoverable(&hashed_payload).expect("signing success");
3601
3602 let mut sig_bytes = [0u8; 65];
3603 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
3604 sig_bytes[64] = recovery_id.to_byte();
3605
3606 let signed_tx = unsigned_tx.with_signature(sig_bytes);
3607
3608 signed_tx.signed_payload()
3609 }
3610
3611 fn setup_finalize_block_benchmark<T>()
3613 -> Result<(Contract<T>, BalanceOf<T>, U256, SigningKey, BlockNumberFor<T>), BenchmarkError>
3614 where
3615 BalanceOf<T>: Into<U256> + TryFrom<U256>,
3616 T: Config,
3617 MomentOf<T>: Into<U256>,
3618 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
3619 {
3620 let (signer_caller, signer_key, _signer_address) = create_test_signer::<T>();
3622 whitelist_account!(signer_caller);
3623
3624 let instance =
3626 Contract::<T>::with_caller(signer_caller.clone(), VmBinaryModule::dummy(), vec![])?;
3627 let storage_deposit = default_deposit_limit::<T>();
3628 let value = Pallet::<T>::min_balance();
3629 let evm_value =
3630 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, 0));
3631
3632 let current_block = BlockNumberFor::<T>::from(1u32);
3634 frame_system::Pallet::<T>::set_block_number(current_block);
3635
3636 Pallet::<T>::clear_outside_frame_logs();
3640
3641 Ok((instance, storage_deposit, evm_value, signer_key, current_block))
3642 }
3643
3644 #[benchmark(pov_mode = Measured)]
3661 fn on_finalize_per_transaction(n: Linear<0, 200>) -> Result<(), BenchmarkError> {
3662 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3663 setup_finalize_block_benchmark::<T>()?;
3664
3665 let fixed_payload_size = 100usize;
3667
3668 if n > 0 {
3670 let _ = Pallet::<T>::on_initialize(current_block);
3672
3673 let input_data = vec![0x42u8; fixed_payload_size];
3675 let receipt_gas_info = ReceiptGasInfo {
3676 gas_used: U256::from(1_000_000),
3677 effective_gas_price: Pallet::<T>::evm_base_fee(),
3678 };
3679
3680 for _ in 0..n {
3681 let signed_transaction = create_signed_transaction::<T>(
3683 &signer_key,
3684 instance.address,
3685 evm_value,
3686 input_data.clone(),
3687 );
3688
3689 let _ = block_storage::bench_with_ethereum_context(|| {
3691 let (encoded_logs, bloom) =
3692 block_storage::get_receipt_details::<T>().unwrap_or_default();
3693
3694 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3695 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3696
3697 block_builder.process_transaction(
3698 signed_transaction,
3699 true,
3700 receipt_gas_info.clone(),
3701 encoded_logs,
3702 bloom,
3703 );
3704
3705 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3706 });
3707 }
3708 }
3709
3710 #[block]
3711 {
3712 let _ = Pallet::<T>::on_finalize(current_block);
3714 }
3715
3716 assert_eq!(Pallet::<T>::eth_block().transactions.len(), n as usize);
3718
3719 Ok(())
3720 }
3721
3722 #[benchmark(pov_mode = Measured)]
3739 fn on_finalize_per_transaction_data(d: Linear<0, 1000>) -> Result<(), BenchmarkError> {
3740 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3741 setup_finalize_block_benchmark::<T>()?;
3742
3743 let fixed_tx_count = 10u32;
3745
3746 let _ = Pallet::<T>::on_initialize(current_block);
3748
3749 let input_data = vec![0x42u8; d as usize];
3751 let receipt_gas_info = ReceiptGasInfo {
3752 gas_used: U256::from(1_000_000),
3753 effective_gas_price: Pallet::<T>::evm_base_fee(),
3754 };
3755
3756 for _ in 0..fixed_tx_count {
3757 let signed_transaction = create_signed_transaction::<T>(
3759 &signer_key,
3760 instance.address,
3761 evm_value,
3762 input_data.clone(),
3763 );
3764
3765 let _ = block_storage::bench_with_ethereum_context(|| {
3767 let (encoded_logs, bloom) =
3768 block_storage::get_receipt_details::<T>().unwrap_or_default();
3769
3770 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3771 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3772
3773 block_builder.process_transaction(
3774 signed_transaction,
3775 true,
3776 receipt_gas_info.clone(),
3777 encoded_logs,
3778 bloom,
3779 );
3780
3781 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3782 });
3783 }
3784
3785 #[block]
3786 {
3787 let _ = Pallet::<T>::on_finalize(current_block);
3789 }
3790
3791 assert_eq!(Pallet::<T>::eth_block().transactions.len(), fixed_tx_count as usize);
3793
3794 Ok(())
3795 }
3796
3797 #[benchmark(pov_mode = Measured)]
3815 fn on_finalize_per_event(e: Linear<0, 100>) -> Result<(), BenchmarkError> {
3816 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3817 setup_finalize_block_benchmark::<T>()?;
3818
3819 let input_data = vec![0x42u8; 100];
3821 let signed_transaction = create_signed_transaction::<T>(
3822 &signer_key,
3823 instance.address,
3824 evm_value,
3825 input_data.clone(),
3826 );
3827
3828 let receipt_gas_info = ReceiptGasInfo {
3829 gas_used: U256::from(1_000_000),
3830 effective_gas_price: Pallet::<T>::evm_base_fee(),
3831 };
3832
3833 let _ = block_storage::bench_with_ethereum_context(|| {
3835 for _ in 0..e {
3839 block_storage::capture_frame_log::<T>(&instance.address, &vec![], &vec![]);
3840 }
3841
3842 let (encoded_logs, bloom) =
3843 block_storage::get_receipt_details::<T>().unwrap_or_default();
3844
3845 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3846 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3847
3848 block_builder.process_transaction(
3849 signed_transaction,
3850 true,
3851 receipt_gas_info.clone(),
3852 encoded_logs,
3853 bloom,
3854 );
3855
3856 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3857 });
3858
3859 #[block]
3860 {
3861 let _ = Pallet::<T>::on_initialize(current_block);
3863
3864 let _ = Pallet::<T>::on_finalize(current_block);
3866 }
3867
3868 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3871
3872 Ok(())
3873 }
3874
3875 #[benchmark(pov_mode = Measured)]
3884 fn on_finalize_per_event_data(d: Linear<0, 16384>) -> Result<(), BenchmarkError> {
3885 let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3886 setup_finalize_block_benchmark::<T>()?;
3887
3888 let input_data = vec![0x42u8; 100];
3890 let signed_transaction = create_signed_transaction::<T>(
3891 &signer_key,
3892 instance.address,
3893 evm_value,
3894 input_data.clone(),
3895 );
3896
3897 let receipt_gas_info = ReceiptGasInfo {
3898 gas_used: U256::from(1_000_000),
3899 effective_gas_price: Pallet::<T>::evm_base_fee(),
3900 };
3901
3902 let (event_data, topics) = if d < 32 {
3904 (vec![0x42u8; d as usize], vec![])
3906 } else {
3907 let num_topics = core::cmp::min(limits::NUM_EVENT_TOPICS, d / 32);
3909 let topic_bytes_used = num_topics * 32;
3910 let data_bytes_remaining = d - topic_bytes_used;
3911
3912 let mut topics = Vec::new();
3914 for topic_index in 0..num_topics {
3915 let topic_data = [topic_index as u8; 32];
3916 topics.push(H256::from(topic_data));
3917 }
3918
3919 let event_data = vec![0x42u8; data_bytes_remaining as usize];
3921
3922 (event_data, topics)
3923 };
3924
3925 let _ = block_storage::bench_with_ethereum_context(|| {
3927 block_storage::capture_frame_log::<T>(&instance.address, &event_data, &topics);
3932
3933 let (encoded_logs, bloom) =
3934 block_storage::get_receipt_details::<T>().unwrap_or_default();
3935
3936 let block_builder_ir = EthBlockBuilderIR::<T>::get();
3937 let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3938
3939 block_builder.process_transaction(
3940 signed_transaction,
3941 true,
3942 receipt_gas_info,
3943 encoded_logs,
3944 bloom,
3945 );
3946
3947 EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3948 });
3949
3950 #[block]
3951 {
3952 let _ = Pallet::<T>::on_initialize(current_block);
3954
3955 let _ = Pallet::<T>::on_finalize(current_block);
3957 }
3958
3959 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3962
3963 Ok(())
3964 }
3965
3966 #[benchmark(pov_mode = Measured)]
3977 fn outside_frame_log(n: Linear<0, 100>) -> Result<(), BenchmarkError> {
3978 let (instance, _storage_deposit, _evm_value, _signer_key, current_block) =
3979 setup_finalize_block_benchmark::<T>()?;
3980
3981 let _ = Pallet::<T>::on_initialize(current_block);
3983
3984 let topics =
3986 vec![H256::repeat_byte(0x11), H256::repeat_byte(0x22), H256::repeat_byte(0x33)];
3987 let data = vec![0x44u8; 32];
3988
3989 for _ in 0..n {
3992 Pallet::<T>::bench_buffer_outside_frame_log(
3993 instance.address,
3994 topics.clone(),
3995 data.clone(),
3996 );
3997 }
3998
3999 #[block]
4000 {
4001 let _ = Pallet::<T>::on_finalize(current_block);
4002 }
4003
4004 assert_eq!(Pallet::<T>::eth_block().transactions.len(), (n > 0) as usize);
4006
4007 Ok(())
4008 }
4009
4010 #[benchmark(pov_mode = Measured)]
4019 fn outside_frame_log_data(d: Linear<0, { limits::EVENT_BYTES }>) -> Result<(), BenchmarkError> {
4020 let (instance, _storage_deposit, _evm_value, _signer_key, current_block) =
4021 setup_finalize_block_benchmark::<T>()?;
4022
4023 let _ = Pallet::<T>::on_initialize(current_block);
4024
4025 let topics =
4026 vec![H256::repeat_byte(0x11), H256::repeat_byte(0x22), H256::repeat_byte(0x33)];
4027 let data = vec![0x44u8; d as usize];
4028
4029 Pallet::<T>::bench_buffer_outside_frame_log(instance.address, topics, data);
4030
4031 #[block]
4032 {
4033 let _ = Pallet::<T>::on_finalize(current_block);
4034 }
4035
4036 assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
4037
4038 Ok(())
4039 }
4040
4041 impl_benchmark_test_suite!(
4042 Contracts,
4043 crate::tests::ExtBuilder::default().build(),
4044 crate::tests::Test,
4045 );
4046}