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