mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 23:31:07 +00:00
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:
@@ -116,7 +116,7 @@ pub mod weights;
|
||||
|
||||
use sp_runtime::{
|
||||
traits::{AtLeast32BitUnsigned, Convert, Member, One, OpaqueKeys, Zero},
|
||||
ConsensusEngineId, KeyTypeId, Perbill, Permill, RuntimeAppPublic,
|
||||
ConsensusEngineId, KeyTypeId, Permill, RuntimeAppPublic,
|
||||
};
|
||||
use sp_staking::SessionIndex;
|
||||
use sp_std::{
|
||||
@@ -298,7 +298,7 @@ pub trait SessionHandler<ValidatorId> {
|
||||
fn on_before_session_ending() {}
|
||||
|
||||
/// A validator got disabled. Act accordingly until a new session begins.
|
||||
fn on_disabled(validator_index: usize);
|
||||
fn on_disabled(validator_index: u32);
|
||||
}
|
||||
|
||||
#[impl_trait_for_tuples::impl_for_tuples(1, 30)]
|
||||
@@ -342,7 +342,7 @@ impl<AId> SessionHandler<AId> for Tuple {
|
||||
for_tuples!( #( Tuple::on_before_session_ending(); )* )
|
||||
}
|
||||
|
||||
fn on_disabled(i: usize) {
|
||||
fn on_disabled(i: u32) {
|
||||
for_tuples!( #( Tuple::on_disabled(i); )* )
|
||||
}
|
||||
}
|
||||
@@ -354,7 +354,7 @@ impl<AId> SessionHandler<AId> for TestSessionHandler {
|
||||
fn on_genesis_session<Ks: OpaqueKeys>(_: &[(AId, Ks)]) {}
|
||||
fn on_new_session<Ks: OpaqueKeys>(_: bool, _: &[(AId, Ks)], _: &[(AId, Ks)]) {}
|
||||
fn on_before_session_ending() {}
|
||||
fn on_disabled(_: usize) {}
|
||||
fn on_disabled(_: u32) {}
|
||||
}
|
||||
|
||||
#[frame_support::pallet]
|
||||
@@ -401,12 +401,6 @@ pub mod pallet {
|
||||
/// The keys.
|
||||
type Keys: OpaqueKeys + Member + Parameter + Default + MaybeSerializeDeserialize;
|
||||
|
||||
/// The fraction of validators set that is safe to be disabled.
|
||||
///
|
||||
/// After the threshold is reached `disabled` method starts to return true,
|
||||
/// which in combination with `pallet_staking` forces a new era.
|
||||
type DisabledValidatorsThreshold: Get<Perbill>;
|
||||
|
||||
/// Weight information for extrinsics in this pallet.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
@@ -514,7 +508,9 @@ pub mod pallet {
|
||||
|
||||
/// Indices of disabled validators.
|
||||
///
|
||||
/// The set is cleared when `on_session_ending` returns a new set of identities.
|
||||
/// The vec is always kept sorted so that we can find whether a given validator is
|
||||
/// disabled using binary search. It gets cleared when `on_session_ending` returns
|
||||
/// a new set of identities.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn disabled_validators)]
|
||||
pub type DisabledValidators<T> = StorageValue<_, Vec<u32>, ValueQuery>;
|
||||
@@ -705,42 +701,34 @@ impl<T: Config> Pallet<T> {
|
||||
T::SessionHandler::on_new_session::<T::Keys>(changed, &session_keys, &queued_amalgamated);
|
||||
}
|
||||
|
||||
/// Disable the validator of index `i`.
|
||||
///
|
||||
/// Returns `true` if this causes a `DisabledValidatorsThreshold` of validators
|
||||
/// to be already disabled.
|
||||
pub fn disable_index(i: usize) -> bool {
|
||||
let (fire_event, threshold_reached) = <DisabledValidators<T>>::mutate(|disabled| {
|
||||
let i = i as u32;
|
||||
if let Err(index) = disabled.binary_search(&i) {
|
||||
let count = <Validators<T>>::decode_len().unwrap_or(0) as u32;
|
||||
let threshold = T::DisabledValidatorsThreshold::get() * count;
|
||||
disabled.insert(index, i);
|
||||
(true, disabled.len() as u32 > threshold)
|
||||
} else {
|
||||
(false, false)
|
||||
}
|
||||
});
|
||||
|
||||
if fire_event {
|
||||
T::SessionHandler::on_disabled(i);
|
||||
/// Disable the validator of index `i`, returns `false` if the validator was already disabled.
|
||||
pub fn disable_index(i: u32) -> bool {
|
||||
if i >= Validators::<T>::decode_len().unwrap_or(0) as u32 {
|
||||
return false
|
||||
}
|
||||
|
||||
threshold_reached
|
||||
<DisabledValidators<T>>::mutate(|disabled| {
|
||||
if let Err(index) = disabled.binary_search(&i) {
|
||||
disabled.insert(index, i);
|
||||
T::SessionHandler::on_disabled(i);
|
||||
return true
|
||||
}
|
||||
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
/// Disable the validator identified by `c`. (If using with the staking pallet,
|
||||
/// this would be their *stash* account.)
|
||||
///
|
||||
/// Returns `Ok(true)` if more than `DisabledValidatorsThreshold` validators in current
|
||||
/// session is already disabled.
|
||||
/// If used with the staking pallet it allows to force a new era in such case.
|
||||
pub fn disable(c: &T::ValidatorId) -> sp_std::result::Result<bool, ()> {
|
||||
/// Returns `false` either if the validator could not be found or it was already
|
||||
/// disabled.
|
||||
pub fn disable(c: &T::ValidatorId) -> bool {
|
||||
Self::validators()
|
||||
.iter()
|
||||
.position(|i| i == c)
|
||||
.map(Self::disable_index)
|
||||
.ok_or(())
|
||||
.map(|i| Self::disable_index(i as u32))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Upgrade the key type from some old type to a new type. Supports adding
|
||||
|
||||
@@ -29,7 +29,6 @@ use sp_runtime::{
|
||||
impl_opaque_keys,
|
||||
testing::{Header, UintAuthorityId},
|
||||
traits::{BlakeTwo256, ConvertInto, IdentityLookup},
|
||||
Perbill,
|
||||
};
|
||||
use sp_staking::SessionIndex;
|
||||
|
||||
@@ -144,7 +143,7 @@ impl SessionHandler<u64> for TestSessionHandler {
|
||||
.collect()
|
||||
});
|
||||
}
|
||||
fn on_disabled(_validator_index: usize) {
|
||||
fn on_disabled(_validator_index: u32) {
|
||||
DISABLED.with(|l| *l.borrow_mut() = true)
|
||||
}
|
||||
fn on_before_session_ending() {
|
||||
@@ -269,10 +268,6 @@ impl pallet_timestamp::Config for Test {
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(33);
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
type ShouldEndSession = TestShouldEndSession;
|
||||
#[cfg(feature = "historical")]
|
||||
@@ -284,7 +279,6 @@ impl Config for Test {
|
||||
type ValidatorIdOf = ConvertInto;
|
||||
type Keys = MockSessionKeys;
|
||||
type Event = Event;
|
||||
type DisabledValidatorsThreshold = DisabledValidatorsThreshold;
|
||||
type NextSessionRotation = ();
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ fn session_keys_generate_output_works_as_set_keys_input() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_true_if_more_than_third_is_disabled() {
|
||||
fn disable_index_returns_false_if_already_disabled() {
|
||||
new_test_ext().execute_with(|| {
|
||||
set_next_validators(vec![1, 2, 3, 4, 5, 6, 7]);
|
||||
force_new_session();
|
||||
@@ -347,10 +347,9 @@ fn return_true_if_more_than_third_is_disabled() {
|
||||
force_new_session();
|
||||
initialize_block(2);
|
||||
|
||||
assert_eq!(Session::disable_index(0), true);
|
||||
assert_eq!(Session::disable_index(0), false);
|
||||
assert_eq!(Session::disable_index(1), false);
|
||||
assert_eq!(Session::disable_index(2), true);
|
||||
assert_eq!(Session::disable_index(3), true);
|
||||
assert_eq!(Session::disable_index(1), true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user