referrerpolicy=no-referrer-when-downgrade

pallet_revive/
benchmarking.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Benchmarks for the revive pallet.
19
20#![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
57/// How many runs we do per API benchmark.
58///
59/// This is picked more or less arbitrary. We experimented with different numbers until
60/// the results appeared to be stable. Reducing the number would speed up the benchmarks
61/// but might make the results less precise.
62const 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
87/// Get the pallet account and whitelist it for benchmarking.
88/// The account is warmed up `on_initialize` so read should not impact the PoV.
89fn 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	// The base weight consumed on processing contracts deletion queue.
108	#[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	// This benchmarks the overhead of loading a code of size `c` byte from storage and into
131	// the execution engine.
132	//
133	// `call_with_pvm_code_per_byte(c) - call_with_pvm_code_per_byte(0)`
134	//
135	// This does **not** include the actual execution for which the gas meter
136	// is responsible. The code used here will just return on call.
137	//
138	// We expect the influence of `c` to be none in this benchmark because every instruction that
139	// is not in the first basic block is never read. We are primarily interested in the
140	// `proof_size` result of this benchmark.
141	#[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	// This benchmarks the overhead of loading a code of size `c` byte from storage and into
162	// the execution engine.
163	/// This is similar to `call_with_pvm_code_per_byte` but for EVM bytecode.
164	#[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	// Measure the amount of time it takes to compile a single basic block.
185	//
186	// (basic_block_compilation(1) - basic_block_compilation(0)).ref_time()
187	//
188	// This is needed because the interpreter will always compile a whole basic block at
189	// a time. To prevent a contract from triggering compilation without doing any execution
190	// we will always charge one max sized block per contract call.
191	//
192	// We ignore the proof size component when using this benchmark as this is already accounted
193	// for in `call_with_pvm_code_per_byte`.
194	#[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	// `c`: Size of the code in bytes.
220	// `i`: Size of the input in bytes.
221	#[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		// uploading the code reserves some balance in the pallet's account
245		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		// contract has the full value
259		assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
260	}
261
262	// `c`: Size of the code in bytes.
263	// `i`: Size of the input in bytes.
264	// `d`: with or without dust value to transfer
265	#[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		// uploading the code reserves some balance in the pallet account
298		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		// contract has the full value
317		assert_eq!(Pallet::<T>::evm_balance(&addr), evm_value);
318	}
319
320	// `i`: Size of the input in bytes.
321	// `s`: Size of e salt in bytes.
322	#[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		// value was removed from the caller
352		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		// contract has the full value
360		assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
361
362		Ok(())
363	}
364
365	// We just call a dummy contract to measure the overhead of the call extrinsic.
366	// The size of the data has no influence on the costs of this extrinsic as long as the contract
367	// won't call `seal_call_data_copy` in its constructor to copy the data to contract memory.
368	// The dummy contract used here does not do this. The costs for the data copy is billed as
369	// part of `seal_call_data_copy`. The costs for invoking a contract of a specific size are not
370	// part of this benchmark because we cannot know the size of the contract when issuing a call
371	// transaction. See `call_with_pvm_code_per_byte` for this.
372	#[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		// value and value transferred via call should be removed from the caller
395		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		// contract should have received the value
403		assert_eq!(T::Currency::balance(&instance.account_id), before + value);
404		// contract should still exist
405		instance.info()?;
406
407		Ok(())
408	}
409
410	// `d`: with or without dust value to transfer
411	#[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		// value and value transferred via call should be removed from the caller
440		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		// contract should have received the value
452		assert_eq!(Pallet::<T>::evm_balance(&instance.address), before + evm_value);
453		// contract should still exist
454		instance.info()?;
455
456		Ok(())
457	}
458
459	// This constructs a contract that is maximal expensive to instrument.
460	// It creates a maximum number of metering blocks per byte.
461	// `c`: Size of the code in bytes.
462	#[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		// uploading the code reserves some balance in the pallet's account
473		assert!(T::Currency::total_balance_on_hold(&pallet_account) > 0u32.into());
474		assert!(<Contract<T>>::code_exists(&hash));
475	}
476
477	// Removing code does not depend on the size of the contract because all the information
478	// needed to verify the removal claim (refcount, owner) is stored in a separate storage
479	// item (`CodeInfoOf`).
480	#[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		// removing the code should have unreserved the deposit
496		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		// we just add some bytes so that the code hash is different
506		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		// use a mapped address for the benchmark, to ensure that we bench the worst
601		// case (and not the fallback case).
602		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		// use correct max_encoded_len when new version of parity-scale-codec is released
729		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		// The pre-runtime digest log is unbounded; usually around 3 items but it can vary.
960		// To get safe benchmark results despite that, populate it with a bunch of random logs to
961		// ensure iteration over many items (we just overestimate the cost of the API).
962		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		// The content of the pre-runtime digest log depends on the configured consensus.
970		// However, mismatching logs are simply ignored. Thus we construct fixtures which will
971		// let the API to return a value in both BABE and AURA consensus.
972
973		// Construct a `Digest` log fixture returning some value in BABE
974		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		// Construct a `Digest` log fixture returning some value in AURA
981		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 the ocst of terminating a contract.
1127	///
1128	/// `r`: whether the old code will be removed as a result of this operation. (1: yes, 0: no)
1129	#[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		// Increment the refcount of the code hash so that it does not get deleted
1138		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 the overhead that topics generate.
1155	// `t`: Number of topics
1156	// `n`: Size of event payload in bytes
1157	#[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, // topics_ptr
1175				num_topic,
1176				topics_data.len() as u32, // data_ptr
1177				n,                        // data_len
1178			);
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	// n: new byte size
1288	// o: old byte size
1289	#[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,           // key_ptr
1312				max_key_len, // key_len
1313				max_key_len, // value_ptr
1314				n,           // value_len
1315			);
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,           // key_ptr
1369				max_key_len, // key_len
1370				out_ptr,     // out_ptr
1371				max_key_len, // out_len_ptr
1372			);
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,           // key_ptr
1428				max_key_len, // key_len
1429				out_ptr,     // out_ptr
1430				max_key_len, // out_len_ptr
1431			);
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	// We use both full and empty benchmarks here instead of benchmarking transient_storage
1441	// (BTreeMap) directly. This approach is necessary because benchmarking this BTreeMap is very
1442	// slow. Additionally, we use linear regression for our benchmarks, and the BTreeMap's log(n)
1443	// complexity can introduce approximation errors.
1444	#[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	// The weight of journal rollbacks should be taken into account when setting storage.
1541	#[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	// n: new byte size
1568	// o: old byte size
1569	#[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,           // key_ptr
1592				max_key_len, // key_len
1593				max_key_len, // value_ptr
1594				n,           // value_len
1595			);
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,           // key_ptr
1655				max_key_len, // key_len
1656				out_ptr,     // out_ptr
1657				max_key_len, // out_len_ptr
1658			);
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,           // key_ptr
1722				max_key_len, // key_len
1723				out_ptr,     // out_ptr
1724				max_key_len, // out_len_ptr
1725			);
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	// t: with or without some value to transfer
1735	// d: with or without dust value to transfer
1736	// i: size of the input data
1737	#[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		// We benchmark the overhead of cloning the input. Not passing it to the contract.
1758		// This is why we set the input here instead of passig it as pointer to the `bench_call`.
1759		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), // flags + callee
1773				u64::MAX,                                     // ref_time_limit
1774				u64::MAX,                                     // proof_size_limit
1775				pack_hi_lo(callee_len, callee_len + deposit_len), // deposit_ptr + value_pr
1776				pack_hi_lo(0, 0),                             // input len + data ptr
1777				pack_hi_lo(0, SENTINEL),                      // output len + data ptr
1778			);
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	// d: 1 if the associated pre-compile has a contract info that needs to be loaded
1790	// i: size of the input data
1791	#[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), // flags + callee
1828				u64::MAX,         // ref_time_limit
1829				u64::MAX,         // proof_size_limit
1830				pack_hi_lo(callee_len, callee_len + deposit_len), /* deposit_ptr +
1831				                   * value_pr */
1832				pack_hi_lo(input_len, callee_len + deposit_len + value_len), /* input len +
1833				                                                              * input ptr */
1834				pack_hi_lo(0, SENTINEL), // output len + output ptr
1835			)
1836		};
1837
1838		// first call of the pre-compile will create its contract info and account
1839		// so we make sure to create it
1840		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),        // flags + address ptr
1876				u64::MAX,                // ref_time_limit
1877				u64::MAX,                // proof_size_limit
1878				address_len,             // deposit_ptr
1879				pack_hi_lo(0, 0),        // input len + data ptr
1880				pack_hi_lo(0, SENTINEL), // output len + ptr
1881			);
1882		}
1883
1884		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1885		Ok(())
1886	}
1887
1888	// t: with or without some value to transfer
1889	// d: with or without dust value to transfer
1890	// i: size of the input data
1891	#[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,                                           // ref_time_limit
1943				u64::MAX,                                           // proof_size_limit
1944				pack_hi_lo(offset(input_len), offset(deposit_len)), // deposit_ptr + value_ptr
1945				pack_hi_lo(input_len, 0),                           // input_data_len + input_data
1946				pack_hi_lo(0, SENTINEL),                            // output_len_ptr + output_ptr
1947				pack_hi_lo(SENTINEL, offset(value_len)),            // address_ptr + salt_ptr
1948			);
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	// `n`: Input to hash in bytes
1963	#[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	// `n`: Input to hash in bytes
2000	#[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	// `n`: Input to hash in bytes
2023	#[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	// `n`: Input to hash in bytes
2037	#[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	// `n`: Input to hash in bytes
2061	#[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	// `n`: Message input length to verify in bytes.
2085	// need some buffer so the code size does not exceed the max code size.
2086	#[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,                              // signature_ptr
2106				sig_len,                        // pub_key_ptr
2107				message_len,                    // message_len
2108				sig_len + pub_key.len() as u32, // message_ptr
2109			);
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	// `n`: pairings to perform
2175	#[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	// `n`: number of rounds to perform
2224	#[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	// Only calling the function itself for the list of
2244	// generated different ECDSA keys.
2245	// This is a slow call: We reduce the number of runs.
2246	#[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, // key_ptr
2258				0,  // output_ptr
2259			);
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 the cost of setting the code hash of a contract.
2267	///
2268	/// `r`: whether the old code will be removed as a result of this operation. (1: yes, 0: no)
2269	#[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		// Increment the refcount of the code hash so that it does not get deleted
2280		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 the cost of executing `r` noop (JUMPDEST) instructions.
2296	#[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 the execution of instructions.
2319	//
2320	// It benchmarks the absolute worst case by allocating a lot of memory
2321	// and then accessing it so that each instruction generates two cache misses.
2322	#[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		// Ideally, this needs to be bigger than the cache.
2328		const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
2329
2330		// This is benchmarked for x86-64.
2331		const CACHE_LINE_SIZE: u64 = 64;
2332
2333		// An 8 byte load from this misalignment will reach into the subsequent line.
2334		const MISALIGNMENT: u64 = 60;
2335
2336		// We only need one address per cache line.
2337		// -1 because we skip the first address
2338		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		// Addresses data will be located inside the aux data.
2356		let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
2357
2358		// Create all possible addresses and shuffle them. This makes sure
2359		// the accesses are random but no address is accessed more than once.
2360		// we skip the first address since it is our entry point
2361		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		// The addresses need to be padded to be one cache line apart.
2370		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		// Copies `memory` to `aux_data_base + MISALIGNMENT`.
2377		// Sets `a0 = MISALIGNMENT` and `a1 = r`.
2378		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		// uses twice the weight once for migration and then for checking if there is another key.
2420		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		// uses twice the weight once for migration and then for checking if there is another key.
2445		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}