1#![cfg(feature = "runtime-benchmarks")]
21use crate::{
22 call_builder::{caller_funding, default_deposit_limit, CallSetup, Contract, VmBinaryModule},
23 evm::runtime::GAS_PRICE,
24 exec::{Key, MomentOf, PrecompileExt},
25 limits,
26 precompiles::{
27 self, run::builtin as run_builtin_precompile, BenchmarkSystem, BuiltinPrecompile, ISystem,
28 },
29 storage::WriteOutcome,
30 vm::pvm,
31 Pallet as Contracts, *,
32};
33use alloc::{vec, vec::Vec};
34use alloy_core::sol_types::SolInterface;
35use codec::{Encode, MaxEncodedLen};
36use frame_benchmarking::v2::*;
37use frame_support::{
38 self, assert_ok,
39 migrations::SteppedMigration,
40 storage::child,
41 traits::fungible::InspectHold,
42 weights::{Weight, WeightMeter},
43};
44use frame_system::RawOrigin;
45use pallet_revive_uapi::{pack_hi_lo, CallFlags, ReturnErrorCode, StorageFlags};
46use sp_consensus_aura::AURA_ENGINE_ID;
47use sp_consensus_babe::{
48 digests::{PreDigest, PrimaryPreDigest},
49 BABE_ENGINE_ID,
50};
51use sp_consensus_slots::Slot;
52use sp_runtime::{
53 generic::{Digest, DigestItem},
54 traits::Zero,
55};
56
57const API_BENCHMARK_RUNS: u32 = 1600;
63
64macro_rules! memory(
65 ($($bytes:expr,)*) => {{
66 vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::<Vec<_>>()
67 }};
68);
69
70macro_rules! build_runtime(
71 ($runtime:ident, $memory:ident: [$($segment:expr,)*]) => {
72 build_runtime!($runtime, _contract, $memory: [$($segment,)*]);
73 };
74 ($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => {
75 build_runtime!($runtime, $contract);
76 let mut $memory = memory!($($bytes,)*);
77 };
78 ($runtime:ident, $contract:ident) => {
79 let mut setup = CallSetup::<T>::default();
80 let $contract = setup.contract();
81 let input = setup.data();
82 let (mut ext, _) = setup.ext();
83 let mut $runtime = $crate::vm::pvm::Runtime::<_, [u8]>::new(&mut ext, input);
84 };
85);
86
87fn whitelisted_pallet_account<T: Config>() -> T::AccountId {
90 let pallet_account = Pallet::<T>::account_id();
91 whitelist_account!(pallet_account);
92 pallet_account
93}
94
95#[benchmarks(
96 where
97 BalanceOf<T>: Into<U256> + TryFrom<U256>,
98 T: Config,
99 MomentOf<T>: Into<U256>,
100 <T as frame_system::Config>::RuntimeEvent: From<pallet::Event<T>>,
101 <T as Config>::RuntimeCall: From<frame_system::Call<T>>,
102 <T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
103)]
104mod benchmarks {
105 use super::*;
106
107 #[benchmark(pov_mode = Measured)]
109 fn on_process_deletion_queue_batch() {
110 #[block]
111 {
112 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
113 }
114 }
115
116 #[benchmark(skip_meta, pov_mode = Measured)]
117 fn on_initialize_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
118 let instance =
119 Contract::<T>::with_storage(VmBinaryModule::dummy(), k, limits::PAYLOAD_BYTES)?;
120 instance.info()?.queue_trie_for_deletion();
121
122 #[block]
123 {
124 ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
125 }
126
127 Ok(())
128 }
129
130 #[benchmark(pov_mode = Measured)]
142 fn call_with_pvm_code_per_byte(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
143 let instance =
144 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::sized(c), vec![])?;
145 let value = Pallet::<T>::min_balance();
146 let storage_deposit = default_deposit_limit::<T>();
147
148 #[extrinsic_call]
149 call(
150 RawOrigin::Signed(instance.caller.clone()),
151 instance.address,
152 value,
153 Weight::MAX,
154 storage_deposit,
155 vec![],
156 );
157
158 Ok(())
159 }
160
161 #[benchmark(pov_mode = Measured)]
165 fn call_with_evm_code_per_byte(c: Linear<1, { 10 * 1024 }>) -> Result<(), BenchmarkError> {
166 let instance =
167 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::evm_sized(c), vec![])?;
168 let value = Pallet::<T>::min_balance();
169 let storage_deposit = default_deposit_limit::<T>();
170
171 #[extrinsic_call]
172 call(
173 RawOrigin::Signed(instance.caller.clone()),
174 instance.address,
175 value,
176 Weight::MAX,
177 storage_deposit,
178 vec![],
179 );
180
181 Ok(())
182 }
183
184 #[benchmark(pov_mode = Measured)]
195 fn basic_block_compilation(b: Linear<0, 1>) -> Result<(), BenchmarkError> {
196 let instance = Contract::<T>::with_caller(
197 whitelisted_caller(),
198 VmBinaryModule::with_num_instructions(limits::code::BASIC_BLOCK_SIZE),
199 vec![],
200 )?;
201 let value = Pallet::<T>::min_balance();
202 let storage_deposit = default_deposit_limit::<T>();
203
204 #[block]
205 {
206 Pallet::<T>::call(
207 RawOrigin::Signed(instance.caller.clone()).into(),
208 instance.address,
209 value,
210 Weight::MAX,
211 storage_deposit,
212 vec![],
213 )?;
214 }
215
216 Ok(())
217 }
218
219 #[benchmark(pov_mode = Measured)]
222 fn instantiate_with_code(
223 c: Linear<0, { 100 * 1024 }>,
224 i: Linear<0, { limits::CALLDATA_BYTES }>,
225 ) {
226 let pallet_account = whitelisted_pallet_account::<T>();
227 let input = vec![42u8; i as usize];
228 let salt = [42u8; 32];
229 let value = Pallet::<T>::min_balance();
230 let caller = whitelisted_caller();
231 T::Currency::set_balance(&caller, caller_funding::<T>());
232 let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
233 let origin = RawOrigin::Signed(caller.clone());
234 Contracts::<T>::map_account(origin.clone().into()).unwrap();
235 let deployer = T::AddressMapper::to_address(&caller);
236 let addr = crate::address::create2(&deployer, &code, &input, &salt);
237 let account_id = T::AddressMapper::to_fallback_account_id(&addr);
238 let storage_deposit = default_deposit_limit::<T>();
239 #[extrinsic_call]
240 _(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt));
241
242 let deposit =
243 T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
244 let code_deposit = T::Currency::balance_on_hold(
246 &HoldReason::CodeUploadDepositReserve.into(),
247 &pallet_account,
248 );
249 let mapping_deposit =
250 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller);
251 assert_eq!(
252 T::Currency::balance(&caller),
253 caller_funding::<T>() -
254 value - deposit -
255 code_deposit - mapping_deposit -
256 Pallet::<T>::min_balance(),
257 );
258 assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
260 }
261
262 #[benchmark(pov_mode = Measured)]
266 fn eth_instantiate_with_code(
267 c: Linear<0, { 100 * 1024 }>,
268 i: Linear<0, { limits::CALLDATA_BYTES }>,
269 d: Linear<0, 1>,
270 ) {
271 let pallet_account = whitelisted_pallet_account::<T>();
272 let input = vec![42u8; i as usize];
273
274 let value = Pallet::<T>::min_balance();
275 let dust = 42u32 * d;
276 let evm_value =
277 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
278
279 let caller = whitelisted_caller();
280 T::Currency::set_balance(&caller, caller_funding::<T>());
281 let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
282 let origin = RawOrigin::Signed(caller.clone());
283 Contracts::<T>::map_account(origin.clone().into()).unwrap();
284 let deployer = T::AddressMapper::to_address(&caller);
285 let nonce = System::<T>::account_nonce(&caller).try_into().unwrap_or_default();
286 let addr = crate::address::create1(&deployer, nonce);
287 let account_id = T::AddressMapper::to_fallback_account_id(&addr);
288 let storage_deposit = default_deposit_limit::<T>();
289
290 assert!(AccountInfoOf::<T>::get(&deployer).is_none());
291
292 #[extrinsic_call]
293 _(origin, evm_value, Weight::MAX, storage_deposit, code, input);
294
295 let deposit =
296 T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
297 let code_deposit = T::Currency::balance_on_hold(
299 &HoldReason::CodeUploadDepositReserve.into(),
300 &pallet_account,
301 );
302 let mapping_deposit =
303 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller);
304
305 assert_eq!(
306 Pallet::<T>::evm_balance(&deployer),
307 Pallet::<T>::convert_native_to_evm(
308 caller_funding::<T>() -
309 Pallet::<T>::min_balance() -
310 Pallet::<T>::min_balance() -
311 value - deposit - code_deposit -
312 mapping_deposit,
313 ) - dust,
314 );
315
316 assert_eq!(Pallet::<T>::evm_balance(&addr), evm_value);
318 }
319
320 #[benchmark(pov_mode = Measured)]
323 fn instantiate(i: Linear<0, { limits::CALLDATA_BYTES }>) -> Result<(), BenchmarkError> {
324 let pallet_account = whitelisted_pallet_account::<T>();
325 let input = vec![42u8; i as usize];
326 let salt = [42u8; 32];
327 let value = Pallet::<T>::min_balance();
328 let caller = whitelisted_caller();
329 T::Currency::set_balance(&caller, caller_funding::<T>());
330 let origin = RawOrigin::Signed(caller.clone());
331 Contracts::<T>::map_account(origin.clone().into()).unwrap();
332 let VmBinaryModule { code, .. } = VmBinaryModule::dummy();
333 let storage_deposit = default_deposit_limit::<T>();
334 let deployer = T::AddressMapper::to_address(&caller);
335 let addr = crate::address::create2(&deployer, &code, &input, &salt);
336 let hash = Contracts::<T>::bare_upload_code(origin.clone().into(), code, storage_deposit)?
337 .code_hash;
338 let account_id = T::AddressMapper::to_fallback_account_id(&addr);
339
340 #[extrinsic_call]
341 _(origin, value, Weight::MAX, storage_deposit, hash, input, Some(salt));
342
343 let deposit =
344 T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
345 let code_deposit = T::Currency::balance_on_hold(
346 &HoldReason::CodeUploadDepositReserve.into(),
347 &pallet_account,
348 );
349 let mapping_deposit =
350 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &account_id);
351 assert_eq!(
353 T::Currency::total_balance(&caller),
354 caller_funding::<T>() -
355 value - deposit -
356 code_deposit - mapping_deposit -
357 Pallet::<T>::min_balance(),
358 );
359 assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
361
362 Ok(())
363 }
364
365 #[benchmark(pov_mode = Measured)]
373 fn call() -> Result<(), BenchmarkError> {
374 let pallet_account = whitelisted_pallet_account::<T>();
375 let data = vec![42u8; 1024];
376 let instance =
377 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
378 let value = Pallet::<T>::min_balance();
379 let origin = RawOrigin::Signed(instance.caller.clone());
380 let before = T::Currency::balance(&instance.account_id);
381 let storage_deposit = default_deposit_limit::<T>();
382 #[extrinsic_call]
383 _(origin, instance.address, value, Weight::MAX, storage_deposit, data);
384 let deposit = T::Currency::balance_on_hold(
385 &HoldReason::StorageDepositReserve.into(),
386 &instance.account_id,
387 );
388 let code_deposit = T::Currency::balance_on_hold(
389 &HoldReason::CodeUploadDepositReserve.into(),
390 &pallet_account,
391 );
392 let mapping_deposit =
393 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller);
394 assert_eq!(
396 T::Currency::balance(&instance.caller),
397 caller_funding::<T>() -
398 value - deposit -
399 code_deposit - mapping_deposit -
400 Pallet::<T>::min_balance()
401 );
402 assert_eq!(T::Currency::balance(&instance.account_id), before + value);
404 instance.info()?;
406
407 Ok(())
408 }
409
410 #[benchmark(pov_mode = Measured)]
412 fn eth_call(d: Linear<0, 1>) -> Result<(), BenchmarkError> {
413 let pallet_account = whitelisted_pallet_account::<T>();
414 let data = vec![42u8; 1024];
415 let instance =
416 Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
417
418 let value = Pallet::<T>::min_balance();
419 let dust = 42u32 * d;
420 let evm_value =
421 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
422
423 let caller_addr = T::AddressMapper::to_address(&instance.caller);
424 let origin = RawOrigin::Signed(instance.caller.clone());
425 let before = Pallet::<T>::evm_balance(&instance.address);
426 let storage_deposit = default_deposit_limit::<T>();
427 #[extrinsic_call]
428 _(origin, instance.address, evm_value, Weight::MAX, storage_deposit, data);
429 let deposit = T::Currency::balance_on_hold(
430 &HoldReason::StorageDepositReserve.into(),
431 &instance.account_id,
432 );
433 let code_deposit = T::Currency::balance_on_hold(
434 &HoldReason::CodeUploadDepositReserve.into(),
435 &pallet_account,
436 );
437 let mapping_deposit =
438 T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller);
439 assert_eq!(
441 Pallet::<T>::evm_balance(&caller_addr),
442 Pallet::<T>::convert_native_to_evm(
443 caller_funding::<T>() -
444 Pallet::<T>::min_balance() -
445 Pallet::<T>::min_balance() -
446 value - deposit - code_deposit -
447 mapping_deposit,
448 ) - dust,
449 );
450
451 assert_eq!(Pallet::<T>::evm_balance(&instance.address), before + evm_value);
453 instance.info()?;
455
456 Ok(())
457 }
458
459 #[benchmark(pov_mode = Measured)]
463 fn upload_code(c: Linear<0, { 100 * 1024 }>) {
464 let caller = whitelisted_caller();
465 let pallet_account = whitelisted_pallet_account::<T>();
466 T::Currency::set_balance(&caller, caller_funding::<T>());
467 let VmBinaryModule { code, hash, .. } = VmBinaryModule::sized(c);
468 let origin = RawOrigin::Signed(caller.clone());
469 let storage_deposit = default_deposit_limit::<T>();
470 #[extrinsic_call]
471 _(origin, code, storage_deposit);
472 assert!(T::Currency::total_balance_on_hold(&pallet_account) > 0u32.into());
474 assert!(<Contract<T>>::code_exists(&hash));
475 }
476
477 #[benchmark(pov_mode = Measured)]
481 fn remove_code() -> Result<(), BenchmarkError> {
482 let caller = whitelisted_caller();
483 let pallet_account = whitelisted_pallet_account::<T>();
484 T::Currency::set_balance(&caller, caller_funding::<T>());
485 let VmBinaryModule { code, hash, .. } = VmBinaryModule::dummy();
486 let origin = RawOrigin::Signed(caller.clone());
487 let storage_deposit = default_deposit_limit::<T>();
488 let uploaded =
489 <Contracts<T>>::bare_upload_code(origin.clone().into(), code, storage_deposit)?;
490 assert_eq!(uploaded.code_hash, hash);
491 assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&pallet_account));
492 assert!(<Contract<T>>::code_exists(&hash));
493 #[extrinsic_call]
494 _(origin, hash);
495 assert_eq!(T::Currency::total_balance_on_hold(&pallet_account), 0u32.into());
497 assert!(<Contract<T>>::code_removed(&hash));
498 Ok(())
499 }
500
501 #[benchmark(pov_mode = Measured)]
502 fn set_code() -> Result<(), BenchmarkError> {
503 let instance =
504 <Contract<T>>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
505 let VmBinaryModule { code, .. } = VmBinaryModule::dummy_unique(128);
507 let origin = RawOrigin::Signed(instance.caller.clone());
508 let storage_deposit = default_deposit_limit::<T>();
509 let hash =
510 <Contracts<T>>::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash;
511 assert_ne!(instance.info()?.code_hash, hash);
512 #[extrinsic_call]
513 _(RawOrigin::Root, instance.address, hash);
514 assert_eq!(instance.info()?.code_hash, hash);
515 Ok(())
516 }
517
518 #[benchmark(pov_mode = Measured)]
519 fn map_account() {
520 let caller = whitelisted_caller();
521 T::Currency::set_balance(&caller, caller_funding::<T>());
522 let origin = RawOrigin::Signed(caller.clone());
523 assert!(!T::AddressMapper::is_mapped(&caller));
524 #[extrinsic_call]
525 _(origin);
526 assert!(T::AddressMapper::is_mapped(&caller));
527 }
528
529 #[benchmark(pov_mode = Measured)]
530 fn unmap_account() {
531 let caller = whitelisted_caller();
532 T::Currency::set_balance(&caller, caller_funding::<T>());
533 let origin = RawOrigin::Signed(caller.clone());
534 <Contracts<T>>::map_account(origin.clone().into()).unwrap();
535 assert!(T::AddressMapper::is_mapped(&caller));
536 #[extrinsic_call]
537 _(origin);
538 assert!(!T::AddressMapper::is_mapped(&caller));
539 }
540
541 #[benchmark(pov_mode = Measured)]
542 fn dispatch_as_fallback_account() {
543 let caller = whitelisted_caller();
544 T::Currency::set_balance(&caller, caller_funding::<T>());
545 let origin = RawOrigin::Signed(caller.clone());
546 let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
547 #[extrinsic_call]
548 _(origin, Box::new(dispatchable));
549 }
550
551 #[benchmark(pov_mode = Measured)]
552 fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) {
553 let mut setup = CallSetup::<T>::new(VmBinaryModule::noop());
554 let (mut ext, module) = setup.ext();
555 let prepared = CallSetup::<T>::prepare_call(&mut ext, module, r.encode(), 0);
556 #[block]
557 {
558 prepared.call().unwrap();
559 }
560 }
561
562 #[benchmark(pov_mode = Measured)]
563 fn seal_caller() {
564 let len = H160::len_bytes();
565 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
566
567 let result;
568 #[block]
569 {
570 result = runtime.bench_caller(memory.as_mut_slice(), 0);
571 }
572
573 assert_ok!(result);
574 assert_eq!(
575 <H160 as Decode>::decode(&mut &memory[..]).unwrap(),
576 T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap())
577 );
578 }
579
580 #[benchmark(pov_mode = Measured)]
581 fn seal_origin() {
582 let len = H160::len_bytes();
583 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
584
585 let result;
586 #[block]
587 {
588 result = runtime.bench_origin(memory.as_mut_slice(), 0);
589 }
590
591 assert_ok!(result);
592 assert_eq!(
593 <H160 as Decode>::decode(&mut &memory[..]).unwrap(),
594 T::AddressMapper::to_address(&runtime.ext().origin().account_id().unwrap())
595 );
596 }
597
598 #[benchmark(pov_mode = Measured)]
599 fn to_account_id() {
600 let account_id = account("precompile_to_account_id", 0, 0);
603 let address = {
604 T::Currency::set_balance(&account_id, caller_funding::<T>());
605 T::AddressMapper::map(&account_id).unwrap();
606 T::AddressMapper::to_address(&account_id)
607 };
608
609 let input_bytes = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
610 input: address.0.into(),
611 })
612 .abi_encode();
613
614 let mut call_setup = CallSetup::<T>::default();
615 let (mut ext, _) = call_setup.ext();
616
617 let result;
618 #[block]
619 {
620 result = run_builtin_precompile(
621 &mut ext,
622 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
623 input_bytes,
624 );
625 }
626 let data = result.unwrap().data;
627 assert_ne!(
628 data.as_slice()[20..32],
629 [0xEE; 12],
630 "fallback suffix found where none should be"
631 );
632 assert_eq!(T::AccountId::decode(&mut data.as_slice()), Ok(account_id),);
633 }
634
635 #[benchmark(pov_mode = Measured)]
636 fn seal_code_hash() {
637 let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
638 let len = <sp_core::H256 as MaxEncodedLen>::max_encoded_len() as u32;
639 build_runtime!(runtime, memory: [vec![0u8; len as _], contract.account_id.encode(), ]);
640
641 let result;
642 #[block]
643 {
644 result = runtime.bench_code_hash(memory.as_mut_slice(), len, 0);
645 }
646
647 assert_ok!(result);
648 assert_eq!(
649 <sp_core::H256 as Decode>::decode(&mut &memory[..]).unwrap(),
650 contract.info().unwrap().code_hash
651 );
652 }
653
654 #[benchmark(pov_mode = Measured)]
655 fn seal_own_code_hash() {
656 let len = <sp_core::H256 as MaxEncodedLen>::max_encoded_len() as u32;
657 build_runtime!(runtime, contract, memory: [vec![0u8; len as _], ]);
658 let result;
659 #[block]
660 {
661 result = runtime.bench_own_code_hash(memory.as_mut_slice(), 0);
662 }
663
664 assert_ok!(result);
665 assert_eq!(
666 <sp_core::H256 as Decode>::decode(&mut &memory[..]).unwrap(),
667 contract.info().unwrap().code_hash
668 );
669 }
670
671 #[benchmark(pov_mode = Measured)]
672 fn seal_code_size() {
673 let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
674 build_runtime!(runtime, memory: [contract.address.encode(),]);
675
676 let result;
677 #[block]
678 {
679 result = runtime.bench_code_size(memory.as_mut_slice(), 0);
680 }
681
682 assert_eq!(result.unwrap(), VmBinaryModule::dummy().code.len() as u64);
683 }
684
685 #[benchmark(pov_mode = Measured)]
686 fn seal_caller_is_origin() {
687 build_runtime!(runtime, memory: []);
688
689 let result;
690 #[block]
691 {
692 result = runtime.bench_caller_is_origin(memory.as_mut_slice());
693 }
694 assert_eq!(result.unwrap(), 1u32);
695 }
696
697 #[benchmark(pov_mode = Measured)]
698 fn seal_caller_is_root() {
699 let mut setup = CallSetup::<T>::default();
700 setup.set_origin(Origin::Root);
701 let (mut ext, _) = setup.ext();
702 let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
703
704 let result;
705 #[block]
706 {
707 result = runtime.bench_caller_is_root([0u8; 0].as_mut_slice());
708 }
709 assert_eq!(result.unwrap(), 1u32);
710 }
711
712 #[benchmark(pov_mode = Measured)]
713 fn seal_address() {
714 let len = H160::len_bytes();
715 build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
716
717 let result;
718 #[block]
719 {
720 result = runtime.bench_address(memory.as_mut_slice(), 0);
721 }
722 assert_ok!(result);
723 assert_eq!(<H160 as Decode>::decode(&mut &memory[..]).unwrap(), runtime.ext().address());
724 }
725
726 #[benchmark(pov_mode = Measured)]
727 fn seal_weight_left() {
728 let len = 18u32;
730 assert!(<Weight as MaxEncodedLen>::max_encoded_len() as u32 != len);
731 build_runtime!(runtime, memory: [32u32.to_le_bytes(), vec![0u8; len as _], ]);
732
733 let result;
734 #[block]
735 {
736 result = runtime.bench_weight_left(memory.as_mut_slice(), 4, 0);
737 }
738 assert_ok!(result);
739 assert_eq!(
740 <Weight as Decode>::decode(&mut &memory[4..]).unwrap(),
741 runtime.ext().gas_meter().gas_left()
742 );
743 }
744
745 #[benchmark(pov_mode = Measured)]
746 fn seal_ref_time_left() {
747 build_runtime!(runtime, memory: [vec![], ]);
748
749 let result;
750 #[block]
751 {
752 result = runtime.bench_ref_time_left(memory.as_mut_slice());
753 }
754 assert_eq!(result.unwrap(), runtime.ext().gas_meter().gas_left().ref_time());
755 }
756
757 #[benchmark(pov_mode = Measured)]
758 fn seal_balance() {
759 build_runtime!(runtime, contract, memory: [[0u8;32], ]);
760 contract.set_balance(BalanceWithDust::new_unchecked::<T>(
761 Pallet::<T>::min_balance() * 2u32.into(),
762 42u32,
763 ));
764
765 let result;
766 #[block]
767 {
768 result = runtime.bench_balance(memory.as_mut_slice(), 0);
769 }
770 assert_ok!(result);
771 assert_eq!(
772 U256::from_little_endian(&memory[..]),
773 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
774 Pallet::<T>::min_balance(),
775 42
776 ))
777 );
778 }
779
780 #[benchmark(pov_mode = Measured)]
781 fn seal_balance_of() {
782 let len = <sp_core::U256 as MaxEncodedLen>::max_encoded_len();
783 let account = account::<T::AccountId>("target", 0, 0);
784 <T as Config>::AddressMapper::map_no_deposit(&account).unwrap();
785
786 let address = T::AddressMapper::to_address(&account);
787 let balance = Pallet::<T>::min_balance() * 2u32.into();
788 T::Currency::set_balance(&account, balance);
789 AccountInfoOf::<T>::insert(&address, AccountInfo { dust: 42, ..Default::default() });
790
791 build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]);
792
793 let result;
794 #[block]
795 {
796 result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0);
797 }
798
799 assert_ok!(result);
800 assert_eq!(
801 U256::from_little_endian(&memory[..len]),
802 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
803 Pallet::<T>::min_balance(),
804 42
805 ))
806 );
807 }
808
809 #[benchmark(pov_mode = Measured)]
810 fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
811 let len = n as usize;
812 let immutable_data = vec![1u8; len];
813
814 build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]);
815
816 <ImmutableDataOf<T>>::insert::<_, BoundedVec<_, _>>(
817 contract.address,
818 immutable_data.clone().try_into().unwrap(),
819 );
820
821 let result;
822 #[block]
823 {
824 result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32);
825 }
826
827 assert_ok!(result);
828 assert_eq!(&memory[0..4], (len as u32).encode());
829 assert_eq!(&memory[4..len + 4], &immutable_data);
830 }
831
832 #[benchmark(pov_mode = Measured)]
833 fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
834 let len = n as usize;
835 let mut memory = vec![1u8; len];
836 let mut setup = CallSetup::<T>::default();
837 let input = setup.data();
838 let (mut ext, _) = setup.ext();
839 ext.override_export(crate::exec::ExportedFunction::Constructor);
840
841 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
842
843 let result;
844 #[block]
845 {
846 result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n);
847 }
848
849 assert_ok!(result);
850 assert_eq!(&memory[..], &<ImmutableDataOf<T>>::get(setup.contract().address).unwrap()[..]);
851 }
852
853 #[benchmark(pov_mode = Measured)]
854 fn seal_value_transferred() {
855 build_runtime!(runtime, memory: [[0u8;32], ]);
856 let result;
857 #[block]
858 {
859 result = runtime.bench_value_transferred(memory.as_mut_slice(), 0);
860 }
861 assert_ok!(result);
862 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().value_transferred());
863 }
864
865 #[benchmark(pov_mode = Measured)]
866 fn seal_minimum_balance() {
867 build_runtime!(runtime, memory: [[0u8;32], ]);
868 let result;
869 #[block]
870 {
871 result = runtime.bench_minimum_balance(memory.as_mut_slice(), 0);
872 }
873 assert_ok!(result);
874 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().minimum_balance());
875 }
876
877 #[benchmark(pov_mode = Measured)]
878 fn seal_return_data_size() {
879 let mut setup = CallSetup::<T>::default();
880 let (mut ext, _) = setup.ext();
881 let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
882 let mut memory = memory!(vec![],);
883 *runtime.ext().last_frame_output_mut() =
884 ExecReturnValue { data: vec![42; 256], ..Default::default() };
885 let result;
886 #[block]
887 {
888 result = runtime.bench_return_data_size(memory.as_mut_slice());
889 }
890 assert_eq!(result.unwrap(), 256);
891 }
892
893 #[benchmark(pov_mode = Measured)]
894 fn seal_call_data_size() {
895 let mut setup = CallSetup::<T>::default();
896 let (mut ext, _) = setup.ext();
897 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 128 as usize]);
898 let mut memory = memory!(vec![0u8; 4],);
899 let result;
900 #[block]
901 {
902 result = runtime.bench_call_data_size(memory.as_mut_slice());
903 }
904 assert_eq!(result.unwrap(), 128);
905 }
906
907 #[benchmark(pov_mode = Measured)]
908 fn seal_gas_limit() {
909 build_runtime!(runtime, memory: []);
910 let result;
911 #[block]
912 {
913 result = runtime.bench_gas_limit(&mut memory);
914 }
915 assert_eq!(result.unwrap(), T::BlockWeights::get().max_block.ref_time());
916 }
917
918 #[benchmark(pov_mode = Measured)]
919 fn seal_gas_price() {
920 build_runtime!(runtime, memory: []);
921 let result;
922 #[block]
923 {
924 result = runtime.bench_gas_price(memory.as_mut_slice());
925 }
926 assert_eq!(result.unwrap(), u64::from(GAS_PRICE));
927 }
928
929 #[benchmark(pov_mode = Measured)]
930 fn seal_base_fee() {
931 build_runtime!(runtime, memory: [[1u8;32], ]);
932 let result;
933 #[block]
934 {
935 result = runtime.bench_base_fee(memory.as_mut_slice(), 0);
936 }
937 assert_ok!(result);
938 assert_eq!(U256::from_little_endian(&memory[..]), U256::zero());
939 }
940
941 #[benchmark(pov_mode = Measured)]
942 fn seal_block_number() {
943 build_runtime!(runtime, memory: [[0u8;32], ]);
944 let result;
945 #[block]
946 {
947 result = runtime.bench_block_number(memory.as_mut_slice(), 0);
948 }
949 assert_ok!(result);
950 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number());
951 }
952
953 #[benchmark(pov_mode = Measured)]
954 fn seal_block_author() {
955 build_runtime!(runtime, memory: [[123u8; 20], ]);
956
957 let mut digest = Digest::default();
958
959 for i in 0..16 {
963 digest.push(DigestItem::PreRuntime([i, i, i, i], vec![i; 128]));
964 digest.push(DigestItem::Consensus([i, i, i, i], vec![i; 128]));
965 digest.push(DigestItem::Seal([i, i, i, i], vec![i; 128]));
966 digest.push(DigestItem::Other(vec![i; 128]));
967 }
968
969 let primary_pre_digest = vec![0; <PrimaryPreDigest as MaxEncodedLen>::max_encoded_len()];
975 let pre_digest =
976 PreDigest::Primary(PrimaryPreDigest::decode(&mut &primary_pre_digest[..]).unwrap());
977 digest.push(DigestItem::PreRuntime(BABE_ENGINE_ID, pre_digest.encode()));
978 digest.push(DigestItem::Seal(BABE_ENGINE_ID, pre_digest.encode()));
979
980 let slot = Slot::default();
982 digest.push(DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode()));
983 digest.push(DigestItem::Seal(AURA_ENGINE_ID, slot.encode()));
984
985 frame_system::Pallet::<T>::initialize(
986 &BlockNumberFor::<T>::from(1u32),
987 &Default::default(),
988 &digest,
989 );
990
991 let result;
992 #[block]
993 {
994 result = runtime.bench_block_author(memory.as_mut_slice(), 0);
995 }
996 assert_ok!(result);
997
998 let block_author = runtime.ext().block_author().unwrap_or(H160::zero());
999 assert_eq!(&memory[..], block_author.as_bytes());
1000 }
1001
1002 #[benchmark(pov_mode = Measured)]
1003 fn seal_block_hash() {
1004 let mut memory = vec![0u8; 64];
1005 let mut setup = CallSetup::<T>::default();
1006 let input = setup.data();
1007 let (mut ext, _) = setup.ext();
1008 ext.set_block_number(BlockNumberFor::<T>::from(1u32));
1009
1010 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
1011
1012 let block_hash = H256::from([1; 32]);
1013 frame_system::BlockHash::<T>::insert(
1014 &BlockNumberFor::<T>::from(0u32),
1015 T::Hash::from(block_hash),
1016 );
1017
1018 let result;
1019 #[block]
1020 {
1021 result = runtime.bench_block_hash(memory.as_mut_slice(), 32, 0);
1022 }
1023 assert_ok!(result);
1024 assert_eq!(&memory[..32], &block_hash.0);
1025 }
1026
1027 #[benchmark(pov_mode = Measured)]
1028 fn seal_now() {
1029 build_runtime!(runtime, memory: [[0u8;32], ]);
1030 let result;
1031 #[block]
1032 {
1033 result = runtime.bench_now(memory.as_mut_slice(), 0);
1034 }
1035 assert_ok!(result);
1036 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now());
1037 }
1038
1039 #[benchmark(pov_mode = Measured)]
1040 fn seal_weight_to_fee() {
1041 build_runtime!(runtime, memory: [[0u8;32], ]);
1042 let weight = Weight::from_parts(500_000, 300_000);
1043 let result;
1044 #[block]
1045 {
1046 result = runtime.bench_weight_to_fee(
1047 memory.as_mut_slice(),
1048 weight.ref_time(),
1049 weight.proof_size(),
1050 0,
1051 );
1052 }
1053 assert_ok!(result);
1054 assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().get_weight_price(weight));
1055 }
1056
1057 #[benchmark(pov_mode = Measured)]
1058 fn seal_copy_to_contract(n: Linear<0, { limits::code::BLOB_BYTES - 4 }>) {
1059 let mut setup = CallSetup::<T>::default();
1060 let (mut ext, _) = setup.ext();
1061 let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
1062 let mut memory = memory!(n.encode(), vec![0u8; n as usize],);
1063 let result;
1064 #[block]
1065 {
1066 result = runtime.write_sandbox_output(
1067 memory.as_mut_slice(),
1068 4,
1069 0,
1070 &vec![42u8; n as usize],
1071 false,
1072 |_| None,
1073 );
1074 }
1075 assert_ok!(result);
1076 assert_eq!(&memory[..4], &n.encode());
1077 assert_eq!(&memory[4..], &vec![42u8; n as usize]);
1078 }
1079
1080 #[benchmark(pov_mode = Measured)]
1081 fn seal_call_data_load() {
1082 let mut setup = CallSetup::<T>::default();
1083 let (mut ext, _) = setup.ext();
1084 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 32]);
1085 let mut memory = memory!(vec![0u8; 32],);
1086 let result;
1087 #[block]
1088 {
1089 result = runtime.bench_call_data_load(memory.as_mut_slice(), 0, 0);
1090 }
1091 assert_ok!(result);
1092 assert_eq!(&memory[..], &vec![42u8; 32]);
1093 }
1094
1095 #[benchmark(pov_mode = Measured)]
1096 fn seal_call_data_copy(n: Linear<0, { limits::code::BLOB_BYTES }>) {
1097 let mut setup = CallSetup::<T>::default();
1098 let (mut ext, _) = setup.ext();
1099 let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; n as usize]);
1100 let mut memory = memory!(vec![0u8; n as usize],);
1101 let result;
1102 #[block]
1103 {
1104 result = runtime.bench_call_data_copy(memory.as_mut_slice(), 0, n, 0);
1105 }
1106 assert_ok!(result);
1107 assert_eq!(&memory[..], &vec![42u8; n as usize]);
1108 }
1109
1110 #[benchmark(pov_mode = Measured)]
1111 fn seal_return(n: Linear<0, { limits::CALLDATA_BYTES }>) {
1112 build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]);
1113
1114 let result;
1115 #[block]
1116 {
1117 result = runtime.bench_seal_return(memory.as_mut_slice(), 0, 0, n);
1118 }
1119
1120 assert!(matches!(
1121 result,
1122 Err(crate::vm::pvm::TrapReason::Return(crate::vm::pvm::ReturnData { .. }))
1123 ));
1124 }
1125
1126 #[benchmark(pov_mode = Measured)]
1130 fn seal_terminate(r: Linear<0, 1>) -> Result<(), BenchmarkError> {
1131 let delete_code = r == 1;
1132 let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1133
1134 build_runtime!(runtime, instance, memory: [beneficiary.encode(),]);
1135 let code_hash = instance.info()?.code_hash;
1136
1137 if !delete_code {
1139 <CodeInfo<T>>::increment_refcount(code_hash).unwrap();
1140 }
1141
1142 let result;
1143 #[block]
1144 {
1145 result = runtime.bench_terminate(memory.as_mut_slice(), 0);
1146 }
1147
1148 assert!(matches!(result, Err(crate::vm::pvm::TrapReason::Termination)));
1149 assert_eq!(PristineCode::<T>::get(code_hash).is_none(), delete_code);
1150
1151 Ok(())
1152 }
1153
1154 #[benchmark(pov_mode = Measured)]
1158 fn seal_deposit_event(
1159 t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>,
1160 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1161 ) {
1162 let num_topic = t as u32;
1163 let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::<Vec<_>>();
1164 let topics_data =
1165 topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::<Vec<u8>>();
1166 let data = vec![42u8; n as _];
1167 build_runtime!(runtime, instance, memory: [ topics_data, data, ]);
1168
1169 let result;
1170 #[block]
1171 {
1172 result = runtime.bench_deposit_event(
1173 memory.as_mut_slice(),
1174 0, num_topic,
1176 topics_data.len() as u32, n, );
1179 }
1180 assert_ok!(result);
1181
1182 let events = System::<T>::events();
1183 let record = &events[events.len() - 1];
1184
1185 assert_eq!(
1186 record.event,
1187 crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(),
1188 );
1189 }
1190
1191 #[benchmark(skip_meta, pov_mode = Measured)]
1192 fn get_storage_empty() -> Result<(), BenchmarkError> {
1193 let max_key_len = limits::STORAGE_KEY_BYTES;
1194 let key = vec![0u8; max_key_len as usize];
1195 let max_value_len = limits::PAYLOAD_BYTES as usize;
1196 let value = vec![1u8; max_value_len];
1197
1198 let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1199 let info = instance.info()?;
1200 let child_trie_info = info.child_trie_info();
1201 info.bench_write_raw(&key, Some(value.clone()), false)
1202 .map_err(|_| "Failed to write to storage during setup.")?;
1203
1204 let result;
1205 #[block]
1206 {
1207 result = child::get_raw(&child_trie_info, &key);
1208 }
1209
1210 assert_eq!(result, Some(value));
1211 Ok(())
1212 }
1213
1214 #[benchmark(skip_meta, pov_mode = Measured)]
1215 fn get_storage_full() -> Result<(), BenchmarkError> {
1216 let max_key_len = limits::STORAGE_KEY_BYTES;
1217 let key = vec![0u8; max_key_len as usize];
1218 let max_value_len = limits::PAYLOAD_BYTES;
1219 let value = vec![1u8; max_value_len as usize];
1220
1221 let instance = Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?;
1222 let info = instance.info()?;
1223 let child_trie_info = info.child_trie_info();
1224 info.bench_write_raw(&key, Some(value.clone()), false)
1225 .map_err(|_| "Failed to write to storage during setup.")?;
1226
1227 let result;
1228 #[block]
1229 {
1230 result = child::get_raw(&child_trie_info, &key);
1231 }
1232
1233 assert_eq!(result, Some(value));
1234 Ok(())
1235 }
1236
1237 #[benchmark(skip_meta, pov_mode = Measured)]
1238 fn set_storage_empty() -> Result<(), BenchmarkError> {
1239 let max_key_len = limits::STORAGE_KEY_BYTES;
1240 let key = vec![0u8; max_key_len as usize];
1241 let max_value_len = limits::PAYLOAD_BYTES as usize;
1242 let value = vec![1u8; max_value_len];
1243
1244 let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1245 let info = instance.info()?;
1246 let child_trie_info = info.child_trie_info();
1247 info.bench_write_raw(&key, Some(vec![42u8; max_value_len]), false)
1248 .map_err(|_| "Failed to write to storage during setup.")?;
1249
1250 let val = Some(value.clone());
1251 let result;
1252 #[block]
1253 {
1254 result = info.bench_write_raw(&key, val, true);
1255 }
1256
1257 assert_ok!(result);
1258 assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
1259 Ok(())
1260 }
1261
1262 #[benchmark(skip_meta, pov_mode = Measured)]
1263 fn set_storage_full() -> Result<(), BenchmarkError> {
1264 let max_key_len = limits::STORAGE_KEY_BYTES;
1265 let key = vec![0u8; max_key_len as usize];
1266 let max_value_len = limits::PAYLOAD_BYTES;
1267 let value = vec![1u8; max_value_len as usize];
1268
1269 let instance = Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?;
1270 let info = instance.info()?;
1271 let child_trie_info = info.child_trie_info();
1272 info.bench_write_raw(&key, Some(vec![42u8; max_value_len as usize]), false)
1273 .map_err(|_| "Failed to write to storage during setup.")?;
1274
1275 let val = Some(value.clone());
1276 let result;
1277 #[block]
1278 {
1279 result = info.bench_write_raw(&key, val, true);
1280 }
1281
1282 assert_ok!(result);
1283 assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
1284 Ok(())
1285 }
1286
1287 #[benchmark(skip_meta, pov_mode = Measured)]
1290 fn seal_set_storage(
1291 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1292 o: Linear<0, { limits::PAYLOAD_BYTES }>,
1293 ) -> Result<(), BenchmarkError> {
1294 let max_key_len = limits::STORAGE_KEY_BYTES;
1295 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1296 .map_err(|_| "Key has wrong length")?;
1297 let value = vec![1u8; n as usize];
1298
1299 build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1300 let info = instance.info()?;
1301
1302 info.write(&key, Some(vec![42u8; o as usize]), None, false)
1303 .map_err(|_| "Failed to write to storage during setup.")?;
1304
1305 let result;
1306 #[block]
1307 {
1308 result = runtime.bench_set_storage(
1309 memory.as_mut_slice(),
1310 StorageFlags::empty().bits(),
1311 0, max_key_len, max_key_len, n, );
1316 }
1317
1318 assert_ok!(result);
1319 assert_eq!(info.read(&key).unwrap(), value);
1320 Ok(())
1321 }
1322
1323 #[benchmark(skip_meta, pov_mode = Measured)]
1324 fn seal_clear_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> {
1325 let max_key_len = limits::STORAGE_KEY_BYTES;
1326 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1327 .map_err(|_| "Key has wrong length")?;
1328 build_runtime!(runtime, instance, memory: [ key.unhashed(), ]);
1329 let info = instance.info()?;
1330
1331 info.write(&key, Some(vec![42u8; n as usize]), None, false)
1332 .map_err(|_| "Failed to write to storage during setup.")?;
1333
1334 let result;
1335 #[block]
1336 {
1337 result = runtime.bench_clear_storage(
1338 memory.as_mut_slice(),
1339 StorageFlags::empty().bits(),
1340 0,
1341 max_key_len,
1342 );
1343 }
1344
1345 assert_ok!(result);
1346 assert!(info.read(&key).is_none());
1347 Ok(())
1348 }
1349
1350 #[benchmark(skip_meta, pov_mode = Measured)]
1351 fn seal_get_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> {
1352 let max_key_len = limits::STORAGE_KEY_BYTES;
1353 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1354 .map_err(|_| "Key has wrong length")?;
1355 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1356 let info = instance.info()?;
1357
1358 info.write(&key, Some(vec![42u8; n as usize]), None, false)
1359 .map_err(|_| "Failed to write to storage during setup.")?;
1360
1361 let out_ptr = max_key_len + 4;
1362 let result;
1363 #[block]
1364 {
1365 result = runtime.bench_get_storage(
1366 memory.as_mut_slice(),
1367 StorageFlags::empty().bits(),
1368 0, max_key_len, out_ptr, max_key_len, );
1373 }
1374
1375 assert_ok!(result);
1376 assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
1377 Ok(())
1378 }
1379
1380 #[benchmark(skip_meta, pov_mode = Measured)]
1381 fn seal_contains_storage(
1382 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1383 ) -> Result<(), BenchmarkError> {
1384 let max_key_len = limits::STORAGE_KEY_BYTES;
1385 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1386 .map_err(|_| "Key has wrong length")?;
1387 build_runtime!(runtime, instance, memory: [ key.unhashed(), ]);
1388 let info = instance.info()?;
1389
1390 info.write(&key, Some(vec![42u8; n as usize]), None, false)
1391 .map_err(|_| "Failed to write to storage during setup.")?;
1392
1393 let result;
1394 #[block]
1395 {
1396 result = runtime.bench_contains_storage(
1397 memory.as_mut_slice(),
1398 StorageFlags::empty().bits(),
1399 0,
1400 max_key_len,
1401 );
1402 }
1403
1404 assert_eq!(result.unwrap(), n);
1405 Ok(())
1406 }
1407
1408 #[benchmark(skip_meta, pov_mode = Measured)]
1409 fn seal_take_storage(n: Linear<0, { limits::PAYLOAD_BYTES }>) -> Result<(), BenchmarkError> {
1410 let max_key_len = limits::STORAGE_KEY_BYTES;
1411 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1412 .map_err(|_| "Key has wrong length")?;
1413 build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1414 let info = instance.info()?;
1415
1416 let value = vec![42u8; n as usize];
1417 info.write(&key, Some(value.clone()), None, false)
1418 .map_err(|_| "Failed to write to storage during setup.")?;
1419
1420 let out_ptr = max_key_len + 4;
1421 let result;
1422 #[block]
1423 {
1424 result = runtime.bench_take_storage(
1425 memory.as_mut_slice(),
1426 StorageFlags::empty().bits(),
1427 0, max_key_len, out_ptr, max_key_len, );
1432 }
1433
1434 assert_ok!(result);
1435 assert!(&info.read(&key).is_none());
1436 assert_eq!(&value, &memory[out_ptr as usize..]);
1437 Ok(())
1438 }
1439
1440 #[benchmark(pov_mode = Ignored)]
1445 fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
1446 let max_value_len = limits::PAYLOAD_BYTES;
1447 let max_key_len = limits::STORAGE_KEY_BYTES;
1448 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1449 .map_err(|_| "Key has wrong length")?;
1450 let value = Some(vec![42u8; max_value_len as _]);
1451 let mut setup = CallSetup::<T>::default();
1452 let (mut ext, _) = setup.ext();
1453 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1454 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1455 let result;
1456 #[block]
1457 {
1458 result = runtime.ext().set_transient_storage(&key, value, false);
1459 }
1460
1461 assert_eq!(result, Ok(WriteOutcome::New));
1462 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
1463 Ok(())
1464 }
1465
1466 #[benchmark(pov_mode = Ignored)]
1467 fn set_transient_storage_full() -> Result<(), BenchmarkError> {
1468 let max_value_len = limits::PAYLOAD_BYTES;
1469 let max_key_len = limits::STORAGE_KEY_BYTES;
1470 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1471 .map_err(|_| "Key has wrong length")?;
1472 let value = Some(vec![42u8; max_value_len as _]);
1473 let mut setup = CallSetup::<T>::default();
1474 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
1475 let (mut ext, _) = setup.ext();
1476 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1477 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1478 let result;
1479 #[block]
1480 {
1481 result = runtime.ext().set_transient_storage(&key, value, false);
1482 }
1483
1484 assert_eq!(result, Ok(WriteOutcome::New));
1485 assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
1486 Ok(())
1487 }
1488
1489 #[benchmark(pov_mode = Ignored)]
1490 fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
1491 let max_value_len = limits::PAYLOAD_BYTES;
1492 let max_key_len = limits::STORAGE_KEY_BYTES;
1493 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1494 .map_err(|_| "Key has wrong length")?;
1495
1496 let mut setup = CallSetup::<T>::default();
1497 let (mut ext, _) = setup.ext();
1498 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1499 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1500 runtime
1501 .ext()
1502 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1503 .map_err(|_| "Failed to write to transient storage during setup.")?;
1504 let result;
1505 #[block]
1506 {
1507 result = runtime.ext().get_transient_storage(&key);
1508 }
1509
1510 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
1511 Ok(())
1512 }
1513
1514 #[benchmark(pov_mode = Ignored)]
1515 fn get_transient_storage_full() -> Result<(), BenchmarkError> {
1516 let max_value_len = limits::PAYLOAD_BYTES;
1517 let max_key_len = limits::STORAGE_KEY_BYTES;
1518 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1519 .map_err(|_| "Key has wrong length")?;
1520
1521 let mut setup = CallSetup::<T>::default();
1522 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
1523 let (mut ext, _) = setup.ext();
1524 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1525 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1526 runtime
1527 .ext()
1528 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1529 .map_err(|_| "Failed to write to transient storage during setup.")?;
1530 let result;
1531 #[block]
1532 {
1533 result = runtime.ext().get_transient_storage(&key);
1534 }
1535
1536 assert_eq!(result, Some(vec![42u8; max_value_len as _]));
1537 Ok(())
1538 }
1539
1540 #[benchmark(pov_mode = Ignored)]
1542 fn rollback_transient_storage() -> Result<(), BenchmarkError> {
1543 let max_value_len = limits::PAYLOAD_BYTES;
1544 let max_key_len = limits::STORAGE_KEY_BYTES;
1545 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1546 .map_err(|_| "Key has wrong length")?;
1547
1548 let mut setup = CallSetup::<T>::default();
1549 setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
1550 let (mut ext, _) = setup.ext();
1551 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1552 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1553 runtime.ext().transient_storage().start_transaction();
1554 runtime
1555 .ext()
1556 .set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1557 .map_err(|_| "Failed to write to transient storage during setup.")?;
1558 #[block]
1559 {
1560 runtime.ext().transient_storage().rollback_transaction();
1561 }
1562
1563 assert_eq!(runtime.ext().get_transient_storage(&key), None);
1564 Ok(())
1565 }
1566
1567 #[benchmark(pov_mode = Measured)]
1570 fn seal_set_transient_storage(
1571 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1572 o: Linear<0, { limits::PAYLOAD_BYTES }>,
1573 ) -> Result<(), BenchmarkError> {
1574 let max_key_len = limits::STORAGE_KEY_BYTES;
1575 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1576 .map_err(|_| "Key has wrong length")?;
1577 let value = vec![1u8; n as usize];
1578 build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]);
1579 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1580 runtime
1581 .ext()
1582 .set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
1583 .map_err(|_| "Failed to write to transient storage during setup.")?;
1584
1585 let result;
1586 #[block]
1587 {
1588 result = runtime.bench_set_storage(
1589 memory.as_mut_slice(),
1590 StorageFlags::TRANSIENT.bits(),
1591 0, max_key_len, max_key_len, n, );
1596 }
1597
1598 assert_ok!(result);
1599 assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
1600 Ok(())
1601 }
1602
1603 #[benchmark(pov_mode = Measured)]
1604 fn seal_clear_transient_storage(
1605 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1606 ) -> Result<(), BenchmarkError> {
1607 let max_key_len = limits::STORAGE_KEY_BYTES;
1608 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1609 .map_err(|_| "Key has wrong length")?;
1610 build_runtime!(runtime, memory: [ key.unhashed(), ]);
1611 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1612 runtime
1613 .ext()
1614 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1615 .map_err(|_| "Failed to write to transient storage during setup.")?;
1616
1617 let result;
1618 #[block]
1619 {
1620 result = runtime.bench_clear_storage(
1621 memory.as_mut_slice(),
1622 StorageFlags::TRANSIENT.bits(),
1623 0,
1624 max_key_len,
1625 );
1626 }
1627
1628 assert_ok!(result);
1629 assert!(runtime.ext().get_transient_storage(&key).is_none());
1630 Ok(())
1631 }
1632
1633 #[benchmark(pov_mode = Measured)]
1634 fn seal_get_transient_storage(
1635 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1636 ) -> Result<(), BenchmarkError> {
1637 let max_key_len = limits::STORAGE_KEY_BYTES;
1638 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1639 .map_err(|_| "Key has wrong length")?;
1640 build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1641 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1642 runtime
1643 .ext()
1644 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1645 .map_err(|_| "Failed to write to transient storage during setup.")?;
1646
1647 let out_ptr = max_key_len + 4;
1648 let result;
1649 #[block]
1650 {
1651 result = runtime.bench_get_storage(
1652 memory.as_mut_slice(),
1653 StorageFlags::TRANSIENT.bits(),
1654 0, max_key_len, out_ptr, max_key_len, );
1659 }
1660
1661 assert_ok!(result);
1662 assert_eq!(
1663 &runtime.ext().get_transient_storage(&key).unwrap(),
1664 &memory[out_ptr as usize..]
1665 );
1666 Ok(())
1667 }
1668
1669 #[benchmark(pov_mode = Measured)]
1670 fn seal_contains_transient_storage(
1671 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1672 ) -> Result<(), BenchmarkError> {
1673 let max_key_len = limits::STORAGE_KEY_BYTES;
1674 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1675 .map_err(|_| "Key has wrong length")?;
1676 build_runtime!(runtime, memory: [ key.unhashed(), ]);
1677 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1678 runtime
1679 .ext()
1680 .set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1681 .map_err(|_| "Failed to write to transient storage during setup.")?;
1682
1683 let result;
1684 #[block]
1685 {
1686 result = runtime.bench_contains_storage(
1687 memory.as_mut_slice(),
1688 StorageFlags::TRANSIENT.bits(),
1689 0,
1690 max_key_len,
1691 );
1692 }
1693
1694 assert_eq!(result.unwrap(), n);
1695 Ok(())
1696 }
1697
1698 #[benchmark(pov_mode = Measured)]
1699 fn seal_take_transient_storage(
1700 n: Linear<0, { limits::PAYLOAD_BYTES }>,
1701 ) -> Result<(), BenchmarkError> {
1702 let n = limits::PAYLOAD_BYTES;
1703 let max_key_len = limits::STORAGE_KEY_BYTES;
1704 let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1705 .map_err(|_| "Key has wrong length")?;
1706 build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1707 runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1708 let value = vec![42u8; n as usize];
1709 runtime
1710 .ext()
1711 .set_transient_storage(&key, Some(value.clone()), false)
1712 .map_err(|_| "Failed to write to transient storage during setup.")?;
1713
1714 let out_ptr = max_key_len + 4;
1715 let result;
1716 #[block]
1717 {
1718 result = runtime.bench_take_storage(
1719 memory.as_mut_slice(),
1720 StorageFlags::TRANSIENT.bits(),
1721 0, max_key_len, out_ptr, max_key_len, );
1726 }
1727
1728 assert_ok!(result);
1729 assert!(&runtime.ext().get_transient_storage(&key).is_none());
1730 assert_eq!(&value, &memory[out_ptr as usize..]);
1731 Ok(())
1732 }
1733
1734 #[benchmark(pov_mode = Measured)]
1738 fn seal_call(t: Linear<0, 1>, d: Linear<0, 1>, i: Linear<0, { limits::code::BLOB_BYTES }>) {
1739 let Contract { account_id: callee, address: callee_addr, .. } =
1740 Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
1741
1742 let callee_bytes = callee.encode();
1743 let callee_len = callee_bytes.len() as u32;
1744
1745 let value: BalanceOf<T> = (1_000_000u32 * t).into();
1746 let dust = 100u32 * d;
1747 let evm_value =
1748 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
1749 let value_bytes = evm_value.encode();
1750
1751 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
1752 let deposit_bytes = Into::<U256>::into(deposit).encode();
1753 let deposit_len = deposit_bytes.len() as u32;
1754
1755 let mut setup = CallSetup::<T>::default();
1756 setup.set_storage_deposit_limit(deposit);
1757 setup.set_data(vec![42; i as usize]);
1760 setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
1761 setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
1762
1763 let (mut ext, _) = setup.ext();
1764 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1765 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
1766
1767 let result;
1768 #[block]
1769 {
1770 result = runtime.bench_call(
1771 memory.as_mut_slice(),
1772 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), );
1779 }
1780
1781 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1782 assert_eq!(
1783 Pallet::<T>::evm_balance(&callee_addr),
1784 evm_value,
1785 "{callee_addr:?} balance should hold {evm_value:?}"
1786 );
1787 }
1788
1789 #[benchmark(pov_mode = Measured)]
1792 fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
1793 use alloy_core::sol_types::SolInterface;
1794 use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
1795
1796 let callee_bytes = if d == 1 {
1797 BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
1798 } else {
1799 BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
1800 };
1801 let callee_len = callee_bytes.len() as u32;
1802
1803 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
1804 let deposit_bytes = Into::<U256>::into(deposit).encode();
1805 let deposit_len = deposit_bytes.len() as u32;
1806
1807 let value: BalanceOf<T> = Zero::zero();
1808 let value_bytes = Into::<U256>::into(value).encode();
1809 let value_len = value_bytes.len() as u32;
1810
1811 let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
1812 input: vec![42_u8; i as usize].into(),
1813 })
1814 .abi_encode();
1815 let input_len = input_bytes.len() as u32;
1816
1817 let mut setup = CallSetup::<T>::default();
1818 setup.set_storage_deposit_limit(deposit);
1819
1820 let (mut ext, _) = setup.ext();
1821 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1822 let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
1823
1824 let mut do_benchmark = || {
1825 runtime.bench_call(
1826 memory.as_mut_slice(),
1827 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), )
1836 };
1837
1838 assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
1841
1842 let result;
1843 #[block]
1844 {
1845 result = do_benchmark();
1846 }
1847
1848 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1849 }
1850
1851 #[benchmark(pov_mode = Measured)]
1852 fn seal_delegate_call() -> Result<(), BenchmarkError> {
1853 let Contract { account_id: address, .. } =
1854 Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
1855
1856 let address_bytes = address.encode();
1857 let address_len = address_bytes.len() as u32;
1858
1859 let deposit: BalanceOf<T> = (u32::MAX - 100).into();
1860 let deposit_bytes = Into::<U256>::into(deposit).encode();
1861
1862 let mut setup = CallSetup::<T>::default();
1863 setup.set_storage_deposit_limit(deposit);
1864 setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
1865
1866 let (mut ext, _) = setup.ext();
1867 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1868 let mut memory = memory!(address_bytes, deposit_bytes,);
1869
1870 let result;
1871 #[block]
1872 {
1873 result = runtime.bench_delegate_call(
1874 memory.as_mut_slice(),
1875 pack_hi_lo(0, 0), u64::MAX, u64::MAX, address_len, pack_hi_lo(0, 0), pack_hi_lo(0, SENTINEL), );
1882 }
1883
1884 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1885 Ok(())
1886 }
1887
1888 #[benchmark(pov_mode = Measured)]
1892 fn seal_instantiate(
1893 t: Linear<0, 1>,
1894 d: Linear<0, 1>,
1895 i: Linear<0, { limits::CALLDATA_BYTES }>,
1896 ) -> Result<(), BenchmarkError> {
1897 let code = VmBinaryModule::dummy();
1898 let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
1899 let hash_bytes = hash.encode();
1900
1901 let value: BalanceOf<T> = (1_000_000u32 * t).into();
1902 let dust = 100u32 * d;
1903 let evm_value =
1904 Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
1905 let value_bytes = evm_value.encode();
1906 let value_len = value_bytes.len() as u32;
1907
1908 let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
1909 let deposit_bytes = Into::<U256>::into(deposit).encode();
1910 let deposit_len = deposit_bytes.len() as u32;
1911
1912 let mut setup = CallSetup::<T>::default();
1913 setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
1914 setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
1915
1916 let account_id = &setup.contract().account_id.clone();
1917 let (mut ext, _) = setup.ext();
1918 let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1919
1920 let input = vec![42u8; i as _];
1921 let input_len = hash_bytes.len() as u32 + input.len() as u32;
1922 let salt = [42u8; 32];
1923 let deployer = T::AddressMapper::to_address(&account_id);
1924 let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
1925 let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
1926
1927 let mut offset = {
1928 let mut current = 0u32;
1929 move |after: u32| {
1930 current += after;
1931 current
1932 }
1933 };
1934
1935 assert!(AccountInfoOf::<T>::get(&addr).is_none());
1936
1937 let result;
1938 #[block]
1939 {
1940 result = runtime.bench_instantiate(
1941 memory.as_mut_slice(),
1942 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)), );
1949 }
1950
1951 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1952 assert!(AccountInfo::<T>::load_contract(&addr).is_some());
1953
1954 assert_eq!(
1955 Pallet::<T>::evm_balance(&addr),
1956 evm_value,
1957 "{addr:?} balance should hold {evm_value:?}"
1958 );
1959 Ok(())
1960 }
1961
1962 #[benchmark(pov_mode = Measured)]
1964 fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
1965 let input = vec![0u8; n as usize];
1966 let mut call_setup = CallSetup::<T>::default();
1967 let (mut ext, _) = call_setup.ext();
1968
1969 let result;
1970 #[block]
1971 {
1972 result = run_builtin_precompile(
1973 &mut ext,
1974 H160::from_low_u64_be(2).as_fixed_bytes(),
1975 input.clone(),
1976 );
1977 }
1978 assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
1979 }
1980
1981 #[benchmark(pov_mode = Measured)]
1982 fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
1983 let input = vec![0u8; n as usize];
1984 let mut call_setup = CallSetup::<T>::default();
1985 let (mut ext, _) = call_setup.ext();
1986
1987 let result;
1988 #[block]
1989 {
1990 result = run_builtin_precompile(
1991 &mut ext,
1992 H160::from_low_u64_be(4).as_fixed_bytes(),
1993 input.clone(),
1994 );
1995 }
1996 assert_eq!(input, result.unwrap().data);
1997 }
1998
1999 #[benchmark(pov_mode = Measured)]
2001 fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2002 use ripemd::Digest;
2003 let input = vec![0u8; n as usize];
2004 let mut call_setup = CallSetup::<T>::default();
2005 let (mut ext, _) = call_setup.ext();
2006
2007 let result;
2008 #[block]
2009 {
2010 result = run_builtin_precompile(
2011 &mut ext,
2012 H160::from_low_u64_be(3).as_fixed_bytes(),
2013 input.clone(),
2014 );
2015 }
2016 let mut expected = [0u8; 32];
2017 expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
2018
2019 assert_eq!(expected.to_vec(), result.unwrap().data);
2020 }
2021
2022 #[benchmark(pov_mode = Measured)]
2024 fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2025 build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
2026
2027 let result;
2028 #[block]
2029 {
2030 result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
2031 }
2032 assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
2033 assert_ok!(result);
2034 }
2035
2036 #[benchmark(pov_mode = Measured)]
2038 fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2039 let input = vec![0u8; n as usize];
2040 let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
2041 input: input.clone().into(),
2042 })
2043 .abi_encode();
2044
2045 let mut call_setup = CallSetup::<T>::default();
2046 let (mut ext, _) = call_setup.ext();
2047
2048 let result;
2049 #[block]
2050 {
2051 result = run_builtin_precompile(
2052 &mut ext,
2053 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2054 input_bytes,
2055 );
2056 }
2057 assert_eq!(sp_io::hashing::blake2_256(&input).to_vec(), result.unwrap().data);
2058 }
2059
2060 #[benchmark(pov_mode = Measured)]
2062 fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2063 let input = vec![0u8; n as usize];
2064 let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
2065 input: input.clone().into(),
2066 })
2067 .abi_encode();
2068
2069 let mut call_setup = CallSetup::<T>::default();
2070 let (mut ext, _) = call_setup.ext();
2071
2072 let result;
2073 #[block]
2074 {
2075 result = run_builtin_precompile(
2076 &mut ext,
2077 H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2078 input_bytes,
2079 );
2080 }
2081 assert_eq!(sp_io::hashing::blake2_128(&input).to_vec(), result.unwrap().data);
2082 }
2083
2084 #[benchmark(pov_mode = Measured)]
2087 fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) {
2088 let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
2089 let message_len = message.len() as u32;
2090
2091 let key_type = sp_core::crypto::KeyTypeId(*b"code");
2092 let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
2093 let sig =
2094 sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
2095 let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
2096 let sig_len = sig.len() as u32;
2097
2098 build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
2099
2100 let result;
2101 #[block]
2102 {
2103 result = runtime.bench_sr25519_verify(
2104 memory.as_mut_slice(),
2105 0, sig_len, message_len, sig_len + pub_key.len() as u32, );
2110 }
2111
2112 assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2113 }
2114
2115 #[benchmark(pov_mode = Measured)]
2116 fn ecdsa_recover() {
2117 use hex_literal::hex;
2118 let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
2119 let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
2120 let mut call_setup = CallSetup::<T>::default();
2121 let (mut ext, _) = call_setup.ext();
2122
2123 let result;
2124
2125 #[block]
2126 {
2127 result =
2128 run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
2129 }
2130
2131 assert_eq!(result.unwrap().data, expected);
2132 }
2133
2134 #[benchmark(pov_mode = Measured)]
2135 fn bn128_add() {
2136 use hex_literal::hex;
2137 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
2138 let expected = hex!(
2139 "0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
2140 );
2141 let mut call_setup = CallSetup::<T>::default();
2142 let (mut ext, _) = call_setup.ext();
2143
2144 let result;
2145 #[block]
2146 {
2147 result =
2148 run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
2149 }
2150
2151 assert_eq!(result.unwrap().data, expected);
2152 }
2153
2154 #[benchmark(pov_mode = Measured)]
2155 fn bn128_mul() {
2156 use hex_literal::hex;
2157 let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
2158 let expected = hex!(
2159 "0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
2160 );
2161 let mut call_setup = CallSetup::<T>::default();
2162 let (mut ext, _) = call_setup.ext();
2163
2164 let result;
2165 #[block]
2166 {
2167 result =
2168 run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
2169 }
2170
2171 assert_eq!(result.unwrap().data, expected);
2172 }
2173
2174 #[benchmark(pov_mode = Measured)]
2176 fn bn128_pairing(n: Linear<0, { 20 }>) {
2177 fn generate_random_ecpairs(n: usize) -> Vec<u8> {
2178 use bn::{AffineG1, AffineG2, Fr, Group, G1, G2};
2179 use rand::SeedableRng;
2180 use rand_pcg::Pcg64;
2181 let mut rng = Pcg64::seed_from_u64(1);
2182
2183 let mut buffer = vec![0u8; n * 192];
2184
2185 let mut write = |element: &bn::Fq, offset: &mut usize| {
2186 element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
2187 *offset += 32
2188 };
2189
2190 for i in 0..n {
2191 let mut offset = i * 192;
2192 let scalar = Fr::random(&mut rng);
2193
2194 let g1 = G1::one() * scalar;
2195 let g2 = G2::one() * scalar;
2196 let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
2197 let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
2198
2199 write(&a.x(), &mut offset);
2200 write(&a.y(), &mut offset);
2201 write(&b.x().imaginary(), &mut offset);
2202 write(&b.x().real(), &mut offset);
2203 write(&b.y().imaginary(), &mut offset);
2204 write(&b.y().real(), &mut offset);
2205 }
2206
2207 buffer
2208 }
2209
2210 let input = generate_random_ecpairs(n as usize);
2211 let mut call_setup = CallSetup::<T>::default();
2212 let (mut ext, _) = call_setup.ext();
2213
2214 let result;
2215 #[block]
2216 {
2217 result =
2218 run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
2219 }
2220 assert_ok!(result);
2221 }
2222
2223 #[benchmark(pov_mode = Measured)]
2225 fn blake2f(n: Linear<0, 1200>) {
2226 use hex_literal::hex;
2227 let input = hex!(
2228 "48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
2229 );
2230 let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
2231 let mut call_setup = CallSetup::<T>::default();
2232 let (mut ext, _) = call_setup.ext();
2233
2234 let result;
2235 #[block]
2236 {
2237 result =
2238 run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
2239 }
2240 assert_ok!(result);
2241 }
2242
2243 #[benchmark(pov_mode = Measured)]
2247 fn seal_ecdsa_to_eth_address() {
2248 let key_type = sp_core::crypto::KeyTypeId(*b"code");
2249 let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
2250 build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
2251
2252 let result;
2253 #[block]
2254 {
2255 result = runtime.bench_ecdsa_to_eth_address(
2256 memory.as_mut_slice(),
2257 20, 0, );
2260 }
2261
2262 assert_ok!(result);
2263 assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
2264 }
2265
2266 #[benchmark(pov_mode = Measured)]
2270 fn seal_set_code_hash(r: Linear<0, 1>) -> Result<(), BenchmarkError> {
2271 let delete_old_code = r == 1;
2272 let code_hash = Contract::<T>::with_index(1, VmBinaryModule::sized(42), vec![])?
2273 .info()?
2274 .code_hash;
2275
2276 build_runtime!(runtime, instance, memory: [ code_hash.encode(),]);
2277 let old_code_hash = instance.info()?.code_hash;
2278
2279 if !delete_old_code {
2281 <CodeInfo<T>>::increment_refcount(old_code_hash).unwrap();
2282 }
2283
2284 let result;
2285 #[block]
2286 {
2287 result = runtime.bench_set_code_hash(memory.as_mut_slice(), 0);
2288 }
2289
2290 assert_ok!(result);
2291 assert_eq!(PristineCode::<T>::get(old_code_hash).is_none(), delete_old_code);
2292 Ok(())
2293 }
2294
2295 #[benchmark(pov_mode = Measured)]
2297 fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
2298 use crate::vm::evm;
2299 use revm::bytecode::Bytecode;
2300
2301 let module = VmBinaryModule::evm_noop(r);
2302 let inputs = evm::EVMInputs::new(vec![]);
2303
2304 let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
2305 let mut setup = CallSetup::<T>::new(module);
2306 let (mut ext, _) = setup.ext();
2307
2308 let result;
2309 #[block]
2310 {
2311 result = evm::call(code, &mut ext, inputs);
2312 }
2313
2314 assert!(result.is_ok());
2315 Ok(())
2316 }
2317
2318 #[benchmark(pov_mode = Ignored)]
2323 fn instr(r: Linear<0, 10_000>) {
2324 use rand::{seq::SliceRandom, SeedableRng};
2325 use rand_pcg::Pcg64;
2326
2327 const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
2329
2330 const CACHE_LINE_SIZE: u64 = 64;
2332
2333 const MISALIGNMENT: u64 = 60;
2335
2336 const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
2339
2340 assert!(
2341 u64::from(r) <= NUM_ADDRESSES / 2,
2342 "If we do too many iterations we run into the risk of loading from warm cache lines",
2343 );
2344
2345 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
2346 let (mut ext, module) = setup.ext();
2347 let mut prepared =
2348 CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
2349
2350 assert!(
2351 u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
2352 "aux data base must be cache aligned"
2353 );
2354
2355 let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
2357
2358 let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
2362 for i in 1..NUM_ADDRESSES {
2363 let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
2364 addresses.push(addr);
2365 }
2366 let mut rng = Pcg64::seed_from_u64(1337);
2367 addresses.shuffle(&mut rng);
2368
2369 let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
2371 for address in addresses {
2372 memory.extend_from_slice(&address);
2373 memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
2374 }
2375
2376 prepared
2379 .setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
2380 .unwrap();
2381
2382 #[block]
2383 {
2384 prepared.call().unwrap();
2385 }
2386 }
2387
2388 #[benchmark(pov_mode = Ignored)]
2389 fn instr_empty_loop(r: Linear<0, 100_000>) {
2390 let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
2391 let (mut ext, module) = setup.ext();
2392 let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
2393 prepared.setup_aux_data(&[], 0, r.into()).unwrap();
2394
2395 #[block]
2396 {
2397 prepared.call().unwrap();
2398 }
2399 }
2400
2401 #[benchmark]
2402 fn v1_migration_step() {
2403 use crate::migrations::v1;
2404 let addr = H160::from([1u8; 20]);
2405 let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
2406
2407 v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
2408 let mut meter = WeightMeter::new();
2409 assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
2410
2411 #[block]
2412 {
2413 v1::Migration::<T>::step(None, &mut meter).unwrap();
2414 }
2415
2416 assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
2417 assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
2418
2419 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
2421 }
2422
2423 #[benchmark]
2424 fn v2_migration_step() {
2425 use crate::migrations::v2;
2426 let code_hash = H256::from([0; 32]);
2427 let old_code_info = v2::Migration::<T>::create_old_code_info(
2428 whitelisted_caller(),
2429 1000u32.into(),
2430 1,
2431 100,
2432 0,
2433 );
2434 v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
2435 let mut meter = WeightMeter::new();
2436
2437 #[block]
2438 {
2439 v2::Migration::<T>::step(None, &mut meter).unwrap();
2440 }
2441
2442 v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
2443
2444 assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
2446 }
2447
2448 impl_benchmark_test_suite!(
2449 Contracts,
2450 crate::tests::ExtBuilder::default().build(),
2451 crate::tests::Test,
2452 );
2453}