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