diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index 36d2373c9f623a76a5e935ffee4a546bc408b20e..7d8559050f3006395a0dd3f5f7821b0dcb83f2c1 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -948,6 +948,11 @@ pub mod pallet { compute: ElectionCompute::Emergency, }; + Self::deposit_event(Event::SolutionStored { + election_compute: ElectionCompute::Emergency, + prev_ejected: QueuedSolution::<T>::exists(), + }); + <QueuedSolution<T>>::put(solution); Ok(()) } @@ -1057,6 +1062,11 @@ pub mod pallet { compute: ElectionCompute::Fallback, }; + Self::deposit_event(Event::SolutionStored { + election_compute: ElectionCompute::Fallback, + prev_ejected: QueuedSolution::<T>::exists(), + }); + <QueuedSolution<T>>::put(solution); Ok(()) } @@ -1792,7 +1802,7 @@ mod tests { use crate::{ mock::{ multi_phase_events, roll_to, AccountId, ExtBuilder, MockWeightInfo, MockedWeightInfo, - MultiPhase, Runtime, SignedMaxSubmissions, System, TargetIndex, Targets, + MultiPhase, Origin, Runtime, SignedMaxSubmissions, System, TargetIndex, Targets, }, Phase, }; @@ -2038,6 +2048,50 @@ mod tests { }) } + #[test] + fn governance_fallback_works() { + ExtBuilder::default().onchain_fallback(false).build_and_execute(|| { + roll_to(25); + assert_eq!(MultiPhase::current_phase(), Phase::Unsigned((true, 25))); + + // Zilch solutions thus far. + assert!(MultiPhase::queued_solution().is_none()); + assert_eq!(MultiPhase::elect().unwrap_err(), ElectionError::Fallback("NoFallback.")); + + // phase is now emergency. + assert_eq!(MultiPhase::current_phase(), Phase::Emergency); + assert!(MultiPhase::queued_solution().is_none()); + + // no single account can trigger this + assert_noop!( + MultiPhase::governance_fallback(Origin::signed(99), None, None), + DispatchError::BadOrigin + ); + + // only root can + assert_ok!(MultiPhase::governance_fallback(Origin::root(), None, None)); + // something is queued now + assert!(MultiPhase::queued_solution().is_some()); + // next election call with fix everything.; + assert!(MultiPhase::elect().is_ok()); + assert_eq!(MultiPhase::current_phase(), Phase::Off); + + assert_eq!( + multi_phase_events(), + vec![ + Event::SignedPhaseStarted { round: 1 }, + Event::UnsignedPhaseStarted { round: 1 }, + Event::ElectionFinalized { election_compute: None }, + Event::SolutionStored { + election_compute: ElectionCompute::Fallback, + prev_ejected: false + }, + Event::ElectionFinalized { election_compute: Some(ElectionCompute::Fallback) } + ] + ); + }) + } + #[test] fn snapshot_too_big_failure_onchain_fallback() { // the `MockStaking` is designed such that if it has too many targets, it simply fails. diff --git a/substrate/frame/election-provider-multi-phase/src/mock.rs b/substrate/frame/election-provider-multi-phase/src/mock.rs index afce24ff6e3f073c95ceb895e9593c3dfcfee852..bbc2d6d43beee088e0d8512a9a1c171dc77f8280 100644 --- a/substrate/frame/election-provider-multi-phase/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/src/mock.rs @@ -18,7 +18,9 @@ use super::*; use crate::{self as multi_phase, unsigned::MinerConfig}; use frame_election_provider_support::{ - data_provider, onchain, ElectionDataProvider, NposSolution, SequentialPhragmen, + data_provider, + onchain::{self, UnboundedExecution}, + ElectionDataProvider, NposSolution, SequentialPhragmen, }; pub use frame_support::{assert_noop, assert_ok, pallet_prelude::GetDefault}; use frame_support::{ @@ -379,7 +381,7 @@ impl crate::Config for Runtime { type WeightInfo = (); type BenchmarkingConfig = TestBenchmarkingConfig; type Fallback = MockFallback; - type GovernanceFallback = NoFallback<Self>; + type GovernanceFallback = UnboundedExecution<OnChainSeqPhragmen>; type ForceOrigin = frame_system::EnsureRoot<AccountId>; type MaxElectingVoters = MaxElectingVoters; type MaxElectableTargets = MaxElectableTargets;