Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Staking e2e test - Add case when ledger active balance falls below ED #14247

Merged
merged 6 commits into from
Jul 18, 2023
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
70 changes: 69 additions & 1 deletion frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod mock;

pub(crate) const LOG_TARGET: &str = "tests::e2e-epm";

use frame_support::assert_ok;
use frame_support::{assert_err, assert_noop, assert_ok};
use mock::*;
use sp_core::Get;
use sp_npos_elections::{to_supports, StakedAssignment};
Expand Down Expand Up @@ -253,6 +253,8 @@ fn continous_slashes_below_offending_threshold() {
}

#[test]
/// Slashed validator sets intentions in the same era of slashing.
///
/// When validators are slashed, they are chilled and removed from the current `VoterList`. Thus,
/// the slashed validator should not be considered in the next validator set. However, if the
/// slashed validator sets its intention to validate again in the same era when it was slashed and
Expand Down Expand Up @@ -319,3 +321,69 @@ fn set_validation_intention_after_chilled() {
assert_eq!(Nominators::<Runtime>::get(21).unwrap().targets, vec![41]);
})
}

#[test]
/// Active ledger balance may fall below ED if account chills before unbounding.
///
/// Unbonding call fails if the remaining ledger's stash balance falls below the existential
/// deposit. However, if the stash is chilled before unbonding, the ledger's active balance may
/// be below ED. In that case, only the stash (or root) can kill the ledger entry by calling
/// `withdraw_unbonded` after the bonding period has passed.
///
/// Related to <https://github.com/paritytech/substrate/issues/14246>.
fn ledger_consistency_active_balance_below_ed() {
use pallet_staking::{Error, Event};

let (mut ext, pool_state, _) =
ExtBuilder::default().staking(StakingExtBuilder::default()).build_offchainify();

ext.execute_with(|| {
assert_eq!(Staking::ledger(&11).unwrap().active, 1000);

// unbonding total of active stake fails because the active ledger balance would fall
// below the `MinNominatorBond`.
assert_noop!(
Staking::unbond(RuntimeOrigin::signed(11), 1000),
Error::<Runtime>::InsufficientBond
);

// however, chilling works as expected.
assert_ok!(Staking::chill(RuntimeOrigin::signed(11)));

// now unbonding the full active balance works, since remainer of the active balance is
// not enforced to be below `MinNominatorBond` if the stash has been chilled.
assert_ok!(Staking::unbond(RuntimeOrigin::signed(11), 1000));

// the active balance of the ledger entry is 0, while total balance is 1000 until
// `withdraw_unbonded` is called.
assert_eq!(Staking::ledger(&11).unwrap().active, 0);
assert_eq!(Staking::ledger(&11).unwrap().total, 1000);

// trying to withdraw the unbonded balance won't work yet because not enough bonding
// eras have passed.
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(Staking::ledger(&11).unwrap().total, 1000);

// tries to reap stash after chilling, which fails since the stash total balance is
// above ED.
assert_err!(
Staking::reap_stash(RuntimeOrigin::signed(11), 21, 0),
Error::<Runtime>::FundedTarget,
);

// check the events so far: 1x Chilled and 1x Unbounded
assert_eq!(
staking_events(),
[Event::Chilled { stash: 11 }, Event::Unbonded { stash: 11, amount: 1000 }]
);

// after advancing `BondingDuration` eras, the `withdraw_unbonded` will unlock the
// chunks and the ledger entry will be cleared, since the ledger active balance is 0.
advance_eras(
<Runtime as pallet_staking::Config>::BondingDuration::get() as usize,
pool_state,
);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert_eq!(Staking::ledger(&11), None);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

use _feps::ExtendedBalance;
use frame_support::{
dispatch::UnfilteredDispatchable, parameter_types, traits, traits::Hooks, weights::constants,
assert_ok, dispatch::UnfilteredDispatchable, parameter_types, traits, traits::Hooks,
weights::constants,
};
use frame_system::EnsureRoot;
use sp_core::{ConstU32, Get};
Expand Down Expand Up @@ -705,6 +706,12 @@ pub(crate) fn start_next_active_era_delayed_solution(
start_active_era(active_era() + 1, pool, true)
}

pub(crate) fn advance_eras(n: usize, pool: Arc<RwLock<PoolState>>) {
for _ in 0..n {
assert_ok!(start_next_active_era(pool.clone()));
}
}

/// Progress until the given era.
pub(crate) fn start_active_era(
era_index: EraIndex,
Expand Down