1// This file is part of Substrate.
23// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
56// 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.
1718use 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::*;
2627#[benchmarks(where
28T: crate::Config + crate::signed::Config + crate::unsigned::Config,
29 <T as frame_system::Config>::RuntimeEvent: TryInto<crate::verifier::Event<T>>
30)]
31mod benchmarks {
32use super::*;
3334fn events_for<T: Config>() -> Vec<Event<T>>
35where
36<T as frame_system::Config>::RuntimeEvent: TryInto<Event<T>>,
37 {
38 frame_system::Pallet::<T>::read_events_for_pallet::<Event<T>>()
39 }
4041#[benchmark(pov_mode = Measured)]
42fn on_initialize_valid_non_terminal() -> Result<(), BenchmarkError> {
43#[cfg(test)]
44crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
45crate::Pallet::<T>::start().unwrap();
4647// roll to signed validation, with a solution stored in the signed pallet
48crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
4950// roll to verification
51crate::Pallet::<T>::roll_until_matches(|| {
52matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
53 });
54// send start signal
55crate::Pallet::<T>::roll_next(true, false);
5657// start signal must have been sent by now
58assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
5960#[block]
61{
62crate::Pallet::<T>::roll_next(true, false);
63 }
64assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp() - 1));
6566Ok(())
67 }
6869#[benchmark(pov_mode = Measured)]
70fn on_initialize_valid_terminal() -> Result<(), BenchmarkError> {
71#[cfg(test)]
72crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
73crate::Pallet::<T>::start().unwrap();
7475// roll to signed validation, with a solution stored in the signed pallet
76assert!(
77 T::SignedValidationPhase::get() >= T::Pages::get().into(),
78"Signed validation phase must be larger than the number of pages"
79);
8081crate::Pallet::<T>::roll_to_signed_and_submit_full_solution()?;
82// roll to before the last page of verification
83crate::Pallet::<T>::roll_until_matches(|| {
84matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
85 });
86// send start signal
87crate::Pallet::<T>::roll_next(true, false);
8889// start signal must have been sent by now
90assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
91for _ in 0..(T::Pages::get() - 1) {
92crate::Pallet::<T>::roll_next(true, false);
93 }
9495// we must have verified all pages by now, minus the last one.
96assert!(matches!(
97&events_for::<T>()[..],
98 [Event::Verified(_, _), .., Event::Verified(1, _)]
99 ));
100101// verify the last page.
102#[block]
103{
104crate::Pallet::<T>::roll_next(true, false);
105 }
106107// we are done
108assert_eq!(StatusStorage::<T>::get(), Status::Nothing);
109// last event is success
110assert!(matches!(
111&events_for::<T>()[..],
112 [Event::Verified(_, _), .., Event::Verified(0, _), Event::Queued(_, None)]
113 ));
114115Ok(())
116 }
117118#[benchmark(pov_mode = Measured)]
119fn on_initialize_invalid_terminal() -> Result<(), BenchmarkError> {
120// this is the verification of the current page + removing all of the previously valid
121 // pages. The worst case is therefore when the last page is invalid, for example the final
122 // score.
123assert!(T::Pages::get() >= 2, "benchmark only works if we have more than 2 pages");
124125#[cfg(test)]
126crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
127crate::Pallet::<T>::start().unwrap();
128129// roll to signed validation, with a solution stored in the signed pallet
130131 // but this solution is corrupt
132let mut paged_solution = crate::Pallet::<T>::roll_to_signed_and_mine_full_solution();
133 paged_solution.score.minimal_stake -= 1;
134crate::Pallet::<T>::submit_full_solution(paged_solution)?;
135136// roll to verification
137crate::Pallet::<T>::roll_until_matches(|| {
138matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
139 });
140// send start signal
141crate::Pallet::<T>::roll_next(true, false);
142143assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
144// verify all pages, except for the last one.
145for i in 0..T::Pages::get() - 1 {
146crate::Pallet::<T>::roll_next(true, false);
147assert_eq!(
148 StatusStorage::<T>::get(),
149 Status::Ongoing(crate::Pallet::<T>::msp() - 1 - i)
150 );
151 }
152153// next page to be verified is the last one
154assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::lsp()));
155assert!(matches!(
156&events_for::<T>()[..],
157 [Event::Verified(_, _), .., Event::Verified(1, _)]
158 ));
159160#[block]
161{
162crate::Pallet::<T>::roll_next(true, false);
163 }
164165// we are now reset.
166assert_eq!(StatusStorage::<T>::get(), Status::Nothing);
167assert!(matches!(
168&events_for::<T>()[..],
169 [
170 ..,
171 Event::Verified(0, _),
172 Event::VerificationFailed(0, FeasibilityError::InvalidScore)
173 ]
174 ));
175176Ok(())
177 }
178179#[benchmark(pov_mode = Measured)]
180fn on_initialize_invalid_non_terminal(
181// number of valid pages that have been verified, before we verify the non-terminal invalid
182 // page.
183v: Linear<0, { T::Pages::get() - 1 }>,
184 ) -> Result<(), BenchmarkError> {
185assert!(T::Pages::get() >= 2, "benchmark only works if we have more than 2 pages");
186187#[cfg(test)]
188crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value());
189crate::Pallet::<T>::start().unwrap();
190191// roll to signed validation, with a solution stored in the signed pallet, but this solution
192 // is corrupt in its msp.
193let mut paged_solution = crate::Pallet::<T>::roll_to_signed_and_mine_full_solution();
194let page_to_corrupt = crate::Pallet::<T>::msp() - v;
195crate::log!(
196 info,
197"pages of solution: {:?}, to corrupt {}, v {}",
198 paged_solution.solution_pages.len(),
199 page_to_corrupt,
200 v
201 );
202 paged_solution.solution_pages[page_to_corrupt as usize].corrupt();
203crate::Pallet::<T>::submit_full_solution(paged_solution)?;
204205// roll to verification
206crate::Pallet::<T>::roll_until_matches(|| {
207matches!(CurrentPhase::<T>::get(), Phase::SignedValidation(_))
208 });
209// send start signal
210crate::Pallet::<T>::roll_next(true, false);
211212// we should be ready to go
213assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp()));
214215// validate the the parameterized number of valid pages.
216for _ in 0..v {
217crate::Pallet::<T>::roll_next(true, false);
218 }
219220// we are still ready to continue
221assert_eq!(StatusStorage::<T>::get(), Status::Ongoing(crate::Pallet::<T>::msp() - v));
222223// verify one page, which will be invalid.
224#[block]
225{
226crate::Pallet::<T>::roll_next(true, false);
227 }
228229// we are now reset, because this page was invalid.
230assert_eq!(StatusStorage::<T>::get(), Status::Nothing);
231232assert!(matches!(
233&events_for::<T>()[..],
234 [.., Event::VerificationFailed(_, FeasibilityError::NposElection(_))]
235 ));
236237Ok(())
238 }
239240impl_benchmark_test_suite!(
241 Pallet,
242crate::mock::ExtBuilder::full().build_unchecked(),
243crate::mock::Runtime
244 );
245}