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