From de769d714c4849234dfb62d665acee4ec81c1745 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 9 Jul 2022 11:13:03 +0100 Subject: [PATCH 1/7] initial draft of fixing slashing --- frame/staking/src/mock.rs | 15 ++++ frame/staking/src/pallet/impls.rs | 45 +++++------ frame/staking/src/tests.rs | 119 ++++++++++++++++++++++++++---- 3 files changed, 145 insertions(+), 34 deletions(-) diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index bd2d8cdc32ce9..70d00c2b648d8 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -549,6 +549,7 @@ impl ExtBuilder { ext } pub fn build_and_execute(self, test: impl FnOnce() -> ()) { + sp_tracing::try_init_simple(); let mut ext = self.build(); ext.execute_with(test); ext.execute_with(post_conditions); @@ -884,6 +885,20 @@ pub(crate) fn staking_events() -> Vec> { .collect() } +parameter_types! { + static StakingEventsIndex: usize = 0; +} + +pub(crate) fn staking_events_since_last_call() -> Vec> { + let all: Vec<_> = System::events() + .into_iter() + .filter_map(|r| if let Event::Staking(inner) = r.event { Some(inner) } else { None }) + .collect(); + let seen = StakingEventsIndex::get(); + StakingEventsIndex::set(all.len()); + all.into_iter().skip(seen).collect() +} + pub(crate) fn balances(who: &AccountId) -> (Balance, Balance) { (Balances::free_balance(who), Balances::reserved_balance(who)) } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 7656eec80a5ff..2e67ce1c47a0f 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -599,20 +599,19 @@ impl Pallet { /// Apply previously-unapplied slashes on the beginning of a new era, after a delay. fn apply_unapplied_slashes(active_era: EraIndex) { - let slash_defer_duration = T::SlashDeferDuration::get(); - ::EarliestUnappliedSlash::mutate(|earliest| { - if let Some(ref mut earliest) = earliest { - let keep_from = active_era.saturating_sub(slash_defer_duration); - for era in (*earliest)..keep_from { - let era_slashes = ::UnappliedSlashes::take(&era); - for slash in era_slashes { - slashing::apply_slash::(slash, era); - } - } - - *earliest = (*earliest).max(keep_from) + if ::UnappliedSlashes::contains_key(&active_era) { + let era_slashes = ::UnappliedSlashes::take(&active_era); + log!( + debug, + "found {} slashes scheduled to be executed in era {}", + active_era, + era_slashes.len() + ); + for slash in era_slashes { + let slash_era = active_era.saturating_sub(T::SlashDeferDuration::get()); + slashing::apply_slash::(slash, slash_era); } - }) + } } /// Add reward points to validators using their stash account ID. @@ -1209,11 +1208,6 @@ where } }; - ::EarliestUnappliedSlash::mutate(|earliest| { - if earliest.is_none() { - *earliest = Some(active_era) - } - }); add_db_reads_writes(1, 1); let slash_defer_duration = T::SlashDeferDuration::get(); @@ -1263,9 +1257,18 @@ where } } else { // Defer to end of some `slash_defer_duration` from now. - ::UnappliedSlashes::mutate(active_era, move |for_later| { - for_later.push(unapplied) - }); + log!( + debug, + "deferring slash of {:?}% happened in {:?} (reported in {:?}) to {:?}", + slash_fraction, + slash_era, + active_era, + slash_era + slash_defer_duration + 1, + ); + ::UnappliedSlashes::mutate( + slash_era + slash_defer_duration + 1, + move |for_later| for_later.push(unapplied), + ); add_db_reads_writes(1, 1); } } else { diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 9a13a818f4b59..39008ff5a199d 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2778,12 +2778,91 @@ fn deferred_slashes_are_deferred() { assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(101), 2000); + let _ = staking_events_since_last_call(); + // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. mock::start_active_era(4); assert_eq!(Balances::free_balance(11), 900); assert_eq!(Balances::free_balance(101), 2000 - (nominated_value / 10)); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::StakersElected, + Event::EraPaid(3, 11075, 33225), + Event::Slashed(11, 100), + Event::Slashed(101, 12) + ] + ); + }) +} + +#[test] +fn retroactive_deferred_slashes_two_eras_before_() { + ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { + assert_eq!(BondingDuration::get(), 3); + + mock::start_active_era(1); + let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); + + mock::start_active_era(3); + on_offence_in_era( + &[OffenceDetails { offender: (11, exposure_11_at_era1), reporters: vec![] }], + &[Perbill::from_percent(10)], + 1, // should be deferred for two full eras, and applied at the beginning of era 4. + DisableStrategy::Never, + ); + let _ = staking_events_since_last_call(); + + mock::start_active_era(4); + + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::StakersElected, + Event::EraPaid(3, 7100, 21300), + Event::Slashed(11, 100), + Event::Slashed(101, 12) + ] + ); + }) +} + +#[test] +fn retroactive_deferred_slashes_one_before() { + ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { + assert_eq!(BondingDuration::get(), 3); + + mock::start_active_era(1); + let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); + + mock::start_active_era(3); + on_offence_in_era( + &[OffenceDetails { offender: (11, exposure_11_at_era1), reporters: vec![] }], + &[Perbill::from_percent(10)], + 2, // should be deferred for two full eras, and applied at the beginning of era 5. + DisableStrategy::Never, + ); + let _ = staking_events_since_last_call(); + + mock::start_active_era(4); + assert_eq!( + staking_events_since_last_call(), + vec![Event::StakersElected, Event::EraPaid(3, 7100, 21300)] + ); + + mock::start_active_era(5); + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::StakersElected, + Event::EraPaid(4, 11075, 33225), + Event::Slashed(11, 100), + Event::Slashed(101, 12) + ] + ); }) } @@ -2872,6 +2951,7 @@ fn remove_deferred() { assert_eq!(Balances::free_balance(101), 2000); let nominated_value = exposure.others.iter().find(|o| o.who == 101).unwrap().value; + // deferred to start of era 4. on_offence_now( &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], &[Perbill::from_percent(10)], @@ -2882,10 +2962,11 @@ fn remove_deferred() { mock::start_active_era(2); + // deferred to start of era 5. on_offence_in_era( &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], &[Perbill::from_percent(15)], - 1, + 2, DisableStrategy::WhenSlashed, ); @@ -2895,7 +2976,8 @@ fn remove_deferred() { Error::::EmptyTargets ); - assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0])); + // cancel one of them. + assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 4, vec![0])); assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(101), 2000); @@ -2907,24 +2989,35 @@ fn remove_deferred() { // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. + let _ = staking_events_since_last_call(); mock::start_active_era(4); // the first slash for 10% was cancelled, so no effect. + assert_eq!( + staking_events_since_last_call(), + vec![Event::StakersElected, Event::EraPaid(3, 11075, 33225)] + ); assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(101), 2000); mock::start_active_era(5); + assert_eq!( + staking_events_since_last_call(), + vec![ + Event::StakersElected, + Event::EraPaid(4, 11075, 33225), + Event::Slashed(11, 150), + Event::Slashed(101, 19) + ] + ); - let slash_10 = Perbill::from_percent(10); - let slash_15 = Perbill::from_percent(15); - let initial_slash = slash_10 * nominated_value; - - let total_slash = slash_15 * nominated_value; - let actual_slash = total_slash - initial_slash; + // 10% slash was cancelled, but the 15% was applied. + let nominator_slash = Perbill::from_percent(15) * nominated_value; + let own_slash = Perbill::from_percent(15) * exposure.own; // 5% slash (15 - 10) processed now. - assert_eq!(Balances::free_balance(11), 950); - assert_eq!(Balances::free_balance(101), 2000 - actual_slash); + assert_eq!(Balances::free_balance(11), 1000 - own_slash); + assert_eq!(Balances::free_balance(101), 2000 - nominator_slash); }) } @@ -2966,7 +3059,7 @@ fn remove_multi_deferred() { &[Perbill::from_percent(25)], ); - assert_eq!(::UnappliedSlashes::get(&1).len(), 5); + assert_eq!(::UnappliedSlashes::get(&4).len(), 5); // fails if list is not sorted assert_noop!( @@ -2984,9 +3077,9 @@ fn remove_multi_deferred() { Error::::InvalidSlashIndex ); - assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 1, vec![0, 2, 4])); + assert_ok!(Staking::cancel_deferred_slash(Origin::root(), 4, vec![0, 2, 4])); - let slashes = ::UnappliedSlashes::get(&1); + let slashes = ::UnappliedSlashes::get(&4); assert_eq!(slashes.len(), 2); assert_eq!(slashes[0].validator, 21); assert_eq!(slashes[1].validator, 42); From f1a4e5365ac86b2d5d46181cf587705899a8369e Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 9 Jul 2022 11:22:34 +0100 Subject: [PATCH 2/7] fix test --- frame/staking/src/tests.rs | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 39008ff5a199d..3bb89c22eb03c 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2962,11 +2962,11 @@ fn remove_deferred() { mock::start_active_era(2); - // deferred to start of era 5. + // reported later, but deferred to start of era 4 as well. on_offence_in_era( &[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }], &[Perbill::from_percent(15)], - 2, + 1, DisableStrategy::WhenSlashed, ); @@ -2992,32 +2992,27 @@ fn remove_deferred() { let _ = staking_events_since_last_call(); mock::start_active_era(4); - // the first slash for 10% was cancelled, so no effect. - assert_eq!( - staking_events_since_last_call(), - vec![Event::StakersElected, Event::EraPaid(3, 11075, 33225)] - ); - assert_eq!(Balances::free_balance(11), 1000); - assert_eq!(Balances::free_balance(101), 2000); - - mock::start_active_era(5); + // the first slash for 10% was cancelled, but the 15% one assert_eq!( staking_events_since_last_call(), vec![ Event::StakersElected, - Event::EraPaid(4, 11075, 33225), - Event::Slashed(11, 150), - Event::Slashed(101, 19) + Event::EraPaid(3, 11075, 33225), + Event::Slashed(11, 50), + Event::Slashed(101, 7) ] ); - // 10% slash was cancelled, but the 15% was applied. - let nominator_slash = Perbill::from_percent(15) * nominated_value; - let own_slash = Perbill::from_percent(15) * exposure.own; + let slash_10 = Perbill::from_percent(10); + let slash_15 = Perbill::from_percent(15); + let initial_slash = slash_10 * nominated_value; + + let total_slash = slash_15 * nominated_value; + let actual_slash = total_slash - initial_slash; // 5% slash (15 - 10) processed now. - assert_eq!(Balances::free_balance(11), 1000 - own_slash); - assert_eq!(Balances::free_balance(101), 2000 - nominator_slash); + assert_eq!(Balances::free_balance(11), 950); + assert_eq!(Balances::free_balance(101), 2000 - actual_slash); }) } From b45331a5e113c77342558fd8810653a9bbf18e7d Mon Sep 17 00:00:00 2001 From: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Date: Wed, 13 Jul 2022 14:06:16 +0100 Subject: [PATCH 3/7] Update frame/staking/src/tests.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Piotr Mikołajczyk --- frame/staking/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 3bb89c22eb03c..599ac8d6fe37c 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2800,7 +2800,7 @@ fn deferred_slashes_are_deferred() { } #[test] -fn retroactive_deferred_slashes_two_eras_before_() { +fn retroactive_deferred_slashes_two_eras_before() { ExtBuilder::default().slash_defer_duration(2).build_and_execute(|| { assert_eq!(BondingDuration::get(), 3); From 1e0162a87dc7d73604f0fb54899195060d3bd81a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 13 Jul 2022 18:39:08 +0100 Subject: [PATCH 4/7] last touches --- frame/staking/src/pallet/impls.rs | 26 ++++++++++++-------------- frame/staking/src/pallet/mod.rs | 4 ---- frame/staking/src/tests.rs | 11 +++++++---- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index 2e67ce1c47a0f..b9932ce8400a0 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -32,7 +32,7 @@ use frame_support::{ use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use pallet_session::historical; use sp_runtime::{ - traits::{Bounded, Convert, SaturatedConversion, Saturating, StaticLookup, Zero}, + traits::{Bounded, Convert, SaturatedConversion, Saturating, StaticLookup, Zero, One}, Perbill, }; use sp_staking::{ @@ -599,18 +599,16 @@ impl Pallet { /// Apply previously-unapplied slashes on the beginning of a new era, after a delay. fn apply_unapplied_slashes(active_era: EraIndex) { - if ::UnappliedSlashes::contains_key(&active_era) { - let era_slashes = ::UnappliedSlashes::take(&active_era); - log!( - debug, - "found {} slashes scheduled to be executed in era {}", - active_era, - era_slashes.len() - ); - for slash in era_slashes { - let slash_era = active_era.saturating_sub(T::SlashDeferDuration::get()); - slashing::apply_slash::(slash, slash_era); - } + let era_slashes = ::UnappliedSlashes::take(&active_era); + log!( + debug, + "found {} slashes scheduled to be executed in era {:?}", + era_slashes.len(), + active_era, + ); + for slash in era_slashes { + let slash_era = active_era.saturating_sub(T::SlashDeferDuration::get()); + slashing::apply_slash::(slash, slash_era); } } @@ -1266,7 +1264,7 @@ where slash_era + slash_defer_duration + 1, ); ::UnappliedSlashes::mutate( - slash_era + slash_defer_duration + 1, + slash_era.saturating_add(slash_defer_duration).saturating_add(One::one()), move |for_later| for_later.push(unapplied), ); add_db_reads_writes(1, 1); diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index e53464195de23..c999020c28167 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -477,10 +477,6 @@ pub mod pallet { ValueQuery, >; - /// The earliest era for which we have a pending, unapplied slash. - #[pallet::storage] - pub(crate) type EarliestUnappliedSlash = StorageValue<_, EraIndex>; - /// The last planned session scheduled by the session pallet. /// /// This is basically in sync with the call to [`pallet_session::SessionManager::new_session`]. diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 3bb89c22eb03c..69e43afd90e1a 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2778,7 +2778,7 @@ fn deferred_slashes_are_deferred() { assert_eq!(Balances::free_balance(11), 1000); assert_eq!(Balances::free_balance(101), 2000); - let _ = staking_events_since_last_call(); + System::reset_events(); // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. @@ -2814,7 +2814,7 @@ fn retroactive_deferred_slashes_two_eras_before_() { 1, // should be deferred for two full eras, and applied at the beginning of era 4. DisableStrategy::Never, ); - let _ = staking_events_since_last_call(); + System::reset_events(); mock::start_active_era(4); @@ -2838,6 +2838,9 @@ fn retroactive_deferred_slashes_one_before() { mock::start_active_era(1); let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); + mock::start_active_era(2); + + mock::start_active_era(3); on_offence_in_era( &[OffenceDetails { offender: (11, exposure_11_at_era1), reporters: vec![] }], @@ -2845,7 +2848,7 @@ fn retroactive_deferred_slashes_one_before() { 2, // should be deferred for two full eras, and applied at the beginning of era 5. DisableStrategy::Never, ); - let _ = staking_events_since_last_call(); + System::reset_events(); mock::start_active_era(4); assert_eq!( @@ -2989,7 +2992,7 @@ fn remove_deferred() { // at the start of era 4, slashes from era 1 are processed, // after being deferred for at least 2 full eras. - let _ = staking_events_since_last_call(); + System::reset_events(); mock::start_active_era(4); // the first slash for 10% was cancelled, but the 15% one From 0475b290bd277fa8ee74ab7a093efccd41fdce94 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Wed, 13 Jul 2022 18:50:11 +0100 Subject: [PATCH 5/7] add more detail about unbonding --- frame/staking/src/tests.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index 69e43afd90e1a..eb27c9adb51b6 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -2838,8 +2838,10 @@ fn retroactive_deferred_slashes_one_before() { mock::start_active_era(1); let exposure_11_at_era1 = Staking::eras_stakers(active_era(), 11); + // unbond at slash era. mock::start_active_era(2); - + assert_ok!(Staking::chill(Origin::signed(10))); + assert_ok!(Staking::unbond(Origin::signed(10), 100)); mock::start_active_era(3); on_offence_in_era( @@ -2853,9 +2855,11 @@ fn retroactive_deferred_slashes_one_before() { mock::start_active_era(4); assert_eq!( staking_events_since_last_call(), - vec![Event::StakersElected, Event::EraPaid(3, 7100, 21300)] + vec![Event::StakersElected, Event::EraPaid(3, 11075, 33225)] ); + assert_eq!(Staking::ledger(10).unwrap().total, 1000); + // slash happens after the next line. mock::start_active_era(5); assert_eq!( staking_events_since_last_call(), @@ -2866,6 +2870,11 @@ fn retroactive_deferred_slashes_one_before() { Event::Slashed(101, 12) ] ); + + // their ledger has already been slashed. + assert_eq!(Staking::ledger(10).unwrap().total, 900); + assert_ok!(Staking::unbond(Origin::signed(10), 1000)); + assert_eq!(Staking::ledger(10).unwrap().total, 900); }) } From c831ff8f2887110f38747265e9e21031f6ed2b63 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 14 Jul 2022 10:09:20 +0100 Subject: [PATCH 6/7] add migration --- frame/staking/src/lib.rs | 1 + frame/staking/src/migrations.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 360d5b5efb58f..9f5698dad29f0 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -834,6 +834,7 @@ enum Releases { V7_0_0, // keep track of number of nominators / validators in map V8_0_0, // populate `VoterList`. V9_0_0, // inject validators into `VoterList` as well. + V10_0_0, // remove `EarliestUnappliedSlash`. } impl Default for Releases { diff --git a/frame/staking/src/migrations.rs b/frame/staking/src/migrations.rs index 14846da8a5d54..101cac0a31348 100644 --- a/frame/staking/src/migrations.rs +++ b/frame/staking/src/migrations.rs @@ -20,6 +20,29 @@ use super::*; use frame_election_provider_support::SortedListProvider; use frame_support::traits::OnRuntimeUpgrade; +pub mod v10 { + use super::*; + use frame_support::storage_alias; + + #[storage_alias] + type EarliestUnappliedSlash = StorageValue, EraIndex>; + + pub struct MigrateToV10(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV10 { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + if StorageVersion::::get() == Releases::V9_0_0 { + EarliestUnappliedSlash::::kill(); + StorageVersion::::put(Releases::V10_0_0); + + T::DbWeight::get().reads_writes(1, 1) + } else { + log!(warn, "MigrateToV10 should be removed."); + T::DbWeight::get().reads(1) + } + } + } +} + pub mod v9 { use super::*; From c07ec9bd0a39b145efab6cd84ebbf0929892e0de Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 26 Jul 2022 10:22:33 +0100 Subject: [PATCH 7/7] fmt --- frame/staking/src/lib.rs | 10 +++++----- frame/staking/src/pallet/impls.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index d61595d4e0381..3fff6312f333f 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -872,11 +872,11 @@ enum Releases { V2_0_0, V3_0_0, V4_0_0, - V5_0_0, // blockable validators. - V6_0_0, // removal of all storage associated with offchain phragmen. - V7_0_0, // keep track of number of nominators / validators in map - V8_0_0, // populate `VoterList`. - V9_0_0, // inject validators into `VoterList` as well. + V5_0_0, // blockable validators. + V6_0_0, // removal of all storage associated with offchain phragmen. + V7_0_0, // keep track of number of nominators / validators in map + V8_0_0, // populate `VoterList`. + V9_0_0, // inject validators into `VoterList` as well. V10_0_0, // remove `EarliestUnappliedSlash`. } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index b9932ce8400a0..68aa97db8a324 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -32,7 +32,7 @@ use frame_support::{ use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin}; use pallet_session::historical; use sp_runtime::{ - traits::{Bounded, Convert, SaturatedConversion, Saturating, StaticLookup, Zero, One}, + traits::{Bounded, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero}, Perbill, }; use sp_staking::{