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