Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the new validator disabling strategy #2226

Merged
merged 99 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
913232a
Handling of disabled validators in backing subsystem (#1259)
tdimitrov Oct 20, 2023
42607ad
Merge branch 'master' into tsv-disabling
tdimitrov Oct 24, 2023
5a6ad28
Merge branch 'master' into tsv-disabling
ordian Oct 26, 2023
c986fd3
Merge branch 'master' into tsv-disabling
tdimitrov Nov 6, 2023
b16c8b6
Merge branch 'master' into tsv-disabling
tdimitrov Nov 6, 2023
bbb6631
Any offender is disabled for a whole era - wip
tdimitrov Nov 8, 2023
c80ae21
Fix `on_offence` compilation errors
tdimitrov Nov 9, 2023
8f5d2b6
Remove `DisableStrategy`
tdimitrov Nov 9, 2023
c8adffe
Merge branch 'master' into tsv-disabling
tdimitrov Nov 9, 2023
2507584
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Nov 9, 2023
bff0240
Fix a compilation error after merge master: `CandidateValidationMessa…
tdimitrov Nov 9, 2023
e601dee
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Nov 9, 2023
95aa673
Update a comment
tdimitrov Nov 10, 2023
873e072
Update comments
tdimitrov Nov 14, 2023
02814e0
Merge branch 'master' into tsv-disabling
tdimitrov Nov 14, 2023
5afbfe3
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Nov 14, 2023
129a986
Replace `OffendingValidatorsThreshold` with `disabling_threshold`. Do…
tdimitrov Nov 13, 2023
ce1539c
Make tests pass
tdimitrov Nov 13, 2023
aee9fe3
Update tests to cover the new disabling strategy
tdimitrov Nov 15, 2023
d23c530
Storage migration for `OffendingValidators`
tdimitrov Nov 15, 2023
d2c5895
Apply suggestions from code review
tdimitrov Nov 16, 2023
06e79d9
Fix storage migration - use `VersionedMigration`, fix storage alias, …
tdimitrov Nov 16, 2023
9f8c598
Rename `disabling_threshold` -> `disabling_limit`
tdimitrov Nov 16, 2023
61b8c22
Merge branch 'master' into tsv-disabling
tdimitrov Nov 16, 2023
0a39a15
Fix test-staking-e2e
tdimitrov Nov 16, 2023
f4f82ec
Fix a compilation error
tdimitrov Nov 16, 2023
879279b
Fix compilation errors in benchmarks
tdimitrov Nov 16, 2023
3f4adaa
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Nov 21, 2023
5936d69
Extract `DisablingStrategy` as a trait
tdimitrov Nov 24, 2023
f330b61
Merge branch 'master' into tsv-disabling
tdimitrov Nov 29, 2023
5e8b367
Fix compilation errors after merge
tdimitrov Nov 29, 2023
f2d4c48
Filter votes from disabled validators in `BackedCandidates` in proces…
tdimitrov Nov 30, 2023
9cb65f8
Fix a typo
tdimitrov Dec 5, 2023
ed203d8
Do not chill validators on offence
tdimitrov Dec 7, 2023
a954ca5
Merge branch 'master' into tsv-disabling
tdimitrov Dec 7, 2023
a210f37
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Dec 7, 2023
6097a32
Don't disable for offences in previous eras
tdimitrov Dec 13, 2023
3109a29
Code review feedback - rework `enters_emergency_phase_after_forcing_b…
tdimitrov Dec 13, 2023
41ce978
Add `DisablingStrategy` to benchmarking mocks
tdimitrov Dec 14, 2023
28d5055
Fix a comment
tdimitrov Dec 14, 2023
71b6f60
`try_state` check for `DisabledValidators`
tdimitrov Dec 14, 2023
790cf4a
Fix another comment
tdimitrov Dec 14, 2023
6dfd0bc
Apply suggestions from code review
tdimitrov Dec 14, 2023
cd185b6
Fix a comment
tdimitrov Dec 14, 2023
cc09228
debug_assert for `DisabledValidators` being sorted
tdimitrov Dec 14, 2023
2552cb6
Unit tests for `UpToByzantineThresholdDisablingStrategy`
tdimitrov Dec 18, 2023
327b9fd
Remove struct DisablingDecision
tdimitrov Jan 2, 2024
c187d50
pub DisablingDecisionContext
tdimitrov Jan 2, 2024
e1899ce
Extract disabling threshold as a generic parameter of `UpToByzantineT…
tdimitrov Jan 4, 2024
1d283dc
Changelog and prdoc
tdimitrov Jan 4, 2024
1ef6e83
Fetch offender info and active set via staking pallet in `make_disabl…
tdimitrov Jan 5, 2024
55ccffe
UpToByzantineThresholdDisablingStrategy -> UpToThresholdDisablingStra…
tdimitrov Jan 5, 2024
52de0f5
Remove `set_validation_intention_after_chilled` from test-staking-e2e…
tdimitrov Jan 5, 2024
87af293
fmt
tdimitrov Jan 5, 2024
3506229
Merge branch 'master' into tsv-disabling
tdimitrov Jan 5, 2024
93f5189
Fix a compilation error from merge
tdimitrov Jan 5, 2024
2cf308f
Merge branch 'master' into tsv-disabling
tdimitrov Jan 8, 2024
68585e1
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Jan 8, 2024
21e662b
Merge branch 'master' into tsv-disabling
tdimitrov Jan 9, 2024
ceb6941
Apply suggestions from code review
tdimitrov Jan 15, 2024
8d56d2f
Return early in `make_disabling_decision` implementation for `UpToThr…
tdimitrov Jan 15, 2024
db43ae1
Remove stale TODO
tdimitrov Jan 15, 2024
18eae58
`DISABLING_FACTOR` becomes `SLASHING_DISABLING_FACTOR` in test-stakin…
tdimitrov Jan 15, 2024
cec4bbe
Don't divide by 0 in `disable_threshold` from `UpToThresholdDisabling…
tdimitrov Jan 15, 2024
859770a
Ensure only the expected set of validators is disabled in `mass_slash…
tdimitrov Jan 16, 2024
6f0d216
Merge branch 'tsv-disabling' into tsv-disabling-for-era
ordian Jan 16, 2024
48da767
Merge branch 'master' into tsv-disabling
tdimitrov Jan 16, 2024
e94fa2c
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Jan 16, 2024
3511b06
Use `defensive!` in disabling threshold calculation
tdimitrov Jan 16, 2024
9a775a5
`make_disabling_decision` returns an `Option` instead of a `Vec`
tdimitrov Jan 17, 2024
0111e69
Update substrate/frame/staking/CHANGELOG.md
tdimitrov Jan 17, 2024
9786407
Merge branch 'master' into tsv-disabling
tdimitrov Jan 18, 2024
5c3e98e
Merge branch 'tsv-disabling' into tsv-disabling-for-era
tdimitrov Jan 18, 2024
aed68dd
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Jan 29, 2024
755192b
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Feb 2, 2024
00456ae
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Feb 28, 2024
460ffd6
Fix a compilation error after merge
tdimitrov Feb 28, 2024
aaf8ee8
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Feb 28, 2024
3f4e73a
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Mar 1, 2024
80fabc5
Update substrate/frame/staking/src/migrations.rs
tdimitrov Mar 12, 2024
9aa8b70
Remove `disabled_validators` getter from `DisabledValidators`
tdimitrov Mar 12, 2024
4e0c3cf
Fix the migration - clear `OffendingValidators` by using `take` inste…
tdimitrov Mar 12, 2024
b71e0db
Ensure `OffendingValidators` after migrating to V15
tdimitrov Mar 12, 2024
37b7c4a
`make_disabling_decision` -> `decision`
tdimitrov Mar 12, 2024
fdb49a3
`DISABLING_THRESHOLD_FACTOR` -> `DISABLING_LIMIT_FACTOR`
tdimitrov Mar 12, 2024
301f48e
Update prdoc
tdimitrov Mar 12, 2024
e3a8528
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Mar 20, 2024
abe8e99
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Apr 11, 2024
cb11fb9
Fix migration
tdimitrov Apr 11, 2024
64ba884
Fix storage migration of `OffendingValidators` (#4083)
tdimitrov Apr 12, 2024
8a509f5
Code review feedback
tdimitrov Apr 23, 2024
049c545
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Apr 23, 2024
d4e4ab3
".git/.scripts/commands/fmt/fmt.sh"
Apr 23, 2024
f84162c
Fix `virtual_nominators_are_lazily_slashed`
tdimitrov Apr 23, 2024
87112ac
Fix `decision` from `UpToLimitDisablingStrategy` (#4262)
tdimitrov Apr 24, 2024
8db8167
Update substrate/frame/staking/src/slashing.rs
tdimitrov Apr 24, 2024
b76e5e7
Update substrate/frame/staking/src/pallet/impls.rs
tdimitrov Apr 24, 2024
a228aff
Remove misleading comment
tdimitrov Apr 25, 2024
1e90461
Merge branch 'master' into tsv-disabling-for-era
tdimitrov Apr 26, 2024
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
11 changes: 1 addition & 10 deletions polkadot/runtime/parachains/src/disputes/slashing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ use sp_runtime::{
KeyTypeId, Perbill,
};
use sp_session::{GetSessionNumber, GetValidatorCount};
use sp_staking::offence::{DisableStrategy, Kind, Offence, OffenceError, ReportOffence};
use sp_staking::offence::{Kind, Offence, OffenceError, ReportOffence};
use sp_std::{
collections::{btree_map::Entry, btree_set::BTreeSet},
prelude::*,
Expand Down Expand Up @@ -134,15 +134,6 @@ where
self.time_slot.clone()
}

fn disable_strategy(&self) -> DisableStrategy {
match self.kind {
SlashingOffenceKind::ForInvalid => DisableStrategy::Always,
// in the future we might change it based on number of disputes initiated:
// <https://github.com/paritytech/polkadot/issues/5946>
SlashingOffenceKind::AgainstValid => DisableStrategy::Never,
}
}

fn slash_fraction(&self, _offenders: u32) -> Perbill {
self.slash_fraction
}
Expand Down
3 changes: 1 addition & 2 deletions polkadot/runtime/test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,6 @@ parameter_types! {
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const MaxExposurePageSize: u32 = 64;
pub const MaxNominators: u32 = 256;
pub storage OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxAuthorities: u32 = 100_000;
pub const OnChainMaxWinners: u32 = u32::MAX;
// Unbounded number of election targets and voters.
Expand Down Expand Up @@ -349,7 +348,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = MaxExposurePageSize;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand All @@ -364,6 +362,7 @@ impl pallet_staking::Config for Runtime {
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = ();
type WeightInfo = ();
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}

parameter_types! {
Expand Down
5 changes: 2 additions & 3 deletions polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,6 @@ parameter_types! {
// this is an unbounded number. We just set it to a reasonably high value, 1 full page
// of nominators.
pub const MaxNominators: u32 = 64;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxNominations: u32 = <NposCompactSolution16 as frame_election_provider_support::NposSolution>::LIMIT as u32;
pub const MaxControllersInDeprecationBatch: u32 = 751;
}
Expand All @@ -634,7 +633,6 @@ impl pallet_staking::Config for Runtime {
type SessionInterface = Self;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = MaxExposurePageSize;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
Expand All @@ -647,6 +645,7 @@ impl pallet_staking::Config for Runtime {
type BenchmarkingConfig = runtime_common::StakingBenchmarkingConfig;
type EventListeners = NominationPools;
type WeightInfo = weights::pallet_staking::WeightInfo<Runtime>;
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}

impl pallet_fast_unstake::Config for Runtime {
Expand Down Expand Up @@ -1649,7 +1648,7 @@ pub mod migrations {
}

/// Unreleased migrations. Add new ones here:
pub type Unreleased = ();
pub type Unreleased = (pallet_staking::migrations::v15::MigrateV14ToV15<Runtime>,);
}

/// Unchecked extrinsic type as expected by this runtime.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ requests = { memory = "2G", cpu = "1" }
[[relaychain.node_groups]]
name = "honest-validator"
count = 3
args = ["-lparachain=debug"]
args = ["-lparachain=debug,runtime::staking=debug"]

[[relaychain.node_groups]]
image = "{{MALUS_IMAGE}}"
Expand Down
28 changes: 28 additions & 0 deletions prdoc/pr_2226.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
title: Validator disabling strategy in runtime

doc:
- audience: Node Operator
description: |
On each committed offence (no matter slashable or not) the offending validator will be
disabled for a whole era.
- audience: Runtime Dev
description: |
The disabling strategy in staking pallet is no longer hardcoded but abstracted away via
`DisablingStrategy` trait. The trait contains a single function (make_disabling_decision) which
is called for each offence. The function makes a decision if (and which) validators should be
disabled. A default implementation is provided - `UpToLimitDisablingStrategy`. It
will be used on Kusama and Polkadot. In nutshell `UpToLimitDisablingStrategy`
disables offenders up to the configured threshold. Offending validators are not disabled for
offences in previous eras. The threshold is controlled via `DISABLING_LIMIT_FACTOR` (a generic
parameter of `UpToLimitDisablingStrategy`).

migrations:
db: []
runtime:
- reference: pallet-staking
description: |
Renames `OffendingValidators` storage item to `DisabledValidators` and changes its type from
`Vec<(u32, bool)>` to `Vec<u32>`.

crates:
- name: pallet-staking
3 changes: 1 addition & 2 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,6 @@ parameter_types! {
pub const SlashDeferDuration: sp_staking::EraIndex = 24 * 7; // 1/4 the bonding duration.
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const MaxNominators: u32 = 64;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub const MaxControllersInDeprecationBatch: u32 = 5900;
pub OffchainRepeat: BlockNumber = 5;
pub HistoryDepth: u32 = 84;
Expand Down Expand Up @@ -690,7 +689,6 @@ impl pallet_staking::Config for Runtime {
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type MaxExposurePageSize = ConstU32<256>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type ElectionProvider = ElectionProviderMultiPhase;
type GenesisElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type VoterList = VoterList;
Expand All @@ -703,6 +701,7 @@ impl pallet_staking::Config for Runtime {
type EventListeners = NominationPools;
type WeightInfo = pallet_staking::weights::SubstrateWeight<Runtime>;
type BenchmarkingConfig = StakingBenchmarkingConfig;
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}

impl pallet_fast_unstake::Config for Runtime {
Expand Down
3 changes: 1 addition & 2 deletions substrate/frame/babe/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ parameter_types! {
pub const BondingDuration: EraIndex = 3;
pub const SlashDeferDuration: EraIndex = 0;
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(16);
pub static ElectionsBounds: ElectionBounds = ElectionBoundsBuilder::default().build();
}

Expand Down Expand Up @@ -174,7 +173,6 @@ impl pallet_staking::Config for Test {
type UnixTime = pallet_timestamp::Pallet<Test>;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = ConstU32<64>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = Self::ElectionProvider;
Expand All @@ -187,6 +185,7 @@ impl pallet_staking::Config for Test {
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}

impl pallet_offences::Config for Test {
Expand Down
3 changes: 1 addition & 2 deletions substrate/frame/beefy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,6 @@ parameter_types! {
pub const SessionsPerEra: SessionIndex = 3;
pub const BondingDuration: EraIndex = 3;
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub const OffendingValidatorsThreshold: Perbill = Perbill::from_percent(17);
pub static ElectionsBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default().build();
}

Expand Down Expand Up @@ -188,7 +187,6 @@ impl pallet_staking::Config for Test {
type UnixTime = pallet_timestamp::Pallet<Test>;
type EraPayout = pallet_staking::ConvertCurve<RewardCurve>;
type MaxExposurePageSize = ConstU32<64>;
type OffendingValidatorsThreshold = OffendingValidatorsThreshold;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = Self::ElectionProvider;
Expand All @@ -201,6 +199,7 @@ impl pallet_staking::Config for Test {
type EventListeners = ();
type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig;
type WeightInfo = ();
type DisablingStrategy = pallet_staking::UpToLimitDisablingStrategy;
}

impl pallet_offences::Config for Test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ pub(crate) const LOG_TARGET: &str = "tests::e2e-epm";
use frame_support::{assert_err, assert_noop, assert_ok};
use mock::*;
use sp_core::Get;
use sp_npos_elections::{to_supports, StakedAssignment};
use sp_runtime::Perbill;

use crate::mock::RuntimeOrigin;
Expand Down Expand Up @@ -127,75 +126,48 @@ fn offchainify_works() {
}

#[test]
/// Replicates the Kusama incident of 8th Dec 2022 and its resolution through the governance
/// Inspired by the Kusama incident of 8th Dec 2022 and its resolution through the governance
/// fallback.
///
/// After enough slashes exceeded the `Staking::OffendingValidatorsThreshold`, the staking pallet
/// set `Forcing::ForceNew`. When a new session starts, staking will start to force a new era and
/// calls <EPM as election_provider>::elect(). If at this point EPM and the staking miners did not
/// have enough time to queue a new solution (snapshot + solution submission), the election request
/// fails. If there is no election fallback mechanism in place, EPM enters in emergency mode.
/// Recovery: Once EPM is in emergency mode, subsequent calls to `elect()` will fail until a new
/// solution is added to EPM's `QueuedSolution` queue. This can be achieved through
/// `Call::set_emergency_election_result` or `Call::governance_fallback` dispatchables. Once a new
/// solution is added to the queue, EPM phase transitions to `Phase::Off` and the election flow
/// restarts. Note that in this test case, the emergency throttling is disabled.
fn enters_emergency_phase_after_forcing_before_elect() {
/// Mass slash of validators shouldn't disable more than 1/3 of them (the byzantine threshold). Also
/// no new era should be forced which could lead to EPM entering emergency mode.
fn mass_slash_doesnt_enter_emergency_phase() {
let epm_builder = EpmExtBuilder::default().disable_emergency_throttling();
let (ext, pool_state, _) = ExtBuilder::default().epm(epm_builder).build_offchainify();

execute_with(ext, || {
log!(
trace,
"current validators (staking): {:?}",
<Runtime as pallet_staking::SessionInterface<AccountId>>::validators()
);
let session_validators_before = Session::validators();

roll_to_epm_off();
assert!(ElectionProviderMultiPhase::current_phase().is_off());
let staking_builder = StakingExtBuilder::default().validator_count(7);
let (mut ext, _, _) = ExtBuilder::default()
.epm(epm_builder)
.staking(staking_builder)
.build_offchainify();

ext.execute_with(|| {
assert_eq!(pallet_staking::ForceEra::<Runtime>::get(), pallet_staking::Forcing::NotForcing);
// slashes so that staking goes into `Forcing::ForceNew`.
slash_through_offending_threshold();

assert_eq!(pallet_staking::ForceEra::<Runtime>::get(), pallet_staking::Forcing::ForceNew);
let active_set_size_before_slash = Session::validators().len();

advance_session_delayed_solution(pool_state.clone());
assert!(ElectionProviderMultiPhase::current_phase().is_emergency());
log_current_time();
// Slash more than 1/3 of the active validators
let mut slashed = slash_half_the_active_set();

let era_before_delayed_next = Staking::current_era();
// try to advance 2 eras.
assert!(start_next_active_era_delayed_solution(pool_state.clone()).is_ok());
assert_eq!(Staking::current_era(), era_before_delayed_next);
assert!(start_next_active_era(pool_state).is_err());
assert_eq!(Staking::current_era(), era_before_delayed_next);
let active_set_size_after_slash = Session::validators().len();

// EPM is still in emergency phase.
assert!(ElectionProviderMultiPhase::current_phase().is_emergency());
// active set should stay the same before and after the slash
assert_eq!(active_set_size_before_slash, active_set_size_after_slash);

// session validator set remains the same.
assert_eq!(Session::validators(), session_validators_before);

// performs recovery through the set emergency result.
let supports = to_supports(&vec![
StakedAssignment { who: 21, distribution: vec![(21, 10)] },
StakedAssignment { who: 31, distribution: vec![(21, 10), (31, 10)] },
StakedAssignment { who: 41, distribution: vec![(41, 10)] },
]);
assert!(ElectionProviderMultiPhase::set_emergency_election_result(
RuntimeOrigin::root(),
supports
)
.is_ok());
// Slashed validators are disabled up to a limit
slashed.truncate(
pallet_staking::UpToLimitDisablingStrategy::<SLASHING_DISABLING_FACTOR>::disable_limit(
active_set_size_after_slash,
),
);

// EPM can now roll to signed phase to proceed with elections. The validator set is the
// expected (ie. set through `set_emergency_election_result`).
roll_to_epm_signed();
//assert!(ElectionProviderMultiPhase::current_phase().is_signed());
assert_eq!(Session::validators(), vec![21, 31, 41]);
assert_eq!(Staking::current_era(), era_before_delayed_next.map(|e| e + 1));
// Find the indices of the disabled validators
let active_set = Session::validators();
let expected_disabled = slashed
.into_iter()
.map(|d| active_set.iter().position(|a| *a == d).unwrap() as u32)
.collect::<Vec<_>>();

assert_eq!(pallet_staking::ForceEra::<Runtime>::get(), pallet_staking::Forcing::NotForcing);
assert_eq!(Session::disabled_validators(), expected_disabled);
});
}

Expand Down Expand Up @@ -253,77 +225,7 @@ fn continuous_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
/// 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 (ext, pool_state, _) = ExtBuilder::default()
.epm(EpmExtBuilder::default())
.staking(StakingExtBuilder::default())
.build_offchainify();

execute_with(ext, || {
assert_eq!(active_era(), 0);
// validator is part of the validator set.
assert!(Session::validators().contains(&41));
assert!(<Runtime as pallet_staking::Config>::VoterList::contains(&41));

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

// 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(&41);
assert!(Session::validators().contains(&41));
assert!(!<Runtime as pallet_staking::Config>::VoterList::contains(&41));
assert_eq!(
staking_events(),
[
Event::Chilled { stash: 41 },
Event::ForceEra { mode: Forcing::ForceNew },
Event::SlashReported {
validator: 41,
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![41]);

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

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

// nominations are still active as before the slash.
assert_eq!(Nominators::<Runtime>::get(21).unwrap().targets, vec![41]);
})
}

#[test]
/// Active ledger balance may fall below ED if account chills before unbonding.
/// 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
Expand Down
Loading
Loading