Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Emergency mode for elections. #8918

Merged
merged 3 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 53 additions & 11 deletions frame/election-provider-multi-phase/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,13 @@ pub enum Phase<Bn> {
/// advising validators not to bother running the unsigned offchain worker.
///
/// As validator nodes are free to edit their OCW code, they could simply ignore this advisory
/// and always compute their own solution. However, by default, when the unsigned phase is passive,
/// the offchain workers will not bother running.
/// and always compute their own solution. However, by default, when the unsigned phase is
/// passive, the offchain workers will not bother running.
Unsigned((bool, Bn)),
/// The emergency phase. This is enabled upon a failing call to `T::ElectionProvider::elect`.
/// After that, the only way to leave this phase is through a successful
/// `T::ElectionProvider::elect`.
Emergency,
}

impl<Bn> Default for Phase<Bn> {
Expand All @@ -323,6 +327,11 @@ impl<Bn> Default for Phase<Bn> {
}

impl<Bn: PartialEq + Eq> Phase<Bn> {
/// Whether the phase is emergency or not.
pub fn is_emergency(&self) -> bool {
matches!(self, Phase::Emergency)
}

/// Whether the phase is signed or not.
pub fn is_signed(&self) -> bool {
matches!(self, Phase::Signed)
Expand Down Expand Up @@ -581,7 +590,7 @@ pub mod pallet {
/// Configuration for the fallback
type Fallback: Get<FallbackStrategy>;

/// Origin that can set the minimum score.
/// Origin that can control this pallet.
type ForceOrigin: EnsureOrigin<Self::Origin>;

/// The configuration of benchmarking.
Expand Down Expand Up @@ -793,6 +802,29 @@ pub mod pallet {
<MinimumUntrustedScore<T>>::set(maybe_next_score);
Ok(())
}

/// Set a solution in the queue, to be handed out to the client of this pallet in the next
/// call to `ElectionProvider::elect`.
///
/// This can only be set by `T::ForceOrigin`, and only when the phase is `Emergency`.
///
/// The solution is not checked for any feasibility and is assumed to be trustworthy, as any
/// feasibility check itself can in principle cause the election process to fail (due to
/// memory/weight constrains).
#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
fn set_emergency_election_result(
origin: OriginFor<T>,
solution: ReadySolution<T::AccountId>,
) -> DispatchResult {
T::ForceOrigin::ensure_origin(origin)?;
ensure!(Self::current_phase().is_emergency(), <Error<T>>::CallNotAllowed);

// Note: we don't `rotate_round` at this point; the next call to
// `ElectionProvider::elect` will not succeed and take care of that.

<QueuedSolution<T>>::put(solution);
Ok(())
}
}

#[pallet::event]
Expand Down Expand Up @@ -828,6 +860,8 @@ pub mod pallet {
PreDispatchWeakSubmission,
/// OCW submitted solution for wrong round
OcwCallWrongEra,
/// The call is now allowed at this point.
CallNotAllowed,
}

#[pallet::origin]
Expand Down Expand Up @@ -1162,14 +1196,14 @@ impl<T: Config> Pallet<T> {
/// 1. Increment round.
/// 2. Change phase to [`Phase::Off`]
/// 3. Clear all snapshot data.
fn post_elect() {
// inc round
fn rotate_round() {
// inc round.
<Round<T>>::mutate(|r| *r = *r + 1);

// change phase
// phase is off now.
<CurrentPhase<T>>::put(Phase::Off);

// kill snapshots
// kill snapshots.
Self::kill_snapshot();
}

Expand Down Expand Up @@ -1219,10 +1253,18 @@ impl<T: Config> ElectionProvider<T::AccountId, T::BlockNumber> for Pallet<T> {
type DataProvider = T::DataProvider;

fn elect() -> Result<(Supports<T::AccountId>, Weight), Self::Error> {
let outcome_and_weight = Self::do_elect();
// IMPORTANT: regardless of if election was `Ok` or `Err`, we shall do some cleanup.
Self::post_elect();
outcome_and_weight
match Self::do_elect() {
Ok((supports, weight)) => {
// all went okay, put sign to be Off, clean snapshot, etc.
Self::rotate_round();
Ok((supports, weight))
},
Err(why) => {
log!(error, "Entering emergency mode.");
<CurrentPhase<T>>::put(Phase::Emergency);
Err(why)
}
}
}
}

Expand Down