[Enhancement] Remove optional Pool subscription from fast-unstake (#12344)

* [Enhancement] Remove optional Pool subscription from fast-unstake

* remove nomination-pools pallet dependency

* fixes

* more fixes

* more fixes

* more fixes
This commit is contained in:
Roman Useinov
2022-09-26 08:05:05 +02:00
committed by GitHub
parent 28637fcb8f
commit 6fee4cb3f2
7 changed files with 105 additions and 378 deletions
-1
View File
@@ -5729,7 +5729,6 @@ dependencies = [
"frame-system",
"log",
"pallet-balances",
"pallet-nomination-pools",
"pallet-staking",
"pallet-staking-reward-curve",
"pallet-timestamp",
-2
View File
@@ -28,7 +28,6 @@ sp-staking = { default-features = false, path = "../../primitives/staking" }
pallet-balances = { default-features = false, path = "../balances" }
pallet-timestamp = { default-features = false, path = "../timestamp" }
pallet-staking = { default-features = false, path = "../staking" }
pallet-nomination-pools = { default-features = false, path = "../nomination-pools" }
frame-election-provider-support = { default-features = false, path = "../election-provider-support" }
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
@@ -55,7 +54,6 @@ std = [
"sp-std/std",
"pallet-staking/std",
"pallet-nomination-pools/std",
"pallet-balances/std",
"pallet-timestamp/std",
"frame-election-provider-support/std",
@@ -26,7 +26,6 @@ use frame_support::{
traits::{Currency, EnsureOrigin, Get, Hooks},
};
use frame_system::RawOrigin;
use pallet_nomination_pools::{Pallet as Pools, PoolId};
use pallet_staking::Pallet as Staking;
use sp_runtime::traits::{StaticLookup, Zero};
use sp_staking::EraIndex;
@@ -76,25 +75,6 @@ pub(crate) fn fast_unstake_events<T: Config>() -> Vec<crate::Event<T>> {
.collect::<Vec<_>>()
}
fn setup_pool<T: Config>() -> PoolId {
let depositor = frame_benchmarking::account::<T::AccountId>("depositor_42", 0, USER_SEED);
let depositor_lookup = l::<T>(depositor.clone());
let stake = Pools::<T>::depositor_min_bond();
CurrencyOf::<T>::make_free_balance_be(&depositor, stake * 10u32.into());
Pools::<T>::create(
RawOrigin::Signed(depositor.clone()).into(),
stake,
depositor_lookup.clone(),
depositor_lookup.clone(),
depositor_lookup,
)
.unwrap();
pallet_nomination_pools::LastPoolId::<T>::get()
}
fn setup_staking<T: Config>(v: u32, until: EraIndex) {
let ed = CurrencyOf::<T>::minimum_balance();
@@ -131,10 +111,8 @@ benchmarks! {
// on_idle, we we don't check anyone, but fully unbond and move them to another pool.
on_idle_unstake {
let who = create_unexposed_nominator::<T>();
let pool_id = setup_pool::<T>();
assert_ok!(FastUnstake::<T>::register_fast_unstake(
RawOrigin::Signed(who.clone()).into(),
Some(pool_id)
));
ErasToCheckPerBlock::<T>::put(1);
@@ -143,7 +121,7 @@ benchmarks! {
on_idle_full_block::<T>();
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: who.clone(), checked: vec![0].try_into().unwrap(), maybe_pool_id: Some(pool_id) })
Some(UnstakeRequest { stash: who.clone(), checked: vec![0].try_into().unwrap() })
);
}
: {
@@ -172,7 +150,6 @@ benchmarks! {
let who = create_unexposed_nominator::<T>();
assert_ok!(FastUnstake::<T>::register_fast_unstake(
RawOrigin::Signed(who.clone()).into(),
None,
));
// no one is queued thus far.
@@ -185,7 +162,7 @@ benchmarks! {
let checked: frame_support::BoundedVec<_, _> = (1..=u).rev().collect::<Vec<EraIndex>>().try_into().unwrap();
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: who.clone(), checked, maybe_pool_id: None })
Some(UnstakeRequest { stash: who.clone(), checked })
);
assert!(matches!(
fast_unstake_events::<T>().last(),
@@ -199,7 +176,7 @@ benchmarks! {
assert_eq!(Queue::<T>::count(), 0);
}
:_(RawOrigin::Signed(who.clone()), None)
:_(RawOrigin::Signed(who.clone()))
verify {
assert_eq!(Queue::<T>::count(), 1);
}
@@ -208,7 +185,6 @@ benchmarks! {
let who = create_unexposed_nominator::<T>();
assert_ok!(FastUnstake::<T>::register_fast_unstake(
RawOrigin::Signed(who.clone()).into(),
None
));
assert_eq!(Queue::<T>::count(), 1);
whitelist_account!(who);
+20 -75
View File
@@ -19,8 +19,7 @@
//!
//! If a nominator is not exposed in any `ErasStakers` (i.e. "has not actively backed any
//! validators in the last `BondingDuration` days"), then they can register themselves in this
//! pallet, unstake faster than having to wait an entire bonding duration, and potentially move
//! into a nomination pool.
//! pallet, unstake faster than having to wait an entire bonding duration.
//!
//! Appearing in the exposure of a validator means being exposed equal to that validator from the
//! point of view of the staking system. This usually means earning rewards with the validator, and
@@ -43,8 +42,7 @@
//! to prevent them from accidentally exposing themselves behind a validator etc.
//!
//! Once processed, if successful, no additional fee for the checking process is taken, and the
//! staker is instantly unbonded. Optionally, if they have asked to join a pool, their *entire*
//! stake is joined into their pool of choice.
//! staker is instantly unbonded.
//!
//! If unsuccessful, meaning that the staker was exposed sometime in the last `BondingDuration` eras
//! they will end up being slashed for the amount of wasted work they have inflicted on the chian.
@@ -85,7 +83,6 @@ pub mod pallet {
use frame_election_provider_support::ElectionProvider;
use frame_support::pallet_prelude::*;
use frame_system::{pallet_prelude::*, RawOrigin};
use pallet_nomination_pools::PoolId;
use pallet_staking::Pallet as Staking;
use sp_runtime::{
traits::{Saturating, Zero},
@@ -109,12 +106,7 @@ pub mod pallet {
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config:
frame_system::Config
+ pallet_staking::Config<
CurrencyBalance = <Self as pallet_nomination_pools::Config>::CurrencyBalance,
> + pallet_nomination_pools::Config
{
pub trait Config: frame_system::Config + pallet_staking::Config {
/// The overarching event type.
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>
@@ -139,10 +131,9 @@ pub mod pallet {
/// The map of all accounts wishing to be unstaked.
///
/// Points the `AccountId` wishing to unstake to the optional `PoolId` they wish to join
/// thereafter.
/// Keeps track of `AccountId` wishing to unstake.
#[pallet::storage]
pub type Queue<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, Option<PoolId>>;
pub type Queue<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
/// Number of eras to check per block.
///
@@ -158,7 +149,7 @@ pub mod pallet {
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A staker was unstaked.
Unstaked { stash: T::AccountId, maybe_pool_id: Option<PoolId>, result: DispatchResult },
Unstaked { stash: T::AccountId, result: DispatchResult },
/// A staker was slashed for requesting fast-unstake whilst being exposed.
Slashed { stash: T::AccountId, amount: BalanceOf<T> },
/// A staker was partially checked for the given eras, but the process did not finish.
@@ -213,16 +204,13 @@ pub mod pallet {
/// they are guaranteed to remain eligible, because the call will chill them as well.
///
/// If the check works, the entire staking data is removed, i.e. the stash is fully
/// unstaked, and they potentially join a pool with their entire bonded stake.
/// unstaked.
///
/// If the check fails, the stash remains chilled and waiting for being unbonded as in with
/// the normal staking system, but they lose part of their unbonding chunks due to consuming
/// the chain's resources.
#[pallet::weight(<T as Config>::WeightInfo::register_fast_unstake())]
pub fn register_fast_unstake(
origin: OriginFor<T>,
maybe_pool_id: Option<PoolId>,
) -> DispatchResult {
pub fn register_fast_unstake(origin: OriginFor<T>) -> DispatchResult {
let ctrl = ensure_signed(origin)?;
let ledger =
@@ -243,12 +231,11 @@ pub mod pallet {
Staking::<T>::unbond(RawOrigin::Signed(ctrl).into(), ledger.total)?;
// enqueue them.
Queue::<T>::insert(ledger.stash, maybe_pool_id);
Queue::<T>::insert(ledger.stash, ());
Ok(())
}
/// Deregister oneself from the fast-unstake (also cancels joining the pool if that was
/// supplied on `register_fast_unstake` .
/// Deregister oneself from the fast-unstake.
///
/// This is useful if one is registered, they are still waiting, and they change their mind.
///
@@ -327,17 +314,12 @@ pub mod pallet {
return T::DbWeight::get().reads(2)
}
let UnstakeRequest { stash, mut checked, maybe_pool_id } = match Head::<T>::take()
.or_else(|| {
// NOTE: there is no order guarantees in `Queue`.
Queue::<T>::drain()
.map(|(stash, maybe_pool_id)| UnstakeRequest {
stash,
maybe_pool_id,
checked: Default::default(),
})
.next()
}) {
let UnstakeRequest { stash, mut checked } = match Head::<T>::take().or_else(|| {
// NOTE: there is no order guarantees in `Queue`.
Queue::<T>::drain()
.map(|(stash, _)| UnstakeRequest { stash, checked: Default::default() })
.next()
}) {
None => {
// There's no `Head` and nothing in the `Queue`, nothing to do here.
return T::DbWeight::get().reads(4)
@@ -392,48 +374,15 @@ pub mod pallet {
// `stash` is not exposed in any era now -- we can let go of them now.
let num_slashing_spans = Staking::<T>::slashing_spans(&stash).iter().count() as u32;
let ctrl = match pallet_staking::Bonded::<T>::get(&stash) {
Some(ctrl) => ctrl,
None => {
Self::deposit_event(Event::<T>::Errored { stash });
return <T as Config>::WeightInfo::on_idle_unstake()
},
};
let ledger = match pallet_staking::Ledger::<T>::get(ctrl) {
Some(ledger) => ledger,
None => {
Self::deposit_event(Event::<T>::Errored { stash });
return <T as Config>::WeightInfo::on_idle_unstake()
},
};
let unstake_result = pallet_staking::Pallet::<T>::force_unstake(
let result = pallet_staking::Pallet::<T>::force_unstake(
RawOrigin::Root.into(),
stash.clone(),
num_slashing_spans,
);
let pool_stake_result = if let Some(pool_id) = maybe_pool_id {
pallet_nomination_pools::Pallet::<T>::join(
RawOrigin::Signed(stash.clone()).into(),
ledger.total,
pool_id,
)
} else {
Ok(())
};
log!(info, "unstaked {:?}, outcome: {:?}", stash, result);
let result = unstake_result.and(pool_stake_result);
log!(
info,
"unstaked {:?}, maybe_pool {:?}, outcome: {:?}",
stash,
maybe_pool_id,
result
);
Self::deposit_event(Event::<T>::Unstaked { stash, maybe_pool_id, result });
Self::deposit_event(Event::<T>::Unstaked { stash, result });
<T as Config>::WeightInfo::on_idle_unstake()
} else {
// eras remaining to be checked.
@@ -471,11 +420,7 @@ pub mod pallet {
// Not exposed in these eras.
match checked.try_extend(unchecked_eras_to_check.clone().into_iter()) {
Ok(_) => {
Head::<T>::put(UnstakeRequest {
stash: stash.clone(),
checked,
maybe_pool_id,
});
Head::<T>::put(UnstakeRequest { stash: stash.clone(), checked });
Self::deposit_event(Event::<T>::Checking {
stash,
eras: unchecked_eras_to_check,
+3 -47
View File
@@ -17,19 +17,10 @@
use crate::{self as fast_unstake};
use frame_support::{
assert_ok,
pallet_prelude::*,
parameter_types,
traits::{ConstU64, ConstU8, Currency},
weights::constants::WEIGHT_PER_SECOND,
PalletId,
};
use sp_runtime::{
traits::{Convert, IdentityLookup},
FixedU128,
pallet_prelude::*, parameter_types, traits::ConstU64, weights::constants::WEIGHT_PER_SECOND,
};
use sp_runtime::traits::{Convert, IdentityLookup};
use frame_system::RawOrigin;
use pallet_staking::{Exposure, IndividualExposure, StakerStatus};
use sp_std::prelude::*;
@@ -153,7 +144,7 @@ impl pallet_staking::Config for Runtime {
type VoterList = pallet_staking::UseNominatorsAndValidatorsMap<Self>;
type TargetList = pallet_staking::UseValidatorsMap<Self>;
type MaxUnlockingChunks = ConstU32<32>;
type OnStakerSlash = Pools;
type OnStakerSlash = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
}
@@ -172,29 +163,6 @@ impl Convert<sp_core::U256, Balance> for U256ToBalance {
}
}
parameter_types! {
pub const PostUnbondingPoolsWindow: u32 = 10;
pub const PoolsPalletId: PalletId = PalletId(*b"py/nopls");
pub static MaxMetadataLen: u32 = 10;
pub static CheckLevel: u8 = 255;
}
impl pallet_nomination_pools::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type Currency = Balances;
type CurrencyBalance = Balance;
type RewardCounter = FixedU128;
type BalanceToU256 = BalanceToU256;
type U256ToBalance = U256ToBalance;
type StakingInterface = Staking;
type PostUnbondingPoolsWindow = PostUnbondingPoolsWindow;
type MaxMetadataLen = MaxMetadataLen;
type MaxUnbonding = ConstU32<8>;
type MaxPointsToBalance = ConstU8<10>;
type PalletId = PoolsPalletId;
}
parameter_types! {
pub static SlashPerEra: u32 = 100;
}
@@ -218,7 +186,6 @@ frame_support::construct_runtime!(
Timestamp: pallet_timestamp,
Balances: pallet_balances,
Staking: pallet_staking,
Pools: pallet_nomination_pools,
FastUnstake: fast_unstake,
}
);
@@ -287,10 +254,6 @@ impl ExtBuilder {
let mut storage =
frame_system::GenesisConfig::default().build_storage::<Runtime>().unwrap();
// create one default pool.
let _ = pallet_nomination_pools::GenesisConfig::<Runtime> { ..Default::default() }
.assimilate_storage(&mut storage);
let validators_range = VALIDATOR_PREFIX..VALIDATOR_PREFIX + VALIDATORS_PER_ERA;
let nominators_range =
NOMINATOR_PREFIX..NOMINATOR_PREFIX + NOMINATORS_PER_VALIDATOR_PER_ERA;
@@ -337,11 +300,6 @@ impl ExtBuilder {
// because we read this value as a measure of how many validators we have.
pallet_staking::ValidatorCount::<Runtime>::put(VALIDATORS_PER_ERA as u32);
// make a pool
let amount_to_bond = Pools::depositor_min_bond();
Balances::make_free_balance_be(&10, amount_to_bond * 5);
assert_ok!(Pools::create(RawOrigin::Signed(10).into(), amount_to_bond, 900, 901, 902));
});
ext
}
@@ -359,14 +317,12 @@ pub(crate) fn run_to_block(n: u64, on_idle: bool) {
while System::block_number() < n {
Balances::on_finalize(System::block_number());
Staking::on_finalize(System::block_number());
Pools::on_finalize(System::block_number());
FastUnstake::on_finalize(System::block_number());
System::set_block_number(System::block_number() + 1);
Balances::on_initialize(System::block_number());
Staking::on_initialize(System::block_number());
Pools::on_initialize(System::block_number());
FastUnstake::on_initialize(System::block_number());
if on_idle {
FastUnstake::on_idle(System::block_number(), BlockWeights::get().max_block);
+79 -223
View File
@@ -20,20 +20,15 @@
use super::*;
use crate::{mock::*, types::*, weights::WeightInfo, Event};
use frame_support::{assert_noop, assert_ok, bounded_vec, pallet_prelude::*, traits::Currency};
use pallet_nomination_pools::{BondedPools, LastPoolId, RewardPools};
use pallet_staking::{CurrentEra, IndividualExposure, RewardDestination};
use sp_runtime::{traits::BadOrigin, DispatchError, ModuleError};
use sp_runtime::traits::BadOrigin;
use sp_staking::StakingInterface;
#[test]
fn test_setup_works() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(BondedPools::<T>::count(), 1);
assert_eq!(RewardPools::<T>::count(), 1);
assert_eq!(Staking::bonding_duration(), 3);
let last_pool = LastPoolId::<T>::get();
assert_eq!(last_pool, 1);
});
}
@@ -41,7 +36,7 @@ fn test_setup_works() {
fn register_works() {
ExtBuilder::default().build_and_execute(|| {
// Controller account registers for fast unstake.
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
// Ensure stash is in the queue.
assert_ne!(Queue::<T>::get(1), None);
});
@@ -56,7 +51,7 @@ fn cannot_register_if_not_bonded() {
}
// Attempt to fast unstake.
assert_noop!(
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1), Some(1_u32)),
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(1)),
Error::<T>::NotController
);
});
@@ -66,10 +61,10 @@ fn cannot_register_if_not_bonded() {
fn cannot_register_if_in_queue() {
ExtBuilder::default().build_and_execute(|| {
// Insert some Queue item
Queue::<T>::insert(1, Some(1_u32));
Queue::<T>::insert(1, ());
// Cannot re-register, already in queue
assert_noop!(
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)),
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)),
Error::<T>::AlreadyQueued
);
});
@@ -79,10 +74,10 @@ fn cannot_register_if_in_queue() {
fn cannot_register_if_head() {
ExtBuilder::default().build_and_execute(|| {
// Insert some Head item for stash
Head::<T>::put(UnstakeRequest { stash: 1, checked: bounded_vec![], maybe_pool_id: None });
Head::<T>::put(UnstakeRequest { stash: 1, checked: bounded_vec![] });
// Controller attempts to regsiter
assert_noop!(
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)),
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)),
Error::<T>::AlreadyHead
);
});
@@ -95,7 +90,7 @@ fn cannot_register_if_has_unlocking_chunks() {
assert_ok!(Staking::unbond(RuntimeOrigin::signed(2), 50_u128));
// Cannot register for fast unstake with unlock chunks active
assert_noop!(
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)),
FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)),
Error::<T>::NotFullyBonded
);
});
@@ -105,7 +100,7 @@ fn cannot_register_if_has_unlocking_chunks() {
fn deregister_works() {
ExtBuilder::default().build_and_execute(|| {
// Controller account registers for fast unstake.
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
// Controller then changes mind and deregisters.
assert_ok!(FastUnstake::deregister(RuntimeOrigin::signed(2)));
// Ensure stash no longer exists in the queue.
@@ -117,7 +112,7 @@ fn deregister_works() {
fn cannot_deregister_if_not_controller() {
ExtBuilder::default().build_and_execute(|| {
// Controller account registers for fast unstake.
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
// Stash tries to deregister.
assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(1)), Error::<T>::NotController);
});
@@ -135,9 +130,9 @@ fn cannot_deregister_if_not_queued() {
fn cannot_deregister_already_head() {
ExtBuilder::default().build_and_execute(|| {
// Controller attempts to register, should fail
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
// Insert some Head item for stash.
Head::<T>::put(UnstakeRequest { stash: 1, checked: bounded_vec![], maybe_pool_id: None });
Head::<T>::put(UnstakeRequest { stash: 1, checked: bounded_vec![] });
// Controller attempts to deregister
assert_noop!(FastUnstake::deregister(RuntimeOrigin::signed(2)), Error::<T>::AlreadyHead);
});
@@ -169,15 +164,15 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// set up Queue item
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1)));
assert_eq!(Queue::<T>::get(1), Some(Some(1)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(Queue::<T>::get(1), Some(()));
// call on_idle with no remaining weight
FastUnstake::on_idle(System::block_number(), Weight::from_ref_time(0));
// assert nothing changed in Queue and Head
assert_eq!(Head::<T>::get(), None);
assert_eq!(Queue::<T>::get(1), Some(Some(1)));
assert_eq!(Queue::<T>::get(1), Some(()));
});
}
@@ -189,8 +184,8 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// given
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1)));
assert_eq!(Queue::<T>::get(1), Some(Some(1)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(Queue::<T>::get(1), Some(()));
assert_eq!(Queue::<T>::count(), 1);
assert_eq!(Head::<T>::get(), None);
@@ -209,7 +204,7 @@ mod on_idle {
);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3], maybe_pool_id: Some(1) })
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3] })
);
// when: another 1 era.
@@ -225,11 +220,7 @@ mod on_idle {
);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2] })
);
// when: then 5 eras, we only need 2 more.
@@ -251,11 +242,7 @@ mod on_idle {
);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1, 0] })
);
// when: not enough weight to unstake:
@@ -267,11 +254,7 @@ mod on_idle {
assert_eq!(fast_unstake_events_since_last_call(), vec![]);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1, 0] })
);
// when: enough weight to get over at least one iteration: then we are unblocked and can
@@ -287,7 +270,7 @@ mod on_idle {
// then we finish the unbonding:
assert_eq!(
fast_unstake_events_since_last_call(),
vec![Event::Unstaked { stash: 1, maybe_pool_id: Some(1), result: Ok(()) }]
vec![Event::Unstaked { stash: 1, result: Ok(()) }]
);
assert_eq!(Head::<T>::get(), None,);
@@ -302,11 +285,11 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// given
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(6), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(8), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(10), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(6)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(8)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(10)));
assert_eq!(Queue::<T>::count(), 5);
assert_eq!(Head::<T>::get(), None);
@@ -317,11 +300,7 @@ mod on_idle {
// then
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1, 0] })
);
assert_eq!(Queue::<T>::count(), 4);
@@ -338,11 +317,7 @@ mod on_idle {
// then
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 5,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: None
}),
Some(UnstakeRequest { stash: 5, checked: bounded_vec![3, 2, 1, 0] }),
);
assert_eq!(Queue::<T>::count(), 3);
@@ -350,7 +325,7 @@ mod on_idle {
fast_unstake_events_since_last_call(),
vec![
Event::Checking { stash: 1, eras: vec![3, 2, 1, 0] },
Event::Unstaked { stash: 1, maybe_pool_id: None, result: Ok(()) },
Event::Unstaked { stash: 1, result: Ok(()) },
Event::Checking { stash: 5, eras: vec![3, 2, 1, 0] }
]
);
@@ -364,10 +339,10 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// register multi accounts for fast unstake
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1)));
assert_eq!(Queue::<T>::get(1), Some(Some(1)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4), Some(1)));
assert_eq!(Queue::<T>::get(3), Some(Some(1)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(Queue::<T>::get(1), Some(()));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(4)));
assert_eq!(Queue::<T>::get(3), Some(()));
// assert 2 queue items are in Queue & None in Head to start with
assert_eq!(Queue::<T>::count(), 2);
@@ -397,9 +372,9 @@ mod on_idle {
fast_unstake_events_since_last_call(),
vec![
Event::Checking { stash: 1, eras: vec![3, 2, 1, 0] },
Event::Unstaked { stash: 1, maybe_pool_id: Some(1), result: Ok(()) },
Event::Unstaked { stash: 1, result: Ok(()) },
Event::Checking { stash: 3, eras: vec![3, 2, 1, 0] },
Event::Unstaked { stash: 3, maybe_pool_id: Some(1), result: Ok(()) },
Event::Unstaked { stash: 3, result: Ok(()) },
]
);
@@ -409,14 +384,14 @@ mod on_idle {
}
#[test]
fn successful_unstake_without_pool_join() {
fn successful_unstake() {
ExtBuilder::default().build_and_execute(|| {
ErasToCheckPerBlock::<T>::put(BondingDuration::get() + 1);
CurrentEra::<T>::put(BondingDuration::get());
// register for fast unstake
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), None));
assert_eq!(Queue::<T>::get(1), Some(None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(Queue::<T>::get(1), Some(()));
// process on idle
next_block(true);
@@ -427,11 +402,7 @@ mod on_idle {
// assert head item present
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1, 0] })
);
next_block(true);
@@ -441,55 +412,7 @@ mod on_idle {
fast_unstake_events_since_last_call(),
vec![
Event::Checking { stash: 1, eras: vec![3, 2, 1, 0] },
Event::Unstaked { stash: 1, maybe_pool_id: None, result: Ok(()) }
]
);
assert_unstaked(&1);
});
}
#[test]
fn successful_unstake_joining_bad_pool() {
ExtBuilder::default().build_and_execute(|| {
ErasToCheckPerBlock::<T>::put(BondingDuration::get() + 1);
CurrentEra::<T>::put(BondingDuration::get());
// register for fast unstake
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(0)));
assert_eq!(Queue::<T>::get(1), Some(Some(0)));
// process on idle
next_block(true);
// assert queue item has been moved to head
assert_eq!(Queue::<T>::get(1), None);
// assert head item present
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: Some(0)
})
);
next_block(true);
assert_eq!(Head::<T>::get(), None,);
assert_eq!(
fast_unstake_events_since_last_call(),
vec![
Event::Checking { stash: 1, eras: vec![3, 2, 1, 0] },
Event::Unstaked {
stash: 1,
maybe_pool_id: Some(0),
result: Err(DispatchError::Module(ModuleError {
index: 4,
error: [0, 0, 0, 0],
message: None
}))
}
Event::Unstaked { stash: 1, result: Ok(()) }
]
);
assert_unstaked(&1);
@@ -503,8 +426,8 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// register for fast unstake
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)));
assert_eq!(Queue::<T>::get(1), Some(Some(1)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(Queue::<T>::get(1), Some(()));
// process on idle
next_block(true);
@@ -515,11 +438,7 @@ mod on_idle {
// assert head item present
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1, 0] })
);
next_block(true);
@@ -529,11 +448,10 @@ mod on_idle {
fast_unstake_events_since_last_call(),
vec![
Event::Checking { stash: 1, eras: vec![3, 2, 1, 0] },
Event::Unstaked { stash: 1, maybe_pool_id: Some(1), result: Ok(()) }
Event::Unstaked { stash: 1, result: Ok(()) }
]
);
assert_unstaked(&1);
assert!(pallet_nomination_pools::PoolMembers::<T>::contains_key(&1));
});
}
@@ -545,8 +463,8 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// register for fast unstake
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)));
assert_eq!(Queue::<T>::get(1), Some(Some(1)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(Queue::<T>::get(1), Some(()));
// process on idle
next_block(true);
@@ -557,40 +475,28 @@ mod on_idle {
// assert head item present
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3], maybe_pool_id: Some(1) })
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1, 0] })
);
next_block(true);
@@ -604,11 +510,10 @@ mod on_idle {
Event::Checking { stash: 1, eras: vec![2] },
Event::Checking { stash: 1, eras: vec![1] },
Event::Checking { stash: 1, eras: vec![0] },
Event::Unstaked { stash: 1, maybe_pool_id: Some(1), result: Ok(()) }
Event::Unstaked { stash: 1, result: Ok(()) }
]
);
assert_unstaked(&1);
assert!(pallet_nomination_pools::PoolMembers::<T>::contains_key(&1));
});
}
@@ -623,39 +528,31 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// register for fast unstake
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), None));
assert_eq!(Queue::<T>::get(1), Some(None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
assert_eq!(Queue::<T>::get(1), Some(()));
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3], maybe_pool_id: None })
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2], maybe_pool_id: None })
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 1, 0] })
);
// when: a new era happens right before one is free.
@@ -670,7 +567,6 @@ mod on_idle {
stash: 1,
// note era 0 is pruned to keep the vector length sane.
checked: bounded_vec![3, 2, 1, 4],
maybe_pool_id: None
})
);
@@ -685,7 +581,7 @@ mod on_idle {
Event::Checking { stash: 1, eras: vec![1] },
Event::Checking { stash: 1, eras: vec![0] },
Event::Checking { stash: 1, eras: vec![4] },
Event::Unstaked { stash: 1, maybe_pool_id: None, result: Ok(()) }
Event::Unstaked { stash: 1, result: Ok(()) }
]
);
assert_unstaked(&1);
@@ -700,23 +596,19 @@ mod on_idle {
CurrentEra::<T>::put(BondingDuration::get());
// register for fast unstake
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), Some(1_u32)));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
// process 2 blocks
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3], maybe_pool_id: Some(1) })
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2] })
);
// when
@@ -726,21 +618,13 @@ mod on_idle {
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2] })
);
// then we register a new era.
@@ -752,22 +636,14 @@ mod on_idle {
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 4],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 4] })
);
// progress to end
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 1,
checked: bounded_vec![3, 2, 4, 1],
maybe_pool_id: Some(1)
})
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3, 2, 4, 1] })
);
// but notice that we don't care about era 0 instead anymore! we're done.
@@ -781,12 +657,11 @@ mod on_idle {
Event::Checking { stash: 1, eras: vec![2] },
Event::Checking { stash: 1, eras: vec![4] },
Event::Checking { stash: 1, eras: vec![1] },
Event::Unstaked { stash: 1, maybe_pool_id: Some(1), result: Ok(()) }
Event::Unstaked { stash: 1, result: Ok(()) }
]
);
assert_unstaked(&1);
assert!(pallet_nomination_pools::PoolMembers::<T>::contains_key(&1));
});
}
@@ -812,26 +687,18 @@ mod on_idle {
assert_ok!(Staking::nominate(RuntimeOrigin::signed(exposed), vec![exposed]));
// register the exposed one.
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(exposed), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(exposed)));
// a few blocks later, we realize they are slashed
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: exposed,
checked: bounded_vec![3],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: exposed, checked: bounded_vec![3] })
);
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: exposed,
checked: bounded_vec![3, 2],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: exposed, checked: bounded_vec![3, 2] })
);
next_block(true);
assert_eq!(Head::<T>::get(), None);
@@ -872,17 +739,13 @@ mod on_idle {
assert_ok!(Staking::nominate(RuntimeOrigin::signed(exposed), vec![exposed]));
// register the exposed one.
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(exposed), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(exposed)));
// a few blocks later, we realize they are slashed
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: exposed,
checked: bounded_vec![3, 2],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: exposed, checked: bounded_vec![3, 2] })
);
next_block(true);
assert_eq!(Head::<T>::get(), None);
@@ -909,10 +772,7 @@ mod on_idle {
RuntimeOrigin::signed(VALIDATOR_PREFIX),
vec![VALIDATOR_PREFIX]
));
assert_ok!(FastUnstake::register_fast_unstake(
RuntimeOrigin::signed(VALIDATOR_PREFIX),
None
));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(VALIDATOR_PREFIX)));
// but they indeed are exposed!
assert!(pallet_staking::ErasStakers::<T>::contains_key(
@@ -943,17 +803,13 @@ mod on_idle {
assert_ok!(Staking::validate(RuntimeOrigin::signed(42), Default::default()));
// let them register:
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(42), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(42)));
// 2 block's enough to unstake them.
next_block(true);
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest {
stash: 42,
checked: bounded_vec![3, 2, 1, 0],
maybe_pool_id: None
})
Some(UnstakeRequest { stash: 42, checked: bounded_vec![3, 2, 1, 0] })
);
next_block(true);
assert_eq!(Head::<T>::get(), None);
@@ -962,7 +818,7 @@ mod on_idle {
fast_unstake_events_since_last_call(),
vec![
Event::Checking { stash: 42, eras: vec![3, 2, 1, 0] },
Event::Unstaked { stash: 42, maybe_pool_id: None, result: Ok(()) }
Event::Unstaked { stash: 42, result: Ok(()) }
]
);
});
@@ -990,7 +846,7 @@ mod signed_extension {
ExtBuilder::default().build_and_execute(|| {
// given: stash for 2 is 1.
// when
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
// then
// stash can't.
@@ -1010,7 +866,7 @@ mod signed_extension {
ExtBuilder::default().build_and_execute(|| {
// given: stash for 2 is 1.
// when
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2), None));
assert_ok!(FastUnstake::register_fast_unstake(RuntimeOrigin::signed(2)));
ErasToCheckPerBlock::<T>::put(1);
CurrentEra::<T>::put(BondingDuration::get());
@@ -1018,7 +874,7 @@ mod signed_extension {
assert_eq!(
Head::<T>::get(),
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3], maybe_pool_id: None })
Some(UnstakeRequest { stash: 1, checked: bounded_vec![3] })
);
// then
@@ -23,7 +23,6 @@ use frame_support::{
traits::{Currency, Get, IsSubType},
BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
};
use pallet_nomination_pools::PoolId;
use scale_info::TypeInfo;
use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidityError};
use sp_staking::EraIndex;
@@ -42,8 +41,6 @@ pub struct UnstakeRequest<AccountId: Eq + PartialEq + Debug, MaxChecked: Get<u32
pub(crate) stash: AccountId,
/// The list of eras for which they have been checked.
pub(crate) checked: BoundedVec<EraIndex, MaxChecked>,
/// The pool they wish to join, if any.
pub(crate) maybe_pool_id: Option<PoolId>,
}
#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo, RuntimeDebugNoBound)]