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::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 (DataProvider) to fill a page of voters/targets \
28as per VoterSnapshotPerBlock and TargetSnapshotPerBlock. Generate at least \
292 * VoterSnapshotPerBlock) nominators and TargetSnapshotPerBlock validators";
30
31// TODO: remove unwraps from all benchmarks of this pallet -- it makes debugging via wasm harder
32
33#[benchmarks(where T: crate::signed::Config + crate::unsigned::Config + crate::verifier::Config)]
34mod benchmarks {
35	use super::*;
36
37	#[benchmark(pov_mode = Measured)]
38	fn on_initialize_nothing() -> Result<(), BenchmarkError> {
39		assert_eq!(CurrentPhase::<T>::get(), Phase::Off);
40
41		#[block]
42		{
43			Pallet::<T>::roll_next(true, false);
44		}
45
46		assert_eq!(CurrentPhase::<T>::get(), Phase::Off);
47		Ok(())
48	}
49
50	#[benchmark(pov_mode = Measured)]
51	fn on_initialize_into_snapshot_msp() -> Result<(), BenchmarkError> {
52		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");
53
54		#[cfg(test)]
55		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
56		crate::Pallet::<T>::start().unwrap();
57
58		assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(T::Pages::get()));
59
60		#[block]
61		{
62			Pallet::<T>::roll_next(true, false);
63		}
64
65		// we have collected the target snapshot only
66		assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(T::Pages::get() - 1));
67		assert_eq!(
68			Snapshot::<T>::targets_decode_len().unwrap() as u32,
69			T::TargetSnapshotPerBlock::get(),
70			"{}",
71			SNAPSHOT_NOT_BIG_ENOUGH
72		);
73		assert_eq!(Snapshot::<T>::voters_decode_len(T::Pages::get() - 1), None);
74
75		Ok(())
76	}
77
78	#[benchmark(pov_mode = Measured)]
79	fn on_initialize_into_snapshot_rest() -> Result<(), BenchmarkError> {
80		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");
81
82		#[cfg(test)]
83		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
84		crate::Pallet::<T>::start().unwrap();
85
86		// roll to the first block of the snapshot.
87		Pallet::<T>::roll_until_matches(|| {
88			CurrentPhase::<T>::get() == Phase::Snapshot(T::Pages::get() - 1)
89		});
90
91		// we have collected the target snapshot only
92		assert_eq!(
93			Snapshot::<T>::targets_decode_len().unwrap() as u32,
94			T::TargetSnapshotPerBlock::get()
95		);
96		// and no voters yet.
97		assert_eq!(Snapshot::<T>::voters_decode_len(T::Pages::get() - 1), None);
98
99		// take one more snapshot page.
100		#[block]
101		{
102			Pallet::<T>::roll_next(true, false);
103		}
104
105		// we have now collected the first page of voters.
106		assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(T::Pages::get() - 2));
107		// it must be full
108		assert_eq!(
109			Snapshot::<T>::voters_decode_len(T::Pages::get() - 1).unwrap() as u32,
110			T::VoterSnapshotPerBlock::get(),
111			"{}",
112			SNAPSHOT_NOT_BIG_ENOUGH
113		);
114		Ok(())
115	}
116
117	#[benchmark(pov_mode = Measured)]
118	fn on_initialize_into_signed() -> Result<(), BenchmarkError> {
119		#[cfg(test)]
120		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
121		crate::Pallet::<T>::start().unwrap();
122
123		Pallet::<T>::roll_until_before_matches(|| {
124			matches!(CurrentPhase::<T>::get(), Phase::Signed(_))
125		});
126
127		assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(0));
128
129		#[block]
130		{
131			Pallet::<T>::roll_next(true, false);
132		}
133
134		assert!(CurrentPhase::<T>::get().is_signed());
135
136		Ok(())
137	}
138
139	#[benchmark(pov_mode = Measured)]
140	fn on_initialize_into_signed_validation() -> Result<(), BenchmarkError> {
141		#[cfg(test)]
142		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
143		crate::Pallet::<T>::start().unwrap();
144
145		Pallet::<T>::roll_until_before_matches(|| {
146			matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
147		});
148
149		assert!(CurrentPhase::<T>::get().is_signed());
150
151		#[block]
152		{
153			Pallet::<T>::roll_next(true, false);
154		}
155
156		Ok(())
157	}
158
159	#[benchmark(pov_mode = Measured)]
160	fn on_initialize_into_unsigned() -> Result<(), BenchmarkError> {
161		#[cfg(test)]
162		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
163		crate::Pallet::<T>::start().unwrap();
164
165		Pallet::<T>::roll_until_before_matches(|| {
166			matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
167		});
168		assert!(matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_)));
169
170		#[block]
171		{
172			Pallet::<T>::roll_next(true, false);
173		}
174
175		assert!(matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_)));
176		Ok(())
177	}
178
179	#[benchmark(pov_mode = Measured)]
180	fn export_non_terminal() -> Result<(), BenchmarkError> {
181		#[cfg(test)]
182		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
183		crate::Pallet::<T>::start().unwrap();
184
185		// submit a full solution.
186		crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
187
188		// fully verify it in the signed validation phase.
189		assert!(T::Verifier::queued_score().is_none());
190		crate::Pallet::<T>::roll_until_matches(|| {
191			matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
192		});
193
194		// full solution is queued.
195		assert!(T::Verifier::queued_score().is_some());
196		assert_eq!(verifier::QueuedSolution::<T>::valid_iter().count() as u32, T::Pages::get());
197
198		// Roll to Done phase to start export
199		crate::Pallet::<T>::roll_until_matches(|| CurrentPhase::<T>::get().is_done());
200
201		#[block]
202		{
203			// tell the data provider to do its election process for one page, while we are fully
204			// ready.
205			T::DataProvider::fetch_page(T::Pages::get() - 1);
206		}
207
208		// we should be in the export phase now.
209		assert_eq!(CurrentPhase::<T>::get(), Phase::Export(T::Pages::get() - 2));
210
211		Ok(())
212	}
213
214	#[benchmark(pov_mode = Measured)]
215	fn export_terminal() -> Result<(), BenchmarkError> {
216		#[cfg(test)]
217		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
218		crate::Pallet::<T>::start().unwrap();
219
220		// submit a full solution.
221		crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
222
223		// fully verify it in the signed validation phase.
224		ensure!(T::Verifier::queued_score().is_none(), "nothing should be queued");
225		crate::Pallet::<T>::roll_until_matches(|| {
226			matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
227		});
228
229		// full solution is queued.
230		ensure!(T::Verifier::queued_score().is_some(), "something should be queued");
231		ensure!(
232			verifier::QueuedSolution::<T>::valid_iter().count() as u32 == T::Pages::get(),
233			"solution should be full"
234		);
235
236		// Roll to Done phase
237		crate::Pallet::<T>::roll_until_matches(|| CurrentPhase::<T>::get().is_done());
238
239		// Start export and fetch all pages except the last one
240		(1..=T::Pages::get() - 1).rev().for_each(T::DataProvider::fetch_page);
241
242		assert_eq!(CurrentPhase::<T>::get(), Phase::Export(0));
243
244		#[block]
245		{
246			T::DataProvider::fetch_page(0);
247		}
248
249		// we should be in the off phase now.
250		assert_eq!(CurrentPhase::<T>::get(), Phase::Off);
251
252		Ok(())
253	}
254
255	#[benchmark(pov_mode = Measured)]
256	fn manage() -> Result<(), BenchmarkError> {
257		// TODO
258		#[block]
259		{}
260		Ok(())
261	}
262
263	impl_benchmark_test_suite!(
264		Pallet,
265		crate::mock::ExtBuilder::full().build_unchecked(),
266		crate::mock::Runtime
267	);
268}