referrerpolicy=no-referrer-when-downgrade

pallet_election_provider_multi_block/verifier/
mod.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
18//! # The Verifier Pallet
19//!
20//! This pallet has no end-user functionality, and is only used internally by other pallets in the
21//! EPMB machinery to verify solutions.
22//!
23//! ### *Feasibility* Check
24//!
25//! Before explaining the pallet itself, it should be explained what a *verification* even means.
26//! Verification of a solution page ([`crate::unsigned::miner::MinerConfig::Solution`]) includes the
27//! process of checking all of its edges against a snapshot to be correct. For instance, all voters
28//! that are presented in a solution page must have actually voted for the winner that they are
29//! backing, based on the snapshot kept in the parent pallet.
30//!
31//! Such checks are bound to each page of the solution, and happen per-page. After checking all of
32//! the edges in each page, a handful of other checks are performed. These checks cannot happen
33//! per-page, and in order to do them we need to have the entire solution checked and verified.
34//!
35//! 1. Check that the total number of winners is sufficient (`DesiredTargets`).
36//! 2. Check that the claimed score ([`sp_npos_elections::ElectionScore`]) is correct,
37//!   * and more than the minimum score that can be specified via [`Verifier::set_minimum_score`].
38//! 3. Check that all of the bounds of the solution are respected, namely
39//!    [`Verifier::MaxBackersPerWinner`], [`Verifier::MaxWinnersPerPage`] and
40//!    [`Verifier::MaxBackersPerWinnerFinal`].
41//!
42//! Note that the common factor of all of the above checks is that they can ONLY be checked after
43//! all pages are already verified. So, in the case of a multi-page verification, these checks are
44//! performed at the last page.
45//!
46//! The errors that can arise while performing the feasibility check are encapsulated in
47//! [`verifier::FeasibilityError`].
48//!
49//! ## Modes of Verification
50//!
51//! The verifier pallet provide two modes of functionality:
52//!
53//! 1. Single or multi-page, synchronous verification. This is useful in the context of single-page,
54//!    emergency, or unsigned solutions that need to be verified on the fly. This is similar to how
55//!    the old school `multi-phase` pallet works. See [`Verifier::verify_synchronous`] and
56//!    [`Verifier::verify_synchronous_multi`].
57//! 2. Multi-page, asynchronous verification. This is useful in the context of multi-page, signed
58//!    solutions. See [`verifier::AsynchronousVerifier`] and [`verifier::SolutionDataProvider`].
59//!
60//! Both of this, plus some helper functions, is exposed via the [`verifier::Verifier`] trait.
61//!
62//! ## Queued Solution
63//!
64//! once a solution has been verified, it is called a *queued solution*. It is sitting in a queue,
65//! waiting for either of:
66//!
67//! 1. being challenged and potentially replaced by better solution, if any.
68//! 2. being exported as the final outcome of the election.
69
70#[cfg(feature = "runtime-benchmarks")]
71pub mod benchmarking;
72mod impls;
73#[cfg(test)]
74mod tests;
75
76// internal imports
77pub use crate::weights::traits::pallet_election_provider_multi_block_verifier::*;
78
79use frame_election_provider_support::PageIndex;
80use frame_support::weights::WeightMeter;
81use impls::SupportsOfVerifier;
82pub use impls::{feasibility_check_page_inner_with_snapshot, pallet::*, Status};
83use sp_core::Get;
84use sp_npos_elections::ElectionScore;
85use sp_runtime::Weight;
86use sp_std::{fmt::Debug, prelude::*};
87
88/// Errors that can happen in the feasibility check.
89#[derive(
90	Debug,
91	Eq,
92	PartialEq,
93	codec::Encode,
94	codec::Decode,
95	codec::DecodeWithMemTracking,
96	scale_info::TypeInfo,
97	Clone,
98)]
99pub enum FeasibilityError {
100	/// Wrong number of winners presented.
101	WrongWinnerCount,
102	/// The snapshot is not available.
103	///
104	/// Kinda defensive: The pallet should technically never attempt to do a feasibility check
105	/// when no snapshot is present.
106	SnapshotUnavailable,
107	/// A vote is invalid.
108	InvalidVote,
109	/// A voter is invalid.
110	InvalidVoter,
111	/// A winner is invalid.
112	InvalidWinner,
113	/// The given score was invalid.
114	InvalidScore,
115	/// The provided round is incorrect.
116	InvalidRound,
117	/// Solution does not have a good enough score.
118	ScoreTooLow,
119	/// The support type failed to be bounded.
120	///
121	/// Relates to [`Config::MaxWinnersPerPage`], [`Config::MaxBackersPerWinner`] or
122	/// `MaxBackersPerWinnerFinal`
123	FailedToBoundSupport,
124	/// Internal error from the election crate.
125	NposElection(sp_npos_elections::Error),
126	/// The solution is incomplete, it has too few pages.
127	///
128	/// This is (somewhat) synonym to `WrongPageCount` in other places.
129	Incomplete,
130}
131
132impl From<sp_npos_elections::Error> for FeasibilityError {
133	fn from(e: sp_npos_elections::Error) -> Self {
134		FeasibilityError::NposElection(e)
135	}
136}
137
138/// The interface of something that can verify solutions for other sub-pallets in the multi-block
139/// election pallet-network.
140pub trait Verifier {
141	/// The solution type.
142	type Solution;
143	/// The account if type.
144	type AccountId;
145
146	/// Maximum number of winners that can be represented in each page.
147	///
148	/// A reasonable value for this should be the maximum number of winners that the election user
149	/// (e.g. the staking pallet) could ever desire.
150	type MaxWinnersPerPage: Get<u32>;
151
152	/// Maximum number of backers, per winner, among all pages of an election.
153	///
154	/// This can only be checked at the very final step of verification.
155	type MaxBackersPerWinnerFinal: Get<u32>;
156
157	/// Maximum number of backers that each winner could have, per page.
158	type MaxBackersPerWinner: Get<u32>;
159
160	/// Set the minimum score that is acceptable for any solution.
161	///
162	/// Henceforth, all solutions must have at least this degree of quality, single-page or
163	/// multi-page.
164	fn set_minimum_score(score: ElectionScore);
165
166	/// The score of the current best solution. `None` if there is none.
167	fn queued_score() -> Option<ElectionScore>;
168
169	/// Check if the claimed score is sufficient to challenge the current queued solution, if any.
170	fn ensure_claimed_score_improves(claimed_score: ElectionScore) -> bool;
171
172	/// Clear all storage items, there's nothing else to do until further notice.
173	fn kill();
174
175	/// Get a single page of the best verified solution, if any.
176	///
177	/// It is the responsibility of the call site to call this function with all appropriate
178	/// `page` arguments.
179	fn get_queued_solution_page(page: PageIndex) -> Option<SupportsOfVerifier<Self>>;
180
181	/// Perform the feasibility check on the given single-page solution.
182	///
183	/// This will perform:
184	///
185	/// 1. feasibility-check
186	/// 2. claimed score is correct and an improvement.
187	/// 3. bounds are respected
188	///
189	/// Corresponding snapshot (represented by `page`) is assumed to be available.
190	///
191	/// If all checks pass, the solution is also queued.
192	fn verify_synchronous(
193		partial_solution: Self::Solution,
194		claimed_score: ElectionScore,
195		page: PageIndex,
196	) -> Result<(), FeasibilityError> {
197		Self::verify_synchronous_multi(vec![partial_solution], vec![page], claimed_score)
198	}
199
200	/// Perform synchronous feasibility check on the given multi-page solution.
201	///
202	/// Same semantics as [`Self::verify_synchronous`], but for multi-page solutions.
203	fn verify_synchronous_multi(
204		partial_solution: Vec<Self::Solution>,
205		pages: Vec<PageIndex>,
206		claimed_score: ElectionScore,
207	) -> Result<(), FeasibilityError>;
208
209	/// Force set a single page solution as the valid one.
210	///
211	/// Will erase any previous solution. Should only be used in case of emergency fallbacks,
212	/// trusted governance solutions and so on.
213	fn force_set_single_page_valid(
214		partial_supports: SupportsOfVerifier<Self>,
215		page: PageIndex,
216		score: ElectionScore,
217	);
218
219	/// Return the execution schedule of this pallet's work to be done per-block (`on_poll`,
220	/// `on_init` independent).
221	///
222	/// Returns a `(Weight, ExecFn)` tuple in-line with `per_block_exec` of the parent block.
223	fn per_block_exec() -> (Weight, Box<dyn Fn(&mut WeightMeter)>);
224}
225
226/// Simple enum to encapsulate the result of the verification of a candidate solution.
227#[derive(Clone, Copy, Debug)]
228#[cfg_attr(test, derive(PartialEq, Eq))]
229pub enum VerificationResult {
230	/// Solution is valid and is queued.
231	Queued,
232	/// Solution is rejected, for whichever of the multiple reasons that it could be.
233	Rejected,
234}
235
236/// Something that can provide candidate solutions to the verifier.
237///
238/// In reality, this can be implemented by the [`crate::signed::Pallet`], where signed solutions are
239/// queued and sorted based on claimed score, and they are put forth one by one, from best to worse.
240pub trait SolutionDataProvider {
241	/// The opaque solution type.
242	type Solution;
243
244	/// Return the `page`th page of the current best solution that the data provider has in store.
245	///
246	/// If no candidate solutions are available, an empty page is returned (i.e., a page that
247	/// contains no solutions and contributes zero to the final score).
248	fn get_page(page: PageIndex) -> Self::Solution;
249
250	/// Get the claimed score of the current best solution.
251	///
252	/// If no score is available, a default/zero score should be returned defensively.
253	fn get_score() -> ElectionScore;
254
255	/// Hook to report back the results of the verification of the current candidate solution that
256	/// is being exposed via [`Self::get_page`] and [`Self::get_score`].
257	///
258	/// Every time that this is called, the verifier [`AsynchronousVerifier`] goes back to the
259	/// [`Status::Nothing`] state, and it is the responsibility of [`Self`] to call `start` again,
260	/// if desired.
261	fn report_result(result: VerificationResult);
262}
263
264/// Something that can do the verification asynchronously.
265pub trait AsynchronousVerifier: Verifier {
266	/// The data provider that can provide the candidate solution, and to whom we report back the
267	/// results.
268	type SolutionDataProvider: SolutionDataProvider;
269
270	/// Get the current stage of the verification process.
271	fn status() -> Status;
272
273	/// Start a verification process.
274	///
275	/// Returns `Ok(())` if verification started successfully, and `Err(..)` if a verification is
276	/// already ongoing and therefore a new one cannot be started.
277	///
278	/// From the coming block onwards, the verifier will start and fetch the relevant information
279	/// and solution pages from [`SolutionDataProvider`]. It is expected that the
280	/// [`SolutionDataProvider`] is ready before calling [`Self::start`].
281	///
282	/// Pages of the solution are fetched sequentially and in order from [`SolutionDataProvider`],
283	/// from `msp` to `lsp`.
284	///
285	/// This ends in either of the two:
286	///
287	/// 1. All pages, including the final checks (like score and other facts that can only be
288	///    derived from a full solution) are valid and the solution is verified. The solution is
289	///    queued and is ready for further export.
290	/// 2. The solution checks verification at one of the steps. Nothing is stored inside the
291	///    verifier pallet and all intermediary data is removed.
292	///
293	/// In both cases, the [`SolutionDataProvider`] is informed via
294	/// [`SolutionDataProvider::report_result`]. It is sensible for the data provide to call `start`
295	/// again if the verification has failed, and nothing otherwise. Indeed, the
296	/// [`SolutionDataProvider`] must adjust its internal state such that it returns a new candidate
297	/// solution after each failure.
298	fn start() -> Result<(), &'static str>;
299}