Automatic withdraw_unbonded upon unbond (#12582)

* Prevents max unbonding chunk slots from being filled when unbonding in the staking pallet

* hardcode num_slashing to unlock chunks automatically

* refactor withdraw logic to do_withdraw; idiomatic rust improvements

* a

* callable unbond() to return a DispatchWithPostInfo to dynamically update the consumed weight

* refunds overpaid fees when unbond with withdraw

* fetches real slashing spans before withdrawal call

* nits

* addresses PR comments

* Adds more testing

* fixes doc comments

* Fixes weight refunding logic for fn unbond

* generalizes  to return used weight or dispatch error

* Update frame/staking/src/pallet/mod.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update frame/staking/src/pallet/mod.rs

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Addresses PR comments

* Add comment to speculative num spans

* adds missing add_slashing_spans in withdraw_unbonded_kill benchmarks

* ".git/.scripts/bench-bot.sh" pallet dev pallet_staking

* fix publish

Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
Co-authored-by: command-bot <>
This commit is contained in:
Gonçalo Pestana
2022-12-15 08:57:19 +00:00
committed by GitHub
parent c1ed1901e1
commit 8e120489d4
6 changed files with 321 additions and 238 deletions
@@ -92,6 +92,45 @@ impl<T: Config> Pallet<T> {
Self::slashable_balance_of_vote_weight(who, issuance)
}
pub(super) fn do_withdraw_unbonded(
controller: &T::AccountId,
num_slashing_spans: u32,
) -> Result<Weight, DispatchError> {
let mut ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?;
let (stash, old_total) = (ledger.stash.clone(), ledger.total);
if let Some(current_era) = Self::current_era() {
ledger = ledger.consolidate_unlocked(current_era)
}
let used_weight =
if ledger.unlocking.is_empty() && ledger.active < T::Currency::minimum_balance() {
// This account must have called `unbond()` with some value that caused the active
// portion to fall below existential deposit + will have no more unlocking chunks
// left. We can now safely remove all staking-related information.
Self::kill_stash(&stash, num_slashing_spans)?;
// Remove the lock.
T::Currency::remove_lock(STAKING_ID, &stash);
T::WeightInfo::withdraw_unbonded_kill(num_slashing_spans)
} else {
// This was the consequence of a partial unbond. just update the ledger and move on.
Self::update_ledger(&controller, &ledger);
// This is only an update, so we use less overall weight.
T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)
};
// `old_total` should never be less than the new total because
// `consolidate_unlocked` strictly subtracts balance.
if ledger.total < old_total {
// Already checked that this won't overflow by entry condition.
let value = old_total - ledger.total;
Self::deposit_event(Event::<T>::Withdrawn { stash, amount: value });
}
Ok(used_weight)
}
pub(super) fn do_payout_stakers(
validator_stash: T::AccountId,
era: EraIndex,
@@ -1568,6 +1607,8 @@ impl<T: Config> StakingInterface for Pallet<T> {
fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult {
let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
Self::unbond(RawOrigin::Signed(ctrl).into(), value)
.map_err(|with_post| with_post.error)
.map(|_| ())
}
fn chill(who: &Self::AccountId) -> DispatchResult {