referrerpolicy=no-referrer-when-downgrade

pallet_election_provider_multi_block/verifier/
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::{Config, Event, FeasibilityError, Pallet, Status, StatusStorage},
20	CurrentPhase, Phase,
21};
22use frame_benchmarking::v2::*;
23use frame_election_provider_support::{ElectionProvider, NposSolution};
24use frame_support::pallet_prelude::*;
25use sp_std::prelude::*;
26
27#[benchmarks(where
28	T: crate::Config + crate::signed::Config + crate::unsigned::Config,
29	<T as frame_system::Config>::RuntimeEvent: TryInto<crate::verifier::Event<T>>
30)]
31mod benchmarks {
32	use super::*;
33
34	fn events_for<T: Config>() -> Vec<Event<T>>
35	where
36		<T as frame_system::Config>::RuntimeEvent: TryInto<Event<T>>,
37	{
38		frame_system::Pallet::<T>::read_events_for_pallet::<Event<T>>()
39	}
40
41	#[benchmark(pov_mode = Measured)]
42	fn verification_valid_non_terminal() -> Result<(), BenchmarkError> {
43		#[cfg(test)]
44		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
45		crate::Pallet::<T>::start().unwrap();
46
47		// roll to signed validation, with a solution stored in the signed pallet
48		crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
49
50		// roll to verification
51		crate::Pallet::<T>::roll_until_matches(|| {
52			matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
53		});
54
55		// start signal must have been sent by now
56		assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
57
58		#[block]
59		{
60			crate::Pallet::<T>::roll_next(false);
61		}
62		assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp() - 1));
63
64		Ok(())
65	}
66
67	#[benchmark(pov_mode = Measured)]
68	fn verification_valid_terminal() -> Result<(), BenchmarkError> {
69		#[cfg(test)]
70		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
71		crate::Pallet::<T>::start().unwrap();
72
73		// roll to signed validation, with a solution stored in the signed pallet
74		assert!(
75			T::SignedValidationPhase::get() >= T::Pages::get().into(),
76			"Signed validation phase must be larger than the number of pages"
77		);
78
79		crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
80		// roll to before the last page of verification
81		crate::Pallet::<T>::roll_until_matches(|| {
82			matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
83		});
84
85		// start signal must have been sent by now
86		assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
87		for _ in 0..(T::Pages::get() - 1) {
88			crate::Pallet::<T>::roll_next(false);
89		}
90
91		// we must have verified all pages by now, minus the last one.
92		assert!(matches!(
93			&events_for::<T>()[..],
94			[Event::Verified(_, _), .., Event::Verified(1, _)]
95		));
96
97		// verify the last page.
98		#[block]
99		{
100			crate::Pallet::<T>::roll_next(false);
101		}
102
103		// we are done
104		assert_eq!(StatusStorage::<T>::get(), Status::Nothing);
105		// last event is success
106		assert!(matches!(
107			&events_for::<T>()[..],
108			[Event::Verified(_, _), .., Event::Verified(0, _), Event::Queued(_, None)]
109		));
110
111		Ok(())
112	}
113
114	#[benchmark(pov_mode = Measured)]
115	fn verification_invalid_terminal() -> Result<(), BenchmarkError> {
116		// this is the verification of the current page + removing all of the previously valid
117		// pages. The worst case is therefore when the last page is invalid, for example the final
118		// score.
119		assert!(T::Pages::get() >= 2, "benchmark only works if we have more than 2 pages");
120
121		#[cfg(test)]
122		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
123		crate::Pallet::<T>::start().unwrap();
124
125		// roll to signed validation, with a solution stored in the signed pallet
126
127		// but this solution is corrupt
128		let mut paged_solution = crate::Pallet::<T>::roll_to_signed_and_mine_full_solution();
129		paged_solution.score.minimal_stake -= 1;
130		crate::Pallet::<T>::submit_full_solution(paged_solution)?;
131
132		// roll to verification
133		crate::Pallet::<T>::roll_until_matches(|| {
134			matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
135		});
136
137		assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
138		// verify all pages, except for the last one.
139		for i in 0..T::Pages::get() - 1 {
140			crate::Pallet::<T>::roll_next(false);
141			assert_eq!(
142				StatusStorage::<T>::get(),
143				Status::Ongoing(crate::Pallet::<T>::msp() - 1 - i)
144			);
145		}
146
147		// next page to be verified is the last one
148		assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::lsp()));
149		assert!(matches!(
150			&events_for::<T>()[..],
151			[Event::Verified(_, _), .., Event::Verified(1, _)]
152		));
153
154		#[block]
155		{
156			crate::Pallet::<T>::roll_next(false);
157		}
158
159		// we are now reset.
160		assert_eq!(StatusStorage::<T>::get(), Status::Nothing);
161		assert!(matches!(
162			&events_for::<T>()[..],
163			[
164				..,
165				Event::Verified(0, _),
166				Event::VerificationFailed(0, FeasibilityError::InvalidScore)
167			]
168		));
169
170		Ok(())
171	}
172
173	#[benchmark(pov_mode = Measured)]
174	fn verification_invalid_non_terminal(
175		// number of valid pages that have been verified, before we verify the non-terminal invalid
176		// page.
177		v: Linear<0, { T::Pages::get() - 1 }>,
178	) -> Result<(), BenchmarkError> {
179		assert!(T::Pages::get() >= 2, "benchmark only works if we have more than 2 pages");
180
181		#[cfg(test)]
182		crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
183		crate::Pallet::<T>::start().unwrap();
184
185		// roll to signed validation, with a solution stored in the signed pallet, but this solution
186		// is corrupt in its msp.
187		let mut paged_solution = crate::Pallet::<T>::roll_to_signed_and_mine_full_solution();
188		let page_to_corrupt = crate::Pallet::<T>::msp() - v;
189		crate::log!(
190			info,
191			"pages of solution: {:?}, to corrupt {}, v {}",
192			paged_solution.solution_pages.len(),
193			page_to_corrupt,
194			v
195		);
196		paged_solution.solution_pages[page_to_corrupt as usize].corrupt();
197		crate::Pallet::<T>::submit_full_solution(paged_solution)?;
198
199		// roll to verification
200		crate::Pallet::<T>::roll_until_matches(|| {
201			matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
202		});
203
204		// we should be ready to go
205		assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
206
207		// validate the the parameterized number of valid pages.
208		for _ in 0..v {
209			crate::Pallet::<T>::roll_next(false);
210		}
211
212		// we are still ready to continue
213		assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp() - v));
214
215		// verify one page, which will be invalid.
216		#[block]
217		{
218			crate::Pallet::<T>::roll_next(false);
219		}
220
221		// we are now reset, because this page was invalid.
222		assert_eq!(StatusStorage::<T>::get(), Status::Nothing);
223
224		assert!(matches!(
225			&events_for::<T>()[..],
226			[.., Event::VerificationFailed(_, FeasibilityError::NposElection(_))]
227		));
228
229		Ok(())
230	}
231
232	impl_benchmark_test_suite!(
233		Pallet,
234		crate::mock::ExtBuilder::full().build_unchecked(),
235		crate::mock::Runtime
236	);
237}