Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions frame/fast-unstake/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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",
Expand Down
30 changes: 3 additions & 27 deletions frame/fast-unstake/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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);

Expand All @@ -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() })
);
}
: {
Expand Down Expand Up @@ -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.
Expand All @@ -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(),
Expand All @@ -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);
}
Expand All @@ -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);
Expand Down
95 changes: 20 additions & 75 deletions frame/fast-unstake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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},
Expand All @@ -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 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later on, you can also tackle another part of #12337 and implement this via trait StakingInterface (if it supports all the needed APIs).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much cleaner

/// The overarching event type.
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as frame_system::Config>::RuntimeEvent>
Expand All @@ -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.
///
Expand All @@ -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.
Expand Down Expand Up @@ -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 =
Expand All @@ -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.
///
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much simpler now, nice.

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.
Expand Down Expand Up @@ -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,
Expand Down
Loading