referrerpolicy=no-referrer-when-downgrade

frame_system_benchmarking/
inner.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//! Frame System benchmarks.
19
20use alloc::{vec, vec::Vec};
21use codec::Encode;
22use frame_benchmarking::v2::*;
23use frame_support::{dispatch::DispatchClass, storage, traits::Get};
24use frame_system::{Call, Pallet as System, RawOrigin};
25use sp_core::storage::well_known_keys;
26use sp_runtime::traits::Hash;
27
28pub struct Pallet<T: Config>(System<T>);
29pub trait Config: frame_system::Config {
30	/// Adds ability to the Runtime to test against their sample code.
31	///
32	/// Default is `../res/kitchensink_runtime.compact.compressed.wasm`.
33	fn prepare_set_code_data() -> Vec<u8> {
34		include_bytes!("../res/kitchensink_runtime.compact.compressed.wasm").to_vec()
35	}
36
37	/// Adds ability to the Runtime to prepare/initialize before running benchmark `set_code`.
38	fn setup_set_code_requirements(_code: &Vec<u8>) -> Result<(), BenchmarkError> {
39		Ok(())
40	}
41
42	/// Adds ability to the Runtime to do custom validation after benchmark.
43	///
44	/// Default is checking for `CodeUpdated` event .
45	fn verify_set_code() {
46		let code = Self::prepare_set_code_data();
47		let hash = Self::Hashing::hash(&code);
48		let want: Self::RuntimeEvent = frame_system::Event::<Self>::CodeUpdated { hash }.into();
49
50		System::<Self>::assert_last_event(want);
51	}
52}
53
54#[benchmarks]
55mod benchmarks {
56	use super::*;
57
58	#[benchmark]
59	fn remark(
60		b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
61	) -> Result<(), BenchmarkError> {
62		let remark_message = vec![1; b as usize];
63		let caller = whitelisted_caller();
64
65		#[extrinsic_call]
66		remark(RawOrigin::Signed(caller), remark_message);
67
68		Ok(())
69	}
70
71	#[benchmark]
72	fn remark_with_event(
73		b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
74	) -> Result<(), BenchmarkError> {
75		let remark_message = vec![1; b as usize];
76		let caller: T::AccountId = whitelisted_caller();
77		let hash = T::Hashing::hash(&remark_message[..]);
78
79		#[extrinsic_call]
80		remark_with_event(RawOrigin::Signed(caller.clone()), remark_message);
81
82		System::<T>::assert_last_event(
83			frame_system::Event::<T>::Remarked { sender: caller, hash }.into(),
84		);
85		Ok(())
86	}
87
88	#[benchmark]
89	fn set_heap_pages() -> Result<(), BenchmarkError> {
90		#[extrinsic_call]
91		set_heap_pages(RawOrigin::Root, Default::default());
92
93		Ok(())
94	}
95
96	#[benchmark]
97	fn set_code() -> Result<(), BenchmarkError> {
98		let runtime_blob = T::prepare_set_code_data();
99		T::setup_set_code_requirements(&runtime_blob)?;
100
101		#[extrinsic_call]
102		set_code(RawOrigin::Root, runtime_blob);
103
104		T::verify_set_code();
105		Ok(())
106	}
107
108	#[benchmark(extra)]
109	fn set_code_without_checks() -> Result<(), BenchmarkError> {
110		// Assume Wasm ~4MB
111		let code = vec![1; 4_000_000 as usize];
112		T::setup_set_code_requirements(&code)?;
113
114		#[block]
115		{
116			System::<T>::set_code_without_checks(RawOrigin::Root.into(), code)?;
117		}
118
119		let code = storage::unhashed::get_raw(well_known_keys::CODE).ok_or("Code not stored.")?;
120		assert_eq!(code.len(), 4_000_000 as usize);
121		Ok(())
122	}
123
124	#[benchmark(skip_meta)]
125	fn set_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
126		// Set up i items to add
127		let mut items = Vec::new();
128		for j in 0..i {
129			let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
130			items.push((hash.clone(), hash.clone()));
131		}
132
133		let items_to_verify = items.clone();
134
135		#[extrinsic_call]
136		set_storage(RawOrigin::Root, items);
137
138		// Verify that they're actually in the storage.
139		for (item, _) in items_to_verify {
140			let value = storage::unhashed::get_raw(&item).ok_or("No value stored")?;
141			assert_eq!(value, *item);
142		}
143		Ok(())
144	}
145
146	#[benchmark(skip_meta)]
147	fn kill_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
148		// Add i items to storage
149		let mut items = Vec::with_capacity(i as usize);
150		for j in 0..i {
151			let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
152			storage::unhashed::put_raw(&hash, &hash);
153			items.push(hash);
154		}
155
156		// Verify that they're actually in the storage.
157		for item in &items {
158			let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
159			assert_eq!(value, *item);
160		}
161
162		let items_to_verify = items.clone();
163
164		#[extrinsic_call]
165		kill_storage(RawOrigin::Root, items);
166
167		// Verify that they're not in the storage anymore.
168		for item in items_to_verify {
169			assert!(storage::unhashed::get_raw(&item).is_none());
170		}
171		Ok(())
172	}
173
174	#[benchmark(skip_meta)]
175	fn kill_prefix(p: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
176		let prefix = p.using_encoded(T::Hashing::hash).as_ref().to_vec();
177		let mut items = Vec::with_capacity(p as usize);
178		// add p items that share a prefix
179		for i in 0..p {
180			let hash = (p, i).using_encoded(T::Hashing::hash).as_ref().to_vec();
181			let key = [&prefix[..], &hash[..]].concat();
182			storage::unhashed::put_raw(&key, &key);
183			items.push(key);
184		}
185
186		// Verify that they're actually in the storage.
187		for item in &items {
188			let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
189			assert_eq!(value, *item);
190		}
191
192		#[extrinsic_call]
193		kill_prefix(RawOrigin::Root, prefix, p);
194
195		// Verify that they're not in the storage anymore.
196		for item in items {
197			assert!(storage::unhashed::get_raw(&item).is_none());
198		}
199		Ok(())
200	}
201
202	#[benchmark]
203	fn authorize_upgrade() -> Result<(), BenchmarkError> {
204		let runtime_blob = T::prepare_set_code_data();
205		T::setup_set_code_requirements(&runtime_blob)?;
206		let hash = T::Hashing::hash(&runtime_blob);
207
208		#[extrinsic_call]
209		authorize_upgrade(RawOrigin::Root, hash);
210
211		assert_eq!(System::<T>::authorized_upgrade().unwrap().code_hash(), &hash);
212		Ok(())
213	}
214
215	#[benchmark]
216	fn apply_authorized_upgrade() -> Result<(), BenchmarkError> {
217		let runtime_blob = T::prepare_set_code_data();
218		T::setup_set_code_requirements(&runtime_blob)?;
219		let hash = T::Hashing::hash(&runtime_blob);
220		// Will be heavier when it needs to do verification (i.e. don't use `...without_checks`).
221		System::<T>::authorize_upgrade(RawOrigin::Root.into(), hash)?;
222
223		#[extrinsic_call]
224		apply_authorized_upgrade(RawOrigin::Root, runtime_blob);
225
226		// Can't check for `CodeUpdated` in parachain upgrades. Just check that the authorization is
227		// gone.
228		assert!(System::<T>::authorized_upgrade().is_none());
229		Ok(())
230	}
231
232	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
233}