From 41805251f9b7f8578eaf844009ff161466ce7f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Sat, 27 May 2023 23:25:45 +0200 Subject: [PATCH 1/3] Staking e2e test - case when ledger active balance falls below ED --- .../test-staking-e2e/src/lib.rs | 61 ++++++++++++++++++- .../test-staking-e2e/src/mock.rs | 8 ++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index 2b4a255dcf606..ca721a5aa1da0 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -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_ok}; use mock::*; use sp_core::Get; use sp_npos_elections::{to_supports, StakedAssignment}; @@ -207,6 +207,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 @@ -274,3 +276,60 @@ fn set_validation_intention_after_chilled() { assert_eq!(Nominators::::get(21).unwrap().targets, vec![81]); }) } + +#[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 existencial +/// 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 . +fn ledger_consistency_active_balance_below_ed() { + use pallet_staking::Error; + + ExtBuilder::default() + .staking(StakingExtBuilder::default()) + .build_and_execute(|| { + 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_err!( + Staking::unbond(RuntimeOrigin::signed(11), 1000), + Error::::InsufficientBond + ); + assert_eq!(Staking::ledger(&11).unwrap().active, 1000); + + // 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::::FundedTarget, + ); + + // 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(::BondingDuration::get() as usize); + assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0)); + assert_eq!(Staking::ledger(&11), None); + }); +} diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index da7ccf6dce9ce..44444be330e97 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -19,7 +19,7 @@ use _feps::ExtendedBalance; use frame_support::{ - parameter_types, traits, + assert_ok, parameter_types, traits, traits::{GenesisBuild, Hooks}, weights::constants, }; @@ -636,6 +636,12 @@ pub(crate) fn start_next_active_era_delayed_solution() -> Result<(), ()> { start_active_era(active_era() + 1, true) } +pub(crate) fn advance_eras(n: usize) { + for _ in 0..n { + assert_ok!(start_next_active_era()); + } +} + /// Progress until the given era. pub(crate) fn start_active_era(era_index: EraIndex, delay_solution: bool) -> Result<(), ()> { let era_before = current_era(); From e3656c4015d2f2c26cd1e824ca77ecc86156cc23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Mon, 5 Jun 2023 09:40:05 +0200 Subject: [PATCH 2/3] Update frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs Co-authored-by: Ankan <10196091+Ank4n@users.noreply.github.com> --- frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index ca721a5aa1da0..317a19c97a40b 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -280,7 +280,7 @@ fn set_validation_intention_after_chilled() { #[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 existencial +/// 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. From 598c16206eca41d9d030bf2b14cbb7e157450a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Pestana?= Date: Mon, 17 Jul 2023 22:46:41 +0200 Subject: [PATCH 3/3] Simplifies test assertions; tests events --- .../test-staking-e2e/src/lib.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs index 317a19c97a40b..e471de19c1c6f 100644 --- a/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs +++ b/frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs @@ -20,7 +20,7 @@ mod mock; pub(crate) const LOG_TARGET: &str = "tests::e2e-epm"; -use frame_support::{assert_err, assert_ok}; +use frame_support::{assert_err, assert_noop, assert_ok}; use mock::*; use sp_core::Get; use sp_npos_elections::{to_supports, StakedAssignment}; @@ -287,7 +287,7 @@ fn set_validation_intention_after_chilled() { /// /// Related to . fn ledger_consistency_active_balance_below_ed() { - use pallet_staking::Error; + use pallet_staking::{Error, Event}; ExtBuilder::default() .staking(StakingExtBuilder::default()) @@ -296,18 +296,17 @@ fn ledger_consistency_active_balance_below_ed() { // unbonding total of active stake fails because the active ledger balance would fall // below the `MinNominatorBond`. - assert_err!( + assert_noop!( Staking::unbond(RuntimeOrigin::signed(11), 1000), Error::::InsufficientBond ); - assert_eq!(Staking::ledger(&11).unwrap().active, 1000); // 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),); + 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. @@ -326,6 +325,12 @@ fn ledger_consistency_active_balance_below_ed() { Error::::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(::BondingDuration::get() as usize);