referrerpolicy=no-referrer-when-downgrade

pallet_election_provider_multi_block/
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
18use crate::{
19	verifier::{self, Verifier},
20	Config, CurrentPhase, Pallet, Phase, Snapshot,
21};
22use frame_benchmarking::v2::*;
23use frame_election_provider_support::{ElectionDataProvider, ElectionProvider};
24use frame_support::{assert_ok, pallet_prelude::*};
25
26const SNAPSHOT_NOT_BIG_ENOUGH: &'static str = "Snapshot page is not full, you should run this \
27benchmark with enough genesis stakers in staking to fill a page of voters/targets \
28as per VoterSnapshotPerBlock and TargetSnapshotPerBlock. Generate at least \
292 * VoterSnapshotPerBlock nominators and TargetSnapshotPerBlock validators. Use `dev_stakers` in \
30genesis config.";
31
32// TODO: remove unwraps from all benchmarks of this pallet -- it makes debugging via wasm harder
33
34#[benchmarks(where T: crate::signed::Config + crate::unsigned::Config + crate::verifier::Config)]
35mod benchmarks {
36	use super::*;
37
38	#[benchmark(pov_mode = Measured)]
39	fn per_block_nothing() -> Result<(), BenchmarkError> {
40		assert_eq!(CurrentPhase::<T>::get(), Phase::Off);
41
42		#[block]
43		{
44			Pallet::<T>::roll_next(false);
45		}
46
47		assert_eq!(CurrentPhase::<T>::get(), Phase::Off);
48		Ok(())
49	}
50
51	#[benchmark(pov_mode = Measured)]
52	fn per_block_snapshot_msp() -> Result<(), BenchmarkError> {
53		assert!(T::Pages::get() >= 2, "this benchmark only works in a runtime with 2 pages or more, set at least `type Pages = 2` for benchmark run");
54
55		#[cfg(test)]
56		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
57		crate::Pallet::<T>::start().unwrap();
58
59		assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(T::Pages::get()));
60
61		#[block]
62		{
63			Pallet::<T>::roll_next(false);
64		}
65
66		// we have collected the target snapshot only
67		assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(T::Pages::get() - 1));
68		assert_eq!(
69			Snapshot::<T>::targets_decode_len().unwrap() as u32,
70			T::TargetSnapshotPerBlock::get(),
71			"{}",
72			SNAPSHOT_NOT_BIG_ENOUGH
73		);
74		assert_eq!(Snapshot::<T>::voters_decode_len(T::Pages::get() - 1), None);
75
76		Ok(())
77	}
78
79	#[benchmark(pov_mode = Measured)]
80	fn per_block_snapshot_rest() -> Result<(), BenchmarkError> {
81		assert!(T::Pages::get() >= 2, "this benchmark only works in a runtime with 2 pages or more, set at least `type Pages = 2` for benchmark run");
82
83		#[cfg(test)]
84		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
85		crate::Pallet::<T>::start().unwrap();
86
87		// roll to the first block of the snapshot.
88		Pallet::<T>::roll_until_matches(|| {
89			CurrentPhase::<T>::get() == Phase::Snapshot(T::Pages::get() - 1)
90		});
91
92		// we have collected the target snapshot only
93		assert_eq!(
94			Snapshot::<T>::targets_decode_len().unwrap() as u32,
95			T::TargetSnapshotPerBlock::get()
96		);
97		// and no voters yet.
98		assert_eq!(Snapshot::<T>::voters_decode_len(T::Pages::get() - 1), None);
99
100		// take one more snapshot page.
101		#[block]
102		{
103			Pallet::<T>::roll_next(false);
104		}
105
106		// we have now collected the first page of voters.
107		assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(T::Pages::get() - 2));
108		// it must be full
109		assert_eq!(
110			Snapshot::<T>::voters_decode_len(T::Pages::get() - 1).unwrap() as u32,
111			T::VoterSnapshotPerBlock::get(),
112			"{}",
113			SNAPSHOT_NOT_BIG_ENOUGH
114		);
115		Ok(())
116	}
117
118	#[benchmark(pov_mode = Measured)]
119	fn per_block_start_signed_validation() -> Result<(), BenchmarkError> {
120		#[cfg(test)]
121		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
122		crate::Pallet::<T>::start().unwrap();
123
124		Pallet::<T>::roll_until_before_matches(|| {
125			matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
126		});
127
128		assert!(CurrentPhase::<T>::get().is_signed());
129
130		#[block]
131		{
132			Pallet::<T>::roll_next(false);
133		}
134
135		assert!(CurrentPhase::<T>::get().is_signed_validation());
136
137		Ok(())
138	}
139
140	#[benchmark(pov_mode = Measured)]
141	fn export_non_terminal() -> Result<(), BenchmarkError> {
142		#[cfg(test)]
143		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
144		crate::Pallet::<T>::start().unwrap();
145
146		// submit a full solution.
147		crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
148
149		// fully verify it in the signed validation phase.
150		assert!(T::Verifier::queued_score().is_none());
151		crate::Pallet::<T>::roll_until_matches(|| {
152			matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
153		});
154
155		// full solution is queued.
156		assert!(T::Verifier::queued_score().is_some());
157		assert_eq!(verifier::QueuedSolution::<T>::valid_iter().count() as u32, T::Pages::get());
158
159		// Roll to Done phase to start export
160		crate::Pallet::<T>::roll_until_matches(|| CurrentPhase::<T>::get().is_done());
161
162		#[block]
163		{
164			// tell the data provider to do its election process for one page, while we are fully
165			// ready.
166			T::DataProvider::fetch_page(T::Pages::get() - 1);
167		}
168
169		// we should be in the export phase now.
170		assert_eq!(CurrentPhase::<T>::get(), Phase::Export(T::Pages::get() - 2));
171
172		Ok(())
173	}
174
175	#[benchmark(pov_mode = Measured)]
176	fn export_terminal() -> Result<(), BenchmarkError> {
177		#[cfg(test)]
178		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
179		crate::Pallet::<T>::start().unwrap();
180
181		// submit a full solution.
182		crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
183
184		// fully verify it in the signed validation phase.
185		ensure!(T::Verifier::queued_score().is_none(), "nothing should be queued");
186		crate::Pallet::<T>::roll_until_matches(|| {
187			matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
188		});
189
190		// full solution is queued.
191		ensure!(T::Verifier::queued_score().is_some(), "something should be queued");
192		ensure!(
193			verifier::QueuedSolution::<T>::valid_iter().count() as u32 == T::Pages::get(),
194			"solution should be full"
195		);
196
197		// Roll to Done phase
198		crate::Pallet::<T>::roll_until_matches(|| CurrentPhase::<T>::get().is_done());
199
200		// Start export and fetch all pages except the last one
201		(1..=T::Pages::get() - 1).rev().for_each(T::DataProvider::fetch_page);
202
203		assert_eq!(CurrentPhase::<T>::get(), Phase::Export(0));
204
205		#[block]
206		{
207			T::DataProvider::fetch_page(0);
208		}
209
210		// we should be in the off phase now.
211		assert_eq!(CurrentPhase::<T>::get(), Phase::Off);
212
213		Ok(())
214	}
215
216	#[benchmark(pov_mode = Measured)]
217	fn manage_fallback() -> Result<(), BenchmarkError> {
218		// heaviest case is emergency set.
219		#[cfg(test)]
220		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
221		crate::Pallet::<T>::start().unwrap();
222
223		// roll to signed so the snapshot exists
224		Pallet::<T>::roll_until_before_matches(|| {
225			matches!(CurrentPhase::<T>::get(), Phase::Signed(_))
226		});
227
228		// set phase to emergency
229		CurrentPhase::<T>::set(Phase::Emergency);
230		let origin = T::ManagerOrigin::try_successful_origin()
231			.map_err(|_| -> BenchmarkError { "cannot create manager origin".into() })?;
232		#[block]
233		{
234			// fallback might decide to fail, that's okay..
235			let maybe_err = Pallet::<T>::manage(origin, crate::ManagerOperation::EmergencyFallback);
236			//.. but it cannot be bad origin.
237			assert!(maybe_err.is_ok() || maybe_err.unwrap_err() != DispatchError::BadOrigin.into());
238		}
239
240		Ok(())
241	}
242
243	#[benchmark(pov_mode = Measured)]
244	fn admin_set() -> Result<(), BenchmarkError> {
245		// heaviest case is emergency set.
246		#[cfg(test)]
247		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
248		crate::Pallet::<T>::start().unwrap();
249
250		// mine a single page solution.
251		let solution = crate::Pallet::<T>::roll_to_signed_and_mine_solution(1);
252
253		// verify to get the support.
254		let (voter_pages, all_targets, desired_targets) =
255			crate::unsigned::miner::OffchainWorkerMiner::<T>::fetch_snapshot(T::Pages::get())
256				.map_err(|_| -> BenchmarkError { "fetch_snapshot".into() })?;
257		let supports = crate::unsigned::miner::BaseMiner::<T::MinerConfig>::check_feasibility(
258			&solution,
259			&voter_pages,
260			&all_targets,
261			desired_targets,
262		)
263		.map_err(|_| -> BenchmarkError { "check_feasibility".into() })?;
264
265		let single_support = supports
266			.first()
267			.cloned()
268			.ok_or_else(|| -> BenchmarkError { "no support".into() })?;
269
270		// set phase to emergency
271		CurrentPhase::<T>::set(Phase::Emergency);
272
273		// nothing is queued in verified just yet.
274		assert!(<T::Verifier as Verifier>::queued_score().is_none());
275
276		let origin = T::AdminOrigin::try_successful_origin()
277			.map_err(|_| -> BenchmarkError { "cannot create admin origin".into() })?;
278		#[block]
279		{
280			assert_ok!(Pallet::<T>::admin(
281				origin,
282				crate::AdminOperation::EmergencySetSolution(
283					sp_std::boxed::Box::new(single_support),
284					solution.score,
285				),
286			));
287		}
288
289		// something is queued now.
290		assert!(<T::Verifier as Verifier>::queued_score().is_some());
291
292		Ok(())
293	}
294
295	impl_benchmark_test_suite!(
296		Pallet,
297		crate::mock::ExtBuilder::full().build_unchecked(),
298		crate::mock::Runtime
299	);
300}