Allow validators to block and kick their nominator set. (#7930)

* Allow validators to block and kick their nominator set.

* migration

* Test

* Better migration

* Fixes

* cargo run --release --features=runtime-benchmarks --manifest-path=bin/node/cli/Cargo.toml -- benchmark --chain=dev --steps=50 --repeat=20 --pallet=pallet_staking --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./frame/staking/src/weights.rs --template=./.maintain/frame-weight-template.hbs

* Update frame/staking/src/lib.rs

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>

Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
Gavin Wood
2021-01-20 12:29:10 +01:00
committed by GitHub
parent 970cc25cef
commit fb5f945a01
7 changed files with 331 additions and 138 deletions
+93 -5
View File
@@ -454,12 +454,17 @@ pub struct ValidatorPrefs {
/// nominators.
#[codec(compact)]
pub commission: Perbill,
/// Whether or not this validator is accepting more nominations. If `true`, then no nominator
/// who is not already nominating this validator may nominate them. By default, validators
/// are accepting nominations.
pub blocked: bool,
}
impl Default for ValidatorPrefs {
fn default() -> Self {
ValidatorPrefs {
commission: Default::default(),
blocked: false,
}
}
}
@@ -896,11 +901,12 @@ enum Releases {
V2_0_0,
V3_0_0,
V4_0_0,
V5_0_0,
}
impl Default for Releases {
fn default() -> Self {
Releases::V4_0_0
Releases::V5_0_0
}
}
@@ -1087,8 +1093,8 @@ decl_storage! {
/// True if network has been upgraded to this version.
/// Storage version of the pallet.
///
/// This is set to v3.0.0 for new networks.
StorageVersion build(|_: &GenesisConfig<T>| Releases::V4_0_0): Releases;
/// This is set to v5.0.0 for new networks.
StorageVersion build(|_: &GenesisConfig<T>| Releases::V5_0_0): Releases;
}
add_extra_genesis {
config(stakers):
@@ -1124,6 +1130,29 @@ decl_storage! {
}
}
pub mod migrations {
use super::*;
#[derive(Decode)]
struct OldValidatorPrefs {
#[codec(compact)]
pub commission: Perbill
}
impl OldValidatorPrefs {
fn upgraded(self) -> ValidatorPrefs {
ValidatorPrefs {
commission: self.commission,
.. Default::default()
}
}
}
pub fn migrate_to_blockable<T: Config>() -> frame_support::weights::Weight {
Validators::<T>::translate::<OldValidatorPrefs, _>(|_, p| Some(p.upgraded()));
ErasValidatorPrefs::<T>::translate::<OldValidatorPrefs, _>(|_, _, p| Some(p.upgraded()));
T::BlockWeights::get().max_block
}
}
decl_event!(
pub enum Event<T> where Balance = BalanceOf<T>, <T as frame_system::Config>::AccountId {
/// The era payout has been set; the first balance is the validator-payout; the second is
@@ -1152,6 +1181,8 @@ decl_event!(
/// An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance`
/// from the unlocking queue. \[stash, amount\]
Withdrawn(AccountId, Balance),
/// A nominator has been kicked from a validator. \[nominator, stash\]
Kicked(AccountId, AccountId),
}
);
@@ -1225,6 +1256,10 @@ decl_error! {
IncorrectSlashingSpans,
/// Internal state has become somehow corrupted and the operation cannot continue.
BadState,
/// Too many nomination targets supplied.
TooManyTargets,
/// A nomination target was supplied that was blocked or otherwise not a validator.
BadTarget,
}
}
@@ -1270,6 +1305,15 @@ decl_module! {
fn deposit_event() = default;
fn on_runtime_upgrade() -> frame_support::weights::Weight {
if StorageVersion::get() == Releases::V4_0_0 {
StorageVersion::put(Releases::V5_0_0);
migrations::migrate_to_blockable::<T>()
} else {
0
}
}
/// sets `ElectionStatus` to `Open(now)` where `now` is the block number at which the
/// election window has opened, if we are at the last session and less blocks than
/// `T::ElectionLookahead` is remaining until the next new session schedule. The offchain
@@ -1675,9 +1719,17 @@ decl_module! {
let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
let stash = &ledger.stash;
ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
ensure!(targets.len() <= MAX_NOMINATIONS, Error::<T>::TooManyTargets);
let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets);
let targets = targets.into_iter()
.take(MAX_NOMINATIONS)
.map(|t| T::Lookup::lookup(t))
.map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
.map(|n| n.and_then(|n| if old.contains(&n) || !Validators::<T>::get(&n).blocked {
Ok(n)
} else {
Err(Error::<T>::BadTarget.into())
}))
.collect::<result::Result<Vec<T::AccountId>, _>>()?;
let nominations = Nominations {
@@ -2168,6 +2220,42 @@ decl_module! {
Ok(adjustments)
}
/// Remove the given nominations from the calling validator.
///
/// Effects will be felt at the beginning of the next era.
///
/// The dispatch origin for this call must be _Signed_ by the controller, not the stash.
/// And, it can be only called when [`EraElectionStatus`] is `Closed`. The controller
/// account should represent a validator.
///
/// - `who`: A list of nominator stash accounts who are nominating this validator which
/// should no longer be nominating this validator.
///
/// Note: Making this call only makes sense if you first set the validator preferences to
/// block any further nominations.
#[weight = T::WeightInfo::kick(who.len() as u32)]
pub fn kick(origin, who: Vec<<T::Lookup as StaticLookup>::Source>) -> DispatchResult {
let controller = ensure_signed(origin)?;
ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed);
let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
let stash = &ledger.stash;
for nom_stash in who.into_iter()
.map(T::Lookup::lookup)
.collect::<Result<Vec<T::AccountId>, _>>()?
.into_iter()
{
Nominators::<T>::mutate(&nom_stash, |maybe_nom| if let Some(ref mut nom) = maybe_nom {
if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
nom.targets.swap_remove(pos);
Self::deposit_event(RawEvent::Kicked(nom_stash.clone(), stash.clone()));
}
});
}
Ok(())
}
}
}