referrerpolicy=no-referrer-when-downgrade

pallet_contracts/benchmarking/
mod.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 contracts pallet
19#![cfg(feature = "runtime-benchmarks")]
20
21mod call_builder;
22mod code;
23mod sandbox;
24use self::{
25	call_builder::CallSetup,
26	code::{body, ImportedMemory, Location, ModuleDefinition, WasmModule},
27	sandbox::Sandbox,
28};
29use crate::{
30	exec::{Key, SeedOf},
31	migration::{
32		codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, v15, v16, MigrationStep,
33	},
34	storage::WriteOutcome,
35	wasm::BenchEnv,
36	Pallet as Contracts, *,
37};
38use alloc::{vec, vec::Vec};
39use codec::{Encode, MaxEncodedLen};
40use frame_benchmarking::v2::*;
41use frame_support::{
42	self, assert_ok,
43	pallet_prelude::StorageVersion,
44	storage::child,
45	traits::{fungible::InspectHold, Currency},
46	weights::{Weight, WeightMeter},
47};
48use frame_system::RawOrigin;
49use pallet_balances;
50use pallet_contracts_uapi::{CallFlags, ReturnErrorCode};
51use sp_runtime::traits::{Bounded, Hash};
52use wasm_instrument::parity_wasm::elements::{Instruction, Local, ValueType};
53
54/// How many runs we do per API benchmark.
55///
56/// This is picked more or less arbitrary. We experimented with different numbers until
57/// the results appeared to be stable. Reducing the number would speed up the benchmarks
58/// but might make the results less precise.
59const API_BENCHMARK_RUNS: u32 = 1600;
60
61/// How many runs we do per instruction benchmark.
62///
63/// Same rationale as for [`API_BENCHMARK_RUNS`]. The number is bigger because instruction
64/// benchmarks are faster.
65const INSTR_BENCHMARK_RUNS: u32 = 5000;
66
67/// Number of layers in a Radix16 unbalanced trie.
68const UNBALANCED_TRIE_LAYERS: u32 = 20;
69
70/// An instantiated and deployed contract.
71#[derive(Clone)]
72struct Contract<T: Config> {
73	caller: T::AccountId,
74	account_id: T::AccountId,
75	addr: AccountIdLookupOf<T>,
76	value: BalanceOf<T>,
77}
78
79impl<T> Contract<T>
80where
81	T: Config + pallet_balances::Config,
82	<BalanceOf<T> as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode,
83{
84	/// Create new contract and use a default account id as instantiator.
85	fn new(module: WasmModule<T>, data: Vec<u8>) -> Result<Contract<T>, &'static str> {
86		Self::with_index(0, module, data)
87	}
88
89	/// Create new contract and use an account id derived from the supplied index as instantiator.
90	fn with_index(
91		index: u32,
92		module: WasmModule<T>,
93		data: Vec<u8>,
94	) -> Result<Contract<T>, &'static str> {
95		Self::with_caller(account("instantiator", index, 0), module, data)
96	}
97
98	/// Create new contract and use the supplied `caller` as instantiator.
99	fn with_caller(
100		caller: T::AccountId,
101		module: WasmModule<T>,
102		data: Vec<u8>,
103	) -> Result<Contract<T>, &'static str> {
104		let value = Pallet::<T>::min_balance();
105		T::Currency::set_balance(&caller, caller_funding::<T>());
106		let salt = vec![0xff];
107		let addr = Contracts::<T>::contract_address(&caller, &module.hash, &data, &salt);
108
109		Contracts::<T>::store_code_raw(module.code, caller.clone())?;
110		Contracts::<T>::instantiate(
111			RawOrigin::Signed(caller.clone()).into(),
112			value,
113			Weight::MAX,
114			None,
115			module.hash,
116			data,
117			salt,
118		)?;
119
120		let result =
121			Contract { caller, account_id: addr.clone(), addr: T::Lookup::unlookup(addr), value };
122
123		ContractInfoOf::<T>::insert(&result.account_id, result.info()?);
124
125		Ok(result)
126	}
127
128	/// Create a new contract with the supplied storage item count and size each.
129	fn with_storage(
130		code: WasmModule<T>,
131		stor_num: u32,
132		stor_size: u32,
133	) -> Result<Self, &'static str> {
134		let contract = Contract::<T>::new(code, vec![])?;
135		let storage_items = (0..stor_num)
136			.map(|i| {
137				let hash = T::Hashing::hash_of(&i)
138					.as_ref()
139					.try_into()
140					.map_err(|_| "Hash too big for storage key")?;
141				Ok((hash, vec![42u8; stor_size as usize]))
142			})
143			.collect::<Result<Vec<_>, &'static str>>()?;
144		contract.store(&storage_items)?;
145		Ok(contract)
146	}
147
148	/// Store the supplied storage items into this contracts storage.
149	fn store(&self, items: &Vec<([u8; 32], Vec<u8>)>) -> Result<(), &'static str> {
150		let info = self.info()?;
151		for item in items {
152			info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false)
153				.map_err(|_| "Failed to write storage to restoration dest")?;
154		}
155		<ContractInfoOf<T>>::insert(&self.account_id, info);
156		Ok(())
157	}
158
159	/// Create a new contract with the specified unbalanced storage trie.
160	fn with_unbalanced_storage_trie(code: WasmModule<T>, key: &[u8]) -> Result<Self, &'static str> {
161		if (key.len() as u32) < UNBALANCED_TRIE_LAYERS.div_ceil(2) {
162			return Err("Key size too small to create the specified trie");
163		}
164
165		let value = vec![16u8; T::Schedule::get().limits.payload_len as usize];
166		let contract = Contract::<T>::new(code, vec![])?;
167		let info = contract.info()?;
168		let child_trie_info = info.child_trie_info();
169		child::put_raw(&child_trie_info, &key, &value);
170		for l in 0..UNBALANCED_TRIE_LAYERS {
171			let pos = l as usize / 2;
172			let mut key_new = key.to_vec();
173			for i in 0u8..16 {
174				key_new[pos] = if l % 2 == 0 {
175					(key_new[pos] & 0xF0) | i
176				} else {
177					(key_new[pos] & 0x0F) | (i << 4)
178				};
179
180				if key == &key_new {
181					continue
182				}
183				child::put_raw(&child_trie_info, &key_new, &value);
184			}
185		}
186		Ok(contract)
187	}
188
189	/// Get the `ContractInfo` of the `addr` or an error if it no longer exists.
190	fn address_info(addr: &T::AccountId) -> Result<ContractInfo<T>, &'static str> {
191		ContractInfoOf::<T>::get(addr).ok_or("Expected contract to exist at this point.")
192	}
193
194	/// Get the `ContractInfo` of this contract or an error if it no longer exists.
195	fn info(&self) -> Result<ContractInfo<T>, &'static str> {
196		Self::address_info(&self.account_id)
197	}
198
199	/// Set the balance of the contract to the supplied amount.
200	fn set_balance(&self, balance: BalanceOf<T>) {
201		T::Currency::set_balance(&self.account_id, balance);
202	}
203
204	/// Returns `true` iff all storage entries related to code storage exist.
205	fn code_exists(hash: &CodeHash<T>) -> bool {
206		<PristineCode<T>>::contains_key(hash) && <CodeInfoOf<T>>::contains_key(&hash)
207	}
208
209	/// Returns `true` iff no storage entry related to code storage exist.
210	fn code_removed(hash: &CodeHash<T>) -> bool {
211		!<PristineCode<T>>::contains_key(hash) && !<CodeInfoOf<T>>::contains_key(&hash)
212	}
213}
214
215/// The funding that each account that either calls or instantiates contracts is funded with.
216fn caller_funding<T: Config>() -> BalanceOf<T> {
217	// Minting can overflow, so we can't abuse of the funding. This value happens to be big enough,
218	// but not too big to make the total supply overflow.
219	BalanceOf::<T>::max_value() / 10_000u32.into()
220}
221
222#[benchmarks(
223	where
224		<BalanceOf<T> as codec::HasCompact>::Type: Clone + Eq + PartialEq + core::fmt::Debug + scale_info::TypeInfo + codec::Encode,
225		T: Config + pallet_balances::Config,
226		BalanceOf<T>: From<<pallet_balances::Pallet<T> as Currency<T::AccountId>>::Balance>,
227		<pallet_balances::Pallet<T> as Currency<T::AccountId>>::Balance: From<BalanceOf<T>>,
228)]
229mod benchmarks {
230	use super::*;
231
232	// The base weight consumed on processing contracts deletion queue.
233	#[benchmark(pov_mode = Measured)]
234	fn on_process_deletion_queue_batch() {
235		#[block]
236		{
237			ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
238		}
239	}
240
241	#[benchmark(skip_meta, pov_mode = Measured)]
242	fn on_initialize_per_trie_key(k: Linear<0, 1024>) -> Result<(), BenchmarkError> {
243		let instance = Contract::<T>::with_storage(
244			WasmModule::dummy(),
245			k,
246			T::Schedule::get().limits.payload_len,
247		)?;
248		instance.info()?.queue_trie_for_deletion();
249
250		#[block]
251		{
252			ContractInfo::<T>::process_deletion_queue_batch(&mut WeightMeter::new())
253		}
254
255		Ok(())
256	}
257
258	// This benchmarks the v9 migration step (update codeStorage).
259	#[benchmark(pov_mode = Measured)]
260	fn v9_migration_step(c: Linear<0, { T::MaxCodeLen::get() }>) {
261		v09::store_old_dummy_code::<T>(c as usize);
262		let mut m = v09::Migration::<T>::default();
263		#[block]
264		{
265			m.step(&mut WeightMeter::new());
266		}
267	}
268
269	// This benchmarks the v10 migration step (use dedicated deposit_account).
270	#[benchmark(pov_mode = Measured)]
271	fn v10_migration_step() -> Result<(), BenchmarkError> {
272		let contract =
273			<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
274
275		v10::store_old_contract_info::<T, pallet_balances::Pallet<T>>(
276			contract.account_id.clone(),
277			contract.info()?,
278		);
279		let mut m = v10::Migration::<T, pallet_balances::Pallet<T>>::default();
280
281		#[block]
282		{
283			m.step(&mut WeightMeter::new());
284		}
285
286		Ok(())
287	}
288
289	// This benchmarks the v11 migration step (Don't rely on reserved balances keeping an account
290	// alive).
291	#[benchmark(pov_mode = Measured)]
292	fn v11_migration_step(k: Linear<0, 1024>) {
293		v11::fill_old_queue::<T>(k as usize);
294		let mut m = v11::Migration::<T>::default();
295
296		#[block]
297		{
298			m.step(&mut WeightMeter::new());
299		}
300	}
301
302	// This benchmarks the v12 migration step (Move `OwnerInfo` to `CodeInfo`,
303	// add `determinism` field to the latter, clear `CodeStorage`
304	// and repay deposits).
305	#[benchmark(pov_mode = Measured)]
306	fn v12_migration_step(c: Linear<0, { T::MaxCodeLen::get() }>) {
307		v12::store_old_dummy_code::<T, pallet_balances::Pallet<T>>(
308			c as usize,
309			account::<T::AccountId>("account", 0, 0),
310		);
311		let mut m = v12::Migration::<T, pallet_balances::Pallet<T>>::default();
312
313		#[block]
314		{
315			m.step(&mut WeightMeter::new());
316		}
317	}
318
319	// This benchmarks the v13 migration step (Add delegate_dependencies field).
320	#[benchmark(pov_mode = Measured)]
321	fn v13_migration_step() -> Result<(), BenchmarkError> {
322		let contract =
323			<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
324
325		v13::store_old_contract_info::<T>(contract.account_id.clone(), contract.info()?);
326		let mut m = v13::Migration::<T>::default();
327
328		#[block]
329		{
330			m.step(&mut WeightMeter::new());
331		}
332		Ok(())
333	}
334
335	// This benchmarks the v14 migration step (Move code owners' reserved balance to be held
336	// instead).
337	#[benchmark(pov_mode = Measured)]
338	fn v14_migration_step() {
339		let account = account::<T::AccountId>("account", 0, 0);
340		T::Currency::set_balance(&account, caller_funding::<T>());
341		v14::store_dummy_code::<T, pallet_balances::Pallet<T>>(account);
342		let mut m = v14::Migration::<T, pallet_balances::Pallet<T>>::default();
343
344		#[block]
345		{
346			m.step(&mut WeightMeter::new());
347		}
348	}
349
350	// This benchmarks the v15 migration step (remove deposit account).
351	#[benchmark(pov_mode = Measured)]
352	fn v15_migration_step() -> Result<(), BenchmarkError> {
353		let contract =
354			<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
355
356		v15::store_old_contract_info::<T>(contract.account_id.clone(), contract.info()?);
357		let mut m = v15::Migration::<T>::default();
358
359		#[block]
360		{
361			m.step(&mut WeightMeter::new());
362		}
363
364		Ok(())
365	}
366
367	// This benchmarks the v16 migration step (Remove ED from base_deposit).
368	#[benchmark(pov_mode = Measured)]
369	fn v16_migration_step() -> Result<(), BenchmarkError> {
370		let contract =
371			<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
372
373		let info = contract.info()?;
374		let base_deposit = v16::store_old_contract_info::<T>(contract.account_id.clone(), &info);
375		let mut m = v16::Migration::<T>::default();
376
377		#[block]
378		{
379			m.step(&mut WeightMeter::new());
380		}
381		let ed = Pallet::<T>::min_balance();
382		let info = v16::ContractInfoOf::<T>::get(&contract.account_id).unwrap();
383		assert_eq!(info.storage_base_deposit, base_deposit - ed);
384		Ok(())
385	}
386
387	// This benchmarks the weight of executing Migration::migrate to execute a noop migration.
388	#[benchmark(pov_mode = Measured)]
389	fn migration_noop() {
390		let version = LATEST_MIGRATION_VERSION;
391		StorageVersion::new(version).put::<Pallet<T>>();
392		#[block]
393		{
394			Migration::<T>::migrate(&mut WeightMeter::new());
395		}
396		assert_eq!(StorageVersion::get::<Pallet<T>>(), version);
397	}
398
399	// This benchmarks the weight of dispatching migrate to execute 1 `NoopMigration`
400	#[benchmark(pov_mode = Measured)]
401	fn migrate() {
402		let latest_version = LATEST_MIGRATION_VERSION;
403		StorageVersion::new(latest_version - 2).put::<Pallet<T>>();
404		<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
405
406		#[extrinsic_call]
407		_(RawOrigin::Signed(whitelisted_caller()), Weight::MAX);
408
409		assert_eq!(StorageVersion::get::<Pallet<T>>(), latest_version - 1);
410	}
411
412	// This benchmarks the weight of running on_runtime_upgrade when there are no migration in
413	// progress.
414	#[benchmark(pov_mode = Measured)]
415	fn on_runtime_upgrade_noop() {
416		let latest_version = LATEST_MIGRATION_VERSION;
417		StorageVersion::new(latest_version).put::<Pallet<T>>();
418		#[block]
419		{
420			<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
421		}
422		assert!(MigrationInProgress::<T>::get().is_none());
423	}
424
425	// This benchmarks the weight of running on_runtime_upgrade when there is a migration in
426	// progress.
427	#[benchmark(pov_mode = Measured)]
428	fn on_runtime_upgrade_in_progress() {
429		let latest_version = LATEST_MIGRATION_VERSION;
430		StorageVersion::new(latest_version - 2).put::<Pallet<T>>();
431		let v = vec![42u8].try_into().ok();
432		MigrationInProgress::<T>::set(v.clone());
433		#[block]
434		{
435			<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
436		}
437		assert!(MigrationInProgress::<T>::get().is_some());
438		assert_eq!(MigrationInProgress::<T>::get(), v);
439	}
440
441	// This benchmarks the weight of running on_runtime_upgrade when there is a migration to
442	// process.
443	#[benchmark(pov_mode = Measured)]
444	fn on_runtime_upgrade() {
445		let latest_version = LATEST_MIGRATION_VERSION;
446		StorageVersion::new(latest_version - 2).put::<Pallet<T>>();
447		#[block]
448		{
449			<Migration<T, false> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
450		}
451		assert!(MigrationInProgress::<T>::get().is_some());
452	}
453
454	// This benchmarks the overhead of loading a code of size `c` byte from storage and into
455	// the sandbox. This does **not** include the actual execution for which the gas meter
456	// is responsible. This is achieved by generating all code to the `deploy` function
457	// which is in the wasm module but not executed on `call`.
458	// The results are supposed to be used as `call_with_code_per_byte(c) -
459	// call_with_code_per_byte(0)`.
460	#[benchmark(pov_mode = Measured)]
461	fn call_with_code_per_byte(
462		c: Linear<0, { T::MaxCodeLen::get() }>,
463	) -> Result<(), BenchmarkError> {
464		let instance = Contract::<T>::with_caller(
465			whitelisted_caller(),
466			WasmModule::sized(c, Location::Deploy, false),
467			vec![],
468		)?;
469		let value = Pallet::<T>::min_balance();
470		let callee = instance.addr;
471
472		#[extrinsic_call]
473		call(RawOrigin::Signed(instance.caller.clone()), callee, value, Weight::MAX, None, vec![]);
474
475		Ok(())
476	}
477
478	// `c`: Size of the code in bytes.
479	// `i`: Size of the input in bytes.
480	// `s`: Size of the salt in bytes.
481	#[benchmark(pov_mode = Measured)]
482	fn instantiate_with_code(
483		c: Linear<0, { T::MaxCodeLen::get() }>,
484		i: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>,
485		s: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>,
486	) {
487		let input = vec![42u8; i as usize];
488		let salt = vec![42u8; s as usize];
489		let value = Pallet::<T>::min_balance();
490		let caller = whitelisted_caller();
491		T::Currency::set_balance(&caller, caller_funding::<T>());
492		let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call, false);
493		let origin = RawOrigin::Signed(caller.clone());
494		let addr = Contracts::<T>::contract_address(&caller, &hash, &input, &salt);
495		#[extrinsic_call]
496		_(origin, value, Weight::MAX, None, code, input, salt);
497
498		let deposit =
499			T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr);
500		// uploading the code reserves some balance in the callers account
501		let code_deposit =
502			T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &caller);
503		assert_eq!(
504			T::Currency::balance(&caller),
505			caller_funding::<T>() - value - deposit - code_deposit - Pallet::<T>::min_balance(),
506		);
507		// contract has the full value
508		assert_eq!(T::Currency::balance(&addr), value + Pallet::<T>::min_balance());
509	}
510
511	// `i`: Size of the input in bytes.
512	// `s`: Size of the salt in bytes.
513	#[benchmark(pov_mode = Measured)]
514	fn instantiate(
515		i: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>,
516		s: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>,
517	) -> Result<(), BenchmarkError> {
518		let input = vec![42u8; i as usize];
519		let salt = vec![42u8; s as usize];
520		let value = Pallet::<T>::min_balance();
521		let caller = whitelisted_caller();
522		T::Currency::set_balance(&caller, caller_funding::<T>());
523		let WasmModule { code, hash, .. } = WasmModule::<T>::dummy();
524		let addr = Contracts::<T>::contract_address(&caller, &hash, &input, &salt);
525		Contracts::<T>::store_code_raw(code, caller.clone())?;
526
527		#[extrinsic_call]
528		_(RawOrigin::Signed(caller.clone()), value, Weight::MAX, None, hash, input, salt);
529
530		let deposit =
531			T::Currency::balance_on_hold(&HoldReason::StorageDepositReserve.into(), &addr);
532		// value was removed from the caller
533		assert_eq!(
534			T::Currency::balance(&caller),
535			caller_funding::<T>() - value - deposit - Pallet::<T>::min_balance(),
536		);
537		// contract has the full value
538		assert_eq!(T::Currency::balance(&addr), value + Pallet::<T>::min_balance());
539
540		Ok(())
541	}
542
543	// We just call a dummy contract to measure the overhead of the call extrinsic.
544	// The size of the data has no influence on the costs of this extrinsic as long as the contract
545	// won't call `seal_input` in its constructor to copy the data to contract memory.
546	// The dummy contract used here does not do this. The costs for the data copy is billed as
547	// part of `seal_input`. The costs for invoking a contract of a specific size are not part
548	// of this benchmark because we cannot know the size of the contract when issuing a call
549	// transaction. See `call_with_code_per_byte` for this.
550	#[benchmark(pov_mode = Measured)]
551	fn call() -> Result<(), BenchmarkError> {
552		let data = vec![42u8; 1024];
553		let instance =
554			Contract::<T>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
555		let value = Pallet::<T>::min_balance();
556		let origin = RawOrigin::Signed(instance.caller.clone());
557		let callee = instance.addr.clone();
558		let before = T::Currency::balance(&instance.account_id);
559		#[extrinsic_call]
560		_(origin, callee, value, Weight::MAX, None, data);
561		let deposit = T::Currency::balance_on_hold(
562			&HoldReason::StorageDepositReserve.into(),
563			&instance.account_id,
564		);
565		// value and value transferred via call should be removed from the caller
566		assert_eq!(
567			T::Currency::balance(&instance.caller),
568			caller_funding::<T>() - instance.value - value - deposit - Pallet::<T>::min_balance(),
569		);
570		// contract should have received the value
571		assert_eq!(T::Currency::balance(&instance.account_id), before + value);
572		// contract should still exist
573		instance.info()?;
574
575		Ok(())
576	}
577
578	// This constructs a contract that is maximal expensive to instrument.
579	// It creates a maximum number of metering blocks per byte.
580	// `c`: Size of the code in bytes.
581	#[benchmark(pov_mode = Measured)]
582	fn upload_code_determinism_enforced(c: Linear<0, { T::MaxCodeLen::get() }>) {
583		let caller = whitelisted_caller();
584		T::Currency::set_balance(&caller, caller_funding::<T>());
585		let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call, false);
586		let origin = RawOrigin::Signed(caller.clone());
587		#[extrinsic_call]
588		upload_code(origin, code, None, Determinism::Enforced);
589		// uploading the code reserves some balance in the callers account
590		assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into());
591		assert!(<Contract<T>>::code_exists(&hash));
592	}
593
594	// Uploading code with [`Determinism::Relaxed`] should be more expensive than uploading code
595	// with [`Determinism::Enforced`], as we always try to save the code with
596	// [`Determinism::Enforced`] first.
597	#[benchmark(pov_mode = Measured)]
598	fn upload_code_determinism_relaxed(c: Linear<0, { T::MaxCodeLen::get() }>) {
599		let caller = whitelisted_caller();
600		T::Currency::set_balance(&caller, caller_funding::<T>());
601		let WasmModule { code, hash, .. } = WasmModule::<T>::sized(c, Location::Call, true);
602		let origin = RawOrigin::Signed(caller.clone());
603		#[extrinsic_call]
604		upload_code(origin, code, None, Determinism::Relaxed);
605		assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into());
606		assert!(<Contract<T>>::code_exists(&hash));
607		// Ensure that the benchmark follows the most expensive path, i.e., the code is saved with
608		assert_eq!(CodeInfoOf::<T>::get(&hash).unwrap().determinism(), Determinism::Relaxed);
609	}
610
611	// Removing code does not depend on the size of the contract because all the information
612	// needed to verify the removal claim (refcount, owner) is stored in a separate storage
613	// item (`CodeInfoOf`).
614	#[benchmark(pov_mode = Measured)]
615	fn remove_code() -> Result<(), BenchmarkError> {
616		let caller = whitelisted_caller();
617		T::Currency::set_balance(&caller, caller_funding::<T>());
618		let WasmModule { code, hash, .. } = WasmModule::<T>::dummy();
619		let origin = RawOrigin::Signed(caller.clone());
620		let uploaded =
621			<Contracts<T>>::bare_upload_code(caller.clone(), code, None, Determinism::Enforced)?;
622		assert_eq!(uploaded.code_hash, hash);
623		assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&caller));
624		assert!(<Contract<T>>::code_exists(&hash));
625		#[extrinsic_call]
626		_(origin, hash);
627		// removing the code should have unreserved the deposit
628		assert_eq!(T::Currency::total_balance_on_hold(&caller), 0u32.into());
629		assert!(<Contract<T>>::code_removed(&hash));
630		Ok(())
631	}
632
633	#[benchmark(pov_mode = Measured)]
634	fn set_code() -> Result<(), BenchmarkError> {
635		let instance =
636			<Contract<T>>::with_caller(whitelisted_caller(), WasmModule::dummy(), vec![])?;
637		// we just add some bytes so that the code hash is different
638		let WasmModule { code, hash, .. } = <WasmModule<T>>::dummy_with_bytes(128);
639		<Contracts<T>>::store_code_raw(code, instance.caller.clone())?;
640		let callee = instance.addr.clone();
641		assert_ne!(instance.info()?.code_hash, hash);
642		#[extrinsic_call]
643		_(RawOrigin::Root, callee, hash);
644		assert_eq!(instance.info()?.code_hash, hash);
645		Ok(())
646	}
647
648	#[benchmark(pov_mode = Measured)]
649	fn noop_host_fn(r: Linear<0, API_BENCHMARK_RUNS>) {
650		let mut setup = CallSetup::<T>::new(WasmModule::noop(r));
651		let (mut ext, module) = setup.ext();
652		let func = CallSetup::<T>::prepare_call(&mut ext, module, vec![]);
653		#[block]
654		{
655			func.call();
656		}
657	}
658
659	#[benchmark(pov_mode = Measured)]
660	fn seal_caller() {
661		let len = <T::AccountId as MaxEncodedLen>::max_encoded_len() as u32;
662		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
663
664		let result;
665		#[block]
666		{
667			result = BenchEnv::seal0_caller(&mut runtime, &mut memory, 4, 0);
668		}
669
670		assert_ok!(result);
671		assert_eq!(
672			&<T::AccountId as Decode>::decode(&mut &memory[4..]).unwrap(),
673			runtime.ext().caller().account_id().unwrap()
674		);
675	}
676
677	#[benchmark(pov_mode = Measured)]
678	fn seal_is_contract() {
679		let Contract { account_id, .. } =
680			Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
681
682		build_runtime!(runtime, memory: [account_id.encode(), ]);
683
684		let result;
685		#[block]
686		{
687			result = BenchEnv::seal0_is_contract(&mut runtime, &mut memory, 0);
688		}
689
690		assert_eq!(result.unwrap(), 1);
691	}
692
693	#[benchmark(pov_mode = Measured)]
694	fn seal_code_hash() {
695		let contract = Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
696		let len = <CodeHash<T> as MaxEncodedLen>::max_encoded_len() as u32;
697		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], contract.account_id.encode(), ]);
698
699		let result;
700		#[block]
701		{
702			result = BenchEnv::seal0_code_hash(&mut runtime, &mut memory, 4 + len, 4, 0);
703		}
704
705		assert_ok!(result);
706		assert_eq!(
707			<CodeHash<T> as Decode>::decode(&mut &memory[4..]).unwrap(),
708			contract.info().unwrap().code_hash
709		);
710	}
711
712	#[benchmark(pov_mode = Measured)]
713	fn seal_own_code_hash() {
714		let len = <CodeHash<T> as MaxEncodedLen>::max_encoded_len() as u32;
715		build_runtime!(runtime, contract, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
716		let result;
717		#[block]
718		{
719			result = BenchEnv::seal0_own_code_hash(&mut runtime, &mut memory, 4, 0);
720		}
721
722		assert_ok!(result);
723		assert_eq!(
724			<CodeHash<T> as Decode>::decode(&mut &memory[4..]).unwrap(),
725			contract.info().unwrap().code_hash
726		);
727	}
728
729	#[benchmark(pov_mode = Measured)]
730	fn seal_caller_is_origin() {
731		build_runtime!(runtime, memory: []);
732
733		let result;
734		#[block]
735		{
736			result = BenchEnv::seal0_caller_is_origin(&mut runtime, &mut memory);
737		}
738		assert_eq!(result.unwrap(), 1u32);
739	}
740
741	#[benchmark(pov_mode = Measured)]
742	fn seal_caller_is_root() {
743		let mut setup = CallSetup::<T>::default();
744		setup.set_origin(Origin::Root);
745		let (mut ext, _) = setup.ext();
746		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
747
748		let result;
749		#[block]
750		{
751			result = BenchEnv::seal0_caller_is_root(&mut runtime, &mut [0u8; 0]);
752		}
753		assert_eq!(result.unwrap(), 1u32);
754	}
755
756	#[benchmark(pov_mode = Measured)]
757	fn seal_address() {
758		let len = <AccountIdOf<T> as MaxEncodedLen>::max_encoded_len() as u32;
759		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
760
761		let result;
762		#[block]
763		{
764			result = BenchEnv::seal0_address(&mut runtime, &mut memory, 4, 0);
765		}
766		assert_ok!(result);
767		assert_eq!(
768			&<T::AccountId as Decode>::decode(&mut &memory[4..]).unwrap(),
769			runtime.ext().address()
770		);
771	}
772
773	#[benchmark(pov_mode = Measured)]
774	fn seal_gas_left() {
775		// use correct max_encoded_len when new version of parity-scale-codec is released
776		let len = 18u32;
777		assert!(<Weight as MaxEncodedLen>::max_encoded_len() as u32 != len);
778		build_runtime!(runtime, memory: [32u32.to_le_bytes(), vec![0u8; len as _], ]);
779
780		let result;
781		#[block]
782		{
783			result = BenchEnv::seal1_gas_left(&mut runtime, &mut memory, 4, 0);
784		}
785		assert_ok!(result);
786		assert_eq!(
787			<Weight as Decode>::decode(&mut &memory[4..]).unwrap(),
788			runtime.ext().gas_meter().gas_left()
789		);
790	}
791
792	#[benchmark(pov_mode = Measured)]
793	fn seal_balance() {
794		let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
795		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
796		let result;
797		#[block]
798		{
799			result = BenchEnv::seal0_seal_balance(&mut runtime, &mut memory, 4, 0);
800		}
801		assert_ok!(result);
802		assert_eq!(
803			<T::Balance as Decode>::decode(&mut &memory[4..]).unwrap(),
804			runtime.ext().balance().into()
805		);
806	}
807
808	#[benchmark(pov_mode = Measured)]
809	fn seal_value_transferred() {
810		let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
811		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
812		let result;
813		#[block]
814		{
815			result = BenchEnv::seal0_value_transferred(&mut runtime, &mut memory, 4, 0);
816		}
817		assert_ok!(result);
818		assert_eq!(
819			<T::Balance as Decode>::decode(&mut &memory[4..]).unwrap(),
820			runtime.ext().value_transferred().into()
821		);
822	}
823
824	#[benchmark(pov_mode = Measured)]
825	fn seal_minimum_balance() {
826		let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
827		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
828		let result;
829		#[block]
830		{
831			result = BenchEnv::seal0_minimum_balance(&mut runtime, &mut memory, 4, 0);
832		}
833		assert_ok!(result);
834		assert_eq!(
835			<T::Balance as Decode>::decode(&mut &memory[4..]).unwrap(),
836			runtime.ext().minimum_balance().into()
837		);
838	}
839
840	#[benchmark(pov_mode = Measured)]
841	fn seal_block_number() {
842		let len = <BlockNumberFor<T> as MaxEncodedLen>::max_encoded_len() as u32;
843		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
844		let result;
845		#[block]
846		{
847			result = BenchEnv::seal0_seal_block_number(&mut runtime, &mut memory, 4, 0);
848		}
849		assert_ok!(result);
850		assert_eq!(
851			<BlockNumberFor<T>>::decode(&mut &memory[4..]).unwrap(),
852			runtime.ext().block_number()
853		);
854	}
855
856	#[benchmark(pov_mode = Measured)]
857	fn seal_now() {
858		let len = <MomentOf<T> as MaxEncodedLen>::max_encoded_len() as u32;
859		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
860		let result;
861		#[block]
862		{
863			result = BenchEnv::seal0_seal_now(&mut runtime, &mut memory, 4, 0);
864		}
865		assert_ok!(result);
866		assert_eq!(<MomentOf<T>>::decode(&mut &memory[4..]).unwrap(), *runtime.ext().now());
867	}
868
869	#[benchmark(pov_mode = Measured)]
870	fn seal_weight_to_fee() {
871		let len = <T::Balance as MaxEncodedLen>::max_encoded_len() as u32;
872		build_runtime!(runtime, memory: [len.to_le_bytes(), vec![0u8; len as _], ]);
873		let weight = Weight::from_parts(500_000, 300_000);
874		let result;
875		#[block]
876		{
877			result = BenchEnv::seal1_weight_to_fee(
878				&mut runtime,
879				&mut memory,
880				weight.ref_time(),
881				weight.proof_size(),
882				4,
883				0,
884			);
885		}
886		assert_ok!(result);
887		assert_eq!(
888			<BalanceOf<T>>::decode(&mut &memory[4..]).unwrap(),
889			runtime.ext().get_weight_price(weight)
890		);
891	}
892
893	#[benchmark(pov_mode = Measured)]
894	fn seal_input(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 - 4 }>) {
895		let mut setup = CallSetup::<T>::default();
896		let (mut ext, _) = setup.ext();
897		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![42u8; n as usize]);
898		let mut memory = memory!(n.to_le_bytes(), vec![0u8; n as usize],);
899		let result;
900		#[block]
901		{
902			result = BenchEnv::seal0_input(&mut runtime, &mut memory, 4, 0);
903		}
904		assert_ok!(result);
905		assert_eq!(&memory[4..], &vec![42u8; n as usize]);
906	}
907
908	#[benchmark(pov_mode = Measured)]
909	fn seal_return(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 - 4 }>) {
910		build_runtime!(runtime, memory: [n.to_le_bytes(), vec![42u8; n as usize], ]);
911
912		let result;
913		#[block]
914		{
915			result = BenchEnv::seal0_seal_return(&mut runtime, &mut memory, 0, 0, n);
916		}
917
918		assert!(matches!(
919			result,
920			Err(crate::wasm::TrapReason::Return(crate::wasm::ReturnData { .. }))
921		));
922	}
923
924	#[benchmark(pov_mode = Measured)]
925	fn seal_terminate(
926		n: Linear<0, { T::MaxDelegateDependencies::get() }>,
927	) -> Result<(), BenchmarkError> {
928		let beneficiary = account::<T::AccountId>("beneficiary", 0, 0);
929		let caller = whitelisted_caller();
930
931		build_runtime!(runtime, memory: [beneficiary.encode(),]);
932
933		T::Currency::set_balance(&caller, caller_funding::<T>());
934
935		(0..n).for_each(|i| {
936			let new_code = WasmModule::<T>::dummy_with_bytes(65 + i);
937			Contracts::<T>::store_code_raw(new_code.code, caller.clone()).unwrap();
938			runtime.ext().lock_delegate_dependency(new_code.hash).unwrap();
939		});
940
941		let result;
942		#[block]
943		{
944			result = BenchEnv::seal1_terminate(&mut runtime, &mut memory, 0);
945		}
946
947		assert!(matches!(result, Err(crate::wasm::TrapReason::Termination)));
948
949		Ok(())
950	}
951
952	// We benchmark only for the maximum subject length. We assume that this is some lowish
953	// number (< 1 KB). Therefore we are not overcharging too much in case a smaller subject is
954	// used.
955	#[benchmark(pov_mode = Measured)]
956	fn seal_random() {
957		let subject_len = T::Schedule::get().limits.subject_len;
958		assert!(subject_len < 1024);
959
960		let output_len =
961			<(SeedOf<T>, BlockNumberFor<T>) as MaxEncodedLen>::max_encoded_len() as u32;
962
963		build_runtime!(runtime, memory: [
964			output_len.to_le_bytes(),
965			vec![42u8; subject_len as _],
966			vec![0u8; output_len as _],
967		]);
968
969		let result;
970		#[block]
971		{
972			result = BenchEnv::seal0_random(
973				&mut runtime,
974				&mut memory,
975				4,               // subject_ptr
976				subject_len,     // subject_len
977				subject_len + 4, // output_ptr
978				0,               // output_len_ptr
979			);
980		}
981
982		assert_ok!(result);
983		assert_ok!(<(SeedOf<T>, BlockNumberFor<T>)>::decode(&mut &memory[subject_len as _..]));
984	}
985
986	// Benchmark the overhead that topics generate.
987	// `t`: Number of topics
988	// `n`: Size of event payload in bytes
989	#[benchmark(pov_mode = Measured)]
990	fn seal_deposit_event(
991		t: Linear<0, { T::Schedule::get().limits.event_topics }>,
992		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
993	) {
994		let topics = (0..t).map(|i| T::Hashing::hash_of(&i)).collect::<Vec<_>>().encode();
995		let topics_len = topics.len() as u32;
996
997		build_runtime!(runtime, memory: [
998			n.to_le_bytes(),
999			topics,
1000			vec![0u8; n as _],
1001		]);
1002
1003		let result;
1004		#[block]
1005		{
1006			result = BenchEnv::seal0_deposit_event(
1007				&mut runtime,
1008				&mut memory,
1009				4,              // topics_ptr
1010				topics_len,     // topics_len
1011				4 + topics_len, // data_ptr
1012				0,              // data_len
1013			);
1014		}
1015
1016		assert_ok!(result);
1017	}
1018
1019	// Benchmark debug_message call
1020	// Whereas this function is used in RPC mode only, it still should be secured
1021	// against an excessive use.
1022	//
1023	// i: size of input in bytes up to maximum allowed contract memory or maximum allowed debug
1024	// buffer size, whichever is less.
1025	#[benchmark]
1026	fn seal_debug_message(
1027		i: Linear<
1028			0,
1029			{
1030				(T::Schedule::get().limits.memory_pages * 64 * 1024)
1031					.min(T::MaxDebugBufferLen::get())
1032			},
1033		>,
1034	) {
1035		let mut setup = CallSetup::<T>::default();
1036		setup.enable_debug_message();
1037		let (mut ext, _) = setup.ext();
1038		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1039		// Fill memory with printable ASCII bytes.
1040		let mut memory = (0..i).zip((32..127).cycle()).map(|i| i.1).collect::<Vec<_>>();
1041
1042		let result;
1043		#[block]
1044		{
1045			result = BenchEnv::seal0_debug_message(&mut runtime, &mut memory, 0, i);
1046		}
1047		assert_ok!(result);
1048		assert_eq!(setup.debug_message().unwrap().len() as u32, i);
1049	}
1050
1051	#[benchmark(skip_meta, pov_mode = Measured)]
1052	fn get_storage_empty() -> Result<(), BenchmarkError> {
1053		let max_key_len = T::MaxStorageKeyLen::get();
1054		let key = vec![0u8; max_key_len as usize];
1055		let max_value_len = T::Schedule::get().limits.payload_len as usize;
1056		let value = vec![1u8; max_value_len];
1057
1058		let instance = Contract::<T>::new(WasmModule::dummy(), vec![])?;
1059		let info = instance.info()?;
1060		let child_trie_info = info.child_trie_info();
1061		info.bench_write_raw(&key, Some(value.clone()), false)
1062			.map_err(|_| "Failed to write to storage during setup.")?;
1063
1064		let result;
1065		#[block]
1066		{
1067			result = child::get_raw(&child_trie_info, &key);
1068		}
1069
1070		assert_eq!(result, Some(value));
1071		Ok(())
1072	}
1073
1074	#[benchmark(skip_meta, pov_mode = Measured)]
1075	fn get_storage_full() -> Result<(), BenchmarkError> {
1076		let max_key_len = T::MaxStorageKeyLen::get();
1077		let key = vec![0u8; max_key_len as usize];
1078		let max_value_len = T::Schedule::get().limits.payload_len;
1079		let value = vec![1u8; max_value_len as usize];
1080
1081		let instance = Contract::<T>::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?;
1082		let info = instance.info()?;
1083		let child_trie_info = info.child_trie_info();
1084		info.bench_write_raw(&key, Some(value.clone()), false)
1085			.map_err(|_| "Failed to write to storage during setup.")?;
1086
1087		let result;
1088		#[block]
1089		{
1090			result = child::get_raw(&child_trie_info, &key);
1091		}
1092
1093		assert_eq!(result, Some(value));
1094		Ok(())
1095	}
1096
1097	#[benchmark(skip_meta, pov_mode = Measured)]
1098	fn set_storage_empty() -> Result<(), BenchmarkError> {
1099		let max_key_len = T::MaxStorageKeyLen::get();
1100		let key = vec![0u8; max_key_len as usize];
1101		let max_value_len = T::Schedule::get().limits.payload_len as usize;
1102		let value = vec![1u8; max_value_len];
1103
1104		let instance = Contract::<T>::new(WasmModule::dummy(), vec![])?;
1105		let info = instance.info()?;
1106		let child_trie_info = info.child_trie_info();
1107		info.bench_write_raw(&key, Some(vec![42u8; max_value_len]), false)
1108			.map_err(|_| "Failed to write to storage during setup.")?;
1109
1110		let val = Some(value.clone());
1111		let result;
1112		#[block]
1113		{
1114			result = info.bench_write_raw(&key, val, true);
1115		}
1116
1117		assert_ok!(result);
1118		assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
1119		Ok(())
1120	}
1121
1122	#[benchmark(skip_meta, pov_mode = Measured)]
1123	fn set_storage_full() -> Result<(), BenchmarkError> {
1124		let max_key_len = T::MaxStorageKeyLen::get();
1125		let key = vec![0u8; max_key_len as usize];
1126		let max_value_len = T::Schedule::get().limits.payload_len;
1127		let value = vec![1u8; max_value_len as usize];
1128
1129		let instance = Contract::<T>::with_unbalanced_storage_trie(WasmModule::dummy(), &key)?;
1130		let info = instance.info()?;
1131		let child_trie_info = info.child_trie_info();
1132		info.bench_write_raw(&key, Some(vec![42u8; max_value_len as usize]), false)
1133			.map_err(|_| "Failed to write to storage during setup.")?;
1134
1135		let val = Some(value.clone());
1136		let result;
1137		#[block]
1138		{
1139			result = info.bench_write_raw(&key, val, true);
1140		}
1141
1142		assert_ok!(result);
1143		assert_eq!(child::get_raw(&child_trie_info, &key).unwrap(), value);
1144		Ok(())
1145	}
1146
1147	// n: new byte size
1148	// o: old byte size
1149	#[benchmark(skip_meta, pov_mode = Measured)]
1150	fn seal_set_storage(
1151		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1152		o: Linear<0, { T::Schedule::get().limits.payload_len }>,
1153	) -> Result<(), BenchmarkError> {
1154		let max_key_len = T::MaxStorageKeyLen::get();
1155		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1156			.map_err(|_| "Key has wrong length")?;
1157		let value = vec![1u8; n as usize];
1158
1159		build_runtime!(runtime, instance, memory: [ key.to_vec(), value.clone(), ]);
1160		let info = instance.info()?;
1161
1162		info.write(&key, Some(vec![42u8; o as usize]), None, false)
1163			.map_err(|_| "Failed to write to storage during setup.")?;
1164
1165		let result;
1166		#[block]
1167		{
1168			result = BenchEnv::seal2_set_storage(
1169				&mut runtime,
1170				&mut memory,
1171				0,           // key_ptr
1172				max_key_len, // key_len
1173				max_key_len, // value_ptr
1174				n,           // value_len
1175			);
1176		}
1177
1178		assert_ok!(result);
1179		assert_eq!(info.read(&key).unwrap(), value);
1180		Ok(())
1181	}
1182
1183	#[benchmark(skip_meta, pov_mode = Measured)]
1184	fn seal_clear_storage(
1185		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1186	) -> Result<(), BenchmarkError> {
1187		let max_key_len = T::MaxStorageKeyLen::get();
1188		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1189			.map_err(|_| "Key has wrong length")?;
1190		build_runtime!(runtime, instance, memory: [ key.to_vec(), ]);
1191		let info = instance.info()?;
1192
1193		info.write(&key, Some(vec![42u8; n as usize]), None, false)
1194			.map_err(|_| "Failed to write to storage during setup.")?;
1195
1196		let result;
1197		#[block]
1198		{
1199			result = BenchEnv::seal1_clear_storage(&mut runtime, &mut memory, 0, max_key_len);
1200		}
1201
1202		assert_ok!(result);
1203		assert!(info.read(&key).is_none());
1204		Ok(())
1205	}
1206
1207	#[benchmark(skip_meta, pov_mode = Measured)]
1208	fn seal_get_storage(
1209		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1210	) -> Result<(), BenchmarkError> {
1211		let max_key_len = T::MaxStorageKeyLen::get();
1212		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1213			.map_err(|_| "Key has wrong length")?;
1214		build_runtime!(runtime, instance, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
1215		let info = instance.info()?;
1216
1217		info.write(&key, Some(vec![42u8; n as usize]), None, false)
1218			.map_err(|_| "Failed to write to storage during setup.")?;
1219
1220		let out_ptr = max_key_len + 4;
1221		let result;
1222		#[block]
1223		{
1224			result = BenchEnv::seal1_get_storage(
1225				&mut runtime,
1226				&mut memory,
1227				0,           // key_ptr
1228				max_key_len, // key_len
1229				out_ptr,     // out_ptr
1230				max_key_len, // out_len_ptr
1231			);
1232		}
1233
1234		assert_ok!(result);
1235		assert_eq!(&info.read(&key).unwrap(), &memory[out_ptr as usize..]);
1236		Ok(())
1237	}
1238
1239	#[benchmark(skip_meta, pov_mode = Measured)]
1240	fn seal_contains_storage(
1241		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1242	) -> Result<(), BenchmarkError> {
1243		let max_key_len = T::MaxStorageKeyLen::get();
1244		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1245			.map_err(|_| "Key has wrong length")?;
1246		build_runtime!(runtime, instance, memory: [ key.to_vec(), ]);
1247		let info = instance.info()?;
1248
1249		info.write(&key, Some(vec![42u8; n as usize]), None, false)
1250			.map_err(|_| "Failed to write to storage during setup.")?;
1251
1252		let result;
1253		#[block]
1254		{
1255			result = BenchEnv::seal1_contains_storage(&mut runtime, &mut memory, 0, max_key_len);
1256		}
1257
1258		assert_eq!(result.unwrap(), n);
1259		Ok(())
1260	}
1261
1262	#[benchmark(skip_meta, pov_mode = Measured)]
1263	fn seal_take_storage(
1264		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1265	) -> Result<(), BenchmarkError> {
1266		let max_key_len = T::MaxStorageKeyLen::get();
1267		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1268			.map_err(|_| "Key has wrong length")?;
1269		build_runtime!(runtime, instance, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
1270		let info = instance.info()?;
1271
1272		let value = vec![42u8; n as usize];
1273		info.write(&key, Some(value.clone()), None, false)
1274			.map_err(|_| "Failed to write to storage during setup.")?;
1275
1276		let out_ptr = max_key_len + 4;
1277		let result;
1278		#[block]
1279		{
1280			result = BenchEnv::seal0_take_storage(
1281				&mut runtime,
1282				&mut memory,
1283				0,           // key_ptr
1284				max_key_len, // key_len
1285				out_ptr,     // out_ptr
1286				max_key_len, // out_len_ptr
1287			);
1288		}
1289
1290		assert_ok!(result);
1291		assert!(&info.read(&key).is_none());
1292		assert_eq!(&value, &memory[out_ptr as usize..]);
1293		Ok(())
1294	}
1295
1296	// We use both full and empty benchmarks here instead of benchmarking transient_storage
1297	// (BTreeMap) directly. This approach is necessary because benchmarking this BTreeMap is very
1298	// slow. Additionally, we use linear regression for our benchmarks, and the BTreeMap's log(n)
1299	// complexity can introduce approximation errors.
1300	#[benchmark(pov_mode = Ignored)]
1301	fn set_transient_storage_empty() -> Result<(), BenchmarkError> {
1302		let max_value_len = T::Schedule::get().limits.payload_len;
1303		let max_key_len = T::MaxStorageKeyLen::get();
1304		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1305			.map_err(|_| "Key has wrong length")?;
1306		let value = Some(vec![42u8; max_value_len as _]);
1307		let mut setup = CallSetup::<T>::default();
1308		let (mut ext, _) = setup.ext();
1309		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1310		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1311		let result;
1312		#[block]
1313		{
1314			result = runtime.ext().set_transient_storage(&key, value, false);
1315		}
1316
1317		assert_eq!(result, Ok(WriteOutcome::New));
1318		assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
1319		Ok(())
1320	}
1321
1322	#[benchmark(pov_mode = Ignored)]
1323	fn set_transient_storage_full() -> Result<(), BenchmarkError> {
1324		let max_value_len = T::Schedule::get().limits.payload_len;
1325		let max_key_len = T::MaxStorageKeyLen::get();
1326		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1327			.map_err(|_| "Key has wrong length")?;
1328		let value = Some(vec![42u8; max_value_len as _]);
1329		let mut setup = CallSetup::<T>::default();
1330		setup.set_transient_storage_size(T::MaxTransientStorageSize::get());
1331		let (mut ext, _) = setup.ext();
1332		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1333		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1334		let result;
1335		#[block]
1336		{
1337			result = runtime.ext().set_transient_storage(&key, value, false);
1338		}
1339
1340		assert_eq!(result, Ok(WriteOutcome::New));
1341		assert_eq!(runtime.ext().get_transient_storage(&key), Some(vec![42u8; max_value_len as _]));
1342		Ok(())
1343	}
1344
1345	#[benchmark(pov_mode = Ignored)]
1346	fn get_transient_storage_empty() -> Result<(), BenchmarkError> {
1347		let max_value_len = T::Schedule::get().limits.payload_len;
1348		let max_key_len = T::MaxStorageKeyLen::get();
1349		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1350			.map_err(|_| "Key has wrong length")?;
1351
1352		let mut setup = CallSetup::<T>::default();
1353		let (mut ext, _) = setup.ext();
1354		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1355		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1356		runtime
1357			.ext()
1358			.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1359			.map_err(|_| "Failed to write to transient storage during setup.")?;
1360		let result;
1361		#[block]
1362		{
1363			result = runtime.ext().get_transient_storage(&key);
1364		}
1365
1366		assert_eq!(result, Some(vec![42u8; max_value_len as _]));
1367		Ok(())
1368	}
1369
1370	#[benchmark(pov_mode = Ignored)]
1371	fn get_transient_storage_full() -> Result<(), BenchmarkError> {
1372		let max_value_len = T::Schedule::get().limits.payload_len;
1373		let max_key_len = T::MaxStorageKeyLen::get();
1374		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1375			.map_err(|_| "Key has wrong length")?;
1376
1377		let mut setup = CallSetup::<T>::default();
1378		setup.set_transient_storage_size(T::MaxTransientStorageSize::get());
1379		let (mut ext, _) = setup.ext();
1380		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1381		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1382		runtime
1383			.ext()
1384			.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1385			.map_err(|_| "Failed to write to transient storage during setup.")?;
1386		let result;
1387		#[block]
1388		{
1389			result = runtime.ext().get_transient_storage(&key);
1390		}
1391
1392		assert_eq!(result, Some(vec![42u8; max_value_len as _]));
1393		Ok(())
1394	}
1395
1396	// The weight of journal rollbacks should be taken into account when setting storage.
1397	#[benchmark(pov_mode = Ignored)]
1398	fn rollback_transient_storage() -> Result<(), BenchmarkError> {
1399		let max_value_len = T::Schedule::get().limits.payload_len;
1400		let max_key_len = T::MaxStorageKeyLen::get();
1401		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1402			.map_err(|_| "Key has wrong length")?;
1403
1404		let mut setup = CallSetup::<T>::default();
1405		setup.set_transient_storage_size(T::MaxTransientStorageSize::get());
1406		let (mut ext, _) = setup.ext();
1407		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1408		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1409		runtime.ext().transient_storage().start_transaction();
1410		runtime
1411			.ext()
1412			.set_transient_storage(&key, Some(vec![42u8; max_value_len as _]), false)
1413			.map_err(|_| "Failed to write to transient storage during setup.")?;
1414		#[block]
1415		{
1416			runtime.ext().transient_storage().rollback_transaction();
1417		}
1418
1419		assert_eq!(runtime.ext().get_transient_storage(&key), None);
1420		Ok(())
1421	}
1422
1423	// n: new byte size
1424	// o: old byte size
1425	#[benchmark(pov_mode = Measured)]
1426	fn seal_set_transient_storage(
1427		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1428		o: Linear<0, { T::Schedule::get().limits.payload_len }>,
1429	) -> Result<(), BenchmarkError> {
1430		let max_key_len = T::MaxStorageKeyLen::get();
1431		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1432			.map_err(|_| "Key has wrong length")?;
1433		let value = vec![1u8; n as usize];
1434		build_runtime!(runtime, memory: [ key.to_vec(), value.clone(), ]);
1435		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1436		runtime
1437			.ext()
1438			.set_transient_storage(&key, Some(vec![42u8; o as usize]), false)
1439			.map_err(|_| "Failed to write to transient storage during setup.")?;
1440
1441		let result;
1442		#[block]
1443		{
1444			result = BenchEnv::seal0_set_transient_storage(
1445				&mut runtime,
1446				&mut memory,
1447				0,           // key_ptr
1448				max_key_len, // key_len
1449				max_key_len, // value_ptr
1450				n,           // value_len
1451			);
1452		}
1453
1454		assert_ok!(result);
1455		assert_eq!(runtime.ext().get_transient_storage(&key).unwrap(), value);
1456		Ok(())
1457	}
1458
1459	#[benchmark(pov_mode = Measured)]
1460	fn seal_clear_transient_storage(
1461		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1462	) -> Result<(), BenchmarkError> {
1463		let max_key_len = T::MaxStorageKeyLen::get();
1464		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1465			.map_err(|_| "Key has wrong length")?;
1466		build_runtime!(runtime, memory: [ key.to_vec(), ]);
1467		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1468		runtime
1469			.ext()
1470			.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1471			.map_err(|_| "Failed to write to transient storage during setup.")?;
1472
1473		let result;
1474		#[block]
1475		{
1476			result =
1477				BenchEnv::seal0_clear_transient_storage(&mut runtime, &mut memory, 0, max_key_len);
1478		}
1479
1480		assert_ok!(result);
1481		assert!(runtime.ext().get_transient_storage(&key).is_none());
1482		Ok(())
1483	}
1484
1485	#[benchmark(pov_mode = Measured)]
1486	fn seal_get_transient_storage(
1487		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1488	) -> Result<(), BenchmarkError> {
1489		let max_key_len = T::MaxStorageKeyLen::get();
1490		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1491			.map_err(|_| "Key has wrong length")?;
1492		build_runtime!(runtime, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
1493		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1494		runtime
1495			.ext()
1496			.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1497			.map_err(|_| "Failed to write to transient storage during setup.")?;
1498
1499		let out_ptr = max_key_len + 4;
1500		let result;
1501		#[block]
1502		{
1503			result = BenchEnv::seal0_get_transient_storage(
1504				&mut runtime,
1505				&mut memory,
1506				0,           // key_ptr
1507				max_key_len, // key_len
1508				out_ptr,     // out_ptr
1509				max_key_len, // out_len_ptr
1510			);
1511		}
1512
1513		assert_ok!(result);
1514		assert_eq!(
1515			&runtime.ext().get_transient_storage(&key).unwrap(),
1516			&memory[out_ptr as usize..]
1517		);
1518		Ok(())
1519	}
1520
1521	#[benchmark(pov_mode = Measured)]
1522	fn seal_contains_transient_storage(
1523		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1524	) -> Result<(), BenchmarkError> {
1525		let max_key_len = T::MaxStorageKeyLen::get();
1526		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1527			.map_err(|_| "Key has wrong length")?;
1528		build_runtime!(runtime, memory: [ key.to_vec(), ]);
1529		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1530		runtime
1531			.ext()
1532			.set_transient_storage(&key, Some(vec![42u8; n as usize]), false)
1533			.map_err(|_| "Failed to write to transient storage during setup.")?;
1534
1535		let result;
1536		#[block]
1537		{
1538			result = BenchEnv::seal0_contains_transient_storage(
1539				&mut runtime,
1540				&mut memory,
1541				0,
1542				max_key_len,
1543			);
1544		}
1545
1546		assert_eq!(result.unwrap(), n);
1547		Ok(())
1548	}
1549
1550	#[benchmark(pov_mode = Measured)]
1551	fn seal_take_transient_storage(
1552		n: Linear<0, { T::Schedule::get().limits.payload_len }>,
1553	) -> Result<(), BenchmarkError> {
1554		let n = T::Schedule::get().limits.payload_len;
1555		let max_key_len = T::MaxStorageKeyLen::get();
1556		let key = Key::<T>::try_from_var(vec![0u8; max_key_len as usize])
1557			.map_err(|_| "Key has wrong length")?;
1558		build_runtime!(runtime, memory: [ key.to_vec(), n.to_le_bytes(), vec![0u8; n as _], ]);
1559		runtime.ext().transient_storage().meter().current_mut().limit = u32::MAX;
1560		let value = vec![42u8; n as usize];
1561		runtime
1562			.ext()
1563			.set_transient_storage(&key, Some(value.clone()), false)
1564			.map_err(|_| "Failed to write to transient storage during setup.")?;
1565
1566		let out_ptr = max_key_len + 4;
1567		let result;
1568		#[block]
1569		{
1570			result = BenchEnv::seal0_take_transient_storage(
1571				&mut runtime,
1572				&mut memory,
1573				0,           // key_ptr
1574				max_key_len, // key_len
1575				out_ptr,     // out_ptr
1576				max_key_len, // out_len_ptr
1577			);
1578		}
1579
1580		assert_ok!(result);
1581		assert!(&runtime.ext().get_transient_storage(&key).is_none());
1582		assert_eq!(&value, &memory[out_ptr as usize..]);
1583		Ok(())
1584	}
1585
1586	// We transfer to unique accounts.
1587	#[benchmark(pov_mode = Measured)]
1588	fn seal_transfer() {
1589		let account = account::<T::AccountId>("receiver", 0, 0);
1590		let value = Pallet::<T>::min_balance();
1591		assert!(value > 0u32.into());
1592
1593		let mut setup = CallSetup::<T>::default();
1594		setup.set_balance(value);
1595		let (mut ext, _) = setup.ext();
1596		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1597
1598		let account_bytes = account.encode();
1599		let account_len = account_bytes.len() as u32;
1600		let value_bytes = value.encode();
1601		let value_len = value_bytes.len() as u32;
1602		let mut memory = memory!(account_bytes, value_bytes,);
1603
1604		let result;
1605		#[block]
1606		{
1607			result = BenchEnv::seal0_transfer(
1608				&mut runtime,
1609				&mut memory,
1610				0, // account_ptr
1611				account_len,
1612				account_len,
1613				value_len,
1614			);
1615		}
1616
1617		assert_ok!(result);
1618	}
1619
1620	// t: with or without some value to transfer
1621	// i: size of the input data
1622	#[benchmark(pov_mode = Measured)]
1623	fn seal_call(t: Linear<0, 1>, i: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
1624		let Contract { account_id: callee, .. } =
1625			Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
1626		let callee_bytes = callee.encode();
1627		let callee_len = callee_bytes.len() as u32;
1628
1629		let value: BalanceOf<T> = t.into();
1630		let value_bytes = value.encode();
1631
1632		let deposit: BalanceOf<T> = (u32::MAX - 100).into();
1633		let deposit_bytes = deposit.encode();
1634		let deposit_len = deposit_bytes.len() as u32;
1635
1636		let mut setup = CallSetup::<T>::default();
1637		setup.set_storage_deposit_limit(deposit);
1638		setup.set_data(vec![42; i as usize]);
1639		setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
1640
1641		let (mut ext, _) = setup.ext();
1642		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1643		let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes,);
1644
1645		let result;
1646		#[block]
1647		{
1648			result = BenchEnv::seal2_call(
1649				&mut runtime,
1650				&mut memory,
1651				CallFlags::CLONE_INPUT.bits(), // flags
1652				0,                             // callee_ptr
1653				0,                             // ref_time_limit
1654				0,                             // proof_size_limit
1655				callee_len,                    // deposit_ptr
1656				callee_len + deposit_len,      // value_ptr
1657				0,                             // input_data_ptr
1658				0,                             // input_data_len
1659				SENTINEL,                      // output_ptr
1660				0,                             // output_len_ptr
1661			);
1662		}
1663
1664		assert_ok!(result);
1665	}
1666
1667	#[benchmark(pov_mode = Measured)]
1668	fn seal_delegate_call() -> Result<(), BenchmarkError> {
1669		let hash = Contract::<T>::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash;
1670
1671		let mut setup = CallSetup::<T>::default();
1672		setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
1673
1674		let (mut ext, _) = setup.ext();
1675		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1676		let mut memory = memory!(hash.encode(),);
1677
1678		let result;
1679		#[block]
1680		{
1681			result = BenchEnv::seal0_delegate_call(
1682				&mut runtime,
1683				&mut memory,
1684				0,        // flags
1685				0,        // code_hash_ptr
1686				0,        // input_data_ptr
1687				0,        // input_data_len
1688				SENTINEL, // output_ptr
1689				0,
1690			);
1691		}
1692
1693		assert_ok!(result);
1694		Ok(())
1695	}
1696
1697	// t: value to transfer
1698	// i: size of input in bytes
1699	// s: size of salt in bytes
1700	#[benchmark(pov_mode = Measured)]
1701	fn seal_instantiate(
1702		i: Linear<0, { (code::max_pages::<T>() - 1) * 64 * 1024 }>,
1703		s: Linear<0, { (code::max_pages::<T>() - 1) * 64 * 1024 }>,
1704	) -> Result<(), BenchmarkError> {
1705		let hash = Contract::<T>::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash;
1706		let hash_bytes = hash.encode();
1707		let hash_len = hash_bytes.len() as u32;
1708
1709		let value: BalanceOf<T> = 1u32.into();
1710		let value_bytes = value.encode();
1711		let value_len = value_bytes.len() as u32;
1712
1713		let deposit: BalanceOf<T> = 0u32.into();
1714		let deposit_bytes = deposit.encode();
1715		let deposit_len = deposit_bytes.len() as u32;
1716
1717		let mut setup = CallSetup::<T>::default();
1718		setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone()));
1719		setup.set_balance(value + (Pallet::<T>::min_balance() * 2u32.into()));
1720
1721		let account_id = &setup.contract().account_id.clone();
1722		let (mut ext, _) = setup.ext();
1723		let mut runtime = crate::wasm::Runtime::new(&mut ext, vec![]);
1724
1725		let input = vec![42u8; i as _];
1726		let salt = vec![42u8; s as _];
1727		let addr = Contracts::<T>::contract_address(&account_id, &hash, &input, &salt);
1728		let mut memory = memory!(hash_bytes, deposit_bytes, value_bytes, input, salt,);
1729
1730		let mut offset = {
1731			let mut current = 0u32;
1732			move |after: u32| {
1733				current += after;
1734				current
1735			}
1736		};
1737
1738		assert!(ContractInfoOf::<T>::get(&addr).is_none());
1739
1740		let result;
1741		#[block]
1742		{
1743			result = BenchEnv::seal2_instantiate(
1744				&mut runtime,
1745				&mut memory,
1746				0,                   // code_hash_ptr
1747				0,                   // ref_time_limit
1748				0,                   // proof_size_limit
1749				offset(hash_len),    // deposit_ptr
1750				offset(deposit_len), // value_ptr
1751				offset(value_len),   // input_data_ptr
1752				i,                   // input_data_len
1753				SENTINEL,            // address_ptr
1754				0,                   // address_len_ptr
1755				SENTINEL,            // output_ptr
1756				0,                   // output_len_ptr
1757				offset(i),           // salt_ptr
1758				s,                   // salt_len
1759			);
1760		}
1761
1762		assert_ok!(result);
1763		assert!(ContractInfoOf::<T>::get(&addr).is_some());
1764		assert_eq!(T::Currency::balance(&addr), Pallet::<T>::min_balance() + value);
1765		Ok(())
1766	}
1767
1768	// `n`: Input to hash in bytes
1769	#[benchmark(pov_mode = Measured)]
1770	fn seal_hash_sha2_256(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
1771		build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
1772
1773		let result;
1774		#[block]
1775		{
1776			result = BenchEnv::seal0_hash_sha2_256(&mut runtime, &mut memory, 32, n, 0);
1777		}
1778		assert_eq!(sp_io::hashing::sha2_256(&memory[32..]), &memory[0..32]);
1779		assert_ok!(result);
1780	}
1781
1782	// `n`: Input to hash in bytes
1783	#[benchmark(pov_mode = Measured)]
1784	fn seal_hash_keccak_256(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
1785		build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
1786
1787		let result;
1788		#[block]
1789		{
1790			result = BenchEnv::seal0_hash_keccak_256(&mut runtime, &mut memory, 32, n, 0);
1791		}
1792		assert_eq!(sp_io::hashing::keccak_256(&memory[32..]), &memory[0..32]);
1793		assert_ok!(result);
1794	}
1795
1796	// `n`: Input to hash in bytes
1797	#[benchmark(pov_mode = Measured)]
1798	fn seal_hash_blake2_256(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
1799		build_runtime!(runtime, memory: [[0u8; 32], vec![0u8; n as usize], ]);
1800
1801		let result;
1802		#[block]
1803		{
1804			result = BenchEnv::seal0_hash_blake2_256(&mut runtime, &mut memory, 32, n, 0);
1805		}
1806		assert_eq!(sp_io::hashing::blake2_256(&memory[32..]), &memory[0..32]);
1807		assert_ok!(result);
1808	}
1809
1810	// `n`: Input to hash in bytes
1811	#[benchmark(pov_mode = Measured)]
1812	fn seal_hash_blake2_128(n: Linear<0, { code::max_pages::<T>() * 64 * 1024 }>) {
1813		build_runtime!(runtime, memory: [[0u8; 16], vec![0u8; n as usize], ]);
1814
1815		let result;
1816		#[block]
1817		{
1818			result = BenchEnv::seal0_hash_blake2_128(&mut runtime, &mut memory, 16, n, 0);
1819		}
1820		assert_eq!(sp_io::hashing::blake2_128(&memory[16..]), &memory[0..16]);
1821		assert_ok!(result);
1822	}
1823
1824	// `n`: Message input length to verify in bytes.
1825	// need some buffer so the code size does not exceed the max code size.
1826	#[benchmark(pov_mode = Measured)]
1827	fn seal_sr25519_verify(n: Linear<0, { T::MaxCodeLen::get() - 255 }>) {
1828		let message = (0..n).zip((32u8..127u8).cycle()).map(|(_, c)| c).collect::<Vec<_>>();
1829		let message_len = message.len() as u32;
1830
1831		let key_type = sp_core::crypto::KeyTypeId(*b"code");
1832		let pub_key = sp_io::crypto::sr25519_generate(key_type, None);
1833		let sig =
1834			sp_io::crypto::sr25519_sign(key_type, &pub_key, &message).expect("Generates signature");
1835		let sig = AsRef::<[u8; 64]>::as_ref(&sig).to_vec();
1836		let sig_len = sig.len() as u32;
1837
1838		build_runtime!(runtime, memory: [sig, pub_key.to_vec(), message, ]);
1839
1840		let result;
1841		#[block]
1842		{
1843			result = BenchEnv::seal0_sr25519_verify(
1844				&mut runtime,
1845				&mut memory,
1846				0,                              // signature_ptr
1847				sig_len,                        // pub_key_ptr
1848				message_len,                    // message_len
1849				sig_len + pub_key.len() as u32, // message_ptr
1850			);
1851		}
1852
1853		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1854	}
1855
1856	#[benchmark(pov_mode = Measured)]
1857	fn seal_ecdsa_recover() {
1858		let message_hash = sp_io::hashing::blake2_256("Hello world".as_bytes());
1859		let key_type = sp_core::crypto::KeyTypeId(*b"code");
1860		let signature = {
1861			let pub_key = sp_io::crypto::ecdsa_generate(key_type, None);
1862			let sig = sp_io::crypto::ecdsa_sign_prehashed(key_type, &pub_key, &message_hash)
1863				.expect("Generates signature");
1864			AsRef::<[u8; 65]>::as_ref(&sig).to_vec()
1865		};
1866
1867		build_runtime!(runtime, memory: [signature, message_hash, [0u8; 33], ]);
1868
1869		let result;
1870		#[block]
1871		{
1872			result = BenchEnv::seal0_ecdsa_recover(
1873				&mut runtime,
1874				&mut memory,
1875				0,       // signature_ptr
1876				65,      // message_hash_ptr
1877				65 + 32, // output_ptr
1878			);
1879		}
1880
1881		assert_eq!(result.unwrap(), ReturnErrorCode::Success);
1882	}
1883
1884	// Only calling the function itself for the list of
1885	// generated different ECDSA keys.
1886	// This is a slow call: We reduce the number of runs.
1887	#[benchmark(pov_mode = Measured)]
1888	fn seal_ecdsa_to_eth_address() {
1889		let key_type = sp_core::crypto::KeyTypeId(*b"code");
1890		let pub_key_bytes = sp_io::crypto::ecdsa_generate(key_type, None).0;
1891		build_runtime!(runtime, memory: [[0u8; 20], pub_key_bytes,]);
1892
1893		let result;
1894		#[block]
1895		{
1896			result = BenchEnv::seal0_ecdsa_to_eth_address(
1897				&mut runtime,
1898				&mut memory,
1899				20, // key_ptr
1900				0,  // output_ptr
1901			);
1902		}
1903
1904		assert_ok!(result);
1905		assert_eq!(&memory[..20], runtime.ext().ecdsa_to_eth_address(&pub_key_bytes).unwrap());
1906	}
1907
1908	#[benchmark(pov_mode = Measured)]
1909	fn seal_set_code_hash() -> Result<(), BenchmarkError> {
1910		let code_hash =
1911			Contract::<T>::with_index(1, WasmModule::dummy(), vec![])?.info()?.code_hash;
1912
1913		build_runtime!(runtime, memory: [ code_hash.encode(),]);
1914
1915		let result;
1916		#[block]
1917		{
1918			result = BenchEnv::seal0_set_code_hash(&mut runtime, &mut memory, 0);
1919		}
1920
1921		assert_ok!(result);
1922		Ok(())
1923	}
1924
1925	#[benchmark(pov_mode = Measured)]
1926	fn lock_delegate_dependency() -> Result<(), BenchmarkError> {
1927		let code_hash = Contract::<T>::with_index(1, WasmModule::dummy_with_bytes(1), vec![])?
1928			.info()?
1929			.code_hash;
1930
1931		build_runtime!(runtime, memory: [ code_hash.encode(),]);
1932
1933		let result;
1934		#[block]
1935		{
1936			result = BenchEnv::seal0_lock_delegate_dependency(&mut runtime, &mut memory, 0);
1937		}
1938
1939		assert_ok!(result);
1940		Ok(())
1941	}
1942
1943	#[benchmark]
1944	fn unlock_delegate_dependency() -> Result<(), BenchmarkError> {
1945		let code_hash = Contract::<T>::with_index(1, WasmModule::dummy_with_bytes(1), vec![])?
1946			.info()?
1947			.code_hash;
1948
1949		build_runtime!(runtime, memory: [ code_hash.encode(),]);
1950		BenchEnv::seal0_lock_delegate_dependency(&mut runtime, &mut memory, 0).unwrap();
1951
1952		let result;
1953		#[block]
1954		{
1955			result = BenchEnv::seal0_unlock_delegate_dependency(&mut runtime, &mut memory, 0);
1956		}
1957
1958		assert_ok!(result);
1959		Ok(())
1960	}
1961
1962	#[benchmark(pov_mode = Measured)]
1963	fn seal_reentrance_count() {
1964		build_runtime!(runtime, memory: []);
1965		let result;
1966		#[block]
1967		{
1968			result = BenchEnv::seal0_reentrance_count(&mut runtime, &mut memory)
1969		}
1970
1971		assert_eq!(result.unwrap(), 0);
1972	}
1973
1974	#[benchmark(pov_mode = Measured)]
1975	fn seal_account_reentrance_count() {
1976		let Contract { account_id, .. } =
1977			Contract::<T>::with_index(1, WasmModule::dummy(), vec![]).unwrap();
1978		build_runtime!(runtime, memory: [account_id.encode(),]);
1979
1980		let result;
1981		#[block]
1982		{
1983			result = BenchEnv::seal0_account_reentrance_count(&mut runtime, &mut memory, 0);
1984		}
1985
1986		assert_eq!(result.unwrap(), 0);
1987	}
1988
1989	#[benchmark(pov_mode = Measured)]
1990	fn seal_instantiation_nonce() {
1991		build_runtime!(runtime, memory: []);
1992
1993		let result;
1994		#[block]
1995		{
1996			result = BenchEnv::seal0_instantiation_nonce(&mut runtime, &mut memory);
1997		}
1998
1999		assert_eq!(result.unwrap(), 1);
2000	}
2001
2002	// We load `i64` values from random linear memory locations and store the loaded
2003	// values back into yet another random linear memory location.
2004	// The random addresses are uniformly distributed across the entire span of the linear memory.
2005	// We do this to enforce random memory accesses which are particularly expensive.
2006	//
2007	// The combination of this computation is our weight base `w_base`.
2008	#[benchmark(pov_mode = Ignored)]
2009	fn instr_i64_load_store(r: Linear<0, INSTR_BENCHMARK_RUNS>) -> Result<(), BenchmarkError> {
2010		use rand::prelude::*;
2011
2012		// We do not need to be secure here. Fixed seed allows for deterministic results.
2013		let mut rng = rand_pcg::Pcg32::seed_from_u64(8446744073709551615);
2014
2015		let memory = ImportedMemory::max::<T>();
2016		let bytes_per_page = 65536;
2017		let bytes_per_memory = memory.max_pages * bytes_per_page;
2018		let mut sbox = Sandbox::from(&WasmModule::<T>::from(ModuleDefinition {
2019			memory: Some(memory),
2020			call_body: Some(body::repeated_with_locals_using(
2021				&[Local::new(1, ValueType::I64)],
2022				r,
2023				|| {
2024					// Instruction sequence to load a `i64` from linear memory
2025					// at a random memory location and store it back into another
2026					// location of the linear memory.
2027					let c0: i32 = rng.gen_range(0..bytes_per_memory as i32);
2028					let c1: i32 = rng.gen_range(0..bytes_per_memory as i32);
2029					[
2030						Instruction::I32Const(c0), // address for `i64.load_8s`
2031						Instruction::I64Load8S(0, 0),
2032						Instruction::SetLocal(0),  /* temporarily store value loaded in
2033						                            * `i64.load_8s` */
2034						Instruction::I32Const(c1), // address for `i64.store8`
2035						Instruction::GetLocal(0),  // value to be stores in `i64.store8`
2036						Instruction::I64Store8(0, 0),
2037					]
2038				},
2039			)),
2040			..Default::default()
2041		}));
2042		#[block]
2043		{
2044			sbox.invoke();
2045		}
2046		Ok(())
2047	}
2048
2049	// This is no benchmark. It merely exist to have an easy way to pretty print the currently
2050	// configured `Schedule` during benchmark development. Check the README on how to print this.
2051	#[benchmark(extra, pov_mode = Ignored)]
2052	fn print_schedule() -> Result<(), BenchmarkError> {
2053		let max_weight = <T as frame_system::Config>::BlockWeights::get().max_block;
2054		let (weight_per_key, key_budget) =
2055			ContractInfo::<T>::deletion_budget(&mut WeightMeter::with_limit(max_weight));
2056		let schedule = T::Schedule::get();
2057		log::info!(target: LOG_TARGET, "
2058		{schedule:#?}
2059		###############################################
2060		Lazy deletion weight per key: {weight_per_key}
2061		Lazy deletion keys per block: {key_budget}
2062		");
2063		#[block]
2064		{}
2065
2066		Err(BenchmarkError::Skip)
2067	}
2068
2069	impl_benchmark_test_suite!(
2070		Contracts,
2071		crate::tests::ExtBuilder::default().build(),
2072		crate::tests::Test,
2073	);
2074}