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

Adds integration test for slashed/chilled validator with subsequent validation intention #13717

Merged
merged 9 commits into from
May 15, 2023
70 changes: 70 additions & 0 deletions frame/election-provider-multi-phase/test-staking-e2e/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod mock;

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

use frame_support::assert_ok;
use mock::*;
use sp_core::Get;
use sp_npos_elections::{to_supports, StakedAssignment};
Expand Down Expand Up @@ -204,3 +205,72 @@ fn continous_slashes_below_offending_threshold() {
}
});
}

#[test]
/// 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
/// chilled, the validator may not be removed from the active validator set across eras, provided
/// it would selected in the subsequent era if there was no slash. Nominators of the slashed
/// validator will also be slashed and chilled, as expected, but the nomination intentions will
/// remain after the validator re-set the intention to be validating again.
///
/// This behaviour is due to removing implicit chill upon slash
/// <https://github.com/paritytech/substrate/pull/12420>.
///
/// Related to <https://github.com/paritytech/substrate/issues/13714>.
fn set_validation_intention_after_chilled() {
use frame_election_provider_support::SortedListProvider;
use pallet_staking::{Event, Forcing, Nominators};

let staking_builder = StakingExtBuilder::default();
let epm_builder = EpmExtBuilder::default();

ExtBuilder::default()
.staking(staking_builder)
.epm(epm_builder)
.build_and_execute(|| {
assert_eq!(active_era(), 0);
// validator is part of the validator set.
assert!(Session::validators().contains(&81));
assert!(<Runtime as pallet_staking::Config>::VoterList::contains(&81));

// nominate validator 81.
assert_ok!(Staking::nominate(RuntimeOrigin::signed(21), vec![81]));
assert_eq!(Nominators::<Runtime>::get(21).unwrap().targets, vec![81]);

// validator is slashed. it is removed from the `VoterList` through chilling but in the
// current era, the validator is still part of the active validator set.
add_slash(&81);
assert!(Session::validators().contains(&81));
assert!(!<Runtime as pallet_staking::Config>::VoterList::contains(&81));
assert_eq!(
staking_events(),
[
Event::Chilled { stash: 81 },
Event::ForceEra { mode: Forcing::ForceNew },
Event::SlashReported {
validator: 81,
slash_era: 0,
fraction: Perbill::from_percent(10)
}
],
);

// after the nominator is slashed and chilled, the nominations remain.
assert_eq!(Nominators::<Runtime>::get(21).unwrap().targets, vec![81]);

// validator sets intention to stake again in the same era it was chilled.
assert_ok!(Staking::validate(RuntimeOrigin::signed(81), Default::default()));

// progress era and check that the slashed validator is still part of the validator
// set.
assert!(start_next_active_era().is_ok());
assert_eq!(active_era(), 1);
assert!(Session::validators().contains(&81));
assert!(<Runtime as pallet_staking::Config>::VoterList::contains(&81));

// nominations are still active as before the slash.
assert_eq!(Nominators::<Runtime>::get(21).unwrap().targets, vec![81]);
})
}
14 changes: 11 additions & 3 deletions frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ impl pallet_balances::Config for Runtime {
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
type MaxHolds = ConstU32<1>;
type MaxFreezes = traits::ConstU32<1>;
type HoldIdentifier = ();
type FreezeIdentifier = ();
type MaxHolds = traits::ConstU32<1>;
type MaxFreezes = traits::ConstU32<1>;
type WeightInfo = ();
}

impl pallet_timestamp::Config for Runtime {
Expand Down Expand Up @@ -781,3 +781,11 @@ pub(crate) fn set_minimum_election_score(
.map(|_| ())
.map_err(|_| ())
}

pub(crate) fn staking_events() -> Vec<pallet_staking::Event<Runtime>> {
System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| if let RuntimeEvent::Staking(inner) = e { Some(inner) } else { None })
.collect::<Vec<_>>()
}