// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use crate::{ verifier::{Config, Event, FeasibilityError, Pallet, Status, StatusStorage}, CurrentPhase, Phase, }; use frame_benchmarking::v2::*; use frame_election_provider_support::{ElectionProvider, NposSolution}; use frame_support::pallet_prelude::*; use sp_std::prelude::*; #[benchmarks(where T: crate::Config + crate::signed::Config + crate::unsigned::Config, ::RuntimeEvent: TryInto> )] mod benchmarks { use super::*; fn events_for() -> Vec> where ::RuntimeEvent: TryInto>, { frame_system::Pallet::::read_events_for_pallet::>() } #[benchmark(pov_mode = Measured)] fn on_initialize_valid_non_terminal() -> Result<(), BenchmarkError> { #[cfg(test)] crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value()); crate::Pallet::::start().unwrap(); // roll to signed validation, with a solution stored in the signed pallet crate::Pallet::::roll_to_signed_and_submit_full_solution()?; // roll to verification crate::Pallet::::roll_until_matches(|| { matches!(CurrentPhase::::get(), Phase::SignedValidation(_)) }); // send start signal crate::Pallet::::roll_next(true, false); // start signal must have been sent by now assert_eq!(StatusStorage::::get(), Status::Ongoing(crate::Pallet::::msp())); #[block] { crate::Pallet::::roll_next(true, false); } assert_eq!(StatusStorage::::get(), Status::Ongoing(crate::Pallet::::msp() - 1)); Ok(()) } #[benchmark(pov_mode = Measured)] fn on_initialize_valid_terminal() -> Result<(), BenchmarkError> { #[cfg(test)] crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value()); crate::Pallet::::start().unwrap(); // roll to signed validation, with a solution stored in the signed pallet assert!( T::SignedValidationPhase::get() >= T::Pages::get().into(), "Signed validation phase must be larger than the number of pages" ); crate::Pallet::::roll_to_signed_and_submit_full_solution()?; // roll to before the last page of verification crate::Pallet::::roll_until_matches(|| { matches!(CurrentPhase::::get(), Phase::SignedValidation(_)) }); // send start signal crate::Pallet::::roll_next(true, false); // start signal must have been sent by now assert_eq!(StatusStorage::::get(), Status::Ongoing(crate::Pallet::::msp())); for _ in 0..(T::Pages::get() - 1) { crate::Pallet::::roll_next(true, false); } // we must have verified all pages by now, minus the last one. assert!(matches!( &events_for::()[..], [Event::Verified(_, _), .., Event::Verified(1, _)] )); // verify the last page. #[block] { crate::Pallet::::roll_next(true, false); } // we are done assert_eq!(StatusStorage::::get(), Status::Nothing); // last event is success assert!(matches!( &events_for::()[..], [Event::Verified(_, _), .., Event::Verified(0, _), Event::Queued(_, None)] )); Ok(()) } #[benchmark(pov_mode = Measured)] fn on_initialize_invalid_terminal() -> Result<(), BenchmarkError> { // this is the verification of the current page + removing all of the previously valid // pages. The worst case is therefore when the last page is invalid, for example the final // score. assert!(T::Pages::get() >= 2, "benchmark only works if we have more than 2 pages"); #[cfg(test)] crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value()); crate::Pallet::::start().unwrap(); // roll to signed validation, with a solution stored in the signed pallet // but this solution is corrupt let mut paged_solution = crate::Pallet::::roll_to_signed_and_mine_full_solution(); paged_solution.score.minimal_stake -= 1; crate::Pallet::::submit_full_solution(paged_solution)?; // roll to verification crate::Pallet::::roll_until_matches(|| { matches!(CurrentPhase::::get(), Phase::SignedValidation(_)) }); // send start signal crate::Pallet::::roll_next(true, false); assert_eq!(StatusStorage::::get(), Status::Ongoing(crate::Pallet::::msp())); // verify all pages, except for the last one. for i in 0..T::Pages::get() - 1 { crate::Pallet::::roll_next(true, false); assert_eq!( StatusStorage::::get(), Status::Ongoing(crate::Pallet::::msp() - 1 - i) ); } // next page to be verified is the last one assert_eq!(StatusStorage::::get(), Status::Ongoing(crate::Pallet::::lsp())); assert!(matches!( &events_for::()[..], [Event::Verified(_, _), .., Event::Verified(1, _)] )); #[block] { crate::Pallet::::roll_next(true, false); } // we are now reset. assert_eq!(StatusStorage::::get(), Status::Nothing); assert!(matches!( &events_for::()[..], [ .., Event::Verified(0, _), Event::VerificationFailed(0, FeasibilityError::InvalidScore) ] )); Ok(()) } #[benchmark(pov_mode = Measured)] fn on_initialize_invalid_non_terminal( // number of valid pages that have been verified, before we verify the non-terminal invalid // page. v: Linear<0, { T::Pages::get() - 1 }>, ) -> Result<(), BenchmarkError> { assert!(T::Pages::get() >= 2, "benchmark only works if we have more than 2 pages"); #[cfg(test)] crate::mock::ElectionStart::set(sp_runtime::traits::Bounded::max_value()); crate::Pallet::::start().unwrap(); // roll to signed validation, with a solution stored in the signed pallet, but this solution // is corrupt in its msp. let mut paged_solution = crate::Pallet::::roll_to_signed_and_mine_full_solution(); let page_to_corrupt = crate::Pallet::::msp() - v; crate::log!( info, "pages of solution: {:?}, to corrupt {}, v {}", paged_solution.solution_pages.len(), page_to_corrupt, v ); paged_solution.solution_pages[page_to_corrupt as usize].corrupt(); crate::Pallet::::submit_full_solution(paged_solution)?; // roll to verification crate::Pallet::::roll_until_matches(|| { matches!(CurrentPhase::::get(), Phase::SignedValidation(_)) }); // send start signal crate::Pallet::::roll_next(true, false); // we should be ready to go assert_eq!(StatusStorage::::get(), Status::Ongoing(crate::Pallet::::msp())); // validate the the parameterized number of valid pages. for _ in 0..v { crate::Pallet::::roll_next(true, false); } // we are still ready to continue assert_eq!(StatusStorage::::get(), Status::Ongoing(crate::Pallet::::msp() - v)); // verify one page, which will be invalid. #[block] { crate::Pallet::::roll_next(true, false); } // we are now reset, because this page was invalid. assert_eq!(StatusStorage::::get(), Status::Nothing); assert!(matches!( &events_for::()[..], [.., Event::VerificationFailed(_, FeasibilityError::NposElection(_))] )); Ok(()) } impl_benchmark_test_suite!( Pallet, crate::mock::ExtBuilder::full().build_unchecked(), crate::mock::Runtime ); }