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