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