referrerpolicy=no-referrer-when-downgrade

pallet_election_provider_multi_phase/
lib.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//! # Multi phase, offchain election provider pallet.
19//!
20//! Currently, this election-provider has two distinct phases (see [`Phase`]), **signed** and
21//! **unsigned**.
22//!
23//! ## Phases
24//!
25//! The timeline of pallet is as follows. At each block,
26//! [`frame_election_provider_support::ElectionDataProvider::next_election_prediction`] is used to
27//! estimate the time remaining to the next call to
28//! [`frame_election_provider_support::ElectionProvider::elect`]. Based on this, a phase is chosen.
29//! The timeline is as follows.
30//!
31//! ```ignore
32//!                                                                    elect()
33//!                 +   <--T::SignedPhase-->  +  <--T::UnsignedPhase-->   +
34//!   +-------------------------------------------------------------------+
35//!    Phase::Off   +       Phase::Signed     +      Phase::Unsigned      +
36//! ```
37//!
38//! Note that the unsigned phase starts [`pallet::Config::UnsignedPhase`] blocks before the
39//! `next_election_prediction`, but only ends when a call to [`ElectionProvider::elect`] happens. If
40//! no `elect` happens, the signed phase is extended.
41//!
42//! > Given this, it is rather important for the user of this pallet to ensure it always terminates
43//! election via `elect` before requesting a new one.
44//!
45//! Each of the phases can be disabled by essentially setting their length to zero. If both phases
46//! have length zero, then the pallet essentially runs only the fallback strategy, denoted by
47//! [`Config::Fallback`].
48//!
49//! ### Signed Phase
50//!
51//! In the signed phase, solutions (of type [`RawSolution`]) are submitted and queued on chain. A
52//! deposit is reserved, based on the size of the solution, for the cost of keeping this solution
53//! on-chain for a number of blocks, and the potential weight of the solution upon being checked. A
54//! maximum of `pallet::Config::SignedMaxSubmissions` solutions are stored. The queue is always
55//! sorted based on score (worse to best).
56//!
57//! Upon arrival of a new solution:
58//!
59//! 1. If the queue is not full, it is stored in the appropriate sorted index.
60//! 2. If the queue is full but the submitted solution is better than one of the queued ones, the
61//!    worse solution is discarded, the bond of the outgoing solution is returned, and the new
62//!    solution is stored in the correct index.
63//! 3. If the queue is full and the solution is not an improvement compared to any of the queued
64//!    ones, it is instantly rejected and no additional bond is reserved.
65//!
66//! A signed solution cannot be reversed, taken back, updated, or retracted. In other words, the
67//! origin can not bail out in any way, if their solution is queued.
68//!
69//! Upon the end of the signed phase, the solutions are examined from best to worse (i.e. `pop()`ed
70//! until drained). Each solution undergoes an expensive `Pallet::feasibility_check`, which ensures
71//! the score claimed by this score was correct, and it is valid based on the election data (i.e.
72//! votes and targets). At each step, if the current best solution passes the feasibility check,
73//! it is considered to be the best one. The sender of the origin is rewarded, and the rest of the
74//! queued solutions get their deposit back and are discarded, without being checked.
75//!
76//! The following example covers all of the cases at the end of the signed phase:
77//!
78//! ```ignore
79//! Queue
80//! +-------------------------------+
81//! |Solution(score=20, valid=false)| +-->  Slashed
82//! +-------------------------------+
83//! |Solution(score=15, valid=true )| +-->  Rewarded, Saved
84//! +-------------------------------+
85//! |Solution(score=10, valid=true )| +-->  Discarded
86//! +-------------------------------+
87//! |Solution(score=05, valid=false)| +-->  Discarded
88//! +-------------------------------+
89//! |             None              |
90//! +-------------------------------+
91//! ```
92//!
93//! Note that both of the bottom solutions end up being discarded and get their deposit back,
94//! despite one of them being *invalid*.
95//!
96//! ## Unsigned Phase
97//!
98//! The unsigned phase will always follow the signed phase, with the specified duration. In this
99//! phase, only validator nodes can submit solutions. A validator node who has offchain workers
100//! enabled will start to mine a solution in this phase and submits it back to the chain as an
101//! unsigned transaction, thus the name _unsigned_ phase. This unsigned transaction can never be
102//! valid if propagated, and it acts similar to an inherent.
103//!
104//! Validators will only submit solutions if the one that they have computed is strictly better than
105//! the best queued one and will limit the weight of the solution to [`MinerConfig::MaxWeight`].
106//!
107//! The unsigned phase can be made passive depending on how the previous signed phase went, by
108//! setting the first inner value of [`Phase`] to `false`. For now, the signed phase is always
109//! active.
110//!
111//! ### Fallback
112//!
113//! If we reach the end of both phases (i.e. call to [`ElectionProvider::elect`] happens) and no
114//! good solution is queued, then the fallback strategy [`pallet::Config::Fallback`] is used to
115//! determine what needs to be done. The on-chain election is slow, and contains no balancing or
116//! reduction post-processing. If [`pallet::Config::Fallback`] fails, the next phase
117//! [`Phase::Emergency`] is enabled, which is a more *fail-safe* approach.
118//!
119//! ### Emergency Phase
120//!
121//! If, for any of the below reasons:
122//!
123//! 1. No **signed** or **unsigned** solution submitted, and no successful [`Config::Fallback`] is
124//!    provided
125//! 2. Any other unforeseen internal error
126//!
127//! A call to `T::ElectionProvider::elect` is made, and `Ok(_)` cannot be returned, then the pallet
128//! proceeds to the [`Phase::Emergency`]. During this phase, any solution can be submitted from
129//! [`Config::ForceOrigin`], without any checking, via [`Pallet::set_emergency_election_result`]
130//! transaction. Hence, `[`Config::ForceOrigin`]` should only be set to a trusted origin, such as
131//! the council or root. Once submitted, the forced solution is kept in [`QueuedSolution`] until the
132//! next call to `T::ElectionProvider::elect`, where it is returned and [`Phase`] goes back to
133//! `Off`.
134//!
135//! This implies that the user of this pallet (i.e. a staking pallet) should re-try calling
136//! `T::ElectionProvider::elect` in case of error, until `OK(_)` is returned.
137//!
138//! To generate an emergency solution, one must only provide one argument: [`Supports`]. This is
139//! essentially a collection of elected winners for the election, and voters who support them. The
140//! supports can be generated by any means. In the simplest case, it could be manual. For example,
141//! in the case of massive network failure or misbehavior, [`Config::ForceOrigin`] might decide to
142//! select only a small number of emergency winners (which would greatly restrict the next validator
143//! set, if this pallet is used with `pallet-staking`). If the failure is for other technical
144//! reasons, then a simple and safe way to generate supports is using the staking-miner binary
145//! provided in the Polkadot repository. This binary has a subcommand named `emergency-solution`
146//! which is capable of connecting to a live network, and generating appropriate `supports` using a
147//! standard algorithm, and outputting the `supports` in hex format, ready for submission. Note that
148//! while this binary lives in the Polkadot repository, this particular subcommand of it can work
149//! against any substrate-based chain.
150//!
151//! See the [`staking-miner`](https://github.com/paritytech/staking-miner-v2) docs for more
152//! information.
153//!
154//! ## Feasible Solution (correct solution)
155//!
156//! All submissions must undergo a feasibility check. Signed solutions are checked one by one at the
157//! end of the signed phase, and the unsigned solutions are checked on the spot. A feasible solution
158//! is as follows:
159//!
160//! 0. **all** of the used indices must be correct.
161//! 1. present *exactly* correct number of winners.
162//! 2. any assignment is checked to match with [`RoundSnapshot::voters`].
163//! 3. the claimed score is valid, based on the fixed point arithmetic accuracy.
164//!
165//! ## Accuracy
166//!
167//! The accuracy of the election is configured via [`SolutionAccuracyOf`] which is the accuracy that
168//! the submitted solutions must adhere to.
169//!
170//! Note that the accuracy is of great importance. The offchain solution should be as small as
171//! possible, reducing solutions size/weight.
172//!
173//! ## Error types
174//!
175//! This pallet provides a verbose error system to ease future debugging and debugging. The overall
176//! hierarchy of errors is as follows:
177//!
178//! 1. [`pallet::Error`]: These are the errors that can be returned in the dispatchables of the
179//!    pallet, either signed or unsigned. Since decomposition with nested enums is not possible
180//!    here, they are prefixed with the logical sub-system to which they belong.
181//! 2. [`ElectionError`]: These are the errors that can be generated while the pallet is doing
182//!    something in automatic scenarios, such as `offchain_worker` or `on_initialize`. These errors
183//!    are helpful for logging and are thus nested as:
184//!    - [`ElectionError::Miner`]: wraps a [`unsigned::MinerError`].
185//!    - [`ElectionError::Feasibility`]: wraps a [`FeasibilityError`].
186//!    - [`ElectionError::Fallback`]: wraps a fallback error.
187//!    - [`ElectionError::DataProvider`]: wraps a static str.
188//!
189//! Note that there could be an overlap between these sub-errors. For example, A
190//! `SnapshotUnavailable` can happen in both miner and feasibility check phase.
191//!
192//! ## Multi-page election support
193//!
194//! The [`frame_election_provider_support::ElectionDataProvider`] and
195//! [`frame_election_provider_support::ElectionProvider`] traits used by this pallet can support a
196//! multi-page election.
197//!
198//! However, this pallet only supports single-page election and data
199//! provider and all the relevant trait implementation and configurations reflect that assumption.
200//!
201//! If external callers request the election of a page index higher than 0, the election will fail
202//! with [`ElectionError::MultiPageNotSupported`].
203//!
204//! ## Future Plans
205//!
206//! **Emergency-phase recovery script**: This script should be taken out of staking-miner in
207//! polkadot and ideally live in `substrate/utils/frame/elections`.
208//!
209//! **Challenge Phase**. We plan on adding a third phase to the pallet, called the challenge phase.
210//! This is a phase in which no further solutions are processed, and the current best solution might
211//! be challenged by anyone (signed or unsigned). The main plan here is to enforce the solution to
212//! be PJR. Checking PJR on-chain is quite expensive, yet proving that a solution is **not** PJR is
213//! rather cheap. If a queued solution is successfully proven bad:
214//!
215//! 1. We must surely slash whoever submitted that solution (might be a challenge for unsigned
216//!    solutions).
217//! 2. We will fallback to the emergency strategy (likely extending the current era).
218//!
219//! **Bailing out**. The functionality of bailing out of a queued solution is nice. A miner can
220//! submit a solution as soon as they _think_ it is high probability feasible, and do the checks
221//! afterwards, and remove their solution (for a small cost of probably just transaction fees, or a
222//! portion of the bond).
223//!
224//! **Conditionally open unsigned phase**: Currently, the unsigned phase is always opened. This is
225//! useful because an honest validator will run substrate OCW code, which should be good enough to
226//! trump a mediocre or malicious signed submission (assuming in the absence of honest signed bots).
227//! If there are signed submissions, they can be checked against an absolute measure (e.g. PJR),
228//! then we can only open the unsigned phase in extreme conditions (i.e. "no good signed solution
229//! received") to spare some work for the active validators.
230//!
231//! **Allow smaller solutions and build up**: For now we only allow solutions that are exactly
232//! [`DesiredTargets`], no more, no less. Over time, we can change this to a [min, max] where any
233//! solution within this range is acceptable, where bigger solutions are prioritized.
234//!
235//! **Score based on (byte) size**: We should always prioritize small solutions over bigger ones, if
236//! there is a tie. Even more harsh should be to enforce the bound of the `reduce` algorithm.
237//!
238//! **Take into account the encode/decode weight in benchmarks.** Currently, we only take into
239//! account the weight of encode/decode in the `submit_unsigned` given its priority. Nonetheless,
240//! all operations on the solution and the snapshot are worthy of taking this into account.
241
242#![cfg_attr(not(feature = "std"), no_std)]
243
244extern crate alloc;
245
246use alloc::{boxed::Box, vec::Vec};
247use codec::{Decode, DecodeWithMemTracking, Encode};
248use frame_election_provider_support::{
249	bounds::{CountBound, ElectionBounds, SizeBound},
250	BoundedSupports, BoundedSupportsOf, ElectionDataProvider, ElectionProvider,
251	InstantElectionProvider, NposSolution, PageIndex,
252};
253use frame_support::{
254	dispatch::DispatchClass,
255	ensure,
256	traits::{Currency, Get, OnUnbalanced, ReservableCurrency},
257	weights::Weight,
258	DefaultNoBound, EqNoBound, PartialEqNoBound,
259};
260use frame_system::{ensure_none, offchain::CreateBare, pallet_prelude::BlockNumberFor};
261use scale_info::TypeInfo;
262use sp_arithmetic::{
263	traits::{CheckedAdd, Zero},
264	UpperOf,
265};
266use sp_npos_elections::{ElectionScore, IdentifierT, Supports, VoteWeight};
267use sp_runtime::{
268	transaction_validity::{
269		InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
270		TransactionValidityError, ValidTransaction,
271	},
272	Debug, DispatchError, ModuleError, PerThing, Perbill, SaturatedConversion,
273};
274
275#[cfg(feature = "try-runtime")]
276use sp_runtime::TryRuntimeError;
277
278#[cfg(feature = "runtime-benchmarks")]
279mod benchmarking;
280#[cfg(test)]
281mod mock;
282#[cfg(all(test, feature = "remote-mining"))]
283mod remote_mining;
284#[macro_use]
285pub mod helpers;
286
287/// This pallet only supports a single page election flow.
288pub(crate) const SINGLE_PAGE: u32 = 0;
289const LOG_TARGET: &str = "runtime::election-provider";
290
291pub mod migrations;
292pub mod signed;
293pub mod unsigned;
294pub mod weights;
295
296pub use signed::{
297	BalanceOf, GeometricDepositBase, NegativeImbalanceOf, PositiveImbalanceOf, SignedSubmission,
298	SignedSubmissionOf, SignedSubmissions, SubmissionIndicesOf,
299};
300use unsigned::VoterOf;
301pub use unsigned::{Miner, MinerConfig};
302pub use weights::WeightInfo;
303
304/// The solution type used by this crate.
305pub type SolutionOf<T> = <T as MinerConfig>::Solution;
306/// The voter index. Derived from [`SolutionOf`].
307pub type SolutionVoterIndexOf<T> = <SolutionOf<T> as NposSolution>::VoterIndex;
308/// The target index. Derived from [`SolutionOf`].
309pub type SolutionTargetIndexOf<T> = <SolutionOf<T> as NposSolution>::TargetIndex;
310/// The accuracy of the election, when submitted from offchain. Derived from [`SolutionOf`].
311pub type SolutionAccuracyOf<T> =
312	<SolutionOf<<T as crate::Config>::MinerConfig> as NposSolution>::Accuracy;
313/// A ready solution parameterized with this pallet's miner config.
314pub type ReadySolutionOf<T> = ReadySolution<
315	<T as MinerConfig>::AccountId,
316	<T as MinerConfig>::MaxWinners,
317	<T as MinerConfig>::MaxBackersPerWinner,
318>;
319/// The fallback election type.
320pub type FallbackErrorOf<T> = <<T as crate::Config>::Fallback as ElectionProvider>::Error;
321
322/// Configuration for the benchmarks of the pallet.
323pub trait BenchmarkingConfig {
324	/// Range of voters.
325	const VOTERS: [u32; 2];
326	/// Range of targets.
327	const TARGETS: [u32; 2];
328	/// Range of active voters.
329	const ACTIVE_VOTERS: [u32; 2];
330	/// Range of desired targets.
331	const DESIRED_TARGETS: [u32; 2];
332	/// Maximum number of voters expected. This is used only for memory-benchmarking of snapshot.
333	const SNAPSHOT_MAXIMUM_VOTERS: u32;
334	/// Maximum number of voters expected. This is used only for memory-benchmarking of miner.
335	const MINER_MAXIMUM_VOTERS: u32;
336	/// Maximum number of targets expected. This is used only for memory-benchmarking.
337	const MAXIMUM_TARGETS: u32;
338}
339
340/// Current phase of the pallet.
341#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
342pub enum Phase<Bn> {
343	/// Nothing, the election is not happening.
344	Off,
345	/// Signed phase is open.
346	Signed,
347	/// Unsigned phase. First element is whether it is active or not, second the starting block
348	/// number.
349	///
350	/// We do not yet check whether the unsigned phase is active or passive. The intent is for the
351	/// blockchain to be able to declare: "I believe that there exists an adequate signed
352	/// solution," advising validators not to bother running the unsigned offchain worker.
353	///
354	/// As validator nodes are free to edit their OCW code, they could simply ignore this advisory
355	/// and always compute their own solution. However, by default, when the unsigned phase is
356	/// passive, the offchain workers will not bother running.
357	Unsigned((bool, Bn)),
358	/// The emergency phase. This is enabled upon a failing call to `T::ElectionProvider::elect`.
359	/// After that, the only way to leave this phase is through a successful
360	/// `T::ElectionProvider::elect`.
361	Emergency,
362}
363
364impl<Bn> Default for Phase<Bn> {
365	fn default() -> Self {
366		Phase::Off
367	}
368}
369
370impl<Bn: PartialEq + Eq> Phase<Bn> {
371	/// Whether the phase is emergency or not.
372	pub fn is_emergency(&self) -> bool {
373		matches!(self, Phase::Emergency)
374	}
375
376	/// Whether the phase is signed or not.
377	pub fn is_signed(&self) -> bool {
378		matches!(self, Phase::Signed)
379	}
380
381	/// Whether the phase is unsigned or not.
382	pub fn is_unsigned(&self) -> bool {
383		matches!(self, Phase::Unsigned(_))
384	}
385
386	/// Whether the phase is unsigned and open or not, with specific start.
387	pub fn is_unsigned_open_at(&self, at: Bn) -> bool {
388		matches!(self, Phase::Unsigned((true, real)) if *real == at)
389	}
390
391	/// Whether the phase is unsigned and open or not.
392	pub fn is_unsigned_open(&self) -> bool {
393		matches!(self, Phase::Unsigned((true, _)))
394	}
395
396	/// Whether the phase is off or not.
397	pub fn is_off(&self) -> bool {
398		matches!(self, Phase::Off)
399	}
400}
401
402/// The type of `Computation` that provided this election data.
403#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, Debug, TypeInfo)]
404pub enum ElectionCompute {
405	/// Election was computed on-chain.
406	OnChain,
407	/// Election was computed with a signed submission.
408	Signed,
409	/// Election was computed with an unsigned submission.
410	Unsigned,
411	/// Election was computed using the fallback
412	Fallback,
413	/// Election was computed with emergency status.
414	Emergency,
415}
416
417impl Default for ElectionCompute {
418	fn default() -> Self {
419		ElectionCompute::OnChain
420	}
421}
422
423/// A raw, unchecked solution.
424///
425/// This is what will get submitted to the chain.
426///
427/// Such a solution should never become effective in anyway before being checked by the
428/// `Pallet::feasibility_check`.
429#[derive(
430	PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, Debug, PartialOrd, Ord, TypeInfo,
431)]
432pub struct RawSolution<S> {
433	/// the solution itself.
434	pub solution: S,
435	/// The _claimed_ score of the solution.
436	pub score: ElectionScore,
437	/// The round at which this solution should be submitted.
438	pub round: u32,
439}
440
441impl<C: Default> Default for RawSolution<C> {
442	fn default() -> Self {
443		// Round 0 is always invalid, only set this to 1.
444		Self { round: 1, solution: Default::default(), score: Default::default() }
445	}
446}
447
448/// A checked solution, ready to be enacted.
449#[derive(
450	PartialEqNoBound, EqNoBound, Clone, Encode, Decode, Debug, DefaultNoBound, scale_info::TypeInfo,
451)]
452#[scale_info(skip_type_params(AccountId, MaxWinners, MaxBackersPerWinner))]
453pub struct ReadySolution<AccountId, MaxWinners, MaxBackersPerWinner>
454where
455	AccountId: IdentifierT,
456	MaxWinners: Get<u32>,
457	MaxBackersPerWinner: Get<u32>,
458{
459	/// The final supports of the solution.
460	///
461	/// This is target-major vector, storing each winners, total backing, and each individual
462	/// backer.
463	pub supports: BoundedSupports<AccountId, MaxWinners, MaxBackersPerWinner>,
464	/// The score of the solution.
465	///
466	/// This is needed to potentially challenge the solution.
467	pub score: ElectionScore,
468	/// How this election was computed.
469	pub compute: ElectionCompute,
470}
471
472/// A snapshot of all the data that is needed for en entire round. They are provided by
473/// [`ElectionDataProvider`] and are kept around until the round is finished.
474///
475/// These are stored together because they are often accessed together.
476#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, Default, TypeInfo)]
477#[scale_info(skip_type_params(T))]
478pub struct RoundSnapshot<AccountId, VoterType> {
479	/// All of the voters.
480	pub voters: Vec<VoterType>,
481	/// All of the targets.
482	pub targets: Vec<AccountId>,
483}
484
485/// Encodes the length of a solution or a snapshot.
486///
487/// This is stored automatically on-chain, and it contains the **size of the entire snapshot**.
488/// This is also used in dispatchables as weight witness data and should **only contain the size of
489/// the presented solution**, not the entire snapshot.
490#[derive(
491	PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, Debug, Default, TypeInfo,
492)]
493pub struct SolutionOrSnapshotSize {
494	/// The length of voters.
495	#[codec(compact)]
496	pub voters: u32,
497	/// The length of targets.
498	#[codec(compact)]
499	pub targets: u32,
500}
501
502/// Internal errors of the pallet.
503///
504/// Note that this is different from [`pallet::Error`].
505#[derive(frame_support::DebugNoBound)]
506#[cfg_attr(feature = "runtime-benchmarks", derive(strum::IntoStaticStr))]
507pub enum ElectionError<T: Config> {
508	/// An error happened in the feasibility check sub-system.
509	Feasibility(FeasibilityError),
510	/// An error in the miner (offchain) sub-system.
511	Miner(unsigned::MinerError),
512	/// An error happened in the data provider.
513	DataProvider(&'static str),
514	/// An error nested in the fallback.
515	Fallback(FallbackErrorOf<T>),
516	/// An error occurred when requesting an election result. The caller expects a multi-paged
517	/// election, which this pallet does not support.
518	MultiPageNotSupported,
519	/// No solution has been queued.
520	NothingQueued,
521}
522
523// NOTE: we have to do this manually because of the additional where clause needed on
524// `FallbackErrorOf<T>`.
525impl<T: Config> PartialEq for ElectionError<T>
526where
527	FallbackErrorOf<T>: PartialEq,
528{
529	fn eq(&self, other: &Self) -> bool {
530		use ElectionError::*;
531		match (self, other) {
532			(Feasibility(x), Feasibility(y)) if x == y => true,
533			(Miner(x), Miner(y)) if x == y => true,
534			(DataProvider(x), DataProvider(y)) if x == y => true,
535			(Fallback(x), Fallback(y)) if x == y => true,
536			(MultiPageNotSupported, MultiPageNotSupported) => true,
537			(NothingQueued, NothingQueued) => true,
538			_ => false,
539		}
540	}
541}
542
543impl<T: Config> From<FeasibilityError> for ElectionError<T> {
544	fn from(e: FeasibilityError) -> Self {
545		ElectionError::Feasibility(e)
546	}
547}
548
549impl<T: Config> From<unsigned::MinerError> for ElectionError<T> {
550	fn from(e: unsigned::MinerError) -> Self {
551		ElectionError::Miner(e)
552	}
553}
554
555/// Errors that can happen in the feasibility check.
556#[derive(Debug, Eq, PartialEq)]
557#[cfg_attr(feature = "runtime-benchmarks", derive(strum::IntoStaticStr))]
558pub enum FeasibilityError {
559	/// Wrong number of winners presented.
560	WrongWinnerCount,
561	/// The snapshot is not available.
562	///
563	/// Kinda defensive: The pallet should technically never attempt to do a feasibility check when
564	/// no snapshot is present.
565	SnapshotUnavailable,
566	/// Internal error from the election crate.
567	NposElection(sp_npos_elections::Error),
568	/// A vote is invalid.
569	InvalidVote,
570	/// A voter is invalid.
571	InvalidVoter,
572	/// The given score was invalid.
573	InvalidScore,
574	/// The provided round is incorrect.
575	InvalidRound,
576	/// Comparison against `MinimumUntrustedScore` failed.
577	UntrustedScoreTooLow,
578	/// Data Provider returned too many desired targets
579	TooManyDesiredTargets,
580	/// Conversion into bounded types failed.
581	///
582	/// Should never happen under correct configurations.
583	BoundedConversionFailed,
584}
585
586impl From<sp_npos_elections::Error> for FeasibilityError {
587	fn from(e: sp_npos_elections::Error) -> Self {
588		FeasibilityError::NposElection(e)
589	}
590}
591
592pub use pallet::*;
593#[frame_support::pallet]
594pub mod pallet {
595	use super::*;
596	use frame_election_provider_support::{InstantElectionProvider, NposSolver};
597	use frame_support::{pallet_prelude::*, traits::EstimateCallFee};
598	use frame_system::pallet_prelude::*;
599	use sp_runtime::traits::Convert;
600
601	#[pallet::config]
602	pub trait Config: frame_system::Config + CreateBare<Call<Self>> {
603		#[allow(deprecated)]
604		type RuntimeEvent: From<Event<Self>>
605			+ IsType<<Self as frame_system::Config>::RuntimeEvent>
606			+ TryInto<Event<Self>>;
607
608		/// Currency type.
609		type Currency: ReservableCurrency<Self::AccountId> + Currency<Self::AccountId>;
610
611		/// Something that can predict the fee of a call. Used to sensibly distribute rewards.
612		type EstimateCallFee: EstimateCallFee<Call<Self>, BalanceOf<Self>>;
613
614		/// Duration of the unsigned phase.
615		type UnsignedPhase: Get<BlockNumberFor<Self>>;
616		/// Duration of the signed phase.
617		type SignedPhase: Get<BlockNumberFor<Self>>;
618
619		/// The minimum amount of improvement to the solution score that defines a solution as
620		/// "better" in the Signed phase.
621		#[pallet::constant]
622		type BetterSignedThreshold: Get<Perbill>;
623
624		/// The repeat threshold of the offchain worker.
625		///
626		/// For example, if it is 5, that means that at least 5 blocks will elapse between attempts
627		/// to submit the worker's solution.
628		#[pallet::constant]
629		type OffchainRepeat: Get<BlockNumberFor<Self>>;
630
631		/// The priority of the unsigned transaction submitted in the unsigned-phase
632		#[pallet::constant]
633		type MinerTxPriority: Get<TransactionPriority>;
634
635		/// Configurations of the embedded miner.
636		///
637		/// Any external software implementing this can use the [`unsigned::Miner`] type provided,
638		/// which can mine new solutions and trim them accordingly.
639		type MinerConfig: crate::unsigned::MinerConfig<
640			AccountId = Self::AccountId,
641			MaxVotesPerVoter = <Self::DataProvider as ElectionDataProvider>::MaxVotesPerVoter,
642			MaxWinners = Self::MaxWinners,
643			MaxBackersPerWinner = Self::MaxBackersPerWinner,
644		>;
645
646		/// Maximum number of signed submissions that can be queued.
647		///
648		/// It is best to avoid adjusting this during an election, as it impacts downstream data
649		/// structures. In particular, `SignedSubmissionIndices<T>` is bounded on this value. If you
650		/// update this value during an election, you _must_ ensure that
651		/// `SignedSubmissionIndices.len()` is less than or equal to the new value. Otherwise,
652		/// attempts to submit new solutions may cause a runtime panic.
653		#[pallet::constant]
654		type SignedMaxSubmissions: Get<u32>;
655
656		/// Maximum weight of a signed solution.
657		///
658		/// If [`Config::MinerConfig`] is being implemented to submit signed solutions (outside of
659		/// this pallet), then [`MinerConfig::solution_weight`] is used to compare against
660		/// this value.
661		#[pallet::constant]
662		type SignedMaxWeight: Get<Weight>;
663
664		/// The maximum amount of unchecked solutions to refund the call fee for.
665		#[pallet::constant]
666		type SignedMaxRefunds: Get<u32>;
667
668		/// Base reward for a signed solution
669		#[pallet::constant]
670		type SignedRewardBase: Get<BalanceOf<Self>>;
671
672		/// Per-byte deposit for a signed solution.
673		#[pallet::constant]
674		type SignedDepositByte: Get<BalanceOf<Self>>;
675
676		/// Per-weight deposit for a signed solution.
677		#[pallet::constant]
678		type SignedDepositWeight: Get<BalanceOf<Self>>;
679
680		/// Maximum number of winners that an election supports.
681		///
682		/// Note: This must always be greater or equal to `T::DataProvider::desired_targets()`.
683		#[pallet::constant]
684		type MaxWinners: Get<u32>;
685
686		/// Maximum number of voters that can support a winner in an election solution.
687		///
688		/// This is needed to ensure election computation is bounded.
689		#[pallet::constant]
690		type MaxBackersPerWinner: Get<u32>;
691
692		/// Something that calculates the signed deposit base based on the signed submissions queue
693		/// size.
694		type SignedDepositBase: Convert<usize, BalanceOf<Self>>;
695
696		/// The maximum number of electing voters and electable targets to put in the snapshot.
697		type ElectionBounds: Get<ElectionBounds>;
698
699		/// Handler for the slashed deposits.
700		type SlashHandler: OnUnbalanced<NegativeImbalanceOf<Self>>;
701
702		/// Handler for the rewards.
703		type RewardHandler: OnUnbalanced<PositiveImbalanceOf<Self>>;
704
705		/// Something that will provide the election data.
706		type DataProvider: ElectionDataProvider<
707			AccountId = Self::AccountId,
708			BlockNumber = BlockNumberFor<Self>,
709		>;
710
711		/// Configuration for the fallback.
712		type Fallback: InstantElectionProvider<
713			AccountId = Self::AccountId,
714			BlockNumber = BlockNumberFor<Self>,
715			DataProvider = Self::DataProvider,
716			MaxBackersPerWinner = Self::MaxBackersPerWinner,
717			MaxWinnersPerPage = Self::MaxWinners,
718		>;
719
720		/// Configuration of the governance-only fallback.
721		///
722		/// As a side-note, it is recommend for test-nets to use `type ElectionProvider =
723		/// BoundedExecution<_>` if the test-net is not expected to have thousands of nominators.
724		type GovernanceFallback: InstantElectionProvider<
725			AccountId = Self::AccountId,
726			BlockNumber = BlockNumberFor<Self>,
727			DataProvider = Self::DataProvider,
728			MaxWinnersPerPage = Self::MaxWinners,
729			MaxBackersPerWinner = Self::MaxBackersPerWinner,
730		>;
731
732		/// OCW election solution miner algorithm implementation.
733		type Solver: NposSolver<AccountId = Self::AccountId>;
734
735		/// Origin that can control this pallet. Note that any action taken by this origin (such)
736		/// as providing an emergency solution is not checked. Thus, it must be a trusted origin.
737		type ForceOrigin: EnsureOrigin<Self::RuntimeOrigin>;
738
739		/// The configuration of benchmarking.
740		type BenchmarkingConfig: BenchmarkingConfig;
741
742		/// The weight of the pallet.
743		type WeightInfo: WeightInfo;
744	}
745
746	// Expose miner configs over the metadata such that they can be re-implemented.
747	#[pallet::extra_constants]
748	impl<T: Config> Pallet<T> {
749		#[pallet::constant_name(MinerMaxLength)]
750		fn max_length() -> u32 {
751			<T::MinerConfig as MinerConfig>::MaxLength::get()
752		}
753
754		#[pallet::constant_name(MinerMaxWeight)]
755		fn max_weight() -> Weight {
756			<T::MinerConfig as MinerConfig>::MaxWeight::get()
757		}
758
759		#[pallet::constant_name(MinerMaxVotesPerVoter)]
760		fn max_votes_per_voter() -> u32 {
761			<T::MinerConfig as MinerConfig>::MaxVotesPerVoter::get()
762		}
763
764		#[pallet::constant_name(MinerMaxWinners)]
765		fn max_winners() -> u32 {
766			<T::MinerConfig as MinerConfig>::MaxWinners::get()
767		}
768	}
769
770	#[pallet::hooks]
771	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
772		fn on_initialize(now: BlockNumberFor<T>) -> Weight {
773			let next_election = T::DataProvider::next_election_prediction(now).max(now);
774
775			let signed_deadline = T::SignedPhase::get() + T::UnsignedPhase::get();
776			let unsigned_deadline = T::UnsignedPhase::get();
777
778			let remaining = next_election - now;
779			let current_phase = CurrentPhase::<T>::get();
780
781			log!(
782				trace,
783				"current phase {:?}, next election {:?}, queued? {:?}, metadata: {:?}",
784				current_phase,
785				next_election,
786				QueuedSolution::<T>::get().map(|rs| (rs.supports.len(), rs.compute, rs.score)),
787				SnapshotMetadata::<T>::get()
788			);
789			match current_phase {
790				Phase::Off if remaining <= signed_deadline && remaining > unsigned_deadline => {
791					// NOTE: if signed-phase length is zero, second part of the if-condition fails.
792					match Self::create_snapshot() {
793						Ok(_) => {
794							Self::phase_transition(Phase::Signed);
795							T::WeightInfo::on_initialize_open_signed()
796						},
797						Err(why) => {
798							// Not much we can do about this at this point.
799							log!(warn, "failed to open signed phase due to {:?}", why);
800							T::WeightInfo::on_initialize_nothing()
801						},
802					}
803				},
804				Phase::Signed | Phase::Off
805					if remaining <= unsigned_deadline && remaining > Zero::zero() =>
806				{
807					// our needs vary according to whether or not the unsigned phase follows a
808					// signed phase
809					let (need_snapshot, enabled) = if current_phase == Phase::Signed {
810						// there was previously a signed phase: close the signed phase, no need for
811						// snapshot.
812						//
813						// Notes:
814						//
815						//   - `Self::finalize_signed_phase()` also appears in `fn do_elect`. This
816						//     is a guard against the case that `elect` is called prematurely. This
817						//     adds a small amount of overhead, but that is unfortunately
818						//     unavoidable.
819						let _ = Self::finalize_signed_phase();
820						// In the future we can consider disabling the unsigned phase if the signed
821						// phase completes successfully, but for now we're enabling it
822						// unconditionally as a defensive measure.
823						(false, true)
824					} else {
825						// No signed phase: create a new snapshot, definitely `enable` the unsigned
826						// phase.
827						(true, true)
828					};
829
830					if need_snapshot {
831						match Self::create_snapshot() {
832							Ok(_) => {
833								Self::phase_transition(Phase::Unsigned((enabled, now)));
834								T::WeightInfo::on_initialize_open_unsigned()
835							},
836							Err(why) => {
837								log!(warn, "failed to open unsigned phase due to {:?}", why);
838								T::WeightInfo::on_initialize_nothing()
839							},
840						}
841					} else {
842						Self::phase_transition(Phase::Unsigned((enabled, now)));
843						T::WeightInfo::on_initialize_open_unsigned()
844					}
845				},
846				_ => T::WeightInfo::on_initialize_nothing(),
847			}
848		}
849
850		fn offchain_worker(now: BlockNumberFor<T>) {
851			use sp_runtime::offchain::storage_lock::{BlockAndTime, StorageLock};
852
853			// Create a lock with the maximum deadline of number of blocks in the unsigned phase.
854			// This should only come useful in an **abrupt** termination of execution, otherwise the
855			// guard will be dropped upon successful execution.
856			let mut lock =
857				StorageLock::<BlockAndTime<frame_system::Pallet<T>>>::with_block_deadline(
858					unsigned::OFFCHAIN_LOCK,
859					T::UnsignedPhase::get().saturated_into(),
860				);
861
862			match lock.try_lock() {
863				Ok(_guard) => {
864					Self::do_synchronized_offchain_worker(now);
865				},
866				Err(deadline) => {
867					log!(debug, "offchain worker lock not released, deadline is {:?}", deadline);
868				},
869			};
870		}
871
872		fn integrity_test() {
873			use core::mem::size_of;
874			// The index type of both voters and targets need to be smaller than that of usize (very
875			// unlikely to be the case, but anyhow)..
876			assert!(size_of::<SolutionVoterIndexOf<T::MinerConfig>>() <= size_of::<usize>());
877			assert!(size_of::<SolutionTargetIndexOf<T::MinerConfig>>() <= size_of::<usize>());
878
879			// ----------------------------
880			// Based on the requirements of [`sp_npos_elections::Assignment::try_normalize`].
881			let max_vote: usize = <SolutionOf<T::MinerConfig> as NposSolution>::LIMIT;
882
883			// 2. Maximum sum of [SolutionAccuracy; 16] must fit into `UpperOf<OffchainAccuracy>`.
884			let maximum_chain_accuracy: Vec<UpperOf<SolutionAccuracyOf<T>>> = (0..max_vote)
885				.map(|_| {
886					<UpperOf<SolutionAccuracyOf<T>>>::from(
887						SolutionAccuracyOf::<T>::one().deconstruct(),
888					)
889				})
890				.collect();
891			let _: UpperOf<SolutionAccuracyOf<T>> = maximum_chain_accuracy
892				.iter()
893				.fold(Zero::zero(), |acc, x| acc.checked_add(x).unwrap());
894
895			// We only accept data provider who's maximum votes per voter matches our
896			// `T::Solution`'s `LIMIT`.
897			//
898			// NOTE that this pallet does not really need to enforce this in runtime. The
899			// solution cannot represent any voters more than `LIMIT` anyhow.
900			assert_eq!(
901				<T::DataProvider as ElectionDataProvider>::MaxVotesPerVoter::get(),
902				<SolutionOf<T::MinerConfig> as NposSolution>::LIMIT as u32,
903			);
904
905			// While it won't cause any failures, setting `SignedMaxRefunds` gt
906			// `SignedMaxSubmissions` is a red flag that the developer does not understand how to
907			// configure this pallet.
908			assert!(T::SignedMaxSubmissions::get() >= T::SignedMaxRefunds::get());
909		}
910
911		#[cfg(feature = "try-runtime")]
912		fn try_state(_n: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
913			Self::do_try_state()
914		}
915	}
916
917	#[pallet::call]
918	impl<T: Config> Pallet<T> {
919		/// Submit a solution for the unsigned phase.
920		///
921		/// The dispatch origin fo this call must be __none__.
922		///
923		/// This submission is checked on the fly. Moreover, this unsigned solution is only
924		/// validated when submitted to the pool from the **local** node. Effectively, this means
925		/// that only active validators can submit this transaction when authoring a block (similar
926		/// to an inherent).
927		///
928		/// To prevent any incorrect solution (and thus wasted time/weight), this transaction will
929		/// panic if the solution submitted by the validator is invalid in any way, effectively
930		/// putting their authoring reward at risk.
931		///
932		/// No deposit or reward is associated with this submission.
933		#[pallet::call_index(0)]
934		#[pallet::weight((
935			T::WeightInfo::submit_unsigned(
936				witness.voters,
937				witness.targets,
938				raw_solution.solution.voter_count() as u32,
939				raw_solution.solution.unique_targets().len() as u32
940			),
941			DispatchClass::Operational,
942		))]
943		pub fn submit_unsigned(
944			origin: OriginFor<T>,
945			raw_solution: Box<RawSolution<SolutionOf<T::MinerConfig>>>,
946			witness: SolutionOrSnapshotSize,
947		) -> DispatchResult {
948			ensure_none(origin)?;
949			let error_message = "Invalid unsigned submission must produce invalid block and \
950				 deprive validator from their authoring reward.";
951
952			// Check score being an improvement, phase, and desired targets.
953			Self::unsigned_pre_dispatch_checks(&raw_solution).expect(error_message);
954
955			// Ensure witness was correct.
956			let SolutionOrSnapshotSize { voters, targets } =
957				SnapshotMetadata::<T>::get().expect(error_message);
958
959			// NOTE: we are asserting, not `ensure`ing -- we want to panic here.
960			assert!(voters as u32 == witness.voters, "{}", error_message);
961			assert!(targets as u32 == witness.targets, "{}", error_message);
962
963			let ready = Self::feasibility_check(*raw_solution, ElectionCompute::Unsigned)
964				.expect(error_message);
965
966			// Store the newly received solution.
967			log!(debug, "queued unsigned solution with score {:?}", ready.score);
968			let ejected_a_solution = QueuedSolution::<T>::exists();
969			QueuedSolution::<T>::put(ready);
970			Self::deposit_event(Event::SolutionStored {
971				compute: ElectionCompute::Unsigned,
972				origin: None,
973				prev_ejected: ejected_a_solution,
974			});
975
976			Ok(())
977		}
978
979		/// Set a new value for `MinimumUntrustedScore`.
980		///
981		/// Dispatch origin must be aligned with `T::ForceOrigin`.
982		///
983		/// This check can be turned off by setting the value to `None`.
984		#[pallet::call_index(1)]
985		#[pallet::weight(T::DbWeight::get().writes(1))]
986		pub fn set_minimum_untrusted_score(
987			origin: OriginFor<T>,
988			maybe_next_score: Option<ElectionScore>,
989		) -> DispatchResult {
990			T::ForceOrigin::ensure_origin(origin)?;
991			MinimumUntrustedScore::<T>::set(maybe_next_score);
992			Ok(())
993		}
994
995		/// Set a solution in the queue, to be handed out to the client of this pallet in the next
996		/// call to `ElectionProvider::elect`.
997		///
998		/// This can only be set by `T::ForceOrigin`, and only when the phase is `Emergency`.
999		///
1000		/// The solution is not checked for any feasibility and is assumed to be trustworthy, as any
1001		/// feasibility check itself can in principle cause the election process to fail (due to
1002		/// memory/weight constrains).
1003		#[pallet::call_index(2)]
1004		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
1005		pub fn set_emergency_election_result(
1006			origin: OriginFor<T>,
1007			supports: Supports<T::AccountId>,
1008		) -> DispatchResult {
1009			T::ForceOrigin::ensure_origin(origin)?;
1010			ensure!(CurrentPhase::<T>::get().is_emergency(), Error::<T>::CallNotAllowed);
1011
1012			// bound supports with T::MaxWinners.
1013			let supports: BoundedSupportsOf<Self> =
1014				supports.try_into().map_err(|_| Error::<T>::TooManyWinners)?;
1015
1016			// Note: we don't `rotate_round` at this point; the next call to
1017			// `ElectionProvider::elect` will succeed and take care of that.
1018			let solution = ReadySolution {
1019				supports,
1020				score: Default::default(),
1021				compute: ElectionCompute::Emergency,
1022			};
1023
1024			Self::deposit_event(Event::SolutionStored {
1025				compute: ElectionCompute::Emergency,
1026				origin: None,
1027				prev_ejected: QueuedSolution::<T>::exists(),
1028			});
1029
1030			QueuedSolution::<T>::put(solution);
1031			Ok(())
1032		}
1033
1034		/// Submit a solution for the signed phase.
1035		///
1036		/// The dispatch origin fo this call must be __signed__.
1037		///
1038		/// The solution is potentially queued, based on the claimed score and processed at the end
1039		/// of the signed phase.
1040		///
1041		/// A deposit is reserved and recorded for the solution. Based on the outcome, the solution
1042		/// might be rewarded, slashed, or get all or a part of the deposit back.
1043		#[pallet::call_index(3)]
1044		#[pallet::weight(T::WeightInfo::submit())]
1045		pub fn submit(
1046			origin: OriginFor<T>,
1047			raw_solution: Box<RawSolution<SolutionOf<T::MinerConfig>>>,
1048		) -> DispatchResult {
1049			let who = ensure_signed(origin)?;
1050
1051			// ensure solution is timely.
1052			ensure!(CurrentPhase::<T>::get().is_signed(), Error::<T>::PreDispatchEarlySubmission);
1053			ensure!(raw_solution.round == Round::<T>::get(), Error::<T>::PreDispatchDifferentRound);
1054
1055			// NOTE: this is the only case where having separate snapshot would have been better
1056			// because could do just decode_len. But we can create abstractions to do this.
1057
1058			// build size. Note: this is not needed for weight calc, thus not input.
1059			// unlikely to ever return an error: if phase is signed, snapshot will exist.
1060			let size = SnapshotMetadata::<T>::get().ok_or(Error::<T>::MissingSnapshotMetadata)?;
1061
1062			ensure!(
1063				Self::solution_weight_of(&raw_solution, size).all_lt(T::SignedMaxWeight::get()),
1064				Error::<T>::SignedTooMuchWeight,
1065			);
1066
1067			// create the submission
1068			let deposit = Self::deposit_for(&raw_solution, size);
1069			let call_fee = {
1070				let call = Call::submit { raw_solution: raw_solution.clone() };
1071				T::EstimateCallFee::estimate_call_fee(&call, None::<Weight>.into())
1072			};
1073
1074			let submission = SignedSubmission {
1075				who: who.clone(),
1076				deposit,
1077				raw_solution: *raw_solution,
1078				call_fee,
1079			};
1080
1081			// insert the submission if the queue has space or it's better than the weakest
1082			// eject the weakest if the queue was full
1083			let mut signed_submissions = Self::signed_submissions();
1084			let maybe_removed = match signed_submissions.insert(submission) {
1085				// it's an error if we failed to insert a submission: this indicates the queue was
1086				// full but our solution had insufficient score to eject any solution
1087				signed::InsertResult::NotInserted => return Err(Error::<T>::SignedQueueFull.into()),
1088				signed::InsertResult::Inserted => None,
1089				signed::InsertResult::InsertedEjecting(weakest) => Some(weakest),
1090			};
1091
1092			// collect deposit. Thereafter, the function cannot fail.
1093			T::Currency::reserve(&who, deposit).map_err(|_| Error::<T>::SignedCannotPayDeposit)?;
1094
1095			let ejected_a_solution = maybe_removed.is_some();
1096			// if we had to remove the weakest solution, unreserve its deposit
1097			if let Some(removed) = maybe_removed {
1098				let _remainder = T::Currency::unreserve(&removed.who, removed.deposit);
1099				debug_assert!(_remainder.is_zero());
1100			}
1101
1102			signed_submissions.put();
1103			Self::deposit_event(Event::SolutionStored {
1104				compute: ElectionCompute::Signed,
1105				origin: Some(who),
1106				prev_ejected: ejected_a_solution,
1107			});
1108			Ok(())
1109		}
1110
1111		/// Trigger the governance fallback.
1112		///
1113		/// This can only be called when [`Phase::Emergency`] is enabled, as an alternative to
1114		/// calling [`Call::set_emergency_election_result`].
1115		#[pallet::call_index(4)]
1116		#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
1117		pub fn governance_fallback(origin: OriginFor<T>) -> DispatchResult {
1118			T::ForceOrigin::ensure_origin(origin)?;
1119			ensure!(CurrentPhase::<T>::get().is_emergency(), Error::<T>::CallNotAllowed);
1120
1121			let RoundSnapshot { voters, targets } =
1122				Snapshot::<T>::get().ok_or(Error::<T>::MissingSnapshotMetadata)?;
1123			let desired_targets =
1124				DesiredTargets::<T>::get().ok_or(Error::<T>::MissingSnapshotMetadata)?;
1125
1126			let supports = T::GovernanceFallback::instant_elect(voters, targets, desired_targets)
1127				.map_err(|e| {
1128				log!(error, "GovernanceFallback failed: {:?}", e);
1129				Error::<T>::FallbackFailed
1130			})?;
1131
1132			let solution = ReadySolution {
1133				supports,
1134				score: Default::default(),
1135				compute: ElectionCompute::Fallback,
1136			};
1137
1138			Self::deposit_event(Event::SolutionStored {
1139				compute: ElectionCompute::Fallback,
1140				origin: None,
1141				prev_ejected: QueuedSolution::<T>::exists(),
1142			});
1143
1144			QueuedSolution::<T>::put(solution);
1145			Ok(())
1146		}
1147	}
1148
1149	#[pallet::event]
1150	#[pallet::generate_deposit(pub(super) fn deposit_event)]
1151	pub enum Event<T: Config> {
1152		/// A solution was stored with the given compute.
1153		///
1154		/// The `origin` indicates the origin of the solution. If `origin` is `Some(AccountId)`,
1155		/// the stored solution was submitted in the signed phase by a miner with the `AccountId`.
1156		/// Otherwise, the solution was stored either during the unsigned phase or by
1157		/// `T::ForceOrigin`. The `bool` is `true` when a previous solution was ejected to make
1158		/// room for this one.
1159		SolutionStored {
1160			compute: ElectionCompute,
1161			origin: Option<T::AccountId>,
1162			prev_ejected: bool,
1163		},
1164		/// The election has been finalized, with the given computation and score.
1165		ElectionFinalized { compute: ElectionCompute, score: ElectionScore },
1166		/// An election failed.
1167		///
1168		/// Not much can be said about which computes failed in the process.
1169		ElectionFailed,
1170		/// An account has been rewarded for their signed submission being finalized.
1171		Rewarded { account: <T as frame_system::Config>::AccountId, value: BalanceOf<T> },
1172		/// An account has been slashed for submitting an invalid signed submission.
1173		Slashed { account: <T as frame_system::Config>::AccountId, value: BalanceOf<T> },
1174		/// There was a phase transition in a given round.
1175		PhaseTransitioned {
1176			from: Phase<BlockNumberFor<T>>,
1177			to: Phase<BlockNumberFor<T>>,
1178			round: u32,
1179		},
1180	}
1181
1182	/// Error of the pallet that can be returned in response to dispatches.
1183	#[pallet::error]
1184	pub enum Error<T> {
1185		/// Submission was too early.
1186		PreDispatchEarlySubmission,
1187		/// Wrong number of winners presented.
1188		PreDispatchWrongWinnerCount,
1189		/// Submission was too weak, score-wise.
1190		PreDispatchWeakSubmission,
1191		/// The queue was full, and the solution was not better than any of the existing ones.
1192		SignedQueueFull,
1193		/// The origin failed to pay the deposit.
1194		SignedCannotPayDeposit,
1195		/// Witness data to dispatchable is invalid.
1196		SignedInvalidWitness,
1197		/// The signed submission consumes too much weight
1198		SignedTooMuchWeight,
1199		/// OCW submitted solution for wrong round
1200		OcwCallWrongEra,
1201		/// Snapshot metadata should exist but didn't.
1202		MissingSnapshotMetadata,
1203		/// `Self::insert_submission` returned an invalid index.
1204		InvalidSubmissionIndex,
1205		/// The call is not allowed at this point.
1206		CallNotAllowed,
1207		/// The fallback failed
1208		FallbackFailed,
1209		/// Some bound not met
1210		BoundNotMet,
1211		/// Submitted solution has too many winners
1212		TooManyWinners,
1213		/// Submission was prepared for a different round.
1214		PreDispatchDifferentRound,
1215	}
1216
1217	#[allow(deprecated)]
1218	#[pallet::validate_unsigned]
1219	impl<T: Config> ValidateUnsigned for Pallet<T> {
1220		type Call = Call<T>;
1221		fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
1222			if let Call::submit_unsigned { raw_solution, .. } = call {
1223				// Discard solution not coming from the local OCW.
1224				match source {
1225					TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ },
1226					_ => return InvalidTransaction::Call.into(),
1227				}
1228
1229				Self::unsigned_pre_dispatch_checks(raw_solution)
1230					.inspect_err(|err| {
1231						log!(debug, "unsigned transaction validation failed due to {:?}", err);
1232					})
1233					.map_err(dispatch_error_to_invalid)?;
1234
1235				ValidTransaction::with_tag_prefix("OffchainElection")
1236					// The higher the score.minimal_stake, the better a solution is.
1237					.priority(
1238						T::MinerTxPriority::get()
1239							.saturating_add(raw_solution.score.minimal_stake.saturated_into()),
1240					)
1241					// Used to deduplicate unsigned solutions: each validator should produce one
1242					// solution per round at most, and solutions are not propagate.
1243					.and_provides(raw_solution.round)
1244					// Transaction should stay in the pool for the duration of the unsigned phase.
1245					.longevity(T::UnsignedPhase::get().saturated_into::<u64>())
1246					// We don't propagate this. This can never be validated at a remote node.
1247					.propagate(false)
1248					.build()
1249			} else {
1250				InvalidTransaction::Call.into()
1251			}
1252		}
1253
1254		fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
1255			if let Call::submit_unsigned { raw_solution, .. } = call {
1256				Self::unsigned_pre_dispatch_checks(raw_solution)
1257					.map_err(dispatch_error_to_invalid)
1258					.map_err(Into::into)
1259			} else {
1260				Err(InvalidTransaction::Call.into())
1261			}
1262		}
1263	}
1264
1265	#[pallet::type_value]
1266	pub fn DefaultForRound() -> u32 {
1267		1
1268	}
1269
1270	/// Internal counter for the number of rounds.
1271	///
1272	/// This is useful for de-duplication of transactions submitted to the pool, and general
1273	/// diagnostics of the pallet.
1274	///
1275	/// This is merely incremented once per every time that an upstream `elect` is called.
1276	#[pallet::storage]
1277	pub type Round<T: Config> = StorageValue<_, u32, ValueQuery, DefaultForRound>;
1278
1279	/// Current phase.
1280	#[pallet::storage]
1281	pub type CurrentPhase<T: Config> = StorageValue<_, Phase<BlockNumberFor<T>>, ValueQuery>;
1282
1283	/// Current best solution, signed or unsigned, queued to be returned upon `elect`.
1284	///
1285	/// Always sorted by score.
1286	#[pallet::storage]
1287	pub type QueuedSolution<T: Config> = StorageValue<_, ReadySolutionOf<T::MinerConfig>>;
1288
1289	/// Snapshot data of the round.
1290	///
1291	/// This is created at the beginning of the signed phase and cleared upon calling `elect`.
1292	/// Note: This storage type must only be mutated through [`SnapshotWrapper`].
1293	#[pallet::storage]
1294	pub type Snapshot<T: Config> = StorageValue<_, RoundSnapshot<T::AccountId, VoterOf<T>>>;
1295
1296	/// Desired number of targets to elect for this round.
1297	///
1298	/// Only exists when [`Snapshot`] is present.
1299	/// Note: This storage type must only be mutated through [`SnapshotWrapper`].
1300	#[pallet::storage]
1301	pub type DesiredTargets<T> = StorageValue<_, u32>;
1302
1303	/// The metadata of the [`RoundSnapshot`]
1304	///
1305	/// Only exists when [`Snapshot`] is present.
1306	/// Note: This storage type must only be mutated through [`SnapshotWrapper`].
1307	#[pallet::storage]
1308	pub type SnapshotMetadata<T: Config> = StorageValue<_, SolutionOrSnapshotSize>;
1309
1310	// The following storage items collectively comprise `SignedSubmissions<T>`, and should never be
1311	// accessed independently. Instead, get `Self::signed_submissions()`, modify it as desired, and
1312	// then do `signed_submissions.put()` when you're done with it.
1313
1314	/// The next index to be assigned to an incoming signed submission.
1315	///
1316	/// Every accepted submission is assigned a unique index; that index is bound to that particular
1317	/// submission for the duration of the election. On election finalization, the next index is
1318	/// reset to 0.
1319	///
1320	/// We can't just use `SignedSubmissionIndices.len()`, because that's a bounded set; past its
1321	/// capacity, it will simply saturate. We can't just iterate over `SignedSubmissionsMap`,
1322	/// because iteration is slow. Instead, we store the value here.
1323	#[pallet::storage]
1324	pub type SignedSubmissionNextIndex<T: Config> = StorageValue<_, u32, ValueQuery>;
1325
1326	/// A sorted, bounded vector of `(score, block_number, index)`, where each `index` points to a
1327	/// value in `SignedSubmissions`.
1328	///
1329	/// We never need to process more than a single signed submission at a time. Signed submissions
1330	/// can be quite large, so we're willing to pay the cost of multiple database accesses to access
1331	/// them one at a time instead of reading and decoding all of them at once.
1332	#[pallet::storage]
1333	pub type SignedSubmissionIndices<T: Config> =
1334		StorageValue<_, SubmissionIndicesOf<T>, ValueQuery>;
1335
1336	/// Unchecked, signed solutions.
1337	///
1338	/// Together with `SubmissionIndices`, this stores a bounded set of `SignedSubmissions` while
1339	/// allowing us to keep only a single one in memory at a time.
1340	///
1341	/// Twox note: the key of the map is an auto-incrementing index which users cannot inspect or
1342	/// affect; we shouldn't need a cryptographically secure hasher.
1343	#[pallet::storage]
1344	pub type SignedSubmissionsMap<T: Config> =
1345		StorageMap<_, Twox64Concat, u32, SignedSubmissionOf<T>, OptionQuery>;
1346
1347	// `SignedSubmissions` items end here.
1348
1349	/// The minimum score that each 'untrusted' solution must attain in order to be considered
1350	/// feasible.
1351	///
1352	/// Can be set via `set_minimum_untrusted_score`.
1353	#[pallet::storage]
1354	pub type MinimumUntrustedScore<T: Config> = StorageValue<_, ElectionScore>;
1355
1356	/// The in-code storage version.
1357	///
1358	/// v1: https://github.com/paritytech/substrate/pull/12237/
1359	const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
1360
1361	#[pallet::pallet]
1362	#[pallet::without_storage_info]
1363	#[pallet::storage_version(STORAGE_VERSION)]
1364	pub struct Pallet<T>(_);
1365}
1366
1367/// This wrapper is created for handling the synchronization of [`Snapshot`], [`SnapshotMetadata`]
1368/// and [`DesiredTargets`] storage items.
1369pub struct SnapshotWrapper<T>(core::marker::PhantomData<T>);
1370
1371impl<T: Config> SnapshotWrapper<T> {
1372	/// Kill all snapshot related storage items at the same time.
1373	pub fn kill() {
1374		Snapshot::<T>::kill();
1375		SnapshotMetadata::<T>::kill();
1376		DesiredTargets::<T>::kill();
1377	}
1378	/// Set all snapshot related storage items at the same time.
1379	pub fn set(metadata: SolutionOrSnapshotSize, desired_targets: u32, buffer: &[u8]) {
1380		SnapshotMetadata::<T>::put(metadata);
1381		DesiredTargets::<T>::put(desired_targets);
1382		sp_io::storage::set(&Snapshot::<T>::hashed_key(), &buffer);
1383	}
1384
1385	/// Check if all of the storage items exist at the same time or all of the storage items do not
1386	/// exist.
1387	#[cfg(feature = "try-runtime")]
1388	pub fn is_consistent() -> bool {
1389		let snapshots = [
1390			Snapshot::<T>::exists(),
1391			SnapshotMetadata::<T>::exists(),
1392			DesiredTargets::<T>::exists(),
1393		];
1394
1395		// All should either exist or not exist
1396		snapshots.iter().skip(1).all(|v| snapshots[0] == *v)
1397	}
1398}
1399
1400impl<T: Config> Pallet<T> {
1401	/// Internal counter for the number of rounds.
1402	///
1403	/// This is useful for de-duplication of transactions submitted to the pool, and general
1404	/// diagnostics of the pallet.
1405	///
1406	/// This is merely incremented once per every time that an upstream `elect` is called.
1407	pub fn round() -> u32 {
1408		Round::<T>::get()
1409	}
1410
1411	/// Current phase.
1412	pub fn current_phase() -> Phase<BlockNumberFor<T>> {
1413		CurrentPhase::<T>::get()
1414	}
1415
1416	/// Current best solution, signed or unsigned, queued to be returned upon `elect`.
1417	///
1418	/// Always sorted by score.
1419	pub fn queued_solution() -> Option<ReadySolutionOf<T::MinerConfig>> {
1420		QueuedSolution::<T>::get()
1421	}
1422
1423	/// Snapshot data of the round.
1424	///
1425	/// This is created at the beginning of the signed phase and cleared upon calling `elect`.
1426	/// Note: This storage type must only be mutated through [`SnapshotWrapper`].
1427	pub fn snapshot() -> Option<RoundSnapshot<T::AccountId, VoterOf<T>>> {
1428		Snapshot::<T>::get()
1429	}
1430
1431	/// Desired number of targets to elect for this round.
1432	///
1433	/// Only exists when [`Snapshot`] is present.
1434	/// Note: This storage type must only be mutated through [`SnapshotWrapper`].
1435	pub fn desired_targets() -> Option<u32> {
1436		DesiredTargets::<T>::get()
1437	}
1438
1439	/// The metadata of the [`RoundSnapshot`]
1440	///
1441	/// Only exists when [`Snapshot`] is present.
1442	/// Note: This storage type must only be mutated through [`SnapshotWrapper`].
1443	pub fn snapshot_metadata() -> Option<SolutionOrSnapshotSize> {
1444		SnapshotMetadata::<T>::get()
1445	}
1446
1447	/// The minimum score that each 'untrusted' solution must attain in order to be considered
1448	/// feasible.
1449	///
1450	/// Can be set via `set_minimum_untrusted_score`.
1451	pub fn minimum_untrusted_score() -> Option<ElectionScore> {
1452		MinimumUntrustedScore::<T>::get()
1453	}
1454
1455	/// Internal logic of the offchain worker, to be executed only when the offchain lock is
1456	/// acquired with success.
1457	fn do_synchronized_offchain_worker(now: BlockNumberFor<T>) {
1458		let current_phase = CurrentPhase::<T>::get();
1459		log!(trace, "lock for offchain worker acquired. Phase = {:?}", current_phase);
1460		match current_phase {
1461			Phase::Unsigned((true, opened)) if opened == now => {
1462				// Mine a new solution, cache it, and attempt to submit it
1463				let initial_output = Self::ensure_offchain_repeat_frequency(now).and_then(|_| {
1464					// This is executed at the beginning of each round. Any cache is now invalid.
1465					// Clear it.
1466					unsigned::kill_ocw_solution::<T>();
1467					Self::mine_check_save_submit()
1468				});
1469				log!(debug, "initial offchain thread output: {:?}", initial_output);
1470			},
1471			Phase::Unsigned((true, opened)) if opened < now => {
1472				// Try and resubmit the cached solution, and recompute ONLY if it is not
1473				// feasible.
1474				let resubmit_output = Self::ensure_offchain_repeat_frequency(now)
1475					.and_then(|_| Self::restore_or_compute_then_maybe_submit());
1476				log!(debug, "resubmit offchain thread output: {:?}", resubmit_output);
1477			},
1478			_ => {},
1479		}
1480	}
1481
1482	/// Phase transition helper.
1483	pub(crate) fn phase_transition(to: Phase<BlockNumberFor<T>>) {
1484		log!(info, "Starting phase {:?}, round {}.", to, Round::<T>::get());
1485		Self::deposit_event(Event::PhaseTransitioned {
1486			from: CurrentPhase::<T>::get(),
1487			to,
1488			round: Round::<T>::get(),
1489		});
1490		CurrentPhase::<T>::put(to);
1491	}
1492
1493	/// Parts of [`create_snapshot`] that happen inside of this pallet.
1494	///
1495	/// Extracted for easier weight calculation.
1496	fn create_snapshot_internal(
1497		targets: Vec<T::AccountId>,
1498		voters: Vec<VoterOf<T>>,
1499		desired_targets: u32,
1500	) {
1501		let metadata =
1502			SolutionOrSnapshotSize { voters: voters.len() as u32, targets: targets.len() as u32 };
1503		log!(info, "creating a snapshot with metadata {:?}", metadata);
1504
1505		// instead of using storage APIs, we do a manual encoding into a fixed-size buffer.
1506		// `encoded_size` encodes it without storing it anywhere, this should not cause any
1507		// allocation.
1508		let snapshot = RoundSnapshot::<T::AccountId, VoterOf<T>> { voters, targets };
1509		let size = snapshot.encoded_size();
1510		log!(debug, "snapshot pre-calculated size {:?}", size);
1511		let mut buffer = Vec::with_capacity(size);
1512		snapshot.encode_to(&mut buffer);
1513
1514		// do some checks.
1515		debug_assert_eq!(buffer, snapshot.encode());
1516		// buffer should have not re-allocated since.
1517		debug_assert!(buffer.len() == size && size == buffer.capacity());
1518
1519		SnapshotWrapper::<T>::set(metadata, desired_targets, &buffer);
1520	}
1521
1522	/// Parts of [`create_snapshot`] that happen outside of this pallet.
1523	///
1524	/// Extracted for easier weight calculation.
1525	///
1526	/// Note: this pallet only supports one page of voter and target snapshots.
1527	fn create_snapshot_external(
1528	) -> Result<(Vec<T::AccountId>, Vec<VoterOf<T>>, u32), ElectionError<T>> {
1529		let election_bounds = T::ElectionBounds::get();
1530		let targets = T::DataProvider::electable_targets_stateless(election_bounds.targets)
1531			.and_then(|t| {
1532				election_bounds.ensure_targets_limits(
1533					CountBound(t.len() as u32),
1534					SizeBound(t.encoded_size() as u32),
1535				)?;
1536				Ok(t)
1537			})
1538			.map_err(ElectionError::DataProvider)?;
1539
1540		let voters = T::DataProvider::electing_voters_stateless(election_bounds.voters)
1541			.and_then(|v| {
1542				election_bounds.ensure_voters_limits(
1543					CountBound(v.len() as u32),
1544					SizeBound(v.encoded_size() as u32),
1545				)?;
1546				Ok(v)
1547			})
1548			.map_err(ElectionError::DataProvider)?;
1549
1550		let mut desired_targets = <Pallet<T> as ElectionProvider>::desired_targets_checked()
1551			.map_err(|e| ElectionError::DataProvider(e))?;
1552
1553		// If `desired_targets` > `targets.len()`, cap `desired_targets` to that level and emit a
1554		// warning
1555		let max_desired_targets: u32 = targets.len() as u32;
1556		if desired_targets > max_desired_targets {
1557			log!(
1558				warn,
1559				"desired_targets: {} > targets.len(): {}, capping desired_targets",
1560				desired_targets,
1561				max_desired_targets
1562			);
1563			desired_targets = max_desired_targets;
1564		}
1565
1566		Ok((targets, voters, desired_targets))
1567	}
1568
1569	/// Creates the snapshot. Writes new data to:
1570	///
1571	/// 1. [`SnapshotMetadata`]
1572	/// 2. [`RoundSnapshot`]
1573	/// 3. [`DesiredTargets`]
1574	///
1575	/// Returns `Ok(())` if operation is okay.
1576	///
1577	/// This is a *self-weighing* function, it will register its own extra weight as
1578	/// [`DispatchClass::Mandatory`] with the system pallet.
1579	pub fn create_snapshot() -> Result<(), ElectionError<T>> {
1580		// this is self-weighing itself..
1581		let (targets, voters, desired_targets) = Self::create_snapshot_external()?;
1582
1583		// ..therefore we only measure the weight of this and add it.
1584		let internal_weight =
1585			T::WeightInfo::create_snapshot_internal(voters.len() as u32, targets.len() as u32);
1586		Self::create_snapshot_internal(targets, voters, desired_targets);
1587		Self::register_weight(internal_weight);
1588		Ok(())
1589	}
1590
1591	/// Register some amount of weight directly with the system pallet.
1592	///
1593	/// This is always mandatory weight.
1594	fn register_weight(weight: Weight) {
1595		frame_system::Pallet::<T>::register_extra_weight_unchecked(
1596			weight,
1597			DispatchClass::Mandatory,
1598		);
1599	}
1600
1601	/// Checks the feasibility of a solution.
1602	pub fn feasibility_check(
1603		raw_solution: RawSolution<SolutionOf<T::MinerConfig>>,
1604		compute: ElectionCompute,
1605	) -> Result<ReadySolutionOf<T::MinerConfig>, FeasibilityError> {
1606		let desired_targets =
1607			DesiredTargets::<T>::get().ok_or(FeasibilityError::SnapshotUnavailable)?;
1608
1609		let snapshot = Snapshot::<T>::get().ok_or(FeasibilityError::SnapshotUnavailable)?;
1610		let round = Round::<T>::get();
1611		let minimum_untrusted_score = MinimumUntrustedScore::<T>::get();
1612
1613		Miner::<T::MinerConfig>::feasibility_check(
1614			raw_solution,
1615			compute,
1616			desired_targets,
1617			snapshot,
1618			round,
1619			minimum_untrusted_score,
1620		)
1621	}
1622
1623	/// Perform the tasks to be done after a new `elect` has been triggered:
1624	///
1625	/// 1. Increment round.
1626	/// 2. Change phase to [`Phase::Off`]
1627	/// 3. Clear all snapshot data.
1628	fn rotate_round() {
1629		// Inc round.
1630		Round::<T>::mutate(|r| *r += 1);
1631
1632		// Phase is off now.
1633		Self::phase_transition(Phase::Off);
1634
1635		// Kill snapshot and relevant metadata (everything created by [`SnapshotMetadata::set`]).
1636		SnapshotWrapper::<T>::kill();
1637	}
1638
1639	fn do_elect() -> Result<BoundedSupportsOf<Self>, ElectionError<T>> {
1640		// We have to unconditionally try finalizing the signed phase here. There are only two
1641		// possibilities:
1642		//
1643		// - signed phase was open, in which case this is essential for correct functioning of the
1644		//   system
1645		// - signed phase was complete or not started, in which case finalization is idempotent and
1646		//   inexpensive (1 read of an empty vector).
1647		let _ = Self::finalize_signed_phase();
1648
1649		QueuedSolution::<T>::take()
1650			.ok_or(ElectionError::<T>::NothingQueued)
1651			.or_else(|_| {
1652				log!(warn, "No solution queued, falling back to instant fallback.",);
1653
1654				#[cfg(feature = "runtime-benchmarks")]
1655				Self::asap();
1656
1657				let (voters, targets, desired_targets) = if T::Fallback::bother() {
1658					let RoundSnapshot { voters, targets } = Snapshot::<T>::get().ok_or(
1659						ElectionError::<T>::Feasibility(FeasibilityError::SnapshotUnavailable),
1660					)?;
1661					let desired_targets = DesiredTargets::<T>::get().ok_or(
1662						ElectionError::<T>::Feasibility(FeasibilityError::SnapshotUnavailable),
1663					)?;
1664					(voters, targets, desired_targets)
1665				} else {
1666					(Default::default(), Default::default(), Default::default())
1667				};
1668				T::Fallback::instant_elect(voters, targets, desired_targets)
1669					.map_err(|fe| ElectionError::Fallback(fe))
1670					.and_then(|supports| {
1671						Ok(ReadySolution {
1672							supports,
1673							score: Default::default(),
1674							compute: ElectionCompute::Fallback,
1675						})
1676					})
1677			})
1678			.map(|ReadySolution { compute, score, supports }| {
1679				Self::deposit_event(Event::ElectionFinalized { compute, score });
1680				log!(info, "Finalized election round with compute {:?}.", compute);
1681				supports
1682			})
1683			.map_err(|err| {
1684				Self::deposit_event(Event::ElectionFailed);
1685				log!(warn, "Failed to finalize election round. reason {:?}", err);
1686				err
1687			})
1688	}
1689
1690	/// record the weight of the given `supports`.
1691	fn weigh_supports(supports: &BoundedSupportsOf<Self>) {
1692		let active_voters = supports
1693			.iter()
1694			.map(|(_, x)| x)
1695			.fold(Zero::zero(), |acc, next| acc + next.voters.len() as u32);
1696		let desired_targets = supports.len() as u32;
1697		Self::register_weight(T::WeightInfo::elect_queued(active_voters, desired_targets));
1698	}
1699}
1700
1701#[cfg(feature = "try-runtime")]
1702impl<T: Config> Pallet<T> {
1703	fn do_try_state() -> Result<(), TryRuntimeError> {
1704		Self::try_state_snapshot()?;
1705		Self::try_state_signed_submissions_map()?;
1706		Self::try_state_phase_off()
1707	}
1708
1709	// [`Snapshot`] state check. Invariants:
1710	// - [`DesiredTargets`] exists if and only if [`Snapshot`] is present.
1711	// - [`SnapshotMetadata`] exist if and only if [`Snapshot`] is present.
1712	fn try_state_snapshot() -> Result<(), TryRuntimeError> {
1713		if SnapshotWrapper::<T>::is_consistent() {
1714			Ok(())
1715		} else {
1716			Err("If snapshot exists, metadata and desired targets should be set too. Otherwise, none should be set.".into())
1717		}
1718	}
1719
1720	// [`SignedSubmissionsMap`] state check. Invariants:
1721	// - All [`SignedSubmissionIndices`] are present in [`SignedSubmissionsMap`], and no more;
1722	// - [`SignedSubmissionNextIndex`] is not present in [`SignedSubmissionsMap`];
1723	// - [`SignedSubmissionIndices`] is sorted by election score.
1724	fn try_state_signed_submissions_map() -> Result<(), TryRuntimeError> {
1725		let mut last_score: ElectionScore = Default::default();
1726		let indices = SignedSubmissionIndices::<T>::get();
1727
1728		for (i, indice) in indices.iter().enumerate() {
1729			let submission = SignedSubmissionsMap::<T>::get(indice.2);
1730			if submission.is_none() {
1731				return Err(
1732					"All signed submissions indices must be part of the submissions map".into()
1733				);
1734			}
1735
1736			if i == 0 {
1737				last_score = indice.0
1738			} else {
1739				if last_score.strict_better(indice.0) {
1740					return Err(
1741						"Signed submission indices vector must be ordered by election score".into(),
1742					);
1743				}
1744				last_score = indice.0;
1745			}
1746		}
1747
1748		if SignedSubmissionsMap::<T>::iter().nth(indices.len()).is_some() {
1749			return Err(
1750				"Signed submissions map length should be the same as the indices vec length".into(),
1751			);
1752		}
1753
1754		match SignedSubmissionNextIndex::<T>::get() {
1755			0 => Ok(()),
1756			next => {
1757				if SignedSubmissionsMap::<T>::get(next).is_some() {
1758					return Err(
1759						"The next submissions index should not be in the submissions maps already"
1760							.into(),
1761					);
1762				} else {
1763					Ok(())
1764				}
1765			},
1766		}
1767	}
1768
1769	// [`Phase::Off`] state check. Invariants:
1770	// - If phase is `Phase::Off`, [`Snapshot`] must be none.
1771	fn try_state_phase_off() -> Result<(), TryRuntimeError> {
1772		match CurrentPhase::<T>::get().is_off() {
1773			false => Ok(()),
1774			true => {
1775				if Snapshot::<T>::get().is_some() {
1776					Err("Snapshot must be none when in Phase::Off".into())
1777				} else {
1778					Ok(())
1779				}
1780			},
1781		}
1782	}
1783}
1784
1785impl<T: Config> ElectionProvider for Pallet<T> {
1786	type AccountId = T::AccountId;
1787	type BlockNumber = BlockNumberFor<T>;
1788	type Error = ElectionError<T>;
1789	type MaxWinnersPerPage = T::MaxWinners;
1790	type MaxBackersPerWinner = T::MaxBackersPerWinner;
1791	type MaxBackersPerWinnerFinal = T::MaxBackersPerWinner;
1792	type Pages = sp_core::ConstU32<1>;
1793	type DataProvider = T::DataProvider;
1794
1795	fn elect(page: PageIndex) -> Result<BoundedSupportsOf<Self>, Self::Error> {
1796		// Note: this pallet **MUST** only by used in the single-page mode.
1797		ensure!(page == SINGLE_PAGE, ElectionError::<T>::MultiPageNotSupported);
1798
1799		let res = match Self::do_elect() {
1800			Ok(bounded_supports) => {
1801				// All went okay, record the weight, put sign to be Off, clean snapshot, etc.
1802				Self::weigh_supports(&bounded_supports);
1803				Self::rotate_round();
1804				Ok(bounded_supports)
1805			},
1806			Err(why) => {
1807				log!(error, "Entering emergency mode: {:?}", why);
1808				Self::phase_transition(Phase::Emergency);
1809				Err(why)
1810			},
1811		};
1812
1813		log!(info, "ElectionProvider::elect({}) => {:?}", page, res.as_ref().map(|s| s.len()));
1814		res
1815	}
1816
1817	fn duration() -> Self::BlockNumber {
1818		let signed: BlockNumberFor<T> = T::SignedPhase::get().saturated_into();
1819		let unsigned: BlockNumberFor<T> = T::UnsignedPhase::get().saturated_into();
1820		signed + unsigned
1821	}
1822
1823	fn start() -> Result<(), Self::Error> {
1824		log!(
1825			warn,
1826			"we received signal, but this pallet works in the basis of legacy pull based election"
1827		);
1828		Ok(())
1829	}
1830
1831	fn status() -> Result<Option<Weight>, ()> {
1832		let has_queued = QueuedSolution::<T>::exists();
1833		let phase = CurrentPhase::<T>::get();
1834		match (phase, has_queued) {
1835			// This pallet is not advanced enough to report any weight, ergo `Default::default()`.
1836			(Phase::Unsigned(_), true) => Ok(Some(Default::default())),
1837			(Phase::Off, _) => Err(()),
1838			_ => Ok(None),
1839		}
1840	}
1841
1842	#[cfg(feature = "runtime-benchmarks")]
1843	fn asap() {
1844		// prepare our snapshot so we can "hopefully" run a fallback.
1845		if !Snapshot::<T>::exists() {
1846			Self::create_snapshot()
1847				.inspect_err(|e| {
1848					crate::log!(error, "failed to create snapshot while asap-preparing: {:?}", e)
1849				})
1850				.unwrap()
1851		}
1852	}
1853}
1854
1855/// convert a DispatchError to a custom InvalidTransaction with the inner code being the error
1856/// number.
1857pub fn dispatch_error_to_invalid(error: DispatchError) -> InvalidTransaction {
1858	let error_number = match error {
1859		DispatchError::Module(ModuleError { error, .. }) => error[0],
1860		_ => 0,
1861	};
1862	InvalidTransaction::Custom(error_number)
1863}
1864
1865#[cfg(test)]
1866mod feasibility_check {
1867	//! All of the tests here should be dedicated to only testing the feasibility check and nothing
1868	//! more. The best way to audit and review these tests is to try and come up with a solution
1869	//! that is invalid, but gets through the system as valid.
1870
1871	use super::*;
1872	use crate::mock::{
1873		raw_solution, roll_to, EpochLength, ExtBuilder, MultiPhase, Runtime, SignedPhase,
1874		TargetIndex, UnsignedPhase, VoterIndex,
1875	};
1876	use frame_support::{assert_noop, assert_ok};
1877
1878	const COMPUTE: ElectionCompute = ElectionCompute::OnChain;
1879
1880	#[test]
1881	fn snapshot_is_there() {
1882		ExtBuilder::default().build_and_execute(|| {
1883			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
1884			assert!(CurrentPhase::<Runtime>::get().is_signed());
1885			let solution = raw_solution();
1886
1887			// kill `Snapshot`, `SnapshotMetadata` and `DesiredTargets` for the storage state to
1888			// be consistent, by using the `SnapshotWrapper` for the try_state checks to pass.
1889			SnapshotWrapper::<Runtime>::kill();
1890
1891			assert_noop!(
1892				MultiPhase::feasibility_check(solution, COMPUTE),
1893				FeasibilityError::SnapshotUnavailable
1894			);
1895		})
1896	}
1897
1898	#[test]
1899	fn round() {
1900		ExtBuilder::default().build_and_execute(|| {
1901			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
1902			assert!(CurrentPhase::<Runtime>::get().is_signed());
1903
1904			let mut solution = raw_solution();
1905			solution.round += 1;
1906			assert_noop!(
1907				MultiPhase::feasibility_check(solution, COMPUTE),
1908				FeasibilityError::InvalidRound
1909			);
1910		})
1911	}
1912
1913	#[test]
1914	fn desired_targets_gets_capped() {
1915		ExtBuilder::default().desired_targets(8).build_and_execute(|| {
1916			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
1917			assert!(CurrentPhase::<Runtime>::get().is_signed());
1918
1919			let raw = raw_solution();
1920
1921			assert_eq!(raw.solution.unique_targets().len(), 4);
1922			// desired_targets is capped to the number of targets which is 4
1923			assert_eq!(DesiredTargets::<Runtime>::get().unwrap(), 4);
1924
1925			// It should succeed
1926			assert_ok!(MultiPhase::feasibility_check(raw, COMPUTE));
1927		})
1928	}
1929
1930	#[test]
1931	fn less_than_desired_targets_fails() {
1932		ExtBuilder::default().desired_targets(8).build_and_execute(|| {
1933			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
1934			assert!(CurrentPhase::<Runtime>::get().is_signed());
1935
1936			let mut raw = raw_solution();
1937
1938			assert_eq!(raw.solution.unique_targets().len(), 4);
1939			// desired_targets is capped to the number of targets which is 4
1940			assert_eq!(DesiredTargets::<Runtime>::get().unwrap(), 4);
1941
1942			// Force the number of winners to be bigger to fail
1943			raw.solution.votes1[0].1 = 4;
1944
1945			// It should succeed
1946			assert_noop!(
1947				MultiPhase::feasibility_check(raw, COMPUTE),
1948				FeasibilityError::WrongWinnerCount,
1949			);
1950		})
1951	}
1952
1953	#[test]
1954	fn winner_indices() {
1955		ExtBuilder::default().desired_targets(2).build_and_execute(|| {
1956			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
1957			assert!(CurrentPhase::<Runtime>::get().is_signed());
1958
1959			let mut raw = raw_solution();
1960			assert_eq!(Snapshot::<Runtime>::get().unwrap().targets.len(), 4);
1961			// ----------------------------------------------------^^ valid range is [0..3].
1962
1963			// Swap all votes from 3 to 4. This will ensure that the number of unique winners will
1964			// still be 4, but one of the indices will be gibberish. Requirement is to make sure 3 a
1965			// winner, which we don't do here.
1966			raw.solution
1967				.votes1
1968				.iter_mut()
1969				.filter(|(_, t)| *t == TargetIndex::from(3u16))
1970				.for_each(|(_, t)| *t += 1);
1971			raw.solution.votes2.iter_mut().for_each(|(_, [(t0, _)], t1)| {
1972				if *t0 == TargetIndex::from(3u16) {
1973					*t0 += 1
1974				};
1975				if *t1 == TargetIndex::from(3u16) {
1976					*t1 += 1
1977				};
1978			});
1979			assert_noop!(
1980				MultiPhase::feasibility_check(raw, COMPUTE),
1981				FeasibilityError::NposElection(sp_npos_elections::Error::SolutionInvalidIndex)
1982			);
1983		})
1984	}
1985
1986	#[test]
1987	fn voter_indices() {
1988		// Should be caught in `solution.into_assignment`.
1989		ExtBuilder::default().desired_targets(2).build_and_execute(|| {
1990			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
1991			assert!(CurrentPhase::<Runtime>::get().is_signed());
1992
1993			let mut solution = raw_solution();
1994			assert_eq!(Snapshot::<Runtime>::get().unwrap().voters.len(), 8);
1995			// ----------------------------------------------------^^ valid range is [0..7].
1996
1997			// Check that there is an index 7 in votes1, and flip to 8.
1998			assert!(
1999				solution
2000					.solution
2001					.votes1
2002					.iter_mut()
2003					.filter(|(v, _)| *v == VoterIndex::from(7u32))
2004					.map(|(v, _)| *v = 8)
2005					.count() > 0
2006			);
2007			assert_noop!(
2008				MultiPhase::feasibility_check(solution, COMPUTE),
2009				FeasibilityError::NposElection(sp_npos_elections::Error::SolutionInvalidIndex),
2010			);
2011		})
2012	}
2013
2014	#[test]
2015	fn voter_votes() {
2016		ExtBuilder::default().desired_targets(2).build_and_execute(|| {
2017			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
2018			assert!(CurrentPhase::<Runtime>::get().is_signed());
2019
2020			let mut solution = raw_solution();
2021			assert_eq!(Snapshot::<Runtime>::get().unwrap().voters.len(), 8);
2022			// ----------------------------------------------------^^ valid range is [0..7].
2023
2024			// First, check that voter at index 7 (40) actually voted for 3 (40) -- this is self
2025			// vote. Then, change the vote to 2 (30).
2026			assert_eq!(
2027				solution
2028					.solution
2029					.votes1
2030					.iter_mut()
2031					.filter(|(v, t)| *v == 7 && *t == 3)
2032					.map(|(_, t)| *t = 2)
2033					.count(),
2034				1,
2035			);
2036			assert_noop!(
2037				MultiPhase::feasibility_check(solution, COMPUTE),
2038				FeasibilityError::InvalidVote,
2039			);
2040		})
2041	}
2042
2043	#[test]
2044	fn score() {
2045		ExtBuilder::default().desired_targets(2).build_and_execute(|| {
2046			roll_to(<EpochLength>::get() - <SignedPhase>::get() - <UnsignedPhase>::get());
2047			assert!(CurrentPhase::<Runtime>::get().is_signed());
2048
2049			let mut solution = raw_solution();
2050			assert_eq!(Snapshot::<Runtime>::get().unwrap().voters.len(), 8);
2051
2052			// Simply faff with the score.
2053			solution.score.minimal_stake += 1;
2054
2055			assert_noop!(
2056				MultiPhase::feasibility_check(solution, COMPUTE),
2057				FeasibilityError::InvalidScore,
2058			);
2059		})
2060	}
2061}
2062
2063#[cfg(test)]
2064mod tests {
2065	use super::*;
2066	use crate::{
2067		mock::{
2068			multi_phase_events, raw_solution, roll_to, roll_to_signed, roll_to_unsigned,
2069			ElectionsBounds, ExtBuilder, MockWeightInfo, MockedWeightInfo, MultiPhase, Runtime,
2070			RuntimeOrigin, SignedMaxSubmissions, System, Voters,
2071		},
2072		Phase,
2073	};
2074	use frame_election_provider_support::bounds::ElectionBoundsBuilder;
2075	use frame_support::{assert_noop, assert_ok};
2076	use sp_npos_elections::{BalancingConfig, Support};
2077
2078	#[test]
2079	fn phase_rotation_works() {
2080		ExtBuilder::default().build_and_execute(|| {
2081			// 0 ------- 15 ------- 25 ------- 30 ------- ------- 45 ------- 55 ------- 60
2082			//           |           |          |                 |           |          |
2083			//         Signed      Unsigned   Elect             Signed     Unsigned    Elect
2084
2085			assert_eq!(System::block_number(), 0);
2086			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Off);
2087			assert_eq!(Round::<Runtime>::get(), 1);
2088
2089			roll_to(4);
2090			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Off);
2091			assert!(Snapshot::<Runtime>::get().is_none());
2092			assert_eq!(Round::<Runtime>::get(), 1);
2093
2094			roll_to_signed();
2095			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Signed);
2096			assert_eq!(
2097				multi_phase_events(),
2098				vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }]
2099			);
2100			assert!(Snapshot::<Runtime>::get().is_some());
2101			assert_eq!(Round::<Runtime>::get(), 1);
2102
2103			roll_to(24);
2104			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Signed);
2105			assert!(Snapshot::<Runtime>::get().is_some());
2106			assert_eq!(Round::<Runtime>::get(), 1);
2107
2108			roll_to_unsigned();
2109			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Unsigned((true, 25)));
2110			assert_eq!(
2111				multi_phase_events(),
2112				vec![
2113					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2114					Event::PhaseTransitioned {
2115						from: Phase::Signed,
2116						to: Phase::Unsigned((true, 25)),
2117						round: 1
2118					},
2119				],
2120			);
2121			assert!(Snapshot::<Runtime>::get().is_some());
2122
2123			roll_to(29);
2124			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Unsigned((true, 25)));
2125			assert!(Snapshot::<Runtime>::get().is_some());
2126
2127			roll_to(30);
2128			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Unsigned((true, 25)));
2129			assert!(Snapshot::<Runtime>::get().is_some());
2130
2131			// We close when upstream tells us to elect.
2132			roll_to(32);
2133			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Unsigned((true, 25)));
2134			assert!(Snapshot::<Runtime>::get().is_some());
2135
2136			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2137
2138			assert!(CurrentPhase::<Runtime>::get().is_off());
2139			assert!(Snapshot::<Runtime>::get().is_none());
2140			assert_eq!(Round::<Runtime>::get(), 2);
2141
2142			roll_to(44);
2143			assert!(CurrentPhase::<Runtime>::get().is_off());
2144
2145			roll_to_signed();
2146			assert!(CurrentPhase::<Runtime>::get().is_signed());
2147
2148			roll_to(55);
2149			assert!(CurrentPhase::<Runtime>::get().is_unsigned_open_at(55));
2150
2151			assert_eq!(
2152				multi_phase_events(),
2153				vec![
2154					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2155					Event::PhaseTransitioned {
2156						from: Phase::Signed,
2157						to: Phase::Unsigned((true, 25)),
2158						round: 1
2159					},
2160					Event::ElectionFinalized {
2161						compute: ElectionCompute::Fallback,
2162						score: ElectionScore {
2163							minimal_stake: 0,
2164							sum_stake: 0,
2165							sum_stake_squared: 0
2166						}
2167					},
2168					Event::PhaseTransitioned {
2169						from: Phase::Unsigned((true, 25)),
2170						to: Phase::Off,
2171						round: 2
2172					},
2173					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 2 },
2174					Event::PhaseTransitioned {
2175						from: Phase::Signed,
2176						to: Phase::Unsigned((true, 55)),
2177						round: 2
2178					},
2179				]
2180			);
2181		})
2182	}
2183
2184	#[test]
2185	fn signed_phase_void() {
2186		ExtBuilder::default().phases(0, 10).build_and_execute(|| {
2187			roll_to(15);
2188			assert!(CurrentPhase::<Runtime>::get().is_off());
2189
2190			roll_to(19);
2191			assert!(CurrentPhase::<Runtime>::get().is_off());
2192
2193			roll_to(20);
2194			assert!(CurrentPhase::<Runtime>::get().is_unsigned_open_at(20));
2195			assert!(Snapshot::<Runtime>::get().is_some());
2196
2197			roll_to(30);
2198			assert!(CurrentPhase::<Runtime>::get().is_unsigned_open_at(20));
2199
2200			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2201
2202			assert!(CurrentPhase::<Runtime>::get().is_off());
2203			assert!(Snapshot::<Runtime>::get().is_none());
2204
2205			assert_eq!(
2206				multi_phase_events(),
2207				vec![
2208					Event::PhaseTransitioned {
2209						from: Phase::Off,
2210						to: Phase::Unsigned((true, 20)),
2211						round: 1
2212					},
2213					Event::ElectionFinalized {
2214						compute: ElectionCompute::Fallback,
2215						score: ElectionScore {
2216							minimal_stake: 0,
2217							sum_stake: 0,
2218							sum_stake_squared: 0
2219						}
2220					},
2221					Event::PhaseTransitioned {
2222						from: Phase::Unsigned((true, 20)),
2223						to: Phase::Off,
2224						round: 2
2225					},
2226				]
2227			);
2228		});
2229	}
2230
2231	#[test]
2232	fn unsigned_phase_void() {
2233		ExtBuilder::default().phases(10, 0).build_and_execute(|| {
2234			roll_to(15);
2235			assert!(CurrentPhase::<Runtime>::get().is_off());
2236
2237			roll_to(19);
2238			assert!(CurrentPhase::<Runtime>::get().is_off());
2239
2240			roll_to_signed();
2241			assert!(CurrentPhase::<Runtime>::get().is_signed());
2242			assert!(Snapshot::<Runtime>::get().is_some());
2243
2244			roll_to(30);
2245			assert!(CurrentPhase::<Runtime>::get().is_signed());
2246
2247			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2248
2249			assert!(CurrentPhase::<Runtime>::get().is_off());
2250			assert!(Snapshot::<Runtime>::get().is_none());
2251
2252			assert_eq!(
2253				multi_phase_events(),
2254				vec![
2255					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2256					Event::ElectionFinalized {
2257						compute: ElectionCompute::Fallback,
2258						score: ElectionScore {
2259							minimal_stake: 0,
2260							sum_stake: 0,
2261							sum_stake_squared: 0
2262						}
2263					},
2264					Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 },
2265				]
2266			)
2267		});
2268	}
2269
2270	#[test]
2271	fn early_termination() {
2272		// An early termination in the signed phase, with no queued solution.
2273		ExtBuilder::default().build_and_execute(|| {
2274			// Signed phase started at block 15 and will end at 25.
2275
2276			roll_to_signed();
2277			assert_eq!(
2278				multi_phase_events(),
2279				vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }]
2280			);
2281			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Signed);
2282			assert_eq!(Round::<Runtime>::get(), 1);
2283
2284			// An unexpected call to elect.
2285			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2286
2287			// We surely can't have any feasible solutions. This will cause an on-chain election.
2288			assert_eq!(
2289				multi_phase_events(),
2290				vec![
2291					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2292					Event::ElectionFinalized {
2293						compute: ElectionCompute::Fallback,
2294						score: Default::default()
2295					},
2296					Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 },
2297				],
2298			);
2299			// All storage items must be cleared.
2300			assert_eq!(Round::<Runtime>::get(), 2);
2301			assert!(Snapshot::<Runtime>::get().is_none());
2302			assert!(SnapshotMetadata::<Runtime>::get().is_none());
2303			assert!(DesiredTargets::<Runtime>::get().is_none());
2304			assert!(QueuedSolution::<Runtime>::get().is_none());
2305			assert!(MultiPhase::signed_submissions().is_empty());
2306		})
2307	}
2308
2309	#[test]
2310	fn early_termination_with_submissions() {
2311		// an early termination in the signed phase, with no queued solution.
2312		ExtBuilder::default().build_and_execute(|| {
2313			// signed phase started at block 15 and will end at 25.
2314
2315			roll_to_signed();
2316			assert_eq!(
2317				multi_phase_events(),
2318				vec![Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 }]
2319			);
2320			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Signed);
2321			assert_eq!(Round::<Runtime>::get(), 1);
2322
2323			// fill the queue with signed submissions
2324			for s in 0..SignedMaxSubmissions::get() {
2325				let solution = RawSolution {
2326					score: ElectionScore { minimal_stake: (5 + s).into(), ..Default::default() },
2327					..Default::default()
2328				};
2329				assert_ok!(MultiPhase::submit(
2330					crate::mock::RuntimeOrigin::signed(99),
2331					Box::new(solution)
2332				));
2333			}
2334
2335			// an unexpected call to elect.
2336			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2337
2338			// all storage items must be cleared.
2339			assert_eq!(Round::<Runtime>::get(), 2);
2340			assert!(Snapshot::<Runtime>::get().is_none());
2341			assert!(SnapshotMetadata::<Runtime>::get().is_none());
2342			assert!(DesiredTargets::<Runtime>::get().is_none());
2343			assert!(QueuedSolution::<Runtime>::get().is_none());
2344			assert!(MultiPhase::signed_submissions().is_empty());
2345
2346			assert_eq!(
2347				multi_phase_events(),
2348				vec![
2349					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2350					Event::SolutionStored {
2351						compute: ElectionCompute::Signed,
2352						origin: Some(99),
2353						prev_ejected: false
2354					},
2355					Event::SolutionStored {
2356						compute: ElectionCompute::Signed,
2357						origin: Some(99),
2358						prev_ejected: false
2359					},
2360					Event::SolutionStored {
2361						compute: ElectionCompute::Signed,
2362						origin: Some(99),
2363						prev_ejected: false
2364					},
2365					Event::SolutionStored {
2366						compute: ElectionCompute::Signed,
2367						origin: Some(99),
2368						prev_ejected: false
2369					},
2370					Event::SolutionStored {
2371						compute: ElectionCompute::Signed,
2372						origin: Some(99),
2373						prev_ejected: false
2374					},
2375					Event::Slashed { account: 99, value: 5 },
2376					Event::Slashed { account: 99, value: 5 },
2377					Event::Slashed { account: 99, value: 5 },
2378					Event::Slashed { account: 99, value: 5 },
2379					Event::Slashed { account: 99, value: 5 },
2380					Event::ElectionFinalized {
2381						compute: ElectionCompute::Fallback,
2382						score: ElectionScore {
2383							minimal_stake: 0,
2384							sum_stake: 0,
2385							sum_stake_squared: 0
2386						}
2387					},
2388					Event::PhaseTransitioned { from: Phase::Signed, to: Phase::Off, round: 2 },
2389				]
2390			);
2391		})
2392	}
2393
2394	#[test]
2395	fn check_events_with_compute_signed() {
2396		ExtBuilder::default().build_and_execute(|| {
2397			roll_to_signed();
2398			assert!(CurrentPhase::<Runtime>::get().is_signed());
2399
2400			let solution = raw_solution();
2401			assert_ok!(MultiPhase::submit(
2402				crate::mock::RuntimeOrigin::signed(99),
2403				Box::new(solution)
2404			));
2405
2406			roll_to(30);
2407			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2408
2409			assert_eq!(
2410				multi_phase_events(),
2411				vec![
2412					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2413					Event::SolutionStored {
2414						compute: ElectionCompute::Signed,
2415						origin: Some(99),
2416						prev_ejected: false
2417					},
2418					Event::Rewarded { account: 99, value: 7 },
2419					Event::PhaseTransitioned {
2420						from: Phase::Signed,
2421						to: Phase::Unsigned((true, 25)),
2422						round: 1
2423					},
2424					Event::ElectionFinalized {
2425						compute: ElectionCompute::Signed,
2426						score: ElectionScore {
2427							minimal_stake: 40,
2428							sum_stake: 100,
2429							sum_stake_squared: 5200
2430						}
2431					},
2432					Event::PhaseTransitioned {
2433						from: Phase::Unsigned((true, 25)),
2434						to: Phase::Off,
2435						round: 2
2436					},
2437				],
2438			);
2439		})
2440	}
2441
2442	#[test]
2443	fn check_events_with_compute_unsigned() {
2444		ExtBuilder::default().build_and_execute(|| {
2445			roll_to_unsigned();
2446			assert!(CurrentPhase::<Runtime>::get().is_unsigned());
2447
2448			// ensure we have snapshots in place.
2449			assert!(Snapshot::<Runtime>::get().is_some());
2450			assert_eq!(DesiredTargets::<Runtime>::get().unwrap(), 2);
2451
2452			// mine seq_phragmen solution with 2 iters.
2453			let (solution, witness, _) = MultiPhase::mine_solution().unwrap();
2454
2455			// ensure this solution is valid.
2456			assert!(QueuedSolution::<Runtime>::get().is_none());
2457			assert_ok!(MultiPhase::submit_unsigned(
2458				crate::mock::RuntimeOrigin::none(),
2459				Box::new(solution),
2460				witness
2461			));
2462			assert!(QueuedSolution::<Runtime>::get().is_some());
2463
2464			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2465
2466			assert_eq!(
2467				multi_phase_events(),
2468				vec![
2469					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2470					Event::PhaseTransitioned {
2471						from: Phase::Signed,
2472						to: Phase::Unsigned((true, 25)),
2473						round: 1
2474					},
2475					Event::SolutionStored {
2476						compute: ElectionCompute::Unsigned,
2477						origin: None,
2478						prev_ejected: false
2479					},
2480					Event::ElectionFinalized {
2481						compute: ElectionCompute::Unsigned,
2482						score: ElectionScore {
2483							minimal_stake: 40,
2484							sum_stake: 100,
2485							sum_stake_squared: 5200
2486						}
2487					},
2488					Event::PhaseTransitioned {
2489						from: Phase::Unsigned((true, 25)),
2490						to: Phase::Off,
2491						round: 2
2492					},
2493				],
2494			);
2495		})
2496	}
2497
2498	#[test]
2499	fn try_elect_multi_page_fails() {
2500		let prepare_election = || {
2501			roll_to_signed();
2502			assert!(Snapshot::<Runtime>::get().is_some());
2503
2504			// submit solution and assert it is queued and ready for elect to be called.
2505			let (solution, _, _) = MultiPhase::mine_solution().unwrap();
2506			assert_ok!(MultiPhase::submit(
2507				crate::mock::RuntimeOrigin::signed(99),
2508				Box::new(solution),
2509			));
2510			roll_to(30);
2511			assert!(QueuedSolution::<Runtime>::get().is_some());
2512		};
2513
2514		ExtBuilder::default().onchain_fallback(false).build_and_execute(|| {
2515			prepare_election();
2516			// single page elect call works as expected.
2517			assert_ok!(MultiPhase::elect(SINGLE_PAGE));
2518		});
2519
2520		ExtBuilder::default().onchain_fallback(false).build_and_execute(|| {
2521			prepare_election();
2522			// multi page calls will fail with multi-page not supported error.
2523			assert_noop!(MultiPhase::elect(SINGLE_PAGE + 1), ElectionError::MultiPageNotSupported);
2524		})
2525	}
2526
2527	#[test]
2528	fn fallback_strategy_works() {
2529		ExtBuilder::default().onchain_fallback(true).build_and_execute(|| {
2530			roll_to_unsigned();
2531			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Unsigned((true, 25)));
2532
2533			// Zilch solutions thus far, but we get a result.
2534			assert!(QueuedSolution::<Runtime>::get().is_none());
2535			let supports = MultiPhase::elect(SINGLE_PAGE).unwrap();
2536
2537			let expected_supports = vec![
2538				(30, Support { total: 40, voters: vec![(2, 5), (4, 5), (30, 30)] }),
2539				(40, Support { total: 60, voters: vec![(2, 5), (3, 10), (4, 5), (40, 40)] }),
2540			]
2541			.try_into()
2542			.unwrap();
2543
2544			assert_eq!(supports, expected_supports);
2545
2546			assert_eq!(
2547				multi_phase_events(),
2548				vec![
2549					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2550					Event::PhaseTransitioned {
2551						from: Phase::Signed,
2552						to: Phase::Unsigned((true, 25)),
2553						round: 1
2554					},
2555					Event::ElectionFinalized {
2556						compute: ElectionCompute::Fallback,
2557						score: ElectionScore {
2558							minimal_stake: 0,
2559							sum_stake: 0,
2560							sum_stake_squared: 0
2561						}
2562					},
2563					Event::PhaseTransitioned {
2564						from: Phase::Unsigned((true, 25)),
2565						to: Phase::Off,
2566						round: 2
2567					},
2568				]
2569			);
2570		});
2571
2572		ExtBuilder::default().onchain_fallback(false).build_and_execute(|| {
2573			roll_to_unsigned();
2574			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Unsigned((true, 25)));
2575
2576			// Zilch solutions thus far.
2577			assert!(QueuedSolution::<Runtime>::get().is_none());
2578			assert_eq!(
2579				MultiPhase::elect(SINGLE_PAGE).unwrap_err(),
2580				ElectionError::Fallback("NoFallback.")
2581			);
2582			// phase is now emergency.
2583			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Emergency);
2584			// snapshot is still there until election finalizes.
2585			assert!(Snapshot::<Runtime>::get().is_some());
2586
2587			assert_eq!(
2588				multi_phase_events(),
2589				vec![
2590					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2591					Event::PhaseTransitioned {
2592						from: Phase::Signed,
2593						to: Phase::Unsigned((true, 25)),
2594						round: 1
2595					},
2596					Event::ElectionFailed,
2597					Event::PhaseTransitioned {
2598						from: Phase::Unsigned((true, 25)),
2599						to: Phase::Emergency,
2600						round: 1
2601					},
2602				]
2603			);
2604		})
2605	}
2606
2607	#[test]
2608	fn governance_fallback_works() {
2609		ExtBuilder::default().onchain_fallback(false).build_and_execute(|| {
2610			roll_to_unsigned();
2611			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Unsigned((true, 25)));
2612
2613			// Zilch solutions thus far.
2614			assert!(QueuedSolution::<Runtime>::get().is_none());
2615			assert_eq!(
2616				MultiPhase::elect(SINGLE_PAGE).unwrap_err(),
2617				ElectionError::Fallback("NoFallback.")
2618			);
2619
2620			// phase is now emergency.
2621			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Emergency);
2622			assert!(QueuedSolution::<Runtime>::get().is_none());
2623			assert!(Snapshot::<Runtime>::get().is_some());
2624
2625			// no single account can trigger this
2626			assert_noop!(
2627				MultiPhase::governance_fallback(RuntimeOrigin::signed(99)),
2628				DispatchError::BadOrigin
2629			);
2630
2631			// only root can
2632			assert_ok!(MultiPhase::governance_fallback(RuntimeOrigin::root()));
2633			// something is queued now
2634			assert!(QueuedSolution::<Runtime>::get().is_some());
2635			// next election call with fix everything.;
2636			assert!(MultiPhase::elect(SINGLE_PAGE).is_ok());
2637			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Off);
2638
2639			assert_eq!(
2640				multi_phase_events(),
2641				vec![
2642					Event::PhaseTransitioned { from: Phase::Off, to: Phase::Signed, round: 1 },
2643					Event::PhaseTransitioned {
2644						from: Phase::Signed,
2645						to: Phase::Unsigned((true, 25)),
2646						round: 1
2647					},
2648					Event::ElectionFailed,
2649					Event::PhaseTransitioned {
2650						from: Phase::Unsigned((true, 25)),
2651						to: Phase::Emergency,
2652						round: 1
2653					},
2654					Event::SolutionStored {
2655						compute: ElectionCompute::Fallback,
2656						origin: None,
2657						prev_ejected: false
2658					},
2659					Event::ElectionFinalized {
2660						compute: ElectionCompute::Fallback,
2661						score: Default::default()
2662					},
2663					Event::PhaseTransitioned { from: Phase::Emergency, to: Phase::Off, round: 2 },
2664				]
2665			);
2666		})
2667	}
2668
2669	#[test]
2670	fn snapshot_too_big_truncate() {
2671		// but if there are too many voters, we simply truncate them.
2672		ExtBuilder::default().build_and_execute(|| {
2673			// we have 8 voters in total.
2674			assert_eq!(Voters::get().len(), 8);
2675			// but we want to take 2.
2676			let new_bounds = ElectionBoundsBuilder::default().voters_count(2.into()).build();
2677			ElectionsBounds::set(new_bounds);
2678
2679			// Signed phase opens just fine.
2680			roll_to_signed();
2681			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Signed);
2682
2683			assert_eq!(
2684				SnapshotMetadata::<Runtime>::get().unwrap(),
2685				SolutionOrSnapshotSize { voters: 2, targets: 4 }
2686			);
2687		})
2688	}
2689
2690	#[test]
2691	fn untrusted_score_verification_is_respected() {
2692		ExtBuilder::default().build_and_execute(|| {
2693			roll_to_signed();
2694			assert_eq!(CurrentPhase::<Runtime>::get(), Phase::Signed);
2695
2696			// set the solution balancing to get the desired score.
2697			crate::mock::Balancing::set(Some(BalancingConfig { iterations: 2, tolerance: 0 }));
2698
2699			let (solution, _, _) = MultiPhase::mine_solution().unwrap();
2700			// Default solution's score.
2701			assert!(matches!(solution.score, ElectionScore { minimal_stake: 50, .. }));
2702
2703			MinimumUntrustedScore::<Runtime>::put(ElectionScore {
2704				minimal_stake: 49,
2705				..Default::default()
2706			});
2707			assert_ok!(MultiPhase::feasibility_check(solution.clone(), ElectionCompute::Signed));
2708
2709			MinimumUntrustedScore::<Runtime>::put(ElectionScore {
2710				minimal_stake: 51,
2711				..Default::default()
2712			});
2713			assert_noop!(
2714				MultiPhase::feasibility_check(solution, ElectionCompute::Signed),
2715				FeasibilityError::UntrustedScoreTooLow,
2716			);
2717		})
2718	}
2719
2720	#[test]
2721	fn number_of_voters_allowed_2sec_block() {
2722		// Just a rough estimate with the substrate weights.
2723		assert_eq!(MockWeightInfo::get(), MockedWeightInfo::Real);
2724
2725		let all_voters: u32 = 10_000;
2726		let all_targets: u32 = 5_000;
2727		let desired: u32 = 1_000;
2728		let weight_with = |active| {
2729			<Runtime as Config>::WeightInfo::submit_unsigned(
2730				all_voters,
2731				all_targets,
2732				active,
2733				desired,
2734			)
2735		};
2736
2737		let mut active = 1;
2738		while weight_with(active)
2739			.all_lte(<Runtime as frame_system::Config>::BlockWeights::get().max_block) ||
2740			active == all_voters
2741		{
2742			active += 1;
2743		}
2744
2745		println!("can support {} voters to yield a weight of {}", active, weight_with(active));
2746	}
2747}