Skip to content

Commit 142ceec

Browse files
staking: do not remove an invulnerable in case of bad solution (#10454)
Invulnerables are not automatically removed from the Invulnerables storage when their solution is rejected. Removal should occur only through governance, not automatically. An operational or network issue that leads to an incomplete submission is much more likely than a bad faith action from an invulnerable. Close paritytech-secops/srlabs_findings#602. --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 39a5245 commit 142ceec

File tree

3 files changed

+18
-5
lines changed

3 files changed

+18
-5
lines changed

prdoc/pr_10454.prdoc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
title: 'staking: do not remove an invulnerable in case of bad solution'
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
Invulnerables are not automatically removed from the Invulnerables storage when their solution is rejected.
6+
Removal should occur only through governance, not automatically.
7+
An operational or network issue that leads to an incomplete submission is much more likely than a bad faith action from an invulnerable.
8+
crates:
9+
- name: pallet-election-provider-multi-block
10+
bump: patch

substrate/frame/election-provider-multi-block/src/signed/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,11 @@ impl<T: Config> Pallet<T> {
10251025
if let Some((loser, metadata)) =
10261026
Submissions::<T>::take_leader_with_data(current_round).defensive()
10271027
{
1028-
// Slash the deposit
1028+
// Slash the deposit.
1029+
// Note that an invulnerable is not expelled from the list despite the slashing.
1030+
// Removal should occur only through governance, not automatically. An operational or
1031+
// network issue that leads to an incomplete submission is much more likely than a bad
1032+
// faith action from an invulnerable.
10291033
let slash = metadata.deposit;
10301034
let _res = T::Currency::burn_held(
10311035
&HoldReason::SignedSubmission.into(),
@@ -1036,7 +1040,6 @@ impl<T: Config> Pallet<T> {
10361040
);
10371041
debug_assert_eq!(_res, Ok(slash));
10381042
Self::deposit_event(Event::<T>::Slashed(current_round, loser.clone(), slash));
1039-
Invulnerables::<T>::mutate(|x| x.retain(|y| y != &loser));
10401043

10411044
// Try to start verification again if we still have submissions
10421045
if let crate::types::Phase::SignedValidation(remaining_blocks) =

substrate/frame/election-provider-multi-block/src/signed/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,7 +1594,7 @@ mod invulnerables {
15941594
}
15951595

15961596
#[test]
1597-
fn slashed_invulnerable_is_expelled() {
1597+
fn slashed_invulnerable_is_not_expelled() {
15981598
ExtBuilder::signed().build_and_execute(|| {
15991599
roll_to_signed_open();
16001600
assert_full_snapshot();
@@ -1630,8 +1630,8 @@ mod invulnerables {
16301630
* (7) ^^^^ */
16311631
);
16321632

1633-
// Verify invulnerable is expelled
1634-
assert!(!Invulnerables::<T>::get().contains(&99));
1633+
// Verify invulnerable is not expelled. It remains in the list despite being slashed.
1634+
assert!(Invulnerables::<T>::get().contains(&99));
16351635
});
16361636
}
16371637
}

0 commit comments

Comments
 (0)