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,
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
76/// How many runs we do per API benchmark.
77///
78/// This is picked more or less arbitrary. We experimented with different numbers until
79/// the results appeared to be stable. Reducing the number would speed up the benchmarks
80/// but might make the results less precise.
81const 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
106/// Get the pallet account and whitelist it for benchmarking.
107/// The account is warmed up `on_initialize` so read should not impact the PoV.
108fn 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	// The base weight consumed on processing contracts deletion queue.
127	#[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	// This benchmarks the overhead of loading a code of size `c` byte from storage and into
150	// the execution engine.
151	//
152	// `call_with_pvm_code_per_byte(c) - call_with_pvm_code_per_byte(0)`
153	//
154	// This does **not** include the actual execution for which the gas meter
155	// is responsible. The code used here will just return on call.
156	//
157	// We expect the influence of `c` to be none in this benchmark because every instruction that
158	// is not in the first basic block is never read. We are primarily interested in the
159	// `proof_size` result of this benchmark.
160	#[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	// This benchmarks the overhead of loading a code of size `c` byte from storage and into
181	// the execution engine.
182	/// This is similar to `call_with_pvm_code_per_byte` but for EVM bytecode.
183	#[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	// Measure the amount of time it takes to compile a single basic block.
204	//
205	// (basic_block_compilation(1) - basic_block_compilation(0)).ref_time()
206	//
207	// This is needed because the interpreter will always compile a whole basic block at
208	// a time. To prevent a contract from triggering compilation without doing any execution
209	// we will always charge one max sized block per contract call.
210	//
211	// We ignore the proof size component when using this benchmark as this is already accounted
212	// for in `call_with_pvm_code_per_byte`.
213	#[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	// `c`: Size of the code in bytes.
239	// `i`: Size of the input in bytes.
240	#[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		// uploading the code reserves some balance in the pallet's account
264		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		// contract has the full value
278		assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
279	}
280
281	// `c`: Size of the code in bytes.
282	// `i`: Size of the input in bytes.
283	// `d`: with or without dust value to transfer
284	#[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		// uploading the code reserves some balance in the pallet account
317		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		// contract has the full value
336		assert_eq!(Pallet::<T>::evm_balance(&addr), evm_value);
337	}
338
339	// `i`: Size of the input in bytes.
340	// `s`: Size of e salt in bytes.
341	#[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		// value was removed from the caller
371		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		// contract has the full value
379		assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
380
381		Ok(())
382	}
383
384	// We just call a dummy contract to measure the overhead of the call extrinsic.
385	// The size of the data has no influence on the costs of this extrinsic as long as the contract
386	// won't call `seal_call_data_copy` in its constructor to copy the data to contract memory.
387	// The dummy contract used here does not do this. The costs for the data copy is billed as
388	// part of `seal_call_data_copy`. The costs for invoking a contract of a specific size are not
389	// part of this benchmark because we cannot know the size of the contract when issuing a call
390	// transaction. See `call_with_pvm_code_per_byte` for this.
391	#[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		// value and value transferred via call should be removed from the caller
414		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		// contract should have received the value
422		assert_eq!(T::Currency::balance(&instance.account_id), before + value);
423		// contract should still exist
424		instance.info()?;
425
426		Ok(())
427	}
428
429	// `d`: with or without dust value to transfer
430	#[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		// value and value transferred via call should be removed from the caller
459		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		// contract should have received the value
471		assert_eq!(Pallet::<T>::evm_balance(&instance.address), before + evm_value);
472		// contract should still exist
473		instance.info()?;
474
475		Ok(())
476	}
477
478	// This constructs a contract that is maximal expensive to instrument.
479	// It creates a maximum number of metering blocks per byte.
480	// `c`: Size of the code in bytes.
481	#[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		// uploading the code reserves some balance in the pallet's account
492		assert!(T::Currency::total_balance_on_hold(&pallet_account) > 0u32.into());
493		assert!(<Contract<T>>::code_exists(&hash));
494	}
495
496	// Removing code does not depend on the size of the contract because all the information
497	// needed to verify the removal claim (refcount, owner) is stored in a separate storage
498	// item (`CodeInfoOf`).
499	#[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		// removing the code should have unreserved the deposit
515		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		// we just add some bytes so that the code hash is different
525		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		// use a mapped address for the benchmark, to ensure that we bench the worst
620		// case (and not the fallback case).
621		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		// The pre-runtime digest log is unbounded; usually around 3 items but it can vary.
1031		// To get safe benchmark results despite that, populate it with a bunch of random logs to
1032		// ensure iteration over many items (we just overestimate the cost of the API).
1033		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		// The content of the pre-runtime digest log depends on the configured consensus.
1041		// However, mismatching logs are simply ignored. Thus we construct fixtures which will
1042		// let the API to return a value in both BABE and AURA consensus.
1043
1044		// Construct a `Digest` log fixture returning some value in BABE
1045		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		// Construct a `Digest` log fixture returning some value in AURA
1052		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 the ocst of terminating a contract.
1198	///
1199	/// `r`: whether the old code will be removed as a result of this operation. (1: yes, 0: no)
1200	#[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		// Increment the refcount of the code hash so that it does not get deleted
1209		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 the overhead that topics generate.
1226	// `t`: Number of topics
1227	// `n`: Size of event payload in bytes
1228	#[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, // topics_ptr
1246				num_topic,
1247				topics_data.len() as u32, // data_ptr
1248				n,                        // data_len
1249			);
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	// n: new byte size
1359	// o: old byte size
1360	#[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,           // key_ptr
1383				max_key_len, // key_len
1384				max_key_len, // value_ptr
1385				n,           // value_len
1386			);
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,           // key_ptr
1440				max_key_len, // key_len
1441				out_ptr,     // out_ptr
1442				max_key_len, // out_len_ptr
1443			);
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,           // key_ptr
1499				max_key_len, // key_len
1500				out_ptr,     // out_ptr
1501				max_key_len, // out_len_ptr
1502			);
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	// We use both full and empty benchmarks here instead of benchmarking transient_storage
1512	// (BTreeMap) directly. This approach is necessary because benchmarking this BTreeMap is very
1513	// slow. Additionally, we use linear regression for our benchmarks, and the BTreeMap's log(n)
1514	// complexity can introduce approximation errors.
1515	#[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	// The weight of journal rollbacks should be taken into account when setting storage.
1612	#[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	// n: new byte size
1639	// o: old byte size
1640	#[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,           // key_ptr
1663				max_key_len, // key_len
1664				max_key_len, // value_ptr
1665				n,           // value_len
1666			);
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,           // key_ptr
1726				max_key_len, // key_len
1727				out_ptr,     // out_ptr
1728				max_key_len, // out_len_ptr
1729			);
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,           // key_ptr
1793				max_key_len, // key_len
1794				out_ptr,     // out_ptr
1795				max_key_len, // out_len_ptr
1796			);
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	// t: with or without some value to transfer
1806	// d: with or without dust value to transfer
1807	// i: size of the input data
1808	#[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		// We benchmark the overhead of cloning the input. Not passing it to the contract.
1829		// This is why we set the input here instead of passig it as pointer to the `bench_call`.
1830		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), // flags + callee
1844				u64::MAX,                                     // ref_time_limit
1845				u64::MAX,                                     // proof_size_limit
1846				pack_hi_lo(callee_len, callee_len + deposit_len), // deposit_ptr + value_pr
1847				pack_hi_lo(0, 0),                             // input len + data ptr
1848				pack_hi_lo(0, SENTINEL),                      // output len + data ptr
1849			);
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	// d: 1 if the associated pre-compile has a contract info that needs to be loaded
1861	// i: size of the input data
1862	#[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), // flags + callee
1899				u64::MAX,         // ref_time_limit
1900				u64::MAX,         // proof_size_limit
1901				pack_hi_lo(callee_len, callee_len + deposit_len), /* deposit_ptr +
1902				                   * value_pr */
1903				pack_hi_lo(input_len, callee_len + deposit_len + value_len), /* input len +
1904				                                                              * input ptr */
1905				pack_hi_lo(0, SENTINEL), // output len + output ptr
1906			)
1907		};
1908
1909		// first call of the pre-compile will create its contract info and account
1910		// so we make sure to create it
1911		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),        // flags + address ptr
1947				u64::MAX,                // ref_time_limit
1948				u64::MAX,                // proof_size_limit
1949				address_len,             // deposit_ptr
1950				pack_hi_lo(0, 0),        // input len + data ptr
1951				pack_hi_lo(0, SENTINEL), // output len + ptr
1952			);
1953		}
1954
1955		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1956		Ok(())
1957	}
1958
1959	// t: with or without some value to transfer
1960	// d: with or without dust value to transfer
1961	// i: size of the input data
1962	#[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,                                           // ref_time_limit
2014				u64::MAX,                                           // proof_size_limit
2015				pack_hi_lo(offset(input_len), offset(deposit_len)), // deposit_ptr + value_ptr
2016				pack_hi_lo(input_len, 0),                           // input_data_len + input_data
2017				pack_hi_lo(0, SENTINEL),                            // output_len_ptr + output_ptr
2018				pack_hi_lo(SENTINEL, offset(value_len)),            // address_ptr + salt_ptr
2019			);
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	// `n`: Input to hash in bytes
2034	#[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	// `n`: Input to hash in bytes
2071	#[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	// `n`: Input to hash in bytes
2094	#[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	// `n`: Input to hash in bytes
2108	#[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	// `n`: Input to hash in bytes
2138	#[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	// `n`: Message input length to verify in bytes.
2168	// need some buffer so the code size does not exceed the max code size.
2169	#[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,                              // signature_ptr
2189				sig_len,                        // pub_key_ptr
2190				message_len,                    // message_len
2191				sig_len + pub_key.len() as u32, // message_ptr
2192			);
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	// `n`: pairings to perform
2258	#[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	// `n`: number of rounds to perform
2307	#[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	// Only calling the function itself for the list of
2327	// generated different ECDSA keys.
2328	// This is a slow call: We reduce the number of runs.
2329	#[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, // key_ptr
2341				0,  // output_ptr
2342			);
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 the cost of setting the code hash of a contract.
2350	///
2351	/// `r`: whether the old code will be removed as a result of this operation. (1: yes, 0: no)
2352	#[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		// Increment the refcount of the code hash so that it does not get deleted
2363		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 the cost of executing `r` noop (JUMPDEST) instructions.
2379	#[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 the execution of instructions.
2399	//
2400	// It benchmarks the absolute worst case by allocating a lot of memory
2401	// and then accessing it so that each instruction generates two cache misses.
2402	#[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		// Ideally, this needs to be bigger than the cache.
2408		const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
2409
2410		// This is benchmarked for x86-64.
2411		const CACHE_LINE_SIZE: u64 = 64;
2412
2413		// An 8 byte load from this misalignment will reach into the subsequent line.
2414		const MISALIGNMENT: u64 = 60;
2415
2416		// We only need one address per cache line.
2417		// -1 because we skip the first address
2418		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		// Addresses data will be located inside the aux data.
2436		let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
2437
2438		// Create all possible addresses and shuffle them. This makes sure
2439		// the accesses are random but no address is accessed more than once.
2440		// we skip the first address since it is our entry point
2441		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		// The addresses need to be padded to be one cache line apart.
2450		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		// Copies `memory` to `aux_data_base + MISALIGNMENT`.
2457		// Sets `a0 = MISALIGNMENT` and `a1 = r`.
2458		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		// Setup stack for extcodecopy instruction: [address, dest_offset, offset, size]
2506		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		// uses twice the weight once for migration and then for checking if there is another key.
2547		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		// uses twice the weight once for migration and then for checking if there is another key.
2572		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}