pallet_election_provider_multi_block/
benchmarking.rs1use 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#[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 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 Pallet::<T>::roll_until_matches(|| {
89 CurrentPhase::<T>::get() == Phase::Snapshot(T::Pages::get() - 1)
90 });
91
92 assert_eq!(
94 Snapshot::<T>::targets_decode_len().unwrap() as u32,
95 T::TargetSnapshotPerBlock::get()
96 );
97 assert_eq!(Snapshot::<T>::voters_decode_len(T::Pages::get() - 1), None);
99
100 #[block]
102 {
103 Pallet::<T>::roll_next(false);
104 }
105
106 assert_eq!(CurrentPhase::<T>::get(), Phase::Snapshot(T::Pages::get() - 2));
108 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 crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
148
149 assert!(T::Verifier::queued_score().is_none());
151 crate::Pallet::<T>::roll_until_matches(|| {
152 matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
153 });
154
155 assert!(T::Verifier::queued_score().is_some());
157 assert_eq!(verifier::QueuedSolution::<T>::valid_iter().count() as u32, T::Pages::get());
158
159 crate::Pallet::<T>::roll_until_matches(|| CurrentPhase::<T>::get().is_done());
161
162 #[block]
163 {
164 T::DataProvider::fetch_page(T::Pages::get() - 1);
167 }
168
169 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 crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
183
184 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 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 crate::Pallet::<T>::roll_until_matches(|| CurrentPhase::<T>::get().is_done());
199
200 (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 assert_eq!(CurrentPhase::<T>::get(), Phase::Off);
212
213 Ok(())
214 }
215
216 #[benchmark(pov_mode = Measured)]
217 fn manage_fallback() -> Result<(), BenchmarkError> {
218 #[cfg(test)]
220 crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
221 crate::Pallet::<T>::start().unwrap();
222
223 Pallet::<T>::roll_until_before_matches(|| {
225 matches!(CurrentPhase::<T>::get(), Phase::Signed(_))
226 });
227
228 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 let maybe_err = Pallet::<T>::manage(origin, crate::ManagerOperation::EmergencyFallback);
236 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 #[cfg(test)]
247 crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
248 crate::Pallet::<T>::start().unwrap();
249
250 let solution = crate::Pallet::<T>::roll_to_signed_and_mine_solution(1);
252
253 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 CurrentPhase::<T>::set(Phase::Emergency);
272
273 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 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}