staking: only disable slashed validators and keep them disabled for whole era (#9448)

* session: remove disabled validators threshold logic

* staking: add logic to track offending validators

* staking: disable validators for the whole era

* frame: fix tests

* staking: add tests for disabling validators handling

* staking: fix adding offending validator when already slashed in era

* address review comments

* session, staking: add comments about sorted vecs

Co-authored-by: Andronik Ordian <write@reusable.software>
This commit is contained in:
André Silva
2021-10-06 17:22:34 +01:00
committed by GitHub
parent 12e9e7ceb3
commit 7e5c022aea
25 changed files with 282 additions and 134 deletions
+51 -13
View File
@@ -56,7 +56,7 @@ use crate::{
use codec::{Decode, Encode};
use frame_support::{
ensure,
traits::{Currency, Imbalance, OnUnbalanced},
traits::{Currency, Get, Imbalance, OnUnbalanced},
};
use scale_info::TypeInfo;
use sp_runtime::{
@@ -278,15 +278,13 @@ pub(crate) fn compute_slash<T: Config>(
// not continue in the next election. also end the slashing span.
spans.end_span(now);
<Pallet<T>>::chill_stash(stash);
// make sure to disable validator till the end of this session
if T::SessionInterface::disable_validator(stash).unwrap_or(false) {
// force a new era, to select a new validator set
<Pallet<T>>::ensure_new_era()
}
}
}
// add the validator to the offenders list and make sure it is disabled for
// the duration of the era
add_offending_validator::<T>(params.stash, true);
let mut nominators_slashed = Vec::new();
reward_payout += slash_nominators::<T>(params, prior_slash_p, &mut nominators_slashed);
@@ -316,13 +314,53 @@ fn kick_out_if_recent<T: Config>(params: SlashParams<T>) {
if spans.era_span(params.slash_era).map(|s| s.index) == Some(spans.span_index()) {
spans.end_span(params.now);
<Pallet<T>>::chill_stash(params.stash);
// make sure to disable validator till the end of this session
if T::SessionInterface::disable_validator(params.stash).unwrap_or(false) {
// force a new era, to select a new validator set
<Pallet<T>>::ensure_new_era()
}
}
// add the validator to the offenders list but since there's no slash being
// applied there's no need to disable the validator
add_offending_validator::<T>(params.stash, false);
}
/// Add the given validator to the offenders list and optionally disable it.
/// If after adding the validator `OffendingValidatorsThreshold` is reached
/// a new era will be forced.
fn add_offending_validator<T: Config>(stash: &T::AccountId, disable: bool) {
<Pallet<T> as Store>::OffendingValidators::mutate(|offending| {
let validators = T::SessionInterface::validators();
let validator_index = match validators.iter().position(|i| i == stash) {
Some(index) => index,
None => return,
};
let validator_index_u32 = validator_index as u32;
match offending.binary_search_by_key(&validator_index_u32, |(index, _)| *index) {
// this is a new offending validator
Err(index) => {
offending.insert(index, (validator_index_u32, disable));
let offending_threshold =
T::OffendingValidatorsThreshold::get() * validators.len() as u32;
if offending.len() >= offending_threshold as usize {
// force a new era, to select a new validator set
<Pallet<T>>::ensure_new_era()
}
if disable {
T::SessionInterface::disable_validator(validator_index_u32);
}
},
Ok(index) => {
if disable && !offending[index].1 {
// the validator had previously offended without being disabled,
// let's make sure we disable it now
offending[index].1 = true;
T::SessionInterface::disable_validator(validator_index_u32);
}
},
}
});
}
/// Slash nominators. Accepts general parameters and the prior slash percentage of the validator.