diff --git a/frame/fast-unstake/src/lib.rs b/frame/fast-unstake/src/lib.rs index 7f226826cbc53..f2faeebc13478 100644 --- a/frame/fast-unstake/src/lib.rs +++ b/frame/fast-unstake/src/lib.rs @@ -91,7 +91,7 @@ pub mod pallet { DispatchResult, }; use sp_staking::{EraIndex, StakingInterface}; - use sp_std::{prelude::*, vec::Vec}; + use sp_std::{collections::btree_set::BTreeSet, prelude::*, vec::Vec}; pub use weights::WeightInfo; #[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)] @@ -429,9 +429,9 @@ pub mod pallet { } }; - let check_stash = |stash, deposit, eras_checked: &mut u32| { + let check_stash = |stash, deposit, eras_checked: &mut BTreeSet| { let is_exposed = unchecked_eras_to_check.iter().any(|e| { - eras_checked.saturating_inc(); + eras_checked.insert(*e); T::Staking::is_exposed_in_era(&stash, e) }); @@ -452,7 +452,7 @@ pub mod pallet { ::WeightInfo::on_idle_unstake() } else { // eras checked so far. - let mut eras_checked = 0u32; + let mut eras_checked = BTreeSet::::new(); let pre_length = stashes.len(); let stashes: BoundedVec<(T::AccountId, BalanceOf), T::BatchSize> = stashes @@ -468,7 +468,7 @@ pub mod pallet { log!( debug, "checked {:?} eras, pre stashes: {:?}, post: {:?}", - eras_checked, + eras_checked.len(), pre_length, post_length, ); @@ -489,7 +489,9 @@ pub mod pallet { }, } - ::WeightInfo::on_idle_check(validator_count * eras_checked) + ::WeightInfo::on_idle_check( + validator_count * eras_checked.len() as u32, + ) } } } diff --git a/frame/staking/src/pallet/impls.rs b/frame/staking/src/pallet/impls.rs index a7190d70c7061..db9aeba6fb58e 100644 --- a/frame/staking/src/pallet/impls.rs +++ b/frame/staking/src/pallet/impls.rs @@ -1569,7 +1569,7 @@ impl StakingInterface for Pallet { } fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult { - let num_slashing_spans = Self::slashing_spans(&who).iter().count() as u32; + let num_slashing_spans = Self::slashing_spans(&who).map_or(0, |s| s.iter().count() as u32); Self::force_unstake(RawOrigin::Root.into(), who.clone(), num_slashing_spans) } diff --git a/frame/staking/src/pallet/mod.rs b/frame/staking/src/pallet/mod.rs index 1d5babe7ffa8f..f04d13d05f468 100644 --- a/frame/staking/src/pallet/mod.rs +++ b/frame/staking/src/pallet/mod.rs @@ -982,7 +982,8 @@ pub mod pallet { // `BondingDuration` to proceed with the unbonding. let maybe_withdraw_weight = { if unlocking == T::MaxUnlockingChunks::get() as usize { - let real_num_slashing_spans = Self::slashing_spans(&controller).iter().count(); + let real_num_slashing_spans = + Self::slashing_spans(&controller).map_or(0, |s| s.iter().count()); Some(Self::do_withdraw_unbonded(&controller, real_num_slashing_spans as u32)?) } else { None diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index fc6fc68e66d5d..5c2766deee307 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -5725,3 +5725,54 @@ fn scale_validator_count_errors() { ); }) } + +mod staking_interface { + use frame_support::storage::with_storage_layer; + use sp_staking::StakingInterface; + + use super::*; + + #[test] + fn force_unstake_with_slash_works() { + ExtBuilder::default().build_and_execute(|| { + // without slash + let _ = with_storage_layer::<(), _, _>(|| { + // bond an account, can unstake + assert_eq!(Staking::bonded(&11), Some(10)); + assert_ok!(::force_unstake(11)); + Err(DispatchError::from("revert")) + }); + + // bond again and add a slash, still can unstake. + assert_eq!(Staking::bonded(&11), Some(10)); + add_slash(&11); + assert_ok!(::force_unstake(11)); + }); + } + + #[test] + fn do_withdraw_unbonded_with_wrong_slash_spans_works_as_expected() { + ExtBuilder::default().build_and_execute(|| { + on_offence_now( + &[OffenceDetails { + offender: (11, Staking::eras_stakers(active_era(), 11)), + reporters: vec![], + }], + &[Perbill::from_percent(100)], + ); + + assert_eq!(Staking::bonded(&11), Some(10)); + + assert_noop!( + Staking::withdraw_unbonded(RuntimeOrigin::signed(10), 0), + Error::::IncorrectSlashingSpans + ); + + let num_slashing_spans = Staking::slashing_spans(&11).map_or(0, |s| s.iter().count()); + assert_ok!(Staking::withdraw_unbonded( + RuntimeOrigin::signed(10), + num_slashing_spans as u32 + )); + }); + } +}