-
Notifications
You must be signed in to change notification settings - Fork 2.7k
better way to resolve Phase::Emergency via governance
#10663
Changes from 1 commit
8e0e16c
1424518
9098ff7
93a9103
4078d87
aee7481
0da2906
0cb18f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -230,7 +230,9 @@ | |
| #![cfg_attr(not(feature = "std"), no_std)] | ||
|
|
||
| use codec::{Decode, Encode}; | ||
| use frame_election_provider_support::{ElectionDataProvider, ElectionProvider}; | ||
| use frame_election_provider_support::{ | ||
| ElectionDataProvider, ElectionProvider, InstantElectionProvider, | ||
| }; | ||
| use frame_support::{ | ||
| dispatch::DispatchResultWithPostInfo, | ||
| ensure, | ||
|
|
@@ -321,6 +323,15 @@ impl<T: Config> ElectionProvider for NoFallback<T> { | |
| } | ||
| } | ||
|
|
||
| impl<T: Config> InstantElectionProvider for NoFallback<T> { | ||
| fn instant_elect( | ||
| _: Option<usize>, | ||
| _: Option<usize>, | ||
| ) -> Result<Supports<T::AccountId>, Self::Error> { | ||
| Err("NoFallback.") | ||
| } | ||
| } | ||
|
|
||
| /// Current phase of the pallet. | ||
| #[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] | ||
| pub enum Phase<Bn> { | ||
|
|
@@ -552,7 +563,7 @@ pub use pallet::*; | |
| #[frame_support::pallet] | ||
| pub mod pallet { | ||
| use super::*; | ||
| use frame_election_provider_support::NposSolver; | ||
| use frame_election_provider_support::{InstantElectionProvider, NposSolver}; | ||
| use frame_support::{pallet_prelude::*, traits::EstimateCallFee}; | ||
| use frame_system::pallet_prelude::*; | ||
|
|
||
|
|
@@ -669,13 +680,20 @@ pub mod pallet { | |
| + NposSolution | ||
| + TypeInfo; | ||
|
|
||
| /// Configuration for the fallback | ||
| /// Configuration for the fallback. | ||
| type Fallback: ElectionProvider< | ||
| AccountId = Self::AccountId, | ||
| BlockNumber = Self::BlockNumber, | ||
| DataProvider = Self::DataProvider, | ||
| >; | ||
|
|
||
| /// Configuration of the governance-only fallback. | ||
| type GovernanceFallback: InstantElectionProvider< | ||
| AccountId = Self::AccountId, | ||
| BlockNumber = Self::BlockNumber, | ||
| DataProvider = Self::DataProvider, | ||
| >; | ||
|
|
||
| /// OCW election solution miner algorithm implementation. | ||
| type Solver: NposSolver<AccountId = Self::AccountId>; | ||
|
|
||
|
|
@@ -1010,6 +1028,37 @@ pub mod pallet { | |
| }); | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Trigger the governance fallback. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but anyone can write a custom one as well. |
||
| /// | ||
| /// This can only be called when [`Phase::Emergency`] is enabled, as an alternative to | ||
| /// calling [`Call::set_emergency_election_result`]. | ||
| #[pallet::weight(T::DbWeight::get().reads_writes(1, 1))] | ||
| pub fn governance_fallback( | ||
| origin: OriginFor<T>, | ||
| maybe_max_voters: Option<u32>, | ||
| maybe_max_targets: Option<u32>, | ||
| ) -> DispatchResult { | ||
| T::ForceOrigin::ensure_origin(origin)?; | ||
| ensure!(Self::current_phase().is_emergency(), <Error<T>>::CallNotAllowed); | ||
|
|
||
| let maybe_max_voters = maybe_max_voters.map(|x| x as usize); | ||
| let maybe_max_targets = maybe_max_targets.map(|x| x as usize); | ||
|
|
||
| let supports = | ||
| T::GovernanceFallback::instant_elect(maybe_max_voters, maybe_max_targets).map_err( | ||
| |e| { | ||
| log!(error, "GovernanceFallback failed: {:?}", e); | ||
| Error::<T>::FallbackFailed | ||
| }, | ||
| )?; | ||
|
|
||
| let solution = | ||
| ReadySolution { supports, score: [0, 0, 0], compute: ElectionCompute::Fallback }; | ||
|
|
||
| <QueuedSolution<T>>::put(solution); | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| #[pallet::event] | ||
|
|
@@ -1060,6 +1109,8 @@ pub mod pallet { | |
| InvalidSubmissionIndex, | ||
| /// The call is not allowed at this point. | ||
| CallNotAllowed, | ||
| /// The fallback failed | ||
| FallbackFailed, | ||
| } | ||
|
|
||
| #[pallet::validate_unsigned] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,7 @@ | |
|
|
||
| //! An implementation of [`ElectionProvider`] that does an on-chain sequential phragmen. | ||
|
|
||
| use crate::{ElectionDataProvider, ElectionProvider}; | ||
| use crate::{ElectionDataProvider, ElectionProvider, InstantElectionProvider}; | ||
| use frame_support::{traits::Get, weights::DispatchClass}; | ||
| use sp_npos_elections::*; | ||
| use sp_std::{collections::btree_map::BTreeMap, marker::PhantomData, prelude::*}; | ||
|
|
@@ -68,16 +68,17 @@ pub trait Config: frame_system::Config { | |
| >; | ||
| } | ||
|
|
||
| impl<T: Config> ElectionProvider for OnChainSequentialPhragmen<T> { | ||
| type AccountId = T::AccountId; | ||
| type BlockNumber = T::BlockNumber; | ||
| type Error = Error; | ||
| type DataProvider = T::DataProvider; | ||
|
|
||
| fn elect() -> Result<Supports<T::AccountId>, Self::Error> { | ||
| let voters = Self::DataProvider::voters(None).map_err(Error::DataProvider)?; | ||
| let targets = Self::DataProvider::targets(None).map_err(Error::DataProvider)?; | ||
| let desired_targets = Self::DataProvider::desired_targets().map_err(Error::DataProvider)?; | ||
| impl<T: Config> OnChainSequentialPhragmen<T> { | ||
| fn elect_with( | ||
| maybe_max_voters: Option<usize>, | ||
| maybe_max_targets: Option<usize>, | ||
| ) -> Result<Supports<T::AccountId>, Error> { | ||
| let voters = <Self as ElectionProvider>::DataProvider::voters(maybe_max_voters) | ||
| .map_err(Error::DataProvider)?; | ||
| let targets = <Self as ElectionProvider>::DataProvider::targets(maybe_max_targets) | ||
| .map_err(Error::DataProvider)?; | ||
| let desired_targets = <Self as ElectionProvider>::DataProvider::desired_targets() | ||
| .map_err(Error::DataProvider)?; | ||
|
|
||
| let stake_map: BTreeMap<T::AccountId, VoteWeight> = voters | ||
| .iter() | ||
|
|
@@ -103,6 +104,26 @@ impl<T: Config> ElectionProvider for OnChainSequentialPhragmen<T> { | |
| } | ||
| } | ||
|
|
||
| impl<T: Config> ElectionProvider for OnChainSequentialPhragmen<T> { | ||
| type AccountId = T::AccountId; | ||
| type BlockNumber = T::BlockNumber; | ||
| type Error = Error; | ||
| type DataProvider = T::DataProvider; | ||
|
|
||
| fn elect() -> Result<Supports<T::AccountId>, Self::Error> { | ||
| Self::elect_with(None, None) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a dq, but why is it safe to not put a limit on the number of voters and targets? Do we assume that if you are using
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is actually NOT safe, and you should NOT use this. I'll document it a bit better.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh? If this is not safe, can you document something right here?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is documented at the type, where it is much more visible than being an inline comment in the code: Note that we never use this except at genesis, as recommended. |
||
| } | ||
| } | ||
|
|
||
| impl<T: Config> InstantElectionProvider for OnChainSequentialPhragmen<T> { | ||
| fn instant_elect( | ||
| maybe_max_voters: Option<usize>, | ||
| maybe_max_targets: Option<usize>, | ||
kianenigma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) -> Result<Supports<Self::AccountId>, Self::Error> { | ||
| Self::elect_with(maybe_max_voters, maybe_max_targets) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.