-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Improve Staking Limits #9193
Improve Staking Limits #9193
Changes from all commits
4ad91dd
5b5375e
9d137e8
f7c1698
4723d5a
1778970
0a15b45
aa5fb4e
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 |
|---|---|---|
|
|
@@ -1216,6 +1216,12 @@ pub mod pallet { | |
| #[pallet::storage] | ||
| pub(crate) type StorageVersion<T: Config> = StorageValue<_, Releases, ValueQuery>; | ||
|
|
||
| /// The threshold for when users can start calling `chill_other` for other validators / nominators. | ||
| /// The threshold is compared to the actual number of validators / nominators (`CountFor*`) in | ||
| /// the system compared to the configured max (`Max*Count`). | ||
| #[pallet::storage] | ||
| pub(crate) type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>; | ||
|
|
||
| #[pallet::genesis_config] | ||
| pub struct GenesisConfig<T: Config> { | ||
| pub history_depth: u32, | ||
|
|
@@ -1714,16 +1720,19 @@ pub mod pallet { | |
| pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult { | ||
| let controller = ensure_signed(origin)?; | ||
|
|
||
| // If this error is reached, we need to adjust the `MinValidatorBond` and start calling `chill_other`. | ||
| // Until then, we explicitly block new validators to protect the runtime. | ||
| if let Some(max_validators) = MaxValidatorsCount::<T>::get() { | ||
| ensure!(CounterForValidators::<T>::get() < max_validators, Error::<T>::TooManyValidators); | ||
| } | ||
|
|
||
| let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; | ||
| ensure!(ledger.active >= MinValidatorBond::<T>::get(), Error::<T>::InsufficientBond); | ||
|
|
||
| let stash = &ledger.stash; | ||
|
|
||
| // Only check limits if they are not already a validator. | ||
| if !Validators::<T>::contains_key(stash) { | ||
| // If this error is reached, we need to adjust the `MinValidatorBond` and start calling `chill_other`. | ||
| // Until then, we explicitly block new validators to protect the runtime. | ||
| if let Some(max_validators) = MaxValidatorsCount::<T>::get() { | ||
| ensure!(CounterForValidators::<T>::get() < max_validators, Error::<T>::TooManyValidators); | ||
| } | ||
| } | ||
|
|
||
| Self::do_remove_nominator(stash); | ||
| Self::do_add_validator(stash, prefs); | ||
| Ok(()) | ||
|
|
@@ -1755,16 +1764,19 @@ pub mod pallet { | |
| ) -> DispatchResult { | ||
| let controller = ensure_signed(origin)?; | ||
|
|
||
| // If this error is reached, we need to adjust the `MinNominatorBond` and start calling `chill_other`. | ||
| // Until then, we explicitly block new nominators to protect the runtime. | ||
| if let Some(max_nominators) = MaxNominatorsCount::<T>::get() { | ||
| ensure!(CounterForNominators::<T>::get() < max_nominators, Error::<T>::TooManyNominators); | ||
| } | ||
|
|
||
| let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; | ||
| ensure!(ledger.active >= MinNominatorBond::<T>::get(), Error::<T>::InsufficientBond); | ||
|
|
||
| let stash = &ledger.stash; | ||
|
|
||
| // Only check limits if they are not already a nominator. | ||
| if !Nominators::<T>::contains_key(stash) { | ||
| // If this error is reached, we need to adjust the `MinNominatorBond` and start calling `chill_other`. | ||
| // Until then, we explicitly block new nominators to protect the runtime. | ||
| if let Some(max_nominators) = MaxNominatorsCount::<T>::get() { | ||
| ensure!(CounterForNominators::<T>::get() < max_nominators, Error::<T>::TooManyNominators); | ||
| } | ||
| } | ||
|
|
||
| ensure!(!targets.is_empty(), Error::<T>::EmptyTargets); | ||
| ensure!(targets.len() <= T::MAX_NOMINATIONS as usize, Error::<T>::TooManyTargets); | ||
|
|
||
|
|
@@ -2266,31 +2278,42 @@ pub mod pallet { | |
| /// | ||
| /// NOTE: Existing nominators and validators will not be affected by this update. | ||
| /// to kick people under the new limits, `chill_other` should be called. | ||
| #[pallet::weight(T::WeightInfo::update_staking_limits())] | ||
| pub fn update_staking_limits( | ||
| #[pallet::weight(T::WeightInfo::set_staking_limits())] | ||
| pub fn set_staking_limits( | ||
| origin: OriginFor<T>, | ||
| min_nominator_bond: BalanceOf<T>, | ||
| min_validator_bond: BalanceOf<T>, | ||
| max_nominator_count: Option<u32>, | ||
| max_validator_count: Option<u32>, | ||
| threshold: Option<Percent>, | ||
| ) -> DispatchResult { | ||
| ensure_root(origin)?; | ||
| MinNominatorBond::<T>::set(min_nominator_bond); | ||
| MinValidatorBond::<T>::set(min_validator_bond); | ||
| MaxNominatorsCount::<T>::set(max_nominator_count); | ||
| MaxValidatorsCount::<T>::set(max_validator_count); | ||
| ChillThreshold::<T>::set(threshold); | ||
| Ok(()) | ||
| } | ||
|
|
||
| /// Declare a `controller` as having no desire to either validator or nominate. | ||
| /// Declare a `controller` to stop participating as either a validator or nominator. | ||
| /// | ||
| /// Effects will be felt at the beginning of the next era. | ||
| /// | ||
| /// The dispatch origin for this call must be _Signed_, but can be called by anyone. | ||
| /// | ||
| /// If the caller is the same as the controller being targeted, then no further checks | ||
| /// are enforced. However, this call can also be made by an third party user who witnesses | ||
| /// that this controller does not satisfy the minimum bond requirements to be in their role. | ||
| /// If the caller is the same as the controller being targeted, then no further checks are | ||
| /// enforced, and this function behaves just like `chill`. | ||
| /// | ||
| /// If the caller is different than the controller being targeted, the following conditions | ||
| /// must be met: | ||
| /// * A `ChillThreshold` must be set and checked which defines how close to the max | ||
| /// nominators or validators we must reach before users can start chilling one-another. | ||
| /// * A `MaxNominatorCount` and `MaxValidatorCount` must be set which is used to determine | ||
| /// how close we are to the threshold. | ||
| /// * A `MinNominatorBond` and `MinValidatorBond` must be set and checked, which determines | ||
| /// if this is a person that should be chilled because they have not met the threshold | ||
| /// bond required. | ||
| /// | ||
| /// This can be helpful if bond requirements are updated, and we need to remove old users | ||
| /// who do not satisfy these requirements. | ||
|
|
@@ -2307,14 +2330,27 @@ pub mod pallet { | |
| let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; | ||
|
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. Should the origin param for The current, permission nature of this call is useful for scenarios where we want a network to be continuously "self adjusting". But I think we can imagine scenarios where this makes more sense as a governance only call (that then can be turned into a permission-less call with a runtime upgrade). While I don't see how it would improve security properties, I think having a call like this go through governance could benefit the community
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. Tagential, is it possible to make a call origin configurable without a runtime upgrade?
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.
Nope, that's logic change and needs altering |
||
| let stash = ledger.stash; | ||
|
|
||
| // If the caller is not the controller, we want to check that the minimum bond | ||
| // requirements are not satisfied, and thus we have reason to chill this user. | ||
| // In order for one user to chill another user, the following conditions must be met: | ||
| // * A `ChillThreshold` is set which defines how close to the max nominators or | ||
| // validators we must reach before users can start chilling one-another. | ||
| // * A `MaxNominatorCount` and `MaxValidatorCount` which is used to determine how close | ||
| // we are to the threshold. | ||
| // * A `MinNominatorBond` and `MinValidatorBond` which is the final condition checked to | ||
| // determine this is a person that should be chilled because they have not met the | ||
| // threshold bond required. | ||
| // | ||
| // Otherwise, if caller is the same as the controller, this is just like `chill`. | ||
| if caller != controller { | ||
| let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?; | ||
| let min_active_bond = if Nominators::<T>::contains_key(&stash) { | ||
| let max_nominator_count = MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?; | ||
| let current_nominator_count = CounterForNominators::<T>::get(); | ||
| ensure!(threshold * max_nominator_count < current_nominator_count, Error::<T>::CannotChillOther); | ||
| MinNominatorBond::<T>::get() | ||
| } else if Validators::<T>::contains_key(&stash) { | ||
| let max_validator_count = MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?; | ||
| let current_validator_count = CounterForValidators::<T>::get(); | ||
| ensure!(threshold * max_validator_count < current_validator_count, Error::<T>::CannotChillOther); | ||
| MinValidatorBond::<T>::get() | ||
| } else { | ||
| Zero::zero() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.