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

Commit da534d7

Browse files
kianenigmaemostovshawntabrizi
authored
better way to resolve Phase::Emergency via governance (#10663)
* better way to resolve Phase::Emergency via governance * Update frame/election-provider-multi-phase/src/lib.rs Co-authored-by: Zeke Mostov <[email protected]> * review grumbles * Update frame/election-provider-support/src/onchain.rs Co-authored-by: Shawn Tabrizi <[email protected]> * revert usize -> u32 Co-authored-by: Zeke Mostov <[email protected]> Co-authored-by: Shawn Tabrizi <[email protected]>
1 parent 71baca7 commit da534d7

File tree

5 files changed

+122
-22
lines changed

5 files changed

+122
-22
lines changed

bin/node/runtime/src/lib.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#![recursion_limit = "256"]
2424

2525
use codec::{Decode, Encode, MaxEncodedLen};
26+
use frame_election_provider_support::onchain;
2627
use frame_support::{
2728
construct_runtime, parameter_types,
2829
traits::{
@@ -528,12 +529,6 @@ parameter_types! {
528529
pub OffchainRepeat: BlockNumber = 5;
529530
}
530531

531-
use frame_election_provider_support::onchain;
532-
impl onchain::Config for Runtime {
533-
type Accuracy = Perbill;
534-
type DataProvider = Staking;
535-
}
536-
537532
pub struct StakingBenchmarkingConfig;
538533
impl pallet_staking::BenchmarkingConfig for StakingBenchmarkingConfig {
539534
type MaxNominators = ConstU32<1000>;
@@ -649,6 +644,11 @@ impl frame_support::pallet_prelude::Get<Option<(usize, sp_npos_elections::Extend
649644
}
650645
}
651646

647+
impl onchain::Config for Runtime {
648+
type Accuracy = Perbill;
649+
type DataProvider = <Self as pallet_election_provider_multi_phase::Config>::DataProvider;
650+
}
651+
652652
impl pallet_election_provider_multi_phase::Config for Runtime {
653653
type Event = Event;
654654
type Currency = Balances;
@@ -671,6 +671,8 @@ impl pallet_election_provider_multi_phase::Config for Runtime {
671671
type DataProvider = Staking;
672672
type Solution = NposSolution16;
673673
type Fallback = pallet_election_provider_multi_phase::NoFallback<Self>;
674+
type GovernanceFallback =
675+
frame_election_provider_support::onchain::OnChainSequentialPhragmen<Self>;
674676
type Solver = frame_election_provider_support::SequentialPhragmen<
675677
AccountId,
676678
pallet_election_provider_multi_phase::SolutionAccuracyOf<Self>,

frame/election-provider-multi-phase/src/lib.rs

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,9 @@
230230
#![cfg_attr(not(feature = "std"), no_std)]
231231

232232
use codec::{Decode, Encode};
233-
use frame_election_provider_support::{ElectionDataProvider, ElectionProvider};
233+
use frame_election_provider_support::{
234+
ElectionDataProvider, ElectionProvider, InstantElectionProvider,
235+
};
234236
use frame_support::{
235237
dispatch::DispatchResultWithPostInfo,
236238
ensure,
@@ -322,6 +324,15 @@ impl<T: Config> ElectionProvider for NoFallback<T> {
322324
}
323325
}
324326

327+
impl<T: Config> InstantElectionProvider for NoFallback<T> {
328+
fn instant_elect(
329+
_: Option<usize>,
330+
_: Option<usize>,
331+
) -> Result<Supports<T::AccountId>, Self::Error> {
332+
Err("NoFallback.")
333+
}
334+
}
335+
325336
/// Current phase of the pallet.
326337
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)]
327338
pub enum Phase<Bn> {
@@ -555,7 +566,7 @@ pub use pallet::*;
555566
#[frame_support::pallet]
556567
pub mod pallet {
557568
use super::*;
558-
use frame_election_provider_support::NposSolver;
569+
use frame_election_provider_support::{InstantElectionProvider, NposSolver};
559570
use frame_support::{pallet_prelude::*, traits::EstimateCallFee};
560571
use frame_system::pallet_prelude::*;
561572

@@ -672,13 +683,23 @@ pub mod pallet {
672683
+ NposSolution
673684
+ TypeInfo;
674685

675-
/// Configuration for the fallback
686+
/// Configuration for the fallback.
676687
type Fallback: ElectionProvider<
677688
AccountId = Self::AccountId,
678689
BlockNumber = Self::BlockNumber,
679690
DataProvider = Self::DataProvider,
680691
>;
681692

693+
/// Configuration of the governance-only fallback.
694+
///
695+
/// As a side-note, it is recommend for test-nets to use `type ElectionProvider =
696+
/// OnChainSeqPhragmen<_>` if the test-net is not expected to have thousands of nominators.
697+
type GovernanceFallback: InstantElectionProvider<
698+
AccountId = Self::AccountId,
699+
BlockNumber = Self::BlockNumber,
700+
DataProvider = Self::DataProvider,
701+
>;
702+
682703
/// OCW election solution miner algorithm implementation.
683704
type Solver: NposSolver<AccountId = Self::AccountId>;
684705

@@ -1013,6 +1034,37 @@ pub mod pallet {
10131034
});
10141035
Ok(())
10151036
}
1037+
1038+
/// Trigger the governance fallback.
1039+
///
1040+
/// This can only be called when [`Phase::Emergency`] is enabled, as an alternative to
1041+
/// calling [`Call::set_emergency_election_result`].
1042+
#[pallet::weight(T::DbWeight::get().reads_writes(1, 1))]
1043+
pub fn governance_fallback(
1044+
origin: OriginFor<T>,
1045+
maybe_max_voters: Option<u32>,
1046+
maybe_max_targets: Option<u32>,
1047+
) -> DispatchResult {
1048+
T::ForceOrigin::ensure_origin(origin)?;
1049+
ensure!(Self::current_phase().is_emergency(), <Error<T>>::CallNotAllowed);
1050+
1051+
let maybe_max_voters = maybe_max_voters.map(|x| x as usize);
1052+
let maybe_max_targets = maybe_max_targets.map(|x| x as usize);
1053+
1054+
let supports =
1055+
T::GovernanceFallback::instant_elect(maybe_max_voters, maybe_max_targets).map_err(
1056+
|e| {
1057+
log!(error, "GovernanceFallback failed: {:?}", e);
1058+
Error::<T>::FallbackFailed
1059+
},
1060+
)?;
1061+
1062+
let solution =
1063+
ReadySolution { supports, score: [0, 0, 0], compute: ElectionCompute::Fallback };
1064+
1065+
<QueuedSolution<T>>::put(solution);
1066+
Ok(())
1067+
}
10161068
}
10171069

10181070
#[pallet::event]
@@ -1063,6 +1115,8 @@ pub mod pallet {
10631115
InvalidSubmissionIndex,
10641116
/// The call is not allowed at this point.
10651117
CallNotAllowed,
1118+
/// The fallback failed
1119+
FallbackFailed,
10661120
}
10671121

10681122
#[pallet::validate_unsigned]

frame/election-provider-multi-phase/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ impl crate::Config for Runtime {
410410
type WeightInfo = DualMockWeightInfo;
411411
type BenchmarkingConfig = TestBenchmarkingConfig;
412412
type Fallback = MockFallback;
413+
type GovernanceFallback = NoFallback<Self>;
413414
type ForceOrigin = frame_system::EnsureRoot<AccountId>;
414415
type Solution = TestNposSolution;
415416
type VoterSnapshotPerBlock = VoterSnapshotPerBlock;

frame/election-provider-support/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,22 @@ pub trait ElectionProvider {
319319
fn elect() -> Result<Supports<Self::AccountId>, Self::Error>;
320320
}
321321

322+
/// A sub-trait of the [`ElectionProvider`] for cases where we need to be sure an election needs to
323+
/// happen instantly, not asynchronously.
324+
///
325+
/// The same `DataProvider` is assumed to be used.
326+
///
327+
/// Consequently, allows for control over the amount of data that is being fetched from the
328+
/// [`ElectionProvider::DataProvider`].
329+
pub trait InstantElectionProvider: ElectionProvider {
330+
/// Elect a new set of winners, instantly, with the given given limits set on the
331+
/// `DataProvider`.
332+
fn instant_elect(
333+
maybe_max_voters: Option<usize>,
334+
maybe_max_targets: Option<usize>,
335+
) -> Result<Supports<Self::AccountId>, Self::Error>;
336+
}
337+
322338
/// An election provider to be used only for testing.
323339
#[cfg(feature = "std")]
324340
pub struct NoElection<X>(sp_std::marker::PhantomData<X>);

frame/election-provider-support/src/onchain.rs

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
//! An implementation of [`ElectionProvider`] that does an on-chain sequential phragmen.
1919
20-
use crate::{ElectionDataProvider, ElectionProvider};
20+
use crate::{ElectionDataProvider, ElectionProvider, InstantElectionProvider};
2121
use frame_support::{traits::Get, weights::DispatchClass};
2222
use sp_npos_elections::*;
2323
use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*};
@@ -47,8 +47,14 @@ impl From<sp_npos_elections::Error> for Error {
4747
/// implementation ignores the additional data of the election data provider and gives no insight on
4848
/// how much weight was consumed.
4949
///
50-
/// Finally, this implementation does not impose any limits on the number of voters and targets that
51-
/// are provided.
50+
/// Finally, the [`ElectionProvider`] implementation of this type does not impose any limits on the
51+
/// number of voters and targets that are fetched. This could potentially make this unsuitable for
52+
/// execution onchain. On the other hand, the [`InstantElectionProvider`] implementation does limit
53+
/// these inputs.
54+
///
55+
/// It is advisable to use the former ([`ElectionProvider::elect`]) only at genesis, or for testing,
56+
/// the latter [`InstantElectionProvider::instant_elect`] for onchain operations, with thoughtful
57+
/// bounds.
5258
pub struct OnChainSequentialPhragmen<T: Config>(PhantomData<T>);
5359

5460
/// Configuration trait of [`OnChainSequentialPhragmen`].
@@ -68,16 +74,17 @@ pub trait Config: frame_system::Config {
6874
>;
6975
}
7076

71-
impl<T: Config> ElectionProvider for OnChainSequentialPhragmen<T> {
72-
type AccountId = T::AccountId;
73-
type BlockNumber = T::BlockNumber;
74-
type Error = Error;
75-
type DataProvider = T::DataProvider;
76-
77-
fn elect() -> Result<Supports<T::AccountId>, Self::Error> {
78-
let voters = Self::DataProvider::voters(None).map_err(Error::DataProvider)?;
79-
let targets = Self::DataProvider::targets(None).map_err(Error::DataProvider)?;
80-
let desired_targets = Self::DataProvider::desired_targets().map_err(Error::DataProvider)?;
77+
impl<T: Config> OnChainSequentialPhragmen<T> {
78+
fn elect_with(
79+
maybe_max_voters: Option<usize>,
80+
maybe_max_targets: Option<usize>,
81+
) -> Result<Supports<T::AccountId>, Error> {
82+
let voters = <Self as ElectionProvider>::DataProvider::voters(maybe_max_voters)
83+
.map_err(Error::DataProvider)?;
84+
let targets = <Self as ElectionProvider>::DataProvider::targets(maybe_max_targets)
85+
.map_err(Error::DataProvider)?;
86+
let desired_targets = <Self as ElectionProvider>::DataProvider::desired_targets()
87+
.map_err(Error::DataProvider)?;
8188

8289
let stake_map: BTreeMap<T::AccountId, VoteWeight> = voters
8390
.iter()
@@ -102,6 +109,26 @@ impl<T: Config> ElectionProvider for OnChainSequentialPhragmen<T> {
102109
}
103110
}
104111

112+
impl<T: Config> ElectionProvider for OnChainSequentialPhragmen<T> {
113+
type AccountId = T::AccountId;
114+
type BlockNumber = T::BlockNumber;
115+
type Error = Error;
116+
type DataProvider = T::DataProvider;
117+
118+
fn elect() -> Result<Supports<T::AccountId>, Self::Error> {
119+
Self::elect_with(None, None)
120+
}
121+
}
122+
123+
impl<T: Config> InstantElectionProvider for OnChainSequentialPhragmen<T> {
124+
fn instant_elect(
125+
maybe_max_voters: Option<usize>,
126+
maybe_max_targets: Option<usize>,
127+
) -> Result<Supports<Self::AccountId>, Self::Error> {
128+
Self::elect_with(maybe_max_voters, maybe_max_targets)
129+
}
130+
}
131+
105132
#[cfg(test)]
106133
mod tests {
107134
use super::*;

0 commit comments

Comments
 (0)