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	Pallet as Contracts,
23	call_builder::{CallSetup, Contract, VmBinaryModule, caller_funding, default_deposit_limit},
24	evm::{
25		TransactionLegacyUnsigned, TransactionSigned, TransactionUnsigned,
26		block_hash::EthereumBlockBuilder, block_storage,
27	},
28	exec::{Key, Origin as ExecOrigin, PrecompileExt},
29	limits,
30	precompiles::{
31		self, BenchmarkStorage, BenchmarkSystem, BuiltinPrecompile,
32		alloy::sol_types::{
33			SolType,
34			sol_data::{Bool, Bytes, FixedBytes, Uint},
35		},
36		run::builtin as run_builtin_precompile,
37	},
38	storage::WriteOutcome,
39	vm::{
40		evm,
41		evm::{Interpreter, instructions, instructions::utility::IntoAddress},
42		pvm,
43	},
44	*,
45};
46use alloc::{vec, vec::Vec};
47use alloy_core::sol_types::{SolInterface, SolValue};
48use codec::{Encode, MaxEncodedLen};
49use frame_benchmarking::v2::*;
50use frame_support::{
51	self, assert_ok,
52	migrations::SteppedMigration,
53	storage::child,
54	traits::{Hooks, fungible::InspectHold},
55	weights::{Weight, WeightMeter},
56};
57use frame_system::RawOrigin;
58use k256::ecdsa::SigningKey;
59use pallet_revive_uapi::{
60	CallFlags, ReturnErrorCode, StorageFlags, pack_hi_lo,
61	precompiles::{storage::IStorage, system::ISystem},
62};
63use revm::bytecode::Bytecode;
64use sp_consensus_aura::AURA_ENGINE_ID;
65use sp_consensus_babe::{
66	BABE_ENGINE_ID,
67	digests::{PreDigest, PrimaryPreDigest},
68};
69use sp_consensus_slots::Slot;
70use sp_runtime::{generic::DigestItem, traits::Zero};
71
72/// How many runs we do per API benchmark.
73///
74/// This is picked more or less arbitrary. We experimented with different numbers until
75/// the results appeared to be stable. Reducing the number would speed up the benchmarks
76/// but might make the results less precise.
77const API_BENCHMARK_RUNS: u32 = 1600;
78
79macro_rules! memory(
80	($($bytes:expr,)*) => {{
81		vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::<Vec<_>>()
82	}};
83);
84
85macro_rules! build_runtime(
86	($runtime:ident, $memory:ident: [$($segment:expr,)*]) => {
87		build_runtime!($runtime, _contract, $memory: [$($segment,)*]);
88	};
89	($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => {
90		build_runtime!($runtime, $contract);
91		let mut $memory = memory!($($bytes,)*);
92	};
93	($runtime:ident, $contract:ident) => {
94		let mut setup = CallSetup::<T>::default();
95		let $contract = setup.contract();
96		let input = setup.data();
97		let (mut ext, _) = setup.ext();
98		let mut $runtime = $crate::vm::pvm::Runtime::<_, [u8]>::new(&mut ext, input);
99	};
100);
101
102/// Get the pallet account and whitelist it for benchmarking.
103/// The account is warmed up `on_initialize` so read should not impact the PoV.
104fn whitelisted_pallet_account<T: Config>() -> T::AccountId {
105	let pallet_account = Pallet::<T>::account_id();
106	whitelist_account!(pallet_account);
107	pallet_account
108}
109
110#[benchmarks(
111	where
112		T: Config,
113		<T as Config>::RuntimeCall: From<frame_system::Call<T>>,
114		<T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
115		OriginFor<T>: From<Origin<T>>,
116)]
117mod benchmarks {
118	use super::*;
119
120	/// The base weight consumed on processing contracts deletion queue.
121	#[benchmark(pov_mode = Measured)]
122	fn deletion_queue_batch() {
123		#[block]
124		{
125			ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
126		}
127	}
128
129	/// Measures the per-entry cost of `process_deletion_queue_batch`: one `DeletionQueue` read
130	/// plus the `DeletionQueue` + `DeletionQueueCounter` writes done by `entry.remove()`.
131	#[benchmark(pov_mode = Measured)]
132	fn deletion_queue_per_entry() -> Result<(), BenchmarkError> {
133		let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
134		ContractInfo::<T>::queue_for_deletion(
135			instance.info()?.trie_id,
136			instance.account_id.clone(),
137		);
138
139		#[block]
140		{
141			ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
142		}
143
144		assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
145		Ok(())
146	}
147
148	#[benchmark(skip_meta, pov_mode = Measured)]
149	fn deletion_queue_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
150		let instance =
151			Contract::<T>::with_storage(VmBinaryModule::dummy(), k, limits::STORAGE_BYTES)?;
152		ContractInfo::<T>::queue_for_deletion(
153			instance.info()?.trie_id,
154			instance.account_id.clone(),
155		);
156
157		#[block]
158		{
159			ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
160		}
161
162		assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
163		Ok(())
164	}
165
166	/// Measures the cost of clearing one [`NativeDepositOf`] row during
167	/// [`ContractInfo::process_deletion_queue_batch`]. Pre-populates the contract with `k`
168	/// per-payer rows and queues the contract for deletion with `native_cleared = false` and
169	/// an empty trie. The deletion queue then drains all rows in one go.
170	#[benchmark(skip_meta, pov_mode = Measured)]
171	fn deletion_queue_per_native_deposit_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
172		use frame_benchmarking::v2::account;
173
174		// Empty trie: zero items, zero bytes; we only want to measure native-deposit cleanup.
175		let instance = Contract::<T>::with_storage(VmBinaryModule::dummy(), 0, 0)?;
176		for i in 0..k {
177			let payer: T::AccountId = account("payer", i, 0);
178			NativeDepositOf::<T>::insert(&instance.account_id, &payer, BalanceOf::<T>::default());
179		}
180		ContractInfo::<T>::queue_for_deletion(
181			instance.info()?.trie_id,
182			instance.account_id.clone(),
183		);
184
185		#[block]
186		{
187			ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
188		}
189
190		assert!(<DeletionQueue<T>>::iter().next().is_none(), "deletion queue should be drained",);
191		Ok(())
192	}
193
194	// This benchmarks the overhead of loading a code of size `c` byte from storage and into
195	// the execution engine.
196	//
197	// `call_with_pvm_code_per_byte(c) - call_with_pvm_code_per_byte(0)`
198	//
199	// This does **not** include the actual execution for which the gas meter
200	// is responsible. The code used here will just return on call.
201	//
202	// We expect the influence of `c` to be none in this benchmark because every instruction that
203	// is not in the first basic block is never read. We are primarily interested in the
204	// `proof_size` result of this benchmark.
205	#[benchmark(pov_mode = Measured)]
206	fn call_with_pvm_code_per_byte(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
207		let instance =
208			Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::sized(c), vec![])?;
209		let value = Pallet::<T>::min_balance();
210		let storage_deposit = default_deposit_limit::<T>();
211
212		#[extrinsic_call]
213		call(
214			RawOrigin::Signed(instance.caller.clone()),
215			instance.address,
216			value,
217			Weight::MAX,
218			storage_deposit,
219			vec![],
220		);
221
222		Ok(())
223	}
224
225	// This benchmarks the overhead of loading a code of size `c` byte from storage and into
226	// the execution engine.
227	/// This is similar to `call_with_pvm_code_per_byte` but for EVM bytecode.
228	#[benchmark(pov_mode = Measured)]
229	fn call_with_evm_code_per_byte(c: Linear<1, { 10 * 1024 }>) -> Result<(), BenchmarkError> {
230		let instance = Contract::<T>::with_caller(
231			whitelisted_caller(),
232			VmBinaryModule::evm_init_code_for_runtime_size(c),
233			vec![],
234		)?;
235		let value = Pallet::<T>::min_balance();
236		let storage_deposit = default_deposit_limit::<T>();
237
238		let code_len = PristineCode::<T>::get(instance.info()?.code_hash)
239			.expect("code should be stored")
240			.len();
241		assert_eq!(
242			code_len, c as usize,
243			"runtime bytecode should be exactly {c} bytes, got {code_len}"
244		);
245
246		#[extrinsic_call]
247		call(
248			RawOrigin::Signed(instance.caller.clone()),
249			instance.address,
250			value,
251			Weight::MAX,
252			storage_deposit,
253			vec![],
254		);
255
256		Ok(())
257	}
258
259	// Measure the amount of time it takes to compile a single basic block.
260	//
261	// (basic_block_compilation(1) - basic_block_compilation(0)).ref_time()
262	//
263	// This is needed because the interpreter will always compile a whole basic block at
264	// a time. To prevent a contract from triggering compilation without doing any execution
265	// we will always charge one max sized block per contract call.
266	//
267	// We ignore the proof size component when using this benchmark as this is already accounted
268	// for in `call_with_pvm_code_per_byte`.
269	#[benchmark(pov_mode = Measured)]
270	fn basic_block_compilation(b: Linear<0, 1>) -> Result<(), BenchmarkError> {
271		let instance = Contract::<T>::with_caller(
272			whitelisted_caller(),
273			VmBinaryModule::with_num_instructions(limits::code::BASIC_BLOCK_SIZE),
274			vec![],
275		)?;
276		let value = Pallet::<T>::min_balance();
277		let storage_deposit = default_deposit_limit::<T>();
278
279		#[block]
280		{
281			Pallet::<T>::call(
282				RawOrigin::Signed(instance.caller.clone()).into(),
283				instance.address,
284				value,
285				Weight::MAX,
286				storage_deposit,
287				vec![],
288			)?;
289		}
290
291		Ok(())
292	}
293
294	// `c`: Size of the code in bytes.
295	// `i`: Size of the input in bytes.
296	#[benchmark(pov_mode = Measured)]
297	fn instantiate_with_code(
298		c: Linear<0, { 100 * 1024 }>,
299		i: Linear<0, { limits::CALLDATA_BYTES }>,
300	) {
301		let pallet_account = whitelisted_pallet_account::<T>();
302		let input = vec![42u8; i as usize];
303		let salt = [42u8; 32];
304		let value = Pallet::<T>::min_balance();
305		let caller = whitelisted_caller();
306		T::Currency::set_balance(&caller, caller_funding::<T>());
307		let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
308		let origin = RawOrigin::Signed(caller.clone());
309		if !T::AddressMapper::is_mapped(&caller) {
310			T::AddressMapper::map(&caller).unwrap();
311		}
312		let deployer = T::AddressMapper::to_address(&caller);
313		let addr = crate::address::create2(&deployer, &code, &input, &salt);
314		let account_id = T::AddressMapper::to_fallback_account_id(&addr);
315		let storage_deposit = default_deposit_limit::<T>();
316		#[extrinsic_call]
317		_(origin, value, Weight::MAX, storage_deposit, code, input, Some(salt));
318
319		let deposit =
320			T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
321		// uploading the code reserves some balance in the pallet's account
322		let code_deposit = T::Currency::balance_on_hold(
323			&HoldReason::CodeUploadDepositReserve.into(),
324			&pallet_account,
325		);
326		let mapping_deposit =
327			T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &caller);
328		assert_eq!(
329			T::Currency::balance(&caller),
330			caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
331		);
332		// contract has the full value
333		assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
334	}
335
336	// `c`: Size of the code in bytes.
337	// `i`: Size of the input in bytes.
338	// `d`: with or without dust value to transfer
339	#[benchmark(pov_mode = Measured)]
340	fn eth_instantiate_with_code(
341		c: Linear<0, { 100 * 1024 }>,
342		i: Linear<0, { limits::CALLDATA_BYTES }>,
343		d: Linear<0, 1>,
344	) -> Result<(), BenchmarkError> {
345		let input = vec![42u8; i as usize];
346
347		// Use an `effective_gas_price` that is not a multiple of `T::NativeToEthRatio`
348		// to hit the code that charge the rounding error so that tx_cost == effective_gas_price *
349		// gas_used
350		let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
351		let value = Pallet::<T>::min_balance();
352		let dust = 42u32 * d;
353		let evm_value =
354			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
355		let caller = whitelisted_caller();
356		T::Currency::set_balance(&caller, caller_funding::<T>());
357		let VmBinaryModule { code, .. } = VmBinaryModule::sized(c);
358		let origin = Origin::EthTransaction(caller.clone());
359		if !T::AddressMapper::is_mapped(&caller) {
360			T::AddressMapper::map(&caller).unwrap();
361		}
362		let deployer = T::AddressMapper::to_address(&caller);
363		let nonce = System::<T>::account_nonce(&caller).try_into().unwrap_or_default();
364		let addr = crate::address::create1(&deployer, nonce);
365
366		assert!(AccountInfoOf::<T>::get(&deployer).is_none());
367
368		<T as Config>::FeeInfo::deposit_txfee(
369			<T as Config>::Currency::issue(caller_funding::<T>()),
370		);
371
372		#[extrinsic_call]
373		_(
374			origin,
375			evm_value,
376			Weight::MAX,
377			U256::MAX,
378			code,
379			input,
380			TransactionSigned::default().signed_payload(),
381			effective_gas_price,
382			0,
383		);
384
385		// contract has the full value
386		assert_eq!(Pallet::<T>::evm_balance(&addr), evm_value);
387		Ok(())
388	}
389
390	#[benchmark(pov_mode = Measured)]
391	fn deposit_eth_extrinsic_revert_event() {
392		#[block]
393		{
394			Pallet::<T>::deposit_event(Event::<T>::EthExtrinsicRevert {
395				dispatch_error: crate::Error::<T>::BenchmarkingError.into(),
396			});
397		}
398	}
399
400	// `i`: Size of the input in bytes.
401	// `s`: Size of e salt in bytes.
402	#[benchmark(pov_mode = Measured)]
403	fn instantiate(i: Linear<0, { limits::CALLDATA_BYTES }>) -> Result<(), BenchmarkError> {
404		let pallet_account = whitelisted_pallet_account::<T>();
405		let input = vec![42u8; i as usize];
406		let salt = [42u8; 32];
407		let value = Pallet::<T>::min_balance();
408		let caller = whitelisted_caller();
409		T::Currency::set_balance(&caller, caller_funding::<T>());
410		let origin = RawOrigin::Signed(caller.clone());
411		if !T::AddressMapper::is_mapped(&caller) {
412			T::AddressMapper::map(&caller).unwrap();
413		}
414		let VmBinaryModule { code, .. } = VmBinaryModule::dummy();
415		let storage_deposit = default_deposit_limit::<T>();
416		let deployer = T::AddressMapper::to_address(&caller);
417		let addr = crate::address::create2(&deployer, &code, &input, &salt);
418		let hash = Contracts::<T>::bare_upload_code(origin.clone().into(), code, storage_deposit)?
419			.code_hash;
420		let account_id = T::AddressMapper::to_fallback_account_id(&addr);
421
422		#[extrinsic_call]
423		_(origin, value, Weight::MAX, storage_deposit, hash, input, Some(salt));
424
425		let deposit =
426			T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &account_id);
427		let code_deposit = T::Currency::balance_on_hold(
428			&HoldReason::CodeUploadDepositReserve.into(),
429			&pallet_account,
430		);
431		let mapping_deposit =
432			T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &account_id);
433		// value was removed from the caller
434		assert_eq!(
435			T::Currency::total_balance(&caller),
436			caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
437		);
438		// contract has the full value
439		assert_eq!(T::Currency::balance(&account_id), value + Pallet::<T>::min_balance());
440
441		Ok(())
442	}
443
444	// We just call a dummy contract to measure the overhead of the call extrinsic.
445	// The size of the data has no influence on the costs of this extrinsic as long as the contract
446	// won't call `seal_call_data_copy` in its constructor to copy the data to contract memory.
447	// The dummy contract used here does not do this. The costs for the data copy is billed as
448	// part of `seal_call_data_copy`. The costs for invoking a contract of a specific size are not
449	// part of this benchmark because we cannot know the size of the contract when issuing a call
450	// transaction. See `call_with_pvm_code_per_byte` for this.
451	#[benchmark(pov_mode = Measured)]
452	fn call() -> Result<(), BenchmarkError> {
453		let pallet_account = whitelisted_pallet_account::<T>();
454		let data = vec![42u8; 1024];
455		let instance =
456			Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
457		let value = Pallet::<T>::min_balance();
458		let origin = RawOrigin::Signed(instance.caller.clone());
459		let before = T::Currency::balance(&instance.account_id);
460		let storage_deposit = default_deposit_limit::<T>();
461		#[extrinsic_call]
462		_(origin, instance.address, value, Weight::MAX, storage_deposit, data);
463		let deposit = T::Currency::balance_on_hold(
464			&HoldReason::StorageDepositReserve.into(),
465			&instance.account_id,
466		);
467		let code_deposit = T::Currency::balance_on_hold(
468			&HoldReason::CodeUploadDepositReserve.into(),
469			&pallet_account,
470		);
471		let mapping_deposit =
472			T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), &instance.caller);
473		// value and value transferred via call should be removed from the caller
474		assert_eq!(
475			T::Currency::balance(&instance.caller),
476			caller_funding::<T>() - value - deposit - code_deposit - mapping_deposit,
477		);
478		// contract should have received the value
479		assert_eq!(T::Currency::balance(&instance.account_id), before + value);
480		// contract should still exist
481		instance.info()?;
482
483		Ok(())
484	}
485
486	// `d`: with or without dust value to transfer
487	#[benchmark(pov_mode = Measured)]
488	fn eth_call(d: Linear<0, 1>) -> Result<(), BenchmarkError> {
489		let data = vec![42u8; 1024];
490		let instance =
491			Contract::<T>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
492
493		// Use an `effective_gas_price` that is not a multiple of `T::NativeToEthRatio`
494		// to hit the code that charge the rounding error so that tx_cost == effective_gas_price *
495		// gas_used
496		let effective_gas_price = Pallet::<T>::evm_base_fee() + 1;
497		let value = Pallet::<T>::min_balance();
498		let dust = 42u32 * d;
499		let evm_value =
500			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
501
502		// need to pass the overdraw check
503		<T as Config>::FeeInfo::deposit_txfee(
504			<T as Config>::Currency::issue(caller_funding::<T>()),
505		);
506
507		let origin = Origin::EthTransaction(instance.caller.clone());
508		let before = Pallet::<T>::evm_balance(&instance.address);
509
510		#[extrinsic_call]
511		_(
512			origin,
513			instance.address,
514			evm_value,
515			Weight::MAX,
516			U256::MAX,
517			data,
518			TransactionSigned::default().signed_payload(),
519			effective_gas_price,
520			0,
521		);
522
523		// contract should have received the value
524		assert_eq!(Pallet::<T>::evm_balance(&instance.address), before + evm_value);
525		// contract should still exist
526		instance.info()?;
527
528		Ok(())
529	}
530
531	// `c`: Size of the RLP encoded Ethereum transaction in bytes.
532	#[benchmark(pov_mode = Measured)]
533	fn eth_substrate_call(c: Linear<0, { 100 * 1024 }>) -> Result<(), BenchmarkError> {
534		let caller = whitelisted_caller();
535		T::Currency::set_balance(&caller, caller_funding::<T>());
536		let origin = Origin::EthTransaction(caller);
537		let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
538		#[extrinsic_call]
539		_(origin, Box::new(dispatchable), vec![42u8; c as usize]);
540		Ok(())
541	}
542
543	// This constructs a contract that is maximal expensive to instrument.
544	// It creates a maximum number of metering blocks per byte.
545	// `c`: Size of the code in bytes.
546	#[benchmark(pov_mode = Measured)]
547	fn upload_code(c: Linear<0, { 100 * 1024 }>) {
548		let caller = whitelisted_caller();
549		let pallet_account = whitelisted_pallet_account::<T>();
550		T::Currency::set_balance(&caller, caller_funding::<T>());
551		let VmBinaryModule { code, hash, .. } = VmBinaryModule::sized(c);
552		let origin = RawOrigin::Signed(caller.clone());
553		let storage_deposit = default_deposit_limit::<T>();
554		#[extrinsic_call]
555		_(origin, code, storage_deposit);
556		// uploading the code reserves some balance in the pallet's account
557		assert!(T::Currency::total_balance_on_hold(&pallet_account) > 0u32.into());
558		assert!(<Contract<T>>::code_exists(&hash));
559	}
560
561	// Removing code does not depend on the size of the contract because all the information
562	// needed to verify the removal claim (refcount, owner) is stored in a separate storage
563	// item (`CodeInfoOf`).
564	#[benchmark(pov_mode = Measured)]
565	fn remove_code() -> Result<(), BenchmarkError> {
566		let caller = whitelisted_caller();
567		let pallet_account = whitelisted_pallet_account::<T>();
568		T::Currency::set_balance(&caller, caller_funding::<T>());
569		let VmBinaryModule { code, hash, .. } = VmBinaryModule::dummy();
570		let origin = RawOrigin::Signed(caller.clone());
571		let storage_deposit = default_deposit_limit::<T>();
572		let uploaded =
573			<Contracts<T>>::bare_upload_code(origin.clone().into(), code, storage_deposit)?;
574		assert_eq!(uploaded.code_hash, hash);
575		assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&pallet_account));
576		assert!(<Contract<T>>::code_exists(&hash));
577		#[extrinsic_call]
578		_(origin, hash);
579		// removing the code should have unreserved the deposit
580		assert_eq!(T::Currency::total_balance_on_hold(&pallet_account), 0u32.into());
581		assert!(<Contract<T>>::code_removed(&hash));
582		Ok(())
583	}
584
585	#[benchmark(pov_mode = Measured)]
586	fn set_code() -> Result<(), BenchmarkError> {
587		let instance =
588			<Contract<T>>::with_caller(whitelisted_caller(), VmBinaryModule::dummy(), vec![])?;
589		// we just add some bytes so that the code hash is different
590		let VmBinaryModule { code, .. } = VmBinaryModule::dummy_unique(128);
591		let origin = RawOrigin::Signed(instance.caller.clone());
592		let storage_deposit = default_deposit_limit::<T>();
593		let hash =
594			<Contracts<T>>::bare_upload_code(origin.into(), code, storage_deposit)?.code_hash;
595		assert_ne!(instance.info()?.code_hash, hash);
596		#[extrinsic_call]
597		_(RawOrigin::Root, instance.address, hash);
598		assert_eq!(instance.info()?.code_hash, hash);
599		Ok(())
600	}
601
602	#[benchmark(pov_mode = Measured)]
603	fn map_account() {
604		let caller = whitelisted_caller();
605		T::Currency::set_balance(&caller, caller_funding::<T>());
606		let origin = RawOrigin::Signed(caller.clone());
607		if T::AddressMapper::is_mapped(&caller) {
608			T::AddressMapper::unmap(&caller).unwrap();
609		}
610		assert!(!T::AddressMapper::is_mapped(&caller));
611		#[extrinsic_call]
612		_(origin);
613		assert!(T::AddressMapper::is_mapped(&caller));
614	}
615
616	#[benchmark(pov_mode = Measured)]
617	fn unmap_account() {
618		let caller = whitelisted_caller();
619		T::Currency::set_balance(&caller, caller_funding::<T>());
620		let origin = RawOrigin::Signed(caller.clone());
621		if !T::AddressMapper::is_mapped(&caller) {
622			T::AddressMapper::map(&caller).unwrap();
623		}
624		assert!(T::AddressMapper::is_mapped(&caller));
625		#[extrinsic_call]
626		_(origin);
627		assert!(!T::AddressMapper::is_mapped(&caller));
628	}
629
630	/// Worst case: every input account is not eth-derived, not yet mapped, and
631	/// already carries an [`HoldReason::AddressMapping`] hold. The per-account
632	/// loop body in `batch_map_accounts` then both inserts the [`OriginalAccount`]
633	/// entry via `map_no_deposit_unchecked` *and* releases the existing hold.
634	#[benchmark(pov_mode = Measured)]
635	fn batch_map_accounts(a: Linear<0, 1024>) -> Result<(), BenchmarkError> {
636		use frame_benchmarking::v2::account;
637
638		let caller: T::AccountId = whitelisted_caller();
639		T::Currency::set_balance(&caller, caller_funding::<T>());
640
641		// Matches the deposit that `AccountId32Mapper::map` would normally take.
642		let deposit = T::DepositPerByte::get()
643			.saturating_mul(52u32.into())
644			.saturating_add(T::DepositPerItem::get());
645
646		let mut accounts = Vec::with_capacity(a as usize);
647		for i in 0..a {
648			let account_id: T::AccountId = account("to_map", i, 0);
649			T::Currency::set_balance(&account_id, caller_funding::<T>());
650			T::Currency::hold(&HoldReason::AddressMapping.into(), &account_id, deposit)?;
651			accounts.push(account_id);
652		}
653
654		#[extrinsic_call]
655		_(RawOrigin::Signed(caller), accounts.clone());
656
657		for account_id in &accounts {
658			assert!(T::AddressMapper::is_mapped(account_id));
659			assert_eq!(
660				T::Currency::balance_on_hold(&HoldReason::AddressMapping.into(), account_id),
661				0u32.into(),
662			);
663		}
664
665		Ok(())
666	}
667
668	#[benchmark(pov_mode = Measured)]
669	fn dispatch_as_fallback_account() {
670		let caller = whitelisted_caller();
671		T::Currency::set_balance(&caller, caller_funding::<T>());
672		let origin = RawOrigin::Signed(caller.clone());
673		let dispatchable = frame_system::Call::remark { remark: vec![] }.into();
674		#[extrinsic_call]
675		_(origin, Box::new(dispatchable));
676	}
677
678	#[benchmark(pov_mode = Measured)]
679	fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) {
680		let mut setup = CallSetup::<T>::new(VmBinaryModule::noop());
681		let (mut ext, module) = setup.ext();
682		let prepared = CallSetup::<T>::prepare_call(&mut ext, module, r.encode(), 0);
683		#[block]
684		{
685			prepared.call().unwrap();
686		}
687	}
688
689	#[benchmark(pov_mode = Measured)]
690	fn seal_caller() {
691		let len = H160::len_bytes();
692		build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
693
694		let result;
695		#[block]
696		{
697			result = runtime.bench_caller(memory.as_mut_slice(), 0);
698		}
699
700		assert_ok!(result);
701		assert_eq!(
702			<H160 as Decode>::decode(&mut &memory[..]).unwrap(),
703			T::AddressMapper::to_address(&runtime.ext().caller().account_id().unwrap())
704		);
705	}
706
707	#[benchmark(pov_mode = Measured)]
708	fn seal_origin() {
709		let len = H160::len_bytes();
710		build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
711
712		let result;
713		#[block]
714		{
715			result = runtime.bench_origin(memory.as_mut_slice(), 0);
716		}
717
718		assert_ok!(result);
719		assert_eq!(
720			<H160 as Decode>::decode(&mut &memory[..]).unwrap(),
721			T::AddressMapper::to_address(&runtime.ext().origin().account_id().unwrap())
722		);
723	}
724
725	#[benchmark(pov_mode = Measured)]
726	fn to_account_id() {
727		// use a mapped address for the benchmark, to ensure that we bench the worst
728		// case (and not the fallback case).
729		let account_id = account("precompile_to_account_id", 0, 0);
730		let address = {
731			T::Currency::set_balance(&account_id, caller_funding::<T>());
732			if !T::AddressMapper::is_mapped(&account_id) {
733				T::AddressMapper::map(&account_id).unwrap();
734			}
735			T::AddressMapper::to_address(&account_id)
736		};
737
738		let input_bytes = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
739			input: address.0.into(),
740		})
741		.abi_encode();
742
743		let mut call_setup = CallSetup::<T>::default();
744		let (mut ext, _) = call_setup.ext();
745
746		let result;
747		#[block]
748		{
749			result = run_builtin_precompile(
750				&mut ext,
751				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
752				input_bytes,
753			);
754		}
755		let raw_data = result.unwrap().data;
756		let data = Bytes::abi_decode(&raw_data).expect("decoding failed");
757		assert_ne!(
758			data.0.as_ref()[20..32],
759			[0xEE; 12],
760			"fallback suffix found where none should be"
761		);
762		assert_eq!(T::AccountId::decode(&mut data.as_ref()), Ok(account_id),);
763	}
764
765	#[benchmark(pov_mode = Measured)]
766	fn seal_code_hash() {
767		let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
768		let len = <sp_core::H256 as MaxEncodedLen>::max_encoded_len() as u32;
769		build_runtime!(runtime, memory: [vec![0u8; len as _], contract.account_id.encode(), ]);
770
771		let result;
772		#[block]
773		{
774			result = runtime.bench_code_hash(memory.as_mut_slice(), len, 0);
775		}
776
777		assert_ok!(result);
778		assert_eq!(
779			<sp_core::H256 as Decode>::decode(&mut &memory[..]).unwrap(),
780			contract.info().unwrap().code_hash
781		);
782	}
783
784	#[benchmark(pov_mode = Measured)]
785	fn own_code_hash() {
786		let input_bytes =
787			ISystem::ISystemCalls::ownCodeHash(ISystem::ownCodeHashCall {}).abi_encode();
788		let mut call_setup = CallSetup::<T>::default();
789		let contract_acc = call_setup.contract().account_id.clone();
790		let caller = call_setup.contract().address;
791		call_setup.set_origin(ExecOrigin::from_account_id(contract_acc));
792		let (mut ext, _) = call_setup.ext();
793
794		let result;
795		#[block]
796		{
797			result = run_builtin_precompile(
798				&mut ext,
799				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
800				input_bytes,
801			);
802		}
803		assert!(result.is_ok());
804		let caller_code_hash = ext.code_hash(&caller);
805		assert_eq!(caller_code_hash.0.to_vec(), result.unwrap().data);
806	}
807
808	#[benchmark(pov_mode = Measured)]
809	fn seal_code_size() {
810		let contract = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
811		build_runtime!(runtime, memory: [contract.address.encode(),]);
812
813		let result;
814		#[block]
815		{
816			result = runtime.bench_code_size(memory.as_mut_slice(), 0);
817		}
818
819		assert_eq!(result.unwrap(), VmBinaryModule::dummy().code.len() as u64);
820	}
821
822	#[benchmark(pov_mode = Measured)]
823	fn caller_is_origin() {
824		let input_bytes =
825			ISystem::ISystemCalls::callerIsOrigin(ISystem::callerIsOriginCall {}).abi_encode();
826
827		let mut call_setup = CallSetup::<T>::default();
828		let (mut ext, _) = call_setup.ext();
829
830		let result;
831		#[block]
832		{
833			result = run_builtin_precompile(
834				&mut ext,
835				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
836				input_bytes,
837			);
838		}
839		let raw_data = result.unwrap().data;
840		let is_origin = Bool::abi_decode(&raw_data[..]).expect("decoding failed");
841		assert!(is_origin);
842	}
843
844	#[benchmark(pov_mode = Measured)]
845	fn caller_is_root() {
846		let input_bytes =
847			ISystem::ISystemCalls::callerIsRoot(ISystem::callerIsRootCall {}).abi_encode();
848
849		let mut setup = CallSetup::<T>::default();
850		setup.set_origin(ExecOrigin::Root);
851		let (mut ext, _) = setup.ext();
852
853		let result;
854		#[block]
855		{
856			result = run_builtin_precompile(
857				&mut ext,
858				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
859				input_bytes,
860			);
861		}
862		let raw_data = result.unwrap().data;
863		let is_root = Bool::abi_decode(&raw_data).expect("decoding failed");
864		assert!(is_root);
865	}
866
867	#[benchmark(pov_mode = Measured)]
868	fn seal_address() {
869		let len = H160::len_bytes();
870		build_runtime!(runtime, memory: [vec![0u8; len as _], ]);
871
872		let result;
873		#[block]
874		{
875			result = runtime.bench_address(memory.as_mut_slice(), 0);
876		}
877		assert_ok!(result);
878		assert_eq!(<H160 as Decode>::decode(&mut &memory[..]).unwrap(), runtime.ext().address());
879	}
880
881	#[benchmark(pov_mode = Measured)]
882	fn weight_left() {
883		let input_bytes =
884			ISystem::ISystemCalls::weightLeft(ISystem::weightLeftCall {}).abi_encode();
885
886		let mut call_setup = CallSetup::<T>::default();
887		let (mut ext, _) = call_setup.ext();
888
889		let weight_left_before = ext.frame_meter().weight_left().unwrap();
890		let result;
891		#[block]
892		{
893			result = run_builtin_precompile(
894				&mut ext,
895				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
896				input_bytes,
897			);
898		}
899		let weight_left_after = ext.frame_meter().weight_left().unwrap();
900		assert_ne!(weight_left_after.ref_time(), 0);
901		assert!(weight_left_before.ref_time() > weight_left_after.ref_time());
902
903		let raw_data = result.unwrap().data;
904		type MyTy = (Uint<64>, Uint<64>);
905		let foo = MyTy::abi_decode(&raw_data[..]).unwrap();
906		assert_eq!(weight_left_after.ref_time(), foo.0);
907	}
908
909	#[benchmark(pov_mode = Measured)]
910	fn seal_ref_time_left() {
911		build_runtime!(runtime, memory: [vec![], ]);
912
913		let result;
914		#[block]
915		{
916			result = runtime.bench_ref_time_left(memory.as_mut_slice());
917		}
918		assert_eq!(result.unwrap(), runtime.ext().gas_left());
919	}
920
921	#[benchmark(pov_mode = Measured)]
922	fn seal_balance() {
923		build_runtime!(runtime, contract, memory: [[0u8;32], ]);
924		contract.set_balance(BalanceWithDust::new_unchecked::<T>(
925			Pallet::<T>::min_balance() * 2u32.into(),
926			42u32,
927		));
928
929		let result;
930		#[block]
931		{
932			result = runtime.bench_balance(memory.as_mut_slice(), 0);
933		}
934		assert_ok!(result);
935		assert_eq!(
936			U256::from_little_endian(&memory[..]),
937			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
938				Pallet::<T>::min_balance(),
939				42
940			))
941		);
942	}
943
944	#[benchmark(pov_mode = Measured)]
945	fn seal_balance_of() {
946		let len = <sp_core::U256 as MaxEncodedLen>::max_encoded_len();
947		let account = account::<T::AccountId>("target", 0, 0);
948		<T as Config>::AddressMapper::map_no_deposit_unchecked(&account).unwrap();
949
950		let address = T::AddressMapper::to_address(&account);
951		let balance = Pallet::<T>::min_balance() * 2u32.into();
952		T::Currency::set_balance(&account, balance);
953		AccountInfoOf::<T>::insert(&address, AccountInfo { dust: 42, ..Default::default() });
954
955		build_runtime!(runtime, memory: [vec![0u8; len], address.0, ]);
956
957		let result;
958		#[block]
959		{
960			result = runtime.bench_balance_of(memory.as_mut_slice(), len as u32, 0);
961		}
962
963		assert_ok!(result);
964		assert_eq!(
965			U256::from_little_endian(&memory[..len]),
966			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(
967				Pallet::<T>::min_balance(),
968				42
969			))
970		);
971	}
972
973	#[benchmark(pov_mode = Measured)]
974	fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
975		let len = n as usize;
976		let immutable_data = vec![1u8; len];
977
978		build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]);
979
980		<ImmutableDataOf<T>>::insert::<_, BoundedVec<_, _>>(
981			contract.address,
982			immutable_data.clone().try_into().unwrap(),
983		);
984
985		let result;
986		#[block]
987		{
988			result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32);
989		}
990
991		assert_ok!(result);
992		assert_eq!(&memory[0..4], (len as u32).encode());
993		assert_eq!(&memory[4..len + 4], &immutable_data);
994	}
995
996	#[benchmark(pov_mode = Measured)]
997	fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
998		let len = n as usize;
999		let mut memory = vec![1u8; len];
1000		let mut setup = CallSetup::<T>::default();
1001		let input = setup.data();
1002		let (mut ext, _) = setup.ext();
1003		ext.override_export(crate::exec::ExportedFunction::Constructor);
1004
1005		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
1006
1007		let result;
1008		#[block]
1009		{
1010			result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n);
1011		}
1012
1013		assert_ok!(result);
1014		assert_eq!(&memory[..], &<ImmutableDataOf<T>>::get(setup.contract().address).unwrap()[..]);
1015	}
1016
1017	#[benchmark(pov_mode = Measured)]
1018	fn seal_value_transferred() {
1019		build_runtime!(runtime, memory: [[0u8;32], ]);
1020		let result;
1021		#[block]
1022		{
1023			result = runtime.bench_value_transferred(memory.as_mut_slice(), 0);
1024		}
1025		assert_ok!(result);
1026		assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().value_transferred());
1027	}
1028
1029	#[benchmark(pov_mode = Measured)]
1030	fn minimum_balance() {
1031		let input_bytes =
1032			ISystem::ISystemCalls::minimumBalance(ISystem::minimumBalanceCall {}).abi_encode();
1033
1034		let mut call_setup = CallSetup::<T>::default();
1035		let (mut ext, _) = call_setup.ext();
1036
1037		let result;
1038		#[block]
1039		{
1040			result = run_builtin_precompile(
1041				&mut ext,
1042				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
1043				input_bytes,
1044			);
1045		}
1046		let min: U256 = crate::Pallet::<T>::convert_native_to_evm(T::Currency::minimum_balance());
1047		let min =
1048			crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&min.to_big_endian())
1049				.unwrap();
1050
1051		let raw_data = result.unwrap().data;
1052		let returned_min =
1053			crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&raw_data)
1054				.expect("decoding failed");
1055		assert_eq!(returned_min, min);
1056	}
1057
1058	#[benchmark(pov_mode = Measured)]
1059	fn seal_return_data_size() {
1060		let mut setup = CallSetup::<T>::default();
1061		let (mut ext, _) = setup.ext();
1062		let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
1063		let mut memory = memory!(vec![],);
1064		*runtime.ext().last_frame_output_mut() =
1065			ExecReturnValue { data: vec![42; 256], ..Default::default() };
1066		let result;
1067		#[block]
1068		{
1069			result = runtime.bench_return_data_size(memory.as_mut_slice());
1070		}
1071		assert_eq!(result.unwrap(), 256);
1072	}
1073
1074	#[benchmark(pov_mode = Measured)]
1075	fn seal_call_data_size() {
1076		let mut setup = CallSetup::<T>::default();
1077		let (mut ext, _) = setup.ext();
1078		let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 128 as usize]);
1079		let mut memory = memory!(vec![0u8; 4],);
1080		let result;
1081		#[block]
1082		{
1083			result = runtime.bench_call_data_size(memory.as_mut_slice());
1084		}
1085		assert_eq!(result.unwrap(), 128);
1086	}
1087
1088	#[benchmark(pov_mode = Measured)]
1089	fn seal_gas_limit() {
1090		build_runtime!(runtime, memory: []);
1091		let result;
1092		#[block]
1093		{
1094			result = runtime.bench_gas_limit(&mut memory);
1095		}
1096		assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_block_gas_limit());
1097	}
1098
1099	#[benchmark(pov_mode = Measured)]
1100	fn seal_gas_price() {
1101		build_runtime!(runtime, memory: []);
1102		let result;
1103		#[block]
1104		{
1105			result = runtime.bench_gas_price(memory.as_mut_slice());
1106		}
1107		assert_eq!(U256::from(result.unwrap()), <Pallet<T>>::evm_base_fee());
1108	}
1109
1110	#[benchmark(pov_mode = Measured)]
1111	fn seal_base_fee() {
1112		build_runtime!(runtime, memory: [[1u8;32], ]);
1113		let result;
1114		#[block]
1115		{
1116			result = runtime.bench_base_fee(memory.as_mut_slice(), 0);
1117		}
1118		assert_ok!(result);
1119		assert_eq!(U256::from_little_endian(&memory[..]), <crate::Pallet<T>>::evm_base_fee());
1120	}
1121
1122	#[benchmark(pov_mode = Measured)]
1123	fn seal_block_number() {
1124		build_runtime!(runtime, memory: [[0u8;32], ]);
1125		let result;
1126		#[block]
1127		{
1128			result = runtime.bench_block_number(memory.as_mut_slice(), 0);
1129		}
1130		assert_ok!(result);
1131		assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().block_number());
1132	}
1133
1134	#[benchmark(pov_mode = Measured)]
1135	fn seal_block_author() {
1136		build_runtime!(runtime, memory: [[123u8; 20], ]);
1137
1138		// The pre-runtime digest log is unbounded; usually around 3 items but it can vary.
1139		// To get safe benchmark results despite that, populate it with a bunch of random logs to
1140		// ensure iteration over many items (we just overestimate the cost of the API).
1141		for i in 0..16 {
1142			frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1143				[i, i, i, i],
1144				vec![i; 128],
1145			));
1146			frame_system::Pallet::<T>::deposit_log(DigestItem::Consensus(
1147				[i, i, i, i],
1148				vec![i; 128],
1149			));
1150			frame_system::Pallet::<T>::deposit_log(DigestItem::Seal([i, i, i, i], vec![i; 128]));
1151			frame_system::Pallet::<T>::deposit_log(DigestItem::Other(vec![i; 128]));
1152		}
1153
1154		// The content of the pre-runtime digest log depends on the configured consensus.
1155		// However, mismatching logs are simply ignored. Thus we construct fixtures which will
1156		// let the API to return a value in both BABE and AURA consensus.
1157
1158		// Construct a `Digest` log fixture returning some value in BABE
1159		let primary_pre_digest = vec![0; <PrimaryPreDigest as MaxEncodedLen>::max_encoded_len()];
1160		let pre_digest =
1161			PreDigest::Primary(PrimaryPreDigest::decode(&mut &primary_pre_digest[..]).unwrap());
1162		frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1163			BABE_ENGINE_ID,
1164			pre_digest.encode(),
1165		));
1166		frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(
1167			BABE_ENGINE_ID,
1168			pre_digest.encode(),
1169		));
1170
1171		// Construct a `Digest` log fixture returning some value in AURA
1172		let slot = Slot::default();
1173		frame_system::Pallet::<T>::deposit_log(DigestItem::PreRuntime(
1174			AURA_ENGINE_ID,
1175			slot.encode(),
1176		));
1177		frame_system::Pallet::<T>::deposit_log(DigestItem::Seal(AURA_ENGINE_ID, slot.encode()));
1178
1179		let result;
1180		#[block]
1181		{
1182			result = runtime.bench_block_author(memory.as_mut_slice(), 0);
1183		}
1184		assert_ok!(result);
1185
1186		let block_author = runtime.ext().block_author();
1187		assert_eq!(&memory[..], block_author.as_bytes());
1188	}
1189
1190	#[benchmark(pov_mode = Measured)]
1191	fn seal_block_hash() {
1192		let mut memory = vec![0u8; 64];
1193		let mut setup = CallSetup::<T>::default();
1194		let input = setup.data();
1195		let (mut ext, _) = setup.ext();
1196		ext.set_block_number(BlockNumberFor::<T>::from(1u32));
1197
1198		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, input);
1199
1200		let block_hash = H256::from([1; 32]);
1201
1202		// Store block hash in pallet-revive BlockHash mapping
1203		crate::BlockHash::<T>::insert(crate::BlockNumberFor::<T>::from(0u32), block_hash);
1204
1205		let result;
1206		#[block]
1207		{
1208			result = runtime.bench_block_hash(memory.as_mut_slice(), 32, 0);
1209		}
1210		assert_ok!(result);
1211		assert_eq!(&memory[..32], &block_hash.0);
1212	}
1213
1214	#[benchmark(pov_mode = Measured)]
1215	fn seal_now() {
1216		build_runtime!(runtime, memory: [[0u8;32], ]);
1217		let result;
1218		#[block]
1219		{
1220			result = runtime.bench_now(memory.as_mut_slice(), 0);
1221		}
1222		assert_ok!(result);
1223		assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().now());
1224	}
1225
1226	#[benchmark(pov_mode = Measured)]
1227	fn seal_copy_to_contract(n: Linear<0, { limits::code::BLOB_BYTES - 4 }>) {
1228		let mut setup = CallSetup::<T>::default();
1229		let (mut ext, _) = setup.ext();
1230		let mut runtime = pvm::Runtime::new(&mut ext, vec![]);
1231		let mut memory = memory!(n.encode(), vec![0u8; n as usize],);
1232		let result;
1233		#[block]
1234		{
1235			result = runtime.write_sandbox_output(
1236				memory.as_mut_slice(),
1237				4,
1238				0,
1239				&vec![42u8; n as usize],
1240				false,
1241				|_| None,
1242			);
1243		}
1244		assert_ok!(result);
1245		assert_eq!(&memory[..4], &n.encode());
1246		assert_eq!(&memory[4..], &vec![42u8; n as usize]);
1247	}
1248
1249	#[benchmark(pov_mode = Measured)]
1250	fn seal_call_data_load() {
1251		let mut setup = CallSetup::<T>::default();
1252		let (mut ext, _) = setup.ext();
1253		let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; 32]);
1254		let mut memory = memory!(vec![0u8; 32],);
1255		let result;
1256		#[block]
1257		{
1258			result = runtime.bench_call_data_load(memory.as_mut_slice(), 0, 0);
1259		}
1260		assert_ok!(result);
1261		assert_eq!(&memory[..], &vec![42u8; 32]);
1262	}
1263
1264	#[benchmark(pov_mode = Measured)]
1265	fn seal_call_data_copy(n: Linear<0, { limits::code::BLOB_BYTES }>) {
1266		let mut setup = CallSetup::<T>::default();
1267		let (mut ext, _) = setup.ext();
1268		let mut runtime = pvm::Runtime::new(&mut ext, vec![42u8; n as usize]);
1269		let mut memory = memory!(vec![0u8; n as usize],);
1270		let result;
1271		#[block]
1272		{
1273			result = runtime.bench_call_data_copy(memory.as_mut_slice(), 0, n, 0);
1274		}
1275		assert_ok!(result);
1276		assert_eq!(&memory[..], &vec![42u8; n as usize]);
1277	}
1278
1279	#[benchmark(pov_mode = Measured)]
1280	fn seal_return(n: Linear<0, { limits::CALLDATA_BYTES }>) {
1281		build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]);
1282
1283		let result;
1284		#[block]
1285		{
1286			result = runtime.bench_seal_return(memory.as_mut_slice(), 0, 0, n);
1287		}
1288
1289		assert!(matches!(
1290			result,
1291			Err(crate::vm::pvm::TrapReason::Return(crate::vm::pvm::ReturnData { .. }))
1292		));
1293	}
1294
1295	/// Benchmark the ocst of terminating a contract.
1296	///
1297	/// `r`: whether the old code will be removed as a result of this operation. (1: yes, 0: no)
1298	#[benchmark(pov_mode = Measured)]
1299	fn seal_terminate(r: Linear<0, 1>) -> Result<(), BenchmarkError> {
1300		let delete_code = r == 1;
1301		let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1302
1303		build_runtime!(runtime, instance, memory: [beneficiary.encode(),]);
1304		let code_hash = instance.info()?.code_hash;
1305
1306		// Increment the refcount of the code hash so that it does not get deleted
1307		if !delete_code {
1308			<CodeInfo<T>>::increment_refcount(code_hash).unwrap();
1309		}
1310
1311		let result;
1312		#[block]
1313		{
1314			result = runtime.bench_terminate(memory.as_mut_slice(), 0);
1315		}
1316
1317		assert!(matches!(result, Err(crate::vm::pvm::TrapReason::Termination)));
1318
1319		Ok(())
1320	}
1321
1322	#[benchmark(pov_mode = Measured)]
1323	fn seal_terminate_logic() -> Result<(), BenchmarkError> {
1324		let caller = whitelisted_caller();
1325		let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
1326		T::AddressMapper::map_no_deposit_unchecked(&beneficiary)?;
1327
1328		build_runtime!(_runtime, instance, _memory: [vec![0u8; 0], ]);
1329		let code_hash = instance.info()?.code_hash;
1330
1331		assert!(PristineCode::<T>::get(code_hash).is_some());
1332
1333		T::Currency::set_balance(&instance.account_id, Pallet::<T>::min_balance() * 10u32.into());
1334
1335		let storage_deposit = T::Currency::balance_on_hold(
1336			&HoldReason::StorageDepositReserve.into(),
1337			&instance.account_id,
1338		);
1339		NativeDepositOf::<T>::insert(&instance.account_id, &caller, storage_deposit);
1340
1341		let mut transaction_meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
1342			weight_limit: Default::default(),
1343			deposit_limit: BalanceOf::<T>::max_value(),
1344		})
1345		.unwrap();
1346		let exec_config = ExecConfig::new_substrate_tx();
1347		let contract_account = &instance.account_id;
1348		let origin = &ExecOrigin::from_account_id(caller);
1349		let beneficiary_clone = beneficiary.clone();
1350		let trie_id = instance.info()?.trie_id.clone();
1351		let code_hash = instance.info()?.code_hash;
1352		let only_if_same_tx = false;
1353
1354		let result;
1355		#[block]
1356		{
1357			result = crate::exec::bench_do_terminate::<T>(
1358				&mut transaction_meter,
1359				&exec_config,
1360				contract_account,
1361				&origin,
1362				beneficiary_clone,
1363				trie_id,
1364				code_hash,
1365				only_if_same_tx,
1366			);
1367		}
1368		result.unwrap();
1369
1370		// Check that the contract is removed
1371		assert!(PristineCode::<T>::get(code_hash).is_none());
1372
1373		// Check that the balance has been transferred away
1374		let balance = <T as Config>::Currency::total_balance(&instance.account_id);
1375		assert_eq!(balance, 0u32.into());
1376
1377		// Check that the beneficiary received the balance
1378		let balance = <T as Config>::Currency::balance(&beneficiary);
1379		assert_eq!(balance, Pallet::<T>::min_balance() + Pallet::<T>::min_balance() * 9u32.into());
1380
1381		Ok(())
1382	}
1383
1384	// Benchmark the overhead that topics generate.
1385	// `t`: Number of topics
1386	// `n`: Size of event payload in bytes
1387	#[benchmark(pov_mode = Measured)]
1388	fn seal_deposit_event(
1389		t: Linear<0, { limits::NUM_EVENT_TOPICS as u32 }>,
1390		n: Linear<0, { limits::EVENT_BYTES }>,
1391	) {
1392		let num_topic = t as u32;
1393		let topics = (0..t).map(|i| H256::repeat_byte(i as u8)).collect::<Vec<_>>();
1394		let topics_data =
1395			topics.iter().flat_map(|hash| hash.as_bytes().to_vec()).collect::<Vec<u8>>();
1396		let data = vec![42u8; n as _];
1397		build_runtime!(runtime, instance, memory: [ topics_data, data, ]);
1398
1399		let result;
1400		#[block]
1401		{
1402			result = runtime.bench_deposit_event(
1403				memory.as_mut_slice(),
1404				0, // topics_ptr
1405				num_topic,
1406				topics_data.len() as u32, // data_ptr
1407				n,                        // data_len
1408			);
1409		}
1410		assert_ok!(result);
1411
1412		let events = System::<T>::events();
1413		let record = &events[events.len() - 1];
1414
1415		assert_eq!(
1416			record.event,
1417			crate::Event::ContractEmitted { contract: instance.address, data, topics }.into(),
1418		);
1419	}
1420
1421	#[benchmark(skip_meta, pov_mode = Measured)]
1422	fn get_storage_empty() -> Result<(), BenchmarkError> {
1423		let max_key_len = limits::STORAGE_KEY_BYTES;
1424		let key = vec![0u8; max_key_len as usize];
1425		let max_value_len = limits::STORAGE_BYTES as usize;
1426		let value = vec![1u8; max_value_len];
1427
1428		let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1429		let info = instance.info()?;
1430		let child_trie_info = info.child_trie_info();
1431		info.bench_write_raw(&key, Some(value.clone()), false)
1432			.map_err(|_| "Failed to write to storage during setup.")?;
1433
1434		let result;
1435		#[block]
1436		{
1437			result = child::get_raw(&child_trie_info, &key);
1438		}
1439
1440		assert_eq!(result, Some(value));
1441		Ok(())
1442	}
1443
1444	#[benchmark(skip_meta, pov_mode = Measured)]
1445	fn get_storage_full() -> Result<(), BenchmarkError> {
1446		let max_key_len = limits::STORAGE_KEY_BYTES;
1447		let key = vec![0u8; max_key_len as usize];
1448		let max_value_len = limits::STORAGE_BYTES;
1449		let value = vec![1u8; max_value_len as usize];
1450
1451		let instance = Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?;
1452		let info = instance.info()?;
1453		let child_trie_info = info.child_trie_info();
1454		info.bench_write_raw(&key, Some(value.clone()), false)
1455			.map_err(|_| "Failed to write to storage during setup.")?;
1456
1457		let result;
1458		#[block]
1459		{
1460			result = child::get_raw(&child_trie_info, &key);
1461		}
1462
1463		assert_eq!(result, Some(value));
1464		Ok(())
1465	}
1466
1467	#[benchmark(skip_meta, pov_mode = Measured)]
1468	fn set_storage_empty() -> Result<(), BenchmarkError> {
1469		let max_key_len = limits::STORAGE_KEY_BYTES;
1470		let key = vec![0u8; max_key_len as usize];
1471		let max_value_len = limits::STORAGE_BYTES as usize;
1472		let value = vec![1u8; max_value_len];
1473
1474		let instance = Contract::<T>::new(VmBinaryModule::dummy(), vec![])?;
1475		let info = instance.info()?;
1476		let child_trie_info = info.child_trie_info();
1477		info.bench_write_raw(&key, Some(vec![42u8; max_value_len]), false)
1478			.map_err(|_| "Failed to write to storage during setup.")?;
1479
1480		let val = Some(value.clone());
1481		let result;
1482		#[block]
1483		{
1484			result = info.bench_write_raw(&key, val, true);
1485		}
1486
1487		assert_ok!(result);
1488		assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
1489		Ok(())
1490	}
1491
1492	#[benchmark(skip_meta, pov_mode = Measured)]
1493	fn set_storage_full() -> Result<(), BenchmarkError> {
1494		let max_key_len = limits::STORAGE_KEY_BYTES;
1495		let key = vec![0u8; max_key_len as usize];
1496		let max_value_len = limits::STORAGE_BYTES;
1497		let value = vec![1u8; max_value_len as usize];
1498
1499		let instance = Contract::<T>::with_unbalanced_storage_trie(VmBinaryModule::dummy(), &key)?;
1500		let info = instance.info()?;
1501		let child_trie_info = info.child_trie_info();
1502		info.bench_write_raw(&key, Some(vec![42u8; max_value_len as usize]), false)
1503			.map_err(|_| "Failed to write to storage during setup.")?;
1504
1505		let val = Some(value.clone());
1506		let result;
1507		#[block]
1508		{
1509			result = info.bench_write_raw(&key, val, true);
1510		}
1511
1512		assert_ok!(result);
1513		assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
1514		Ok(())
1515	}
1516
1517	// n: new byte size
1518	// o: old byte size
1519	#[benchmark(skip_meta, pov_mode = Measured)]
1520	fn seal_set_storage(
1521		n: Linear<0, { limits::STORAGE_BYTES }>,
1522		o: Linear<0, { limits::STORAGE_BYTES }>,
1523	) -> Result<(), BenchmarkError> {
1524		let max_key_len = limits::STORAGE_KEY_BYTES;
1525		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1526			.map_err(|_| "Key has wrong length")?;
1527		let value = vec![1u8; n as usize];
1528
1529		build_runtime!(runtime, instance, memory: [ key.unhashed(), value.clone(), ]);
1530		let info = instance.info()?;
1531
1532		info.write(&key, Some(vec![42u8; o as usize]), None, false)
1533			.map_err(|_| "Failed to write to storage during setup.")?;
1534
1535		let result;
1536		#[block]
1537		{
1538			result = runtime.bench_set_storage(
1539				memory.as_mut_slice(),
1540				StorageFlags::empty().bits(),
1541				0,           // key_ptr
1542				max_key_len, // key_len
1543				max_key_len, // value_ptr
1544				n,           // value_len
1545			);
1546		}
1547
1548		assert_ok!(result);
1549		assert_eq!(info.read(&key).unwrap(), value);
1550		Ok(())
1551	}
1552
1553	#[benchmark(skip_meta, pov_mode = Measured)]
1554	fn clear_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1555		let max_key_len = limits::STORAGE_KEY_BYTES;
1556		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1557			.map_err(|_| "Key has wrong length")?;
1558
1559		let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
1560			flags: StorageFlags::empty().bits(),
1561			key: vec![0u8; max_key_len as usize].into(),
1562			isFixedKey: false,
1563		})
1564		.abi_encode();
1565
1566		let mut call_setup = CallSetup::<T>::default();
1567		let (mut ext, _) = call_setup.ext();
1568		ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1569			.map_err(|_| "Failed to write to storage during setup.")?;
1570
1571		let result;
1572		#[block]
1573		{
1574			result = run_builtin_precompile(
1575				&mut ext,
1576				H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1577				input_bytes,
1578			);
1579		}
1580		assert_ok!(result);
1581		assert!(ext.get_storage(&key).is_none());
1582
1583		Ok(())
1584	}
1585
1586	#[benchmark(skip_meta, pov_mode = Measured)]
1587	fn seal_get_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
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		build_runtime!(runtime, instance, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1592		let info = instance.info()?;
1593
1594		info.write(&key, Some(vec![42u8; n as usize]), None, false)
1595			.map_err(|_| "Failed to write to storage during setup.")?;
1596
1597		let out_ptr = max_key_len + 4;
1598		let result;
1599		#[block]
1600		{
1601			result = runtime.bench_get_storage(
1602				memory.as_mut_slice(),
1603				StorageFlags::empty().bits(),
1604				0,           // key_ptr
1605				max_key_len, // key_len
1606				out_ptr,     // out_ptr
1607				max_key_len, // out_len_ptr
1608			);
1609		}
1610
1611		assert_ok!(result);
1612		assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
1613		Ok(())
1614	}
1615
1616	#[benchmark(skip_meta, pov_mode = Measured)]
1617	fn contains_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1618		let max_key_len = limits::STORAGE_KEY_BYTES;
1619		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1620			.map_err(|_| "Key has wrong length")?;
1621		let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
1622			flags: StorageFlags::empty().bits(),
1623			key: vec![0u8; max_key_len as usize].into(),
1624			isFixedKey: false,
1625		})
1626		.abi_encode();
1627
1628		let mut call_setup = CallSetup::<T>::default();
1629		let (mut ext, _) = call_setup.ext();
1630		ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1631			.map_err(|_| "Failed to write to storage during setup.")?;
1632
1633		let result;
1634		#[block]
1635		{
1636			result = run_builtin_precompile(
1637				&mut ext,
1638				H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1639				input_bytes,
1640			);
1641		}
1642		assert_ok!(result);
1643		assert!(ext.get_storage(&key).is_some());
1644
1645		Ok(())
1646	}
1647
1648	#[benchmark(skip_meta, pov_mode = Measured)]
1649	fn take_storage(n: Linear<0, { limits::STORAGE_BYTES }>) -> Result<(), BenchmarkError> {
1650		let max_key_len = limits::STORAGE_KEY_BYTES;
1651		let key = Key::try_from_var(vec![3u8; max_key_len as usize])
1652			.map_err(|_| "Key has wrong length")?;
1653
1654		let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
1655			flags: StorageFlags::empty().bits(),
1656			key: vec![3u8; max_key_len as usize].into(),
1657			isFixedKey: false,
1658		})
1659		.abi_encode();
1660
1661		let mut call_setup = CallSetup::<T>::default();
1662		let (mut ext, _) = call_setup.ext();
1663		ext.set_storage(&key, Some(vec![42u8; n as usize]), false)
1664			.map_err(|_| "Failed to write to storage during setup.")?;
1665
1666		let result;
1667		#[block]
1668		{
1669			result = run_builtin_precompile(
1670				&mut ext,
1671				H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1672				input_bytes,
1673			);
1674		}
1675		assert_ok!(result);
1676		assert!(ext.get_storage(&key).is_none());
1677
1678		Ok(())
1679	}
1680
1681	// We use both full and empty benchmarks here instead of benchmarking transient_storage
1682	// (BTreeMap) directly. This approach is necessary because benchmarking this BTreeMap is very
1683	// slow. Additionally, we use linear regression for our benchmarks, and the BTreeMap's log(n)
1684	// complexity can introduce approximation errors.
1685	#[benchmark(pov_mode = Ignored)]
1686	fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
1687		let max_value_len = limits::STORAGE_BYTES;
1688		let max_key_len = limits::STORAGE_KEY_BYTES;
1689		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1690			.map_err(|_| "Key has wrong length")?;
1691		let value = Some(vec![42u8; max_value_len as _]);
1692		let mut setup = CallSetup::<T>::default();
1693		let (mut ext, _) = setup.ext();
1694		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1695		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1696		let result;
1697		#[block]
1698		{
1699			result = runtime.ext().set_transient_storage(&key, value, false);
1700		}
1701
1702		assert_eq!(result, Ok(WriteOutcome::New));
1703		assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
1704		Ok(())
1705	}
1706
1707	#[benchmark(pov_mode = Ignored)]
1708	fn set_transient_storage_full() -> Result<(), BenchmarkError> {
1709		let max_value_len = limits::STORAGE_BYTES;
1710		let max_key_len = limits::STORAGE_KEY_BYTES;
1711		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1712			.map_err(|_| "Key has wrong length")?;
1713		let value = Some(vec![42u8; max_value_len as _]);
1714		let mut setup = CallSetup::<T>::default();
1715		setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
1716		let (mut ext, _) = setup.ext();
1717		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1718		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1719		let result;
1720		#[block]
1721		{
1722			result = runtime.ext().set_transient_storage(&key, value, false);
1723		}
1724
1725		assert_eq!(result, Ok(WriteOutcome::New));
1726		assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
1727		Ok(())
1728	}
1729
1730	#[benchmark(pov_mode = Ignored)]
1731	fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
1732		let max_value_len = limits::STORAGE_BYTES;
1733		let max_key_len = limits::STORAGE_KEY_BYTES;
1734		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1735			.map_err(|_| "Key has wrong length")?;
1736
1737		let mut setup = CallSetup::<T>::default();
1738		let (mut ext, _) = setup.ext();
1739		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1740		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1741		runtime
1742			.ext()
1743			.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1744			.map_err(|_| "Failed to write to transient storage during setup.")?;
1745		let result;
1746		#[block]
1747		{
1748			result = runtime.ext().get_transient_storage(&key);
1749		}
1750
1751		assert_eq!(result, Some(vec![42u8; max_value_len as _]));
1752		Ok(())
1753	}
1754
1755	#[benchmark(pov_mode = Ignored)]
1756	fn get_transient_storage_full() -> Result<(), BenchmarkError> {
1757		let max_value_len = limits::STORAGE_BYTES;
1758		let max_key_len = limits::STORAGE_KEY_BYTES;
1759		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1760			.map_err(|_| "Key has wrong length")?;
1761
1762		let mut setup = CallSetup::<T>::default();
1763		setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
1764		let (mut ext, _) = setup.ext();
1765		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1766		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1767		runtime
1768			.ext()
1769			.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1770			.map_err(|_| "Failed to write to transient storage during setup.")?;
1771		let result;
1772		#[block]
1773		{
1774			result = runtime.ext().get_transient_storage(&key);
1775		}
1776
1777		assert_eq!(result, Some(vec![42u8; max_value_len as _]));
1778		Ok(())
1779	}
1780
1781	// The weight of journal rollbacks should be taken into account when setting storage.
1782	#[benchmark(pov_mode = Ignored)]
1783	fn rollback_transient_storage() -> Result<(), BenchmarkError> {
1784		let max_value_len = limits::STORAGE_BYTES;
1785		let max_key_len = limits::STORAGE_KEY_BYTES;
1786		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1787			.map_err(|_| "Key has wrong length")?;
1788
1789		let mut setup = CallSetup::<T>::default();
1790		setup.set_transient_storage_size(limits::TRANSIENT_STORAGE_BYTES);
1791		let (mut ext, _) = setup.ext();
1792		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
1793		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1794		runtime.ext().transient_storage().start_transaction();
1795		runtime
1796			.ext()
1797			.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1798			.map_err(|_| "Failed to write to transient storage during setup.")?;
1799		#[block]
1800		{
1801			runtime.ext().transient_storage().rollback_transaction();
1802		}
1803
1804		assert_eq!(runtime.ext().get_transient_storage(&key), None);
1805		Ok(())
1806	}
1807
1808	// n: new byte size
1809	// o: old byte size
1810	#[benchmark(pov_mode = Measured)]
1811	fn seal_set_transient_storage(
1812		n: Linear<0, { limits::STORAGE_BYTES }>,
1813		o: Linear<0, { limits::STORAGE_BYTES }>,
1814	) -> Result<(), BenchmarkError> {
1815		let max_key_len = limits::STORAGE_KEY_BYTES;
1816		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1817			.map_err(|_| "Key has wrong length")?;
1818		let value = vec![1u8; n as usize];
1819		build_runtime!(runtime, memory: [ key.unhashed(), value.clone(), ]);
1820		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1821		runtime
1822			.ext()
1823			.set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
1824			.map_err(|_| "Failed to write to transient storage during setup.")?;
1825
1826		let result;
1827		#[block]
1828		{
1829			result = runtime.bench_set_storage(
1830				memory.as_mut_slice(),
1831				StorageFlags::TRANSIENT.bits(),
1832				0,           // key_ptr
1833				max_key_len, // key_len
1834				max_key_len, // value_ptr
1835				n,           // value_len
1836			);
1837		}
1838
1839		assert_ok!(result);
1840		assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
1841		Ok(())
1842	}
1843
1844	#[benchmark(pov_mode = Measured)]
1845	fn seal_clear_transient_storage(
1846		n: Linear<0, { limits::STORAGE_BYTES }>,
1847	) -> Result<(), BenchmarkError> {
1848		let max_key_len = limits::STORAGE_KEY_BYTES;
1849		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1850			.map_err(|_| "Key has wrong length")?;
1851		let input_bytes = IStorage::IStorageCalls::clearStorage(IStorage::clearStorageCall {
1852			flags: StorageFlags::TRANSIENT.bits(),
1853			key: vec![0u8; max_key_len as usize].into(),
1854			isFixedKey: false,
1855		})
1856		.abi_encode();
1857
1858		let mut call_setup = CallSetup::<T>::default();
1859		let (mut ext, _) = call_setup.ext();
1860		ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1861			.map_err(|_| "Failed to write to transient storage during setup.")?;
1862
1863		let result;
1864		#[block]
1865		{
1866			result = run_builtin_precompile(
1867				&mut ext,
1868				H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1869				input_bytes,
1870			);
1871		}
1872		assert_ok!(result);
1873		assert!(ext.get_transient_storage(&key).is_none());
1874
1875		Ok(())
1876	}
1877
1878	#[benchmark(pov_mode = Measured)]
1879	fn seal_get_transient_storage(
1880		n: Linear<0, { limits::STORAGE_BYTES }>,
1881	) -> Result<(), BenchmarkError> {
1882		let max_key_len = limits::STORAGE_KEY_BYTES;
1883		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1884			.map_err(|_| "Key has wrong length")?;
1885		build_runtime!(runtime, memory: [ key.unhashed(), n.to_le_bytes(), vec![0u8; n as _], ]);
1886		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1887		runtime
1888			.ext()
1889			.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1890			.map_err(|_| "Failed to write to transient storage during setup.")?;
1891
1892		let out_ptr = max_key_len + 4;
1893		let result;
1894		#[block]
1895		{
1896			result = runtime.bench_get_storage(
1897				memory.as_mut_slice(),
1898				StorageFlags::TRANSIENT.bits(),
1899				0,           // key_ptr
1900				max_key_len, // key_len
1901				out_ptr,     // out_ptr
1902				max_key_len, // out_len_ptr
1903			);
1904		}
1905
1906		assert_ok!(result);
1907		assert_eq!(
1908			&runtime.ext().get_transient_storage(&key).unwrap(),
1909			&memory[out_ptr as usize..]
1910		);
1911		Ok(())
1912	}
1913
1914	#[benchmark(pov_mode = Measured)]
1915	fn seal_contains_transient_storage(
1916		n: Linear<0, { limits::STORAGE_BYTES }>,
1917	) -> Result<(), BenchmarkError> {
1918		let max_key_len = limits::STORAGE_KEY_BYTES;
1919		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1920			.map_err(|_| "Key has wrong length")?;
1921
1922		let input_bytes = IStorage::IStorageCalls::containsStorage(IStorage::containsStorageCall {
1923			flags: StorageFlags::TRANSIENT.bits(),
1924			key: vec![0u8; max_key_len as usize].into(),
1925			isFixedKey: false,
1926		})
1927		.abi_encode();
1928
1929		let mut call_setup = CallSetup::<T>::default();
1930		let (mut ext, _) = call_setup.ext();
1931		ext.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1932			.map_err(|_| "Failed to write to transient storage during setup.")?;
1933
1934		let result;
1935		#[block]
1936		{
1937			result = run_builtin_precompile(
1938				&mut ext,
1939				H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1940				input_bytes,
1941			);
1942		}
1943		assert!(result.is_ok());
1944		assert!(ext.get_transient_storage(&key).is_some());
1945
1946		Ok(())
1947	}
1948
1949	#[benchmark(pov_mode = Measured)]
1950	fn seal_take_transient_storage(
1951		n: Linear<0, { limits::STORAGE_BYTES }>,
1952	) -> Result<(), BenchmarkError> {
1953		let n = limits::STORAGE_BYTES;
1954		let value = vec![42u8; n as usize];
1955		let max_key_len = limits::STORAGE_KEY_BYTES;
1956		let key = Key::try_from_var(vec![0u8; max_key_len as usize])
1957			.map_err(|_| "Key has wrong length")?;
1958
1959		let input_bytes = IStorage::IStorageCalls::takeStorage(IStorage::takeStorageCall {
1960			flags: StorageFlags::TRANSIENT.bits(),
1961			key: vec![0u8; max_key_len as usize].into(),
1962			isFixedKey: false,
1963		})
1964		.abi_encode();
1965
1966		let mut call_setup = CallSetup::<T>::default();
1967		let (mut ext, _) = call_setup.ext();
1968		ext.set_transient_storage(&key, Some(value), false)
1969			.map_err(|_| "Failed to write to transient storage during setup.")?;
1970
1971		let result;
1972		#[block]
1973		{
1974			result = run_builtin_precompile(
1975				&mut ext,
1976				H160(BenchmarkStorage::<T>::MATCHER.base_address()).as_fixed_bytes(),
1977				input_bytes,
1978			);
1979		}
1980		assert!(result.is_ok());
1981		assert!(ext.get_transient_storage(&key).is_none());
1982
1983		Ok(())
1984	}
1985
1986	// t: with or without some value to transfer
1987	// d: with or without dust value to transfer
1988	// i: size of the input data
1989	#[benchmark(pov_mode = Measured)]
1990	fn seal_call(t: Linear<0, 1>, d: Linear<0, 1>, i: Linear<0, { limits::code::BLOB_BYTES }>) {
1991		let Contract { account_id: callee, address: callee_addr, .. } =
1992			Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
1993
1994		let callee_bytes = callee.encode();
1995		let callee_len = callee_bytes.len() as u32;
1996
1997		let value: BalanceOf<T> = (1_000_000u32 * t).into();
1998		let dust = 100u32 * d;
1999		let evm_value =
2000			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2001		let value_bytes = evm_value.encode();
2002
2003		let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2004		let deposit_bytes = Into::<U256>::into(deposit).encode();
2005		let deposit_len = deposit_bytes.len() as u32;
2006
2007		let mut setup = CallSetup::<T>::default();
2008		setup.set_storage_deposit_limit(deposit);
2009		// We benchmark the overhead of cloning the input. Not passing it to the contract.
2010		// This is why we set the input here instead of passig it as pointer to the `bench_call`.
2011		setup.set_data(vec![42; i as usize]);
2012		setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2013		setup.set_balance(value + 1u32.into() + Pallet::<T>::min_balance());
2014
2015		let (mut ext, _) = setup.ext();
2016		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2017		let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
2018
2019		let result;
2020		#[block]
2021		{
2022			result = runtime.bench_call(
2023				memory.as_mut_slice(),
2024				pack_hi_lo(CallFlags::CLONE_INPUT.bits(), 0), // flags + callee
2025				u64::MAX,                                     // ref_time_limit
2026				u64::MAX,                                     // proof_size_limit
2027				pack_hi_lo(callee_len, callee_len + deposit_len), // deposit_ptr + value_pr
2028				pack_hi_lo(0, 0),                             // input len + data ptr
2029				pack_hi_lo(0, SENTINEL),                      // output len + data ptr
2030			);
2031		}
2032
2033		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2034		assert_eq!(
2035			Pallet::<T>::evm_balance(&callee_addr),
2036			evm_value,
2037			"{callee_addr:?} balance should hold {evm_value:?}"
2038		);
2039	}
2040
2041	// d: 1 if the associated pre-compile has a contract info that needs to be loaded
2042	// i: size of the input data
2043	#[benchmark(pov_mode = Measured)]
2044	fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::CALLDATA_BYTES - 100 }>) {
2045		use alloy_core::sol_types::SolInterface;
2046		use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking};
2047
2048		let callee_bytes = if d == 1 {
2049			BenchmarkWithInfo::<T>::MATCHER.base_address().to_vec()
2050		} else {
2051			BenchmarkNoInfo::<T>::MATCHER.base_address().to_vec()
2052		};
2053		let callee_len = callee_bytes.len() as u32;
2054
2055		let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2056		let deposit_bytes = Into::<U256>::into(deposit).encode();
2057		let deposit_len = deposit_bytes.len() as u32;
2058
2059		let value: BalanceOf<T> = Zero::zero();
2060		let value_bytes = Into::<U256>::into(value).encode();
2061		let value_len = value_bytes.len() as u32;
2062
2063		let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall {
2064			input: vec![42_u8; i as usize].into(),
2065		})
2066		.abi_encode();
2067		let input_len = input_bytes.len() as u32;
2068
2069		let mut setup = CallSetup::<T>::default();
2070		setup.set_storage_deposit_limit(deposit);
2071
2072		let (mut ext, _) = setup.ext();
2073		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2074		let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,);
2075
2076		let mut do_benchmark = || {
2077			runtime.bench_call(
2078				memory.as_mut_slice(),
2079				pack_hi_lo(0, 0), // flags + callee
2080				u64::MAX,         // ref_time_limit
2081				u64::MAX,         // proof_size_limit
2082				pack_hi_lo(callee_len, callee_len + deposit_len), /* deposit_ptr +
2083				                   * value_pr */
2084				pack_hi_lo(input_len, callee_len + deposit_len + value_len), /* input len +
2085				                                                              * input ptr */
2086				pack_hi_lo(0, SENTINEL), // output len + output ptr
2087			)
2088		};
2089
2090		// first call of the pre-compile will create its contract info and account
2091		// so we make sure to create it
2092		assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success);
2093
2094		let result;
2095		#[block]
2096		{
2097			result = do_benchmark();
2098		}
2099
2100		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2101	}
2102
2103	#[benchmark(pov_mode = Measured)]
2104	fn seal_delegate_call() -> Result<(), BenchmarkError> {
2105		let Contract { account_id: address, .. } =
2106			Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![]).unwrap();
2107
2108		let address_bytes = address.encode();
2109		let address_len = address_bytes.len() as u32;
2110
2111		let deposit: BalanceOf<T> = (u32::MAX - 100).into();
2112		let deposit_bytes = Into::<U256>::into(deposit).encode();
2113
2114		let mut setup = CallSetup::<T>::default();
2115		setup.set_storage_deposit_limit(deposit);
2116		setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2117
2118		let (mut ext, _) = setup.ext();
2119		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2120		let mut memory = memory!(address_bytes, deposit_bytes,);
2121
2122		let result;
2123		#[block]
2124		{
2125			result = runtime.bench_delegate_call(
2126				memory.as_mut_slice(),
2127				pack_hi_lo(0, 0),        // flags + address ptr
2128				u64::MAX,                // ref_time_limit
2129				u64::MAX,                // proof_size_limit
2130				address_len,             // deposit_ptr
2131				pack_hi_lo(0, 0),        // input len + data ptr
2132				pack_hi_lo(0, SENTINEL), // output len + ptr
2133			);
2134		}
2135
2136		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2137		Ok(())
2138	}
2139
2140	// t: with or without some value to transfer
2141	// d: with or without dust value to transfer
2142	// i: size of the input data
2143	#[benchmark(pov_mode = Measured)]
2144	fn seal_instantiate(
2145		t: Linear<0, 1>,
2146		d: Linear<0, 1>,
2147		i: Linear<0, { limits::CALLDATA_BYTES }>,
2148	) -> Result<(), BenchmarkError> {
2149		let code = VmBinaryModule::dummy();
2150		let hash = Contract::<T>::with_index(1, VmBinaryModule::dummy(), vec![])?.info()?.code_hash;
2151		let hash_bytes = hash.encode();
2152
2153		let value: BalanceOf<T> = (1_000_000u32 * t).into();
2154		let dust = 100u32 * d;
2155		let evm_value =
2156			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust));
2157		let value_bytes = evm_value.encode();
2158		let value_len = value_bytes.len() as u32;
2159
2160		let deposit: BalanceOf<T> = BalanceOf::<T>::max_value();
2161		let deposit_bytes = Into::<U256>::into(deposit).encode();
2162		let deposit_len = deposit_bytes.len() as u32;
2163
2164		let mut setup = CallSetup::<T>::default();
2165		setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2166		setup.set_balance(value + 1u32.into() + (Pallet::<T>::min_balance() * 2u32.into()));
2167
2168		let account_id = &setup.contract().account_id.clone();
2169		let (mut ext, _) = setup.ext();
2170		let mut runtime = pvm::Runtime::<_, [u8]>::new(&mut ext, vec![]);
2171
2172		let input = vec![42u8; i as _];
2173		let input_len = hash_bytes.len() as u32 + input.len() as u32;
2174		let salt = [42u8; 32];
2175		let deployer = T::AddressMapper::to_address(&account_id);
2176		let addr = crate::address::create2(&deployer, &code.code, &input, &salt);
2177		let mut memory = memory!(hash_bytes, input, deposit_bytes, value_bytes, salt,);
2178
2179		let mut offset = {
2180			let mut current = 0u32;
2181			move |after: u32| {
2182				current += after;
2183				current
2184			}
2185		};
2186
2187		assert!(AccountInfoOf::<T>::get(&addr).is_none());
2188
2189		let result;
2190		#[block]
2191		{
2192			result = runtime.bench_instantiate(
2193				memory.as_mut_slice(),
2194				u64::MAX,                                           // ref_time_limit
2195				u64::MAX,                                           // proof_size_limit
2196				pack_hi_lo(offset(input_len), offset(deposit_len)), // deposit_ptr + value_ptr
2197				pack_hi_lo(input_len, 0),                           // input_data_len + input_data
2198				pack_hi_lo(0, SENTINEL),                            // output_len_ptr + output_ptr
2199				pack_hi_lo(SENTINEL, offset(value_len)),            // address_ptr + salt_ptr
2200			);
2201		}
2202
2203		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2204		assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2205
2206		assert_eq!(
2207			Pallet::<T>::evm_balance(&addr),
2208			evm_value,
2209			"{addr:?} balance should hold {evm_value:?}"
2210		);
2211		Ok(())
2212	}
2213
2214	// t: with or without some value to transfer
2215	// d: with or without dust value to transfer
2216	// i: size of the init code (max 49152 bytes per EIP-3860)
2217	#[benchmark(pov_mode = Measured)]
2218	fn evm_instantiate(
2219		t: Linear<0, 1>,
2220		d: Linear<0, 1>,
2221		i: Linear<{ 10 * 1024 }, { 48 * 1024 }>,
2222	) -> Result<(), BenchmarkError> {
2223		use crate::vm::evm::instructions::BENCH_INIT_CODE;
2224		let mut setup = CallSetup::<T>::new(VmBinaryModule::evm_init_code_for_runtime_size(0));
2225		setup.set_origin(ExecOrigin::from_account_id(setup.contract().account_id.clone()));
2226		setup.set_balance(caller_funding::<T>());
2227
2228		let (mut ext, _) = setup.ext();
2229		let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
2230
2231		let value = {
2232			let value: BalanceOf<T> = (1_000_000u32 * t).into();
2233			let dust = 100u32 * d;
2234			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, dust))
2235		};
2236
2237		let init_code = vec![BENCH_INIT_CODE; i as usize];
2238		let _ = interpreter.memory.resize(0, init_code.len());
2239		let salt = U256::from(42u64);
2240		interpreter.memory.set_data(0, 0, init_code.len(), &init_code);
2241
2242		// Setup stack for create instruction [value, offset, size, salt]
2243		let _ = interpreter.stack.push(salt);
2244		let _ = interpreter.stack.push(U256::from(init_code.len()));
2245		let _ = interpreter.stack.push(U256::zero());
2246		let _ = interpreter.stack.push(value);
2247
2248		let result;
2249		#[block]
2250		{
2251			result = instructions::contract::create::<true, _>(&mut interpreter);
2252		}
2253
2254		assert!(result.is_continue());
2255		let addr = interpreter.stack.top().unwrap().into_address();
2256		assert!(AccountInfo::<T>::load_contract(&addr).is_some());
2257		assert_eq!(Pallet::<T>::code(&addr).len(), revm::primitives::eip170::MAX_CODE_SIZE);
2258		assert_eq!(Pallet::<T>::evm_balance(&addr), value, "balance should hold {value:?}");
2259		Ok(())
2260	}
2261
2262	// `n`: Input to hash in bytes
2263	#[benchmark(pov_mode = Measured)]
2264	fn sha2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2265		let input = vec![0u8; n as usize];
2266		let mut call_setup = CallSetup::<T>::default();
2267		let (mut ext, _) = call_setup.ext();
2268
2269		let result;
2270		#[block]
2271		{
2272			result = run_builtin_precompile(
2273				&mut ext,
2274				H160::from_low_u64_be(2).as_fixed_bytes(),
2275				input.clone(),
2276			);
2277		}
2278		assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data);
2279	}
2280
2281	#[benchmark(pov_mode = Measured)]
2282	fn identity(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2283		let input = vec![0u8; n as usize];
2284		let mut call_setup = CallSetup::<T>::default();
2285		let (mut ext, _) = call_setup.ext();
2286
2287		let result;
2288		#[block]
2289		{
2290			result = run_builtin_precompile(
2291				&mut ext,
2292				H160::from_low_u64_be(4).as_fixed_bytes(),
2293				input.clone(),
2294			);
2295		}
2296		assert_eq!(input, result.unwrap().data);
2297	}
2298
2299	// `n`: Input to hash in bytes
2300	#[benchmark(pov_mode = Measured)]
2301	fn ripemd_160(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2302		use ripemd::Digest;
2303		let input = vec![0u8; n as usize];
2304		let mut call_setup = CallSetup::<T>::default();
2305		let (mut ext, _) = call_setup.ext();
2306
2307		let result;
2308		#[block]
2309		{
2310			result = run_builtin_precompile(
2311				&mut ext,
2312				H160::from_low_u64_be(3).as_fixed_bytes(),
2313				input.clone(),
2314			);
2315		}
2316		let mut expected = [0u8; 32];
2317		expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input));
2318
2319		assert_eq!(expected.to_vec(), result.unwrap().data);
2320	}
2321
2322	// `n`: Input to hash in bytes
2323	#[benchmark(pov_mode = Measured)]
2324	fn seal_hash_keccak_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2325		build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
2326
2327		let result;
2328		#[block]
2329		{
2330			result = runtime.bench_hash_keccak_256(memory.as_mut_slice(), 32, n, 0);
2331		}
2332		assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
2333		assert_ok!(result);
2334	}
2335
2336	// `n`: Input to hash in bytes
2337	#[benchmark(pov_mode = Measured)]
2338	fn hash_blake2_256(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2339		let input = vec![0u8; n as usize];
2340		let input_bytes = ISystem::ISystemCalls::hashBlake256(ISystem::hashBlake256Call {
2341			input: input.clone().into(),
2342		})
2343		.abi_encode();
2344
2345		let mut call_setup = CallSetup::<T>::default();
2346		let (mut ext, _) = call_setup.ext();
2347
2348		let result;
2349		#[block]
2350		{
2351			result = run_builtin_precompile(
2352				&mut ext,
2353				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2354				input_bytes,
2355			);
2356		}
2357		let truth: [u8; 32] = sp_io::hashing::blake2_256(&input);
2358		let truth = FixedBytes::<32>::abi_encode(&truth);
2359		let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed");
2360
2361		let raw_data = result.unwrap().data;
2362		let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed");
2363		assert_eq!(truth, ret_hash);
2364	}
2365
2366	// `n`: Input to hash in bytes
2367	#[benchmark(pov_mode = Measured)]
2368	fn hash_blake2_128(n: Linear<0, { limits::code::BLOB_BYTES }>) {
2369		let input = vec![0u8; n as usize];
2370		let input_bytes = ISystem::ISystemCalls::hashBlake128(ISystem::hashBlake128Call {
2371			input: input.clone().into(),
2372		})
2373		.abi_encode();
2374
2375		let mut call_setup = CallSetup::<T>::default();
2376		let (mut ext, _) = call_setup.ext();
2377
2378		let result;
2379		#[block]
2380		{
2381			result = run_builtin_precompile(
2382				&mut ext,
2383				H160(BenchmarkSystem::<T>::MATCHER.base_address()).as_fixed_bytes(),
2384				input_bytes,
2385			);
2386		}
2387		let truth: [u8; 16] = sp_io::hashing::blake2_128(&input);
2388		let truth = FixedBytes::<16>::abi_encode(&truth);
2389		let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed");
2390
2391		let raw_data = result.unwrap().data;
2392		let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed");
2393		assert_eq!(truth, ret_hash);
2394	}
2395
2396	// `n`: Message input length to verify in bytes.
2397	// need some buffer so the code size does not exceed the max code size.
2398	#[benchmark(pov_mode = Measured)]
2399	fn seal_sr25519_verify(n: Linear<0, { limits::code::BLOB_BYTES - 255 }>) {
2400		let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
2401		let message_len = message.len() as u32;
2402
2403		let key_type = sp_core::crypto::KeyTypeId(*b"code");
2404		let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
2405		let sig =
2406			sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
2407		let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
2408		let sig_len = sig.len() as u32;
2409
2410		build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
2411
2412		let result;
2413		#[block]
2414		{
2415			result = runtime.bench_sr25519_verify(
2416				memory.as_mut_slice(),
2417				0,                              // signature_ptr
2418				sig_len,                        // pub_key_ptr
2419				message_len,                    // message_len
2420				sig_len + pub_key.len() as u32, // message_ptr
2421			);
2422		}
2423
2424		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
2425	}
2426
2427	#[benchmark(pov_mode = Measured)]
2428	fn ecdsa_recover() {
2429		use hex_literal::hex;
2430		let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec();
2431		let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b");
2432		let mut call_setup = CallSetup::<T>::default();
2433		let (mut ext, _) = call_setup.ext();
2434
2435		let result;
2436
2437		#[block]
2438		{
2439			result =
2440				run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input);
2441		}
2442
2443		assert_eq!(result.unwrap().data, expected);
2444	}
2445
2446	#[benchmark(pov_mode = Measured)]
2447	fn p256_verify() {
2448		use hex_literal::hex;
2449		let input = hex!("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").to_vec();
2450		let expected = U256::one().to_big_endian();
2451		let mut call_setup = CallSetup::<T>::default();
2452		let (mut ext, _) = call_setup.ext();
2453
2454		let result;
2455
2456		#[block]
2457		{
2458			result = run_builtin_precompile(
2459				&mut ext,
2460				H160::from_low_u64_be(0x100).as_fixed_bytes(),
2461				input,
2462			);
2463		}
2464
2465		assert_eq!(result.unwrap().data, expected);
2466	}
2467
2468	#[benchmark(pov_mode = Measured)]
2469	fn bn128_add() {
2470		use hex_literal::hex;
2471		let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec();
2472		let expected = hex!(
2473			"0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"
2474		);
2475		let mut call_setup = CallSetup::<T>::default();
2476		let (mut ext, _) = call_setup.ext();
2477
2478		let result;
2479		#[block]
2480		{
2481			result =
2482				run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input);
2483		}
2484
2485		assert_eq!(result.unwrap().data, expected);
2486	}
2487
2488	#[benchmark(pov_mode = Measured)]
2489	fn bn128_mul() {
2490		use hex_literal::hex;
2491		let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec();
2492		let expected = hex!(
2493			"0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"
2494		);
2495		let mut call_setup = CallSetup::<T>::default();
2496		let (mut ext, _) = call_setup.ext();
2497
2498		let result;
2499		#[block]
2500		{
2501			result =
2502				run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input);
2503		}
2504
2505		assert_eq!(result.unwrap().data, expected);
2506	}
2507
2508	// `n`: pairings to perform
2509	#[benchmark(pov_mode = Measured)]
2510	fn bn128_pairing(n: Linear<0, { 20 }>) {
2511		fn generate_random_ecpairs(n: usize) -> Vec<u8> {
2512			use bn::{AffineG1, AffineG2, Fr, G1, G2, Group};
2513			use rand::SeedableRng;
2514			use rand_pcg::Pcg64;
2515			let mut rng = Pcg64::seed_from_u64(1);
2516
2517			let mut buffer = vec![0u8; n * 192];
2518
2519			let mut write = |element: &bn::Fq, offset: &mut usize| {
2520				element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap();
2521				*offset += 32
2522			};
2523
2524			for i in 0..n {
2525				let mut offset = i * 192;
2526				let scalar = Fr::random(&mut rng);
2527
2528				let g1 = G1::one() * scalar;
2529				let g2 = G2::one() * scalar;
2530				let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve");
2531				let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve");
2532
2533				write(&a.x(), &mut offset);
2534				write(&a.y(), &mut offset);
2535				write(&b.x().imaginary(), &mut offset);
2536				write(&b.x().real(), &mut offset);
2537				write(&b.y().imaginary(), &mut offset);
2538				write(&b.y().real(), &mut offset);
2539			}
2540
2541			buffer
2542		}
2543
2544		let input = generate_random_ecpairs(n as usize);
2545		let mut call_setup = CallSetup::<T>::default();
2546		let (mut ext, _) = call_setup.ext();
2547
2548		let result;
2549		#[block]
2550		{
2551			result =
2552				run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input);
2553		}
2554		assert_ok!(result);
2555	}
2556
2557	// `n`: number of rounds to perform
2558	#[benchmark(pov_mode = Measured)]
2559	fn blake2f(n: Linear<0, 1200>) {
2560		use hex_literal::hex;
2561		let input = hex!(
2562			"48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001"
2563		);
2564		let input = n.to_be_bytes().to_vec().into_iter().chain(input.to_vec()).collect::<Vec<_>>();
2565		let mut call_setup = CallSetup::<T>::default();
2566		let (mut ext, _) = call_setup.ext();
2567
2568		let result;
2569		#[block]
2570		{
2571			result =
2572				run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input);
2573		}
2574		assert_ok!(result);
2575	}
2576
2577	// Only calling the function itself for the list of
2578	// generated different ECDSA keys.
2579	// This is a slow call: We reduce the number of runs.
2580	#[benchmark(pov_mode = Measured)]
2581	fn seal_ecdsa_to_eth_address() {
2582		let key_type = sp_core::crypto::KeyTypeId(*b"code");
2583		let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
2584		build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
2585
2586		let result;
2587		#[block]
2588		{
2589			result = runtime.bench_ecdsa_to_eth_address(
2590				memory.as_mut_slice(),
2591				20, // key_ptr
2592				0,  // output_ptr
2593			);
2594		}
2595
2596		assert_ok!(result);
2597		assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
2598	}
2599
2600	/// Benchmark the cost of executing `r` noop (JUMPDEST) instructions.
2601	#[benchmark(pov_mode = Measured)]
2602	fn evm_opcode(r: Linear<0, 10_000>) -> Result<(), BenchmarkError> {
2603		let module = VmBinaryModule::evm_noop(r);
2604		let inputs = vec![];
2605
2606		let code = Bytecode::new_raw(revm::primitives::Bytes::from(module.code.clone()));
2607		let mut setup = CallSetup::<T>::new(module);
2608		let (mut ext, _) = setup.ext();
2609
2610		let result;
2611		#[block]
2612		{
2613			result = evm::call(code, &mut ext, inputs);
2614		}
2615
2616		assert!(result.is_ok());
2617		Ok(())
2618	}
2619
2620	// Benchmark the execution of instructions.
2621	//
2622	// It benchmarks the absolute worst case by allocating a lot of memory
2623	// and then accessing it so that each instruction generates two cache misses.
2624	#[benchmark(pov_mode = Ignored)]
2625	fn instr(r: Linear<0, 10_000>) {
2626		use rand::{SeedableRng, seq::SliceRandom};
2627		use rand_pcg::Pcg64;
2628
2629		// Ideally, this needs to be bigger than the cache.
2630		const MEMORY_SIZE: u64 = sp_core::MAX_POSSIBLE_ALLOCATION as u64;
2631
2632		// This is benchmarked for x86-64.
2633		const CACHE_LINE_SIZE: u64 = 64;
2634
2635		// An 8 byte load from this misalignment will reach into the subsequent line.
2636		const MISALIGNMENT: u64 = 60;
2637
2638		// We only need one address per cache line.
2639		// -1 because we skip the first address
2640		const NUM_ADDRESSES: u64 = (MEMORY_SIZE - MISALIGNMENT) / CACHE_LINE_SIZE - 1;
2641
2642		assert!(
2643			u64::from(r) <= NUM_ADDRESSES / 2,
2644			"If we do too many iterations we run into the risk of loading from warm cache lines",
2645		);
2646
2647		let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(true));
2648		let (mut ext, module) = setup.ext();
2649		let mut prepared =
2650			CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), MEMORY_SIZE as u32);
2651
2652		assert!(
2653			u64::from(prepared.aux_data_base()) & (CACHE_LINE_SIZE - 1) == 0,
2654			"aux data base must be cache aligned"
2655		);
2656
2657		// Addresses data will be located inside the aux data.
2658		let misaligned_base = u64::from(prepared.aux_data_base()) + MISALIGNMENT;
2659
2660		// Create all possible addresses and shuffle them. This makes sure
2661		// the accesses are random but no address is accessed more than once.
2662		// we skip the first address since it is our entry point
2663		let mut addresses = Vec::with_capacity(NUM_ADDRESSES as usize);
2664		for i in 1..NUM_ADDRESSES {
2665			let addr = (misaligned_base + i * CACHE_LINE_SIZE).to_le_bytes();
2666			addresses.push(addr);
2667		}
2668		let mut rng = Pcg64::seed_from_u64(1337);
2669		addresses.shuffle(&mut rng);
2670
2671		// The addresses need to be padded to be one cache line apart.
2672		let mut memory = Vec::with_capacity((NUM_ADDRESSES * CACHE_LINE_SIZE) as usize);
2673		for address in addresses {
2674			memory.extend_from_slice(&address);
2675			memory.resize(memory.len() + CACHE_LINE_SIZE as usize - address.len(), 0);
2676		}
2677
2678		// Copies `memory` to `aux_data_base + MISALIGNMENT`.
2679		// Sets `a0 = MISALIGNMENT` and `a1 = r`.
2680		prepared
2681			.setup_aux_data(memory.as_slice(), MISALIGNMENT as u32, r.into())
2682			.unwrap();
2683
2684		#[block]
2685		{
2686			prepared.call().unwrap();
2687		}
2688	}
2689
2690	#[benchmark(pov_mode = Ignored)]
2691	fn instr_empty_loop(r: Linear<0, 10_000>) {
2692		let mut setup = CallSetup::<T>::new(VmBinaryModule::instr(false));
2693		let (mut ext, module) = setup.ext();
2694		let mut prepared = CallSetup::<T>::prepare_call(&mut ext, module, Vec::new(), 0);
2695		prepared.setup_aux_data(&[], 0, r.into()).unwrap();
2696
2697		#[block]
2698		{
2699			prepared.call().unwrap();
2700		}
2701	}
2702
2703	#[benchmark(pov_mode = Measured)]
2704	fn extcodecopy(n: Linear<1_000, 10_000>) -> Result<(), BenchmarkError> {
2705		let module = VmBinaryModule::sized(n);
2706		let mut setup = CallSetup::<T>::new(module);
2707		let contract = setup.contract();
2708
2709		let (mut ext, _) = setup.ext();
2710		let mut interpreter = Interpreter::new(Default::default(), Default::default(), &mut ext);
2711
2712		// Setup stack for extcodecopy instruction: [address, dest_offset, offset, size]
2713		let _ = interpreter.stack.push(U256::from(n));
2714		let _ = interpreter.stack.push(U256::from(0u32));
2715		let _ = interpreter.stack.push(U256::from(0u32));
2716		let _ = interpreter.stack.push(contract.address);
2717
2718		let result;
2719		#[block]
2720		{
2721			result = instructions::host::extcodecopy(&mut interpreter);
2722		}
2723
2724		assert!(result.is_continue());
2725		assert_eq!(
2726			*interpreter.memory.slice(0..n as usize),
2727			PristineCode::<T>::get(contract.info()?.code_hash).unwrap()[0..n as usize],
2728			"Memory should contain the contract's code after extcodecopy"
2729		);
2730
2731		Ok(())
2732	}
2733
2734	#[benchmark]
2735	fn v1_migration_step() {
2736		use crate::migrations::v1;
2737		let addr = H160::from([1u8; 20]);
2738		let contract_info = ContractInfo::new(&addr, 1u32.into(), Default::default()).unwrap();
2739
2740		v1::old::ContractInfoOf::<T>::insert(addr, contract_info.clone());
2741		let mut meter = WeightMeter::new();
2742		assert_eq!(AccountInfo::<T>::load_contract(&addr), None);
2743
2744		#[block]
2745		{
2746			v1::Migration::<T>::step(None, &mut meter).unwrap();
2747		}
2748
2749		assert_eq!(v1::old::ContractInfoOf::<T>::get(&addr), None);
2750		assert_eq!(AccountInfo::<T>::load_contract(&addr).unwrap(), contract_info);
2751
2752		// uses twice the weight once for migration and then for checking if there is another key.
2753		assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v1_migration_step() * 2);
2754	}
2755
2756	#[benchmark]
2757	fn v2_migration_step() {
2758		use crate::migrations::v2;
2759		let code_hash = H256::from([0; 32]);
2760		let old_code_info = v2::Migration::<T>::create_old_code_info(
2761			whitelisted_caller(),
2762			1000u32.into(),
2763			1,
2764			100,
2765			0,
2766		);
2767		v2::Migration::<T>::insert_old_code_info(code_hash, old_code_info.clone());
2768		let mut meter = WeightMeter::new();
2769
2770		#[block]
2771		{
2772			v2::Migration::<T>::step(None, &mut meter).unwrap();
2773		}
2774
2775		v2::Migration::<T>::assert_migrated_code_info(code_hash, &old_code_info);
2776
2777		// uses twice the weight once for migration and then for checking if there is another key.
2778		assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v2_migration_step() * 2);
2779	}
2780
2781	#[benchmark]
2782	fn v3_migration_step() {
2783		use crate::migrations::v3;
2784		// Remove all pre-existing accounts
2785		let _ = frame_system::Account::<T>::clear(u32::MAX, None);
2786
2787		let account = account::<T::AccountId>("target", 0, 0);
2788		T::Currency::mint_into(&account, Pallet::<T>::min_balance())
2789			.expect("should mint into account");
2790
2791		// clear the mapping so the migration has work to do
2792		let addr = T::AddressMapper::to_address(&account);
2793		crate::OriginalAccount::<T>::remove(addr);
2794
2795		assert!(!T::AddressMapper::is_mapped(&account));
2796		let mut meter = WeightMeter::new();
2797
2798		#[block]
2799		{
2800			v3::Migration::<T>::step(None, &mut meter).unwrap();
2801		}
2802
2803		assert!(T::AddressMapper::is_mapped(&account));
2804
2805		// uses twice the weight: once for migration and then for checking if there is another key.
2806		assert_eq!(meter.consumed(), <T as Config>::WeightInfo::v3_migration_step() * 2);
2807	}
2808
2809	/// One iteration of v4 phase 1: credit the uploader's [`NativeDepositOf`] bucket.
2810	///
2811	/// Seeds two codes and primes the cursor with the first stored entry so the benched
2812	/// iteration exercises the `iter_from` path that dominates phase 1 in production.
2813	#[benchmark]
2814	fn v4_code_upload_step() {
2815		use crate::migrations::v4;
2816
2817		let _ = CodeInfoOf::<T>::clear(u32::MAX, None);
2818
2819		let owner: T::AccountId = whitelisted_caller();
2820		let deposit: BalanceOf<T> = 1_000u32.into();
2821
2822		let pallet_account = Pallet::<T>::account_id();
2823		T::Currency::mint_into(&pallet_account, Pallet::<T>::min_balance()).unwrap();
2824		T::Currency::mint_into(&pallet_account, deposit).unwrap();
2825		T::Currency::hold(&HoldReason::CodeUploadDepositReserve.into(), &pallet_account, deposit)
2826			.unwrap();
2827
2828		CodeInfoOf::<T>::insert(
2829			H256::from([1u8; 32]),
2830			CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
2831		);
2832		CodeInfoOf::<T>::insert(
2833			H256::from([2u8; 32]),
2834			CodeInfo::<T>::new_with_deposit(owner.clone(), deposit),
2835		);
2836
2837		let first = match v4::Migration::<T>::step_once(None) {
2838			Some(v4::Cursor::CodeUpload(h)) => h,
2839			other => panic!("expected CodeUpload cursor, got {other:?}"),
2840		};
2841		let cursor = Some(v4::Cursor::CodeUpload(first));
2842
2843		#[block]
2844		{
2845			let _ = v4::Migration::<T>::step_once(cursor);
2846		}
2847
2848		assert_eq!(
2849			NativeDepositOf::<T>::get(&pallet_account, &owner),
2850			deposit + deposit,
2851			"both code uploads credited to owner",
2852		);
2853	}
2854
2855	/// One iteration of v4 phase 2: burn native hold, mint and hold PGAS for a single contract.
2856	///
2857	/// Seeds two contracts and primes the cursor with the first stored entry so the benched
2858	/// iteration exercises the `iter_from` path that dominates phase 2 in production.
2859	#[benchmark]
2860	fn v4_contract_step() {
2861		use crate::migrations::v4;
2862
2863		let _ = AccountInfoOf::<T>::clear(u32::MAX, None);
2864
2865		let code_hash = H256::from([0u8; 32]);
2866		let deposit: BalanceOf<T> = 1_000u32.into();
2867
2868		for byte in [0x41u8, 0x42u8] {
2869			let addr = H160::from([byte; 20]);
2870			let contract_account = T::AddressMapper::to_account_id(&addr);
2871			let info =
2872				ContractInfo::<T>::new(&addr, 1u32.into(), code_hash).expect("fresh contract info");
2873			AccountInfoOf::<T>::insert(
2874				addr,
2875				crate::storage::AccountInfo::<T> {
2876					account_type: crate::storage::AccountType::Contract(info),
2877					dust: 0,
2878				},
2879			);
2880			T::Currency::mint_into(&contract_account, Pallet::<T>::min_balance()).unwrap();
2881			T::Currency::mint_into(&contract_account, deposit).unwrap();
2882			T::Currency::hold(
2883				&HoldReason::StorageDepositReserve.into(),
2884				&contract_account,
2885				deposit,
2886			)
2887			.unwrap();
2888		}
2889
2890		let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::Contract(None))) {
2891			Some(v4::Cursor::Contract(Some(addr))) => addr,
2892			other => panic!("expected Contract cursor, got {other:?}"),
2893		};
2894		let cursor = Some(v4::Cursor::Contract(Some(first)));
2895
2896		#[block]
2897		{
2898			let _ = v4::Migration::<T>::step_once(cursor);
2899		}
2900
2901		// `migrate_native_to_pgas` is a no-op for `Deposit = ()`, so the hold only clears on
2902		// PGAS-backed runtimes. On non-PGAS runtimes the benchmark still measures the iter cost.
2903		if T::Deposit::SUPPORTS_PGAS {
2904			for byte in [0x41u8, 0x42u8] {
2905				let addr = H160::from([byte; 20]);
2906				let contract_account = T::AddressMapper::to_account_id(&addr);
2907				assert_eq!(
2908					T::Currency::balance_on_hold(
2909						&HoldReason::StorageDepositReserve.into(),
2910						&contract_account,
2911					),
2912					0u32.into(),
2913					"native storage deposit burned for {addr:?}",
2914				);
2915			}
2916		}
2917	}
2918
2919	/// One iteration of v4 phase 3: rewrite a legacy [`v4::old::DeletionQueue`] entry into the
2920	/// new [`DeletionQueue`] format.
2921	///
2922	/// Seeds two legacy entries and primes the cursor with the first stored entry so the benched
2923	/// iteration exercises the `iter_from` path.
2924	#[benchmark]
2925	fn v4_deletion_queue_step() {
2926		use crate::migrations::v4;
2927
2928		let _ = v4::old::DeletionQueue::<T>::clear(u32::MAX, None);
2929
2930		let trie_a: TrieId = vec![0xAAu8; 16].try_into().unwrap();
2931		let trie_b: TrieId = vec![0xBBu8; 24].try_into().unwrap();
2932		v4::old::DeletionQueue::<T>::insert(0u32, trie_a);
2933		v4::old::DeletionQueue::<T>::insert(1u32, trie_b);
2934
2935		let first = match v4::Migration::<T>::step_once(Some(v4::Cursor::DeletionQueue(None))) {
2936			Some(v4::Cursor::DeletionQueue(Some(key))) => key,
2937			other => panic!("expected DeletionQueue cursor, got {other:?}"),
2938		};
2939		let cursor = Some(v4::Cursor::DeletionQueue(Some(first)));
2940
2941		#[block]
2942		{
2943			let _ = v4::Migration::<T>::step_once(cursor);
2944		}
2945
2946		assert!(
2947			DeletionQueue::<T>::get(0u32).is_some() && DeletionQueue::<T>::get(1u32).is_some(),
2948			"both legacy entries rewritten into the new format",
2949		);
2950	}
2951
2952	/// Helper function to create a test signer for finalize_block benchmark
2953	fn create_test_signer<T: Config>() -> (T::AccountId, SigningKey, H160) {
2954		use hex_literal::hex;
2955		// dev::alith()
2956		let signer_account_id = hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
2957		let signer_priv_key =
2958			hex!("5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133");
2959
2960		let signer_key = SigningKey::from_bytes(&signer_priv_key.into()).expect("valid key");
2961
2962		let signer_address = H160::from_slice(&signer_account_id);
2963		let signer_caller = T::AddressMapper::to_fallback_account_id(&signer_address);
2964
2965		(signer_caller, signer_key, signer_address)
2966	}
2967
2968	/// Helper function to create and sign a transaction for finalize_block benchmark
2969	fn create_signed_transaction<T: Config>(
2970		signer_key: &SigningKey,
2971		target_address: H160,
2972		value: U256,
2973		input_data: Vec<u8>,
2974	) -> Vec<u8> {
2975		let unsigned_tx: TransactionUnsigned = TransactionLegacyUnsigned {
2976			to: Some(target_address),
2977			value,
2978			chain_id: Some(T::ChainId::get().into()),
2979			input: input_data.into(),
2980			..Default::default()
2981		}
2982		.into();
2983
2984		let hashed_payload = sp_io::hashing::keccak_256(&unsigned_tx.unsigned_payload());
2985		let (signature, recovery_id) =
2986			signer_key.sign_prehash_recoverable(&hashed_payload).expect("signing success");
2987
2988		let mut sig_bytes = [0u8; 65];
2989		sig_bytes[..64].copy_from_slice(&signature.to_bytes());
2990		sig_bytes[64] = recovery_id.to_byte();
2991
2992		let signed_tx = unsigned_tx.with_signature(sig_bytes);
2993
2994		signed_tx.signed_payload()
2995	}
2996
2997	/// Helper function to generate common finalize_block benchmark setup
2998	fn setup_finalize_block_benchmark<T>()
2999	-> Result<(Contract<T>, BalanceOf<T>, U256, SigningKey, BlockNumberFor<T>), BenchmarkError>
3000	where
3001		BalanceOf<T>: Into<U256> + TryFrom<U256>,
3002		T: Config,
3003		MomentOf<T>: Into<U256>,
3004		<T as frame_system::Config>::Hash: frame_support::traits::IsType<H256>,
3005	{
3006		// Setup test signer
3007		let (signer_caller, signer_key, _signer_address) = create_test_signer::<T>();
3008		whitelist_account!(signer_caller);
3009
3010		// Setup contract instance
3011		let instance =
3012			Contract::<T>::with_caller(signer_caller.clone(), VmBinaryModule::dummy(), vec![])?;
3013		let storage_deposit = default_deposit_limit::<T>();
3014		let value = Pallet::<T>::min_balance();
3015		let evm_value =
3016			Pallet::<T>::convert_native_to_evm(BalanceWithDust::new_unchecked::<T>(value, 0));
3017
3018		// Setup block
3019		let current_block = BlockNumberFor::<T>::from(1u32);
3020		frame_system::Pallet::<T>::set_block_number(current_block);
3021
3022		Ok((instance, storage_deposit, evm_value, signer_key, current_block))
3023	}
3024
3025	/// Benchmark the `on_finalize` hook scaling with number of transactions.
3026	///
3027	/// This benchmark measures the marginal computational cost of adding transactions
3028	/// to a block during finalization, with fixed payload size to isolate transaction
3029	/// count scaling effects.
3030	///
3031	/// ## Parameters:
3032	/// - `n`: Number of transactions in the block (0-200)
3033	///
3034	/// ## Test Setup:
3035	/// - Creates `n` transactions with fixed 100-byte payloads
3036	/// - Pre-populates block builder storage with test data
3037	///
3038	/// ## Usage:
3039	/// Use this with `on_finalize_per_byte` to calculate total cost:
3040	/// `total_cost = base + (n × per_tx_cost) + (total_bytes × per_byte_cost)`
3041	#[benchmark(pov_mode = Measured)]
3042	fn on_finalize_per_transaction(n: Linear<0, 200>) -> Result<(), BenchmarkError> {
3043		let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3044			setup_finalize_block_benchmark::<T>()?;
3045
3046		// Fixed payload size to isolate transaction count effects
3047		let fixed_payload_size = 100usize;
3048
3049		// Pre-populate InflightTransactions with n transactions of fixed size
3050		if n > 0 {
3051			// Initialize block
3052			let _ = Pallet::<T>::on_initialize(current_block);
3053
3054			// Create input data of fixed size for consistent transaction payloads
3055			let input_data = vec![0x42u8; fixed_payload_size];
3056			let receipt_gas_info = ReceiptGasInfo {
3057				gas_used: U256::from(1_000_000),
3058				effective_gas_price: Pallet::<T>::evm_base_fee(),
3059			};
3060
3061			for _ in 0..n {
3062				// Create real signed transaction with fixed-size input data
3063				let signed_transaction = create_signed_transaction::<T>(
3064					&signer_key,
3065					instance.address,
3066					evm_value,
3067					input_data.clone(),
3068				);
3069
3070				// Store transaction
3071				let _ = block_storage::bench_with_ethereum_context(|| {
3072					let (encoded_logs, bloom) =
3073						block_storage::get_receipt_details().unwrap_or_default();
3074
3075					let block_builder_ir = EthBlockBuilderIR::<T>::get();
3076					let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3077
3078					block_builder.process_transaction(
3079						signed_transaction,
3080						true,
3081						receipt_gas_info.clone(),
3082						encoded_logs,
3083						bloom,
3084					);
3085
3086					EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3087				});
3088			}
3089		}
3090
3091		#[block]
3092		{
3093			// Measure only the finalization cost with n transactions of fixed size
3094			let _ = Pallet::<T>::on_finalize(current_block);
3095		}
3096
3097		// Verify transaction count
3098		assert_eq!(Pallet::<T>::eth_block().transactions.len(), n as usize);
3099
3100		Ok(())
3101	}
3102
3103	/// Benchmark the `on_finalize` hook scaling with transaction payload size.
3104	///
3105	/// This benchmark measures the marginal computational cost of processing
3106	/// larger transaction payloads during finalization, with fixed transaction count
3107	/// to isolate payload size scaling effects.
3108	///
3109	/// ## Parameters:
3110	/// - `d`: Payload size per transaction in bytes (0-1000)
3111	///
3112	/// ## Test Setup:
3113	/// - Creates 10 transactions with payload size `d`
3114	/// - Pre-populates block builder storage with test data
3115	///
3116	/// ## Usage:
3117	/// Use this with `on_finalize_per_transaction` to calculate total cost:
3118	/// `total_cost = base + (n × per_tx_cost) + (total_bytes × per_byte_cost)`
3119	#[benchmark(pov_mode = Measured)]
3120	fn on_finalize_per_transaction_data(d: Linear<0, 1000>) -> Result<(), BenchmarkError> {
3121		let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3122			setup_finalize_block_benchmark::<T>()?;
3123
3124		// Fixed transaction count to isolate payload size effects
3125		let fixed_tx_count = 10u32;
3126
3127		// Initialize block
3128		let _ = Pallet::<T>::on_initialize(current_block);
3129
3130		// Create input data of variable size p for realistic transaction payloads
3131		let input_data = vec![0x42u8; d as usize];
3132		let receipt_gas_info = ReceiptGasInfo {
3133			gas_used: U256::from(1_000_000),
3134			effective_gas_price: Pallet::<T>::evm_base_fee(),
3135		};
3136
3137		for _ in 0..fixed_tx_count {
3138			// Create real signed transaction with variable-size input data
3139			let signed_transaction = create_signed_transaction::<T>(
3140				&signer_key,
3141				instance.address,
3142				evm_value,
3143				input_data.clone(),
3144			);
3145
3146			// Store transaction
3147			let _ = block_storage::bench_with_ethereum_context(|| {
3148				let (encoded_logs, bloom) =
3149					block_storage::get_receipt_details().unwrap_or_default();
3150
3151				let block_builder_ir = EthBlockBuilderIR::<T>::get();
3152				let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3153
3154				block_builder.process_transaction(
3155					signed_transaction,
3156					true,
3157					receipt_gas_info.clone(),
3158					encoded_logs,
3159					bloom,
3160				);
3161
3162				EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3163			});
3164		}
3165
3166		#[block]
3167		{
3168			// Measure only the finalization cost with fixed count, variable payload size
3169			let _ = Pallet::<T>::on_finalize(current_block);
3170		}
3171
3172		// Verify transaction count
3173		assert_eq!(Pallet::<T>::eth_block().transactions.len(), fixed_tx_count as usize);
3174
3175		Ok(())
3176	}
3177
3178	/// Benchmark the `on_finalize` per-event costs.
3179	///
3180	/// This benchmark measures the computational cost of processing events
3181	/// within the finalization process, isolating the overhead of event count.
3182	/// Uses a single transaction with varying numbers of minimal events.
3183	///
3184	/// ## Parameters:
3185	/// - `e`: Number of events per transaction
3186	///
3187	/// ## Test Setup:
3188	/// - Creates 1 transaction with `e` ContractEmitted events
3189	/// - Each event contains minimal data (no topics, empty data field)
3190	///
3191	/// ## Usage:
3192	/// Measures the per-event processing overhead during finalization
3193	/// - Fixed cost: `on_finalize_per_event(0)` - baseline finalization cost
3194	/// - Per event: `on_finalize_per_event(e)` - linear scaling with event count
3195	#[benchmark(pov_mode = Measured)]
3196	fn on_finalize_per_event(e: Linear<0, 100>) -> Result<(), BenchmarkError> {
3197		let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3198			setup_finalize_block_benchmark::<T>()?;
3199
3200		// Create a single transaction with e events, each with minimal data
3201		let input_data = vec![0x42u8; 100];
3202		let signed_transaction = create_signed_transaction::<T>(
3203			&signer_key,
3204			instance.address,
3205			evm_value,
3206			input_data.clone(),
3207		);
3208
3209		let receipt_gas_info = ReceiptGasInfo {
3210			gas_used: U256::from(1_000_000),
3211			effective_gas_price: Pallet::<T>::evm_base_fee(),
3212		};
3213
3214		// Store transaction
3215		let _ = block_storage::bench_with_ethereum_context(|| {
3216			let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
3217
3218			let block_builder_ir = EthBlockBuilderIR::<T>::get();
3219			let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3220
3221			block_builder.process_transaction(
3222				signed_transaction,
3223				true,
3224				receipt_gas_info.clone(),
3225				encoded_logs,
3226				bloom,
3227			);
3228
3229			EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3230		});
3231
3232		// Create e events with minimal data to isolate event count overhead
3233		for _ in 0..e {
3234			block_storage::capture_ethereum_log(&instance.address, &vec![], &vec![]);
3235		}
3236
3237		#[block]
3238		{
3239			// Initialize block
3240			let _ = Pallet::<T>::on_initialize(current_block);
3241
3242			// Measure the finalization cost with e events
3243			let _ = Pallet::<T>::on_finalize(current_block);
3244		}
3245
3246		// Verify transaction count
3247		assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3248
3249		Ok(())
3250	}
3251
3252	/// ## Test Setup:
3253	/// - Creates 1 transaction with 1 ContractEmitted event
3254	/// - Event contains `d` total bytes of data across data field and topics
3255	///
3256	/// ## Usage:
3257	/// Measures the per-byte event data processing overhead during finalization
3258	/// - Fixed cost: `on_finalize_per_event_data(0)` - baseline cost with empty event
3259	/// - Per byte: `on_finalize_per_event_data(d)` - linear scaling with data size
3260	#[benchmark(pov_mode = Measured)]
3261	fn on_finalize_per_event_data(d: Linear<0, 16384>) -> Result<(), BenchmarkError> {
3262		let (instance, _storage_deposit, evm_value, signer_key, current_block) =
3263			setup_finalize_block_benchmark::<T>()?;
3264
3265		// Create a single transaction with one event containing d bytes of data
3266		let input_data = vec![0x42u8; 100];
3267		let signed_transaction = create_signed_transaction::<T>(
3268			&signer_key,
3269			instance.address,
3270			evm_value,
3271			input_data.clone(),
3272		);
3273
3274		let receipt_gas_info = ReceiptGasInfo {
3275			gas_used: U256::from(1_000_000),
3276			effective_gas_price: Pallet::<T>::evm_base_fee(),
3277		};
3278
3279		// Store transaction
3280		let _ = block_storage::bench_with_ethereum_context(|| {
3281			let (encoded_logs, bloom) = block_storage::get_receipt_details().unwrap_or_default();
3282
3283			let block_builder_ir = EthBlockBuilderIR::<T>::get();
3284			let mut block_builder = EthereumBlockBuilder::<T>::from_ir(block_builder_ir);
3285
3286			block_builder.process_transaction(
3287				signed_transaction,
3288				true,
3289				receipt_gas_info,
3290				encoded_logs,
3291				bloom,
3292			);
3293
3294			EthBlockBuilderIR::<T>::put(block_builder.to_ir());
3295		});
3296
3297		// Create one event with d bytes of data distributed across topics and data field
3298		let (event_data, topics) = if d < 32 {
3299			// If total data is less than 32 bytes, put all in data field
3300			(vec![0x42u8; d as usize], vec![])
3301		} else {
3302			// Fill topics first, then put remaining bytes in data field
3303			let num_topics = core::cmp::min(limits::NUM_EVENT_TOPICS, d / 32);
3304			let topic_bytes_used = num_topics * 32;
3305			let data_bytes_remaining = d - topic_bytes_used;
3306
3307			// Create topics filled with sequential data
3308			let mut topics = Vec::new();
3309			for topic_index in 0..num_topics {
3310				let topic_data = [topic_index as u8; 32];
3311				topics.push(H256::from(topic_data));
3312			}
3313
3314			// Remaining bytes go to data field
3315			let event_data = vec![0x42u8; data_bytes_remaining as usize];
3316
3317			(event_data, topics)
3318		};
3319
3320		block_storage::capture_ethereum_log(&instance.address, &event_data, &topics);
3321
3322		#[block]
3323		{
3324			// Initialize block
3325			let _ = Pallet::<T>::on_initialize(current_block);
3326
3327			// Measure the finalization cost with d bytes of event data
3328			let _ = Pallet::<T>::on_finalize(current_block);
3329		}
3330
3331		// Verify transaction count
3332		assert_eq!(Pallet::<T>::eth_block().transactions.len(), 1);
3333
3334		Ok(())
3335	}
3336
3337	impl_benchmark_test_suite!(
3338		Contracts,
3339		crate::tests::ExtBuilder::default().build(),
3340		crate::tests::Test,
3341	);
3342}