Skip to content

Commit 5b8b4d8

Browse files
Overkillusordiansw10pa
authored andcommitted
Introduction of Approval Slashes [Disabling Strategy Stage 4] (#6827)
Implementation of Stage 4 of the new [Disabling Strategy](#4359). Stage 3 needs to be deployed before this goes live. ([Stage 3 PR](#5724)) Currently only backers are ever slashed as a result of disputes. This PR creates 3 types of offences with respective slashes: - ForInvalidBacked (100%) -> hard punishment as backers bear all the responsbility for validity - ForInvalidApproved (2%) -> minor punishment to deter lazy validators - AgainstValid (0%) -> very minor punishment (this causes disablement so opp cost) Closes #4746 IMPL details: This will require a change to parachains primitives which are used both on the runtime and node side. Rolling it out is time consuming so the plan is to release it in 2 parts. First updade runtime only with new offence types but keep old runtime apis (mapping from new format to old) so the node logic can remain the same. This will allow us to have the new offences for disputes concluding on-chain. Past session disputes that require proof submissions will temporarily not support the new slash types (but they are rarer). This will make the network more robust in the short term (as it is much easier to deploy). As the second part we can update the runtime apis and node side to fully support the new types. TODOs: - [x] new offence types - [ ] ~~migrations~~ (migration not needed because codec is compatible in conversion from old->new) - [ ] ~~migration tests~~ - [x] conversion (new -> old) to preserve old runtime apis - [x] conversion of disputeProofs (old -> new) to maintain old past session dispute behaviour - [ ] new runtime apis (postponed for later) - [x] disabling for spammy validators zombienet test - [x] disabling for lazy validators zombienet test - [ ] unit tests --------- Co-authored-by: ordian <[email protected]> Co-authored-by: Stephane Gurgenidze <[email protected]> Co-authored-by: ordian <[email protected]>
1 parent 5a903f3 commit 5b8b4d8

6 files changed

Lines changed: 258 additions & 71 deletions

File tree

polkadot/primitives/src/v8/slashing.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ use alloc::{collections::btree_map::BTreeMap, vec::Vec};
2121
use codec::{Decode, DecodeWithMemTracking, Encode};
2222
use scale_info::TypeInfo;
2323

24-
/// The kind of the dispute offence.
24+
/// The kind of the slashing offence (those come from disputes).
25+
///
26+
/// Notes:
27+
/// Will soon be fully eclipsed by the expanded `DisputeOffenceKind` enum.
28+
/// Only kept for backwards compatibility through old runtime apis.
2529
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
2630
pub enum SlashingOffenceKind {
2731
/// A severe offence when a validator backed an invalid block.
@@ -54,6 +58,11 @@ impl DisputesTimeSlot {
5458

5559
/// We store most of the information about a lost dispute on chain. This struct
5660
/// is required to identify and verify it.
61+
///
62+
/// Notes:
63+
/// Will soon be fully eclipsed by the expanded vstaging `DisputeProof` struct
64+
/// that uses the newer `DisputeOffenceKind` enum instead.
65+
/// Only kept for backwards compatibility.
5766
#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
5867
pub struct DisputeProof {
5968
/// Time slot when the dispute occurred.
@@ -68,6 +77,11 @@ pub struct DisputeProof {
6877

6978
/// Slashes that are waiting to be applied once we have validator key
7079
/// identification.
80+
///
81+
/// Notes:
82+
/// Will soon be fully eclipsed by the expanded vstaging `PendingSlashes` struct
83+
/// that uses the newer `DisputeOffenceKind` enum instead.
84+
/// Only kept for backwards compatibility.
7185
#[derive(Encode, Decode, TypeInfo, Debug, Clone)]
7286
pub struct PendingSlashes {
7387
/// Indices and keys of the validators who lost a dispute and are pending

polkadot/primitives/src/vstaging/mod.rs

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
1616

1717
//! Staging Primitives.
18-
use crate::{ValidatorIndex, ValidityAttestation};
18+
use crate::{slashing::DisputesTimeSlot, ValidatorId, ValidatorIndex, ValidityAttestation};
1919

2020
// Put any primitives used by staging APIs functions here
2121
use super::{
@@ -1036,7 +1036,7 @@ pub fn transpose_claim_queue(
10361036
}
10371037

10381038
#[cfg(test)]
1039-
mod tests {
1039+
mod candidate_receipt_tests {
10401040
use super::*;
10411041
use crate::{
10421042
v8::{
@@ -1503,3 +1503,110 @@ mod tests {
15031503
assert_eq!(old_ccr_hash, new_ccr.hash());
15041504
}
15051505
}
1506+
1507+
// Approval Slashes primitives
1508+
/// Supercedes the old 'SlashingOffenceKind' enum.
1509+
#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
1510+
pub enum DisputeOffenceKind {
1511+
/// A severe offence when a validator backed an invalid block
1512+
/// (backing only)
1513+
#[codec(index = 0)]
1514+
ForInvalidBacked,
1515+
/// A minor offence when a validator disputed a valid block.
1516+
/// (approval checking and dispute vote only)
1517+
#[codec(index = 1)]
1518+
AgainstValid,
1519+
/// A medium offence when a validator approved an invalid block
1520+
/// (approval checking and dispute vote only)
1521+
#[codec(index = 2)]
1522+
ForInvalidApproved,
1523+
}
1524+
1525+
/// impl for a conversion from SlashingOffenceKind to DisputeOffenceKind
1526+
/// This creates DisputeOffenceKind that never contains ForInvalidApproved since it was not
1527+
/// supported in the past
1528+
impl From<super::v8::slashing::SlashingOffenceKind> for DisputeOffenceKind {
1529+
fn from(value: super::v8::slashing::SlashingOffenceKind) -> Self {
1530+
match value {
1531+
super::v8::slashing::SlashingOffenceKind::ForInvalid => Self::ForInvalidBacked,
1532+
super::v8::slashing::SlashingOffenceKind::AgainstValid => Self::AgainstValid,
1533+
}
1534+
}
1535+
}
1536+
1537+
/// impl for a tryFrom conversion from DisputeOffenceKind to SlashingOffenceKind
1538+
impl TryFrom<DisputeOffenceKind> for super::v8::slashing::SlashingOffenceKind {
1539+
type Error = ();
1540+
1541+
fn try_from(value: DisputeOffenceKind) -> Result<Self, Self::Error> {
1542+
match value {
1543+
DisputeOffenceKind::ForInvalidBacked => Ok(Self::ForInvalid),
1544+
DisputeOffenceKind::AgainstValid => Ok(Self::AgainstValid),
1545+
DisputeOffenceKind::ForInvalidApproved => Err(()),
1546+
}
1547+
}
1548+
}
1549+
1550+
/// Slashes that are waiting to be applied once we have validator key
1551+
/// identification.
1552+
#[derive(Encode, Decode, TypeInfo, Debug, Clone)]
1553+
pub struct PendingSlashes {
1554+
/// Indices and keys of the validators who lost a dispute and are pending
1555+
/// slashes.
1556+
pub keys: BTreeMap<ValidatorIndex, ValidatorId>,
1557+
/// The dispute outcome.
1558+
pub kind: DisputeOffenceKind,
1559+
}
1560+
1561+
impl From<super::v8::slashing::PendingSlashes> for PendingSlashes {
1562+
fn from(old: super::v8::slashing::PendingSlashes) -> Self {
1563+
let keys = old.keys;
1564+
let kind = old.kind.into();
1565+
Self { keys, kind }
1566+
}
1567+
}
1568+
1569+
impl TryFrom<PendingSlashes> for super::v8::slashing::PendingSlashes {
1570+
type Error = ();
1571+
1572+
fn try_from(value: PendingSlashes) -> Result<Self, Self::Error> {
1573+
Ok(Self { keys: value.keys, kind: value.kind.try_into()? })
1574+
}
1575+
}
1576+
1577+
/// We store most of the information about a lost dispute on chain. This struct
1578+
/// is required to identify and verify it.
1579+
#[derive(PartialEq, Eq, Clone, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
1580+
pub struct DisputeProof {
1581+
/// Time slot when the dispute occurred.
1582+
pub time_slot: DisputesTimeSlot,
1583+
/// The dispute outcome.
1584+
pub kind: DisputeOffenceKind,
1585+
/// The index of the validator who lost a dispute.
1586+
pub validator_index: ValidatorIndex,
1587+
/// The parachain session key of the validator.
1588+
pub validator_id: ValidatorId,
1589+
}
1590+
1591+
impl From<super::v8::slashing::DisputeProof> for DisputeProof {
1592+
fn from(old: super::v8::slashing::DisputeProof) -> Self {
1593+
let time_slot = old.time_slot;
1594+
let kind = old.kind.into(); // infallible conversion
1595+
let validator_index = old.validator_index;
1596+
let validator_id = old.validator_id;
1597+
Self { time_slot, kind, validator_index, validator_id }
1598+
}
1599+
}
1600+
1601+
impl TryFrom<DisputeProof> for super::v8::slashing::DisputeProof {
1602+
type Error = ();
1603+
1604+
fn try_from(value: DisputeProof) -> Result<Self, Self::Error> {
1605+
Ok(Self {
1606+
time_slot: value.time_slot,
1607+
kind: value.kind.try_into()?,
1608+
validator_index: value.validator_index,
1609+
validator_id: value.validator_id,
1610+
})
1611+
}
1612+
}

polkadot/runtime/parachains/src/disputes.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,18 @@ impl RewardValidators for () {
8383

8484
/// Punishment hooks for disputes.
8585
pub trait SlashingHandler<BlockNumber> {
86-
/// Punish a series of validators who were for an invalid parablock. This is
87-
/// expected to be a major punishment.
86+
/// Punish a series of validators who were for an invalid parablock.
87+
/// This is expected to trigger a large punishment for backers
88+
/// and a medium punishment for other approvers.
8889
fn punish_for_invalid(
8990
session: SessionIndex,
9091
candidate_hash: CandidateHash,
9192
losers: impl IntoIterator<Item = ValidatorIndex>,
9293
backers: impl IntoIterator<Item = ValidatorIndex>,
9394
);
9495

95-
/// Punish a series of validators who were against a valid parablock. This
96-
/// is expected to be a minor punishment.
96+
/// Punish a series of validators who were against a valid parablock.
97+
/// This is expected to be a minor punishment.
9798
fn punish_against_valid(
9899
session: SessionIndex,
99100
candidate_hash: CandidateHash,

0 commit comments

Comments
 (0)