Skip to main content

dig_slashing/appeal/
ground.rs

1//! Per-variant appeal payloads + ground enums.
2//!
3//! Traces to: [SPEC.md §3.6](../../../docs/resources/SPEC.md).
4//!
5//! # Shape
6//!
7//! Every appeal carries a `ground` (categorical reason the appeal is
8//! being filed) and an opaque `witness` byte vec. Grounds are closed
9//! enums — `serde_bytes` annotation on witness keeps CBOR/MessagePack
10//! encodings compact (DSL-110).
11
12use serde::{Deserialize, Serialize};
13
14/// Grounds on which a `ProposerSlashing` can be appealed.
15///
16/// Traces to [SPEC §3.6.1](../../../docs/resources/SPEC.md). Each
17/// ground mirrors a DSL-013 precondition (`verify_proposer_slashing`)
18/// — when an appeal proves one of these holds, the slash must be
19/// reverted because the evidence verifier would have rejected the
20/// submission had it been working correctly.
21///
22/// # Variant index
23///
24/// - `HeadersIdentical` — byte-equal headers (DSL-034).
25/// - `ProposerIndexMismatch` — different proposer on the two headers
26///   (DSL-035).
27/// - `SignatureAInvalid` — sig A fails BLS verify (DSL-036).
28/// - `SignatureBInvalid` — sig B fails BLS verify (DSL-037).
29/// - `SlotMismatch` — different slots (DSL-038).
30/// - `ValidatorNotActiveAtEpoch` — accused inactive at the offense
31///   epoch (DSL-039).
32#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
33pub enum ProposerAppealGround {
34    /// The two signed headers are byte-equal (no equivocation).
35    HeadersIdentical,
36    /// `signed_header_a.message.proposer_index !=
37    /// signed_header_b.message.proposer_index`.
38    ProposerIndexMismatch,
39    /// Signature A fails BLS verify against the canonical signing
40    /// message.
41    SignatureAInvalid,
42    /// Signature B fails BLS verify.
43    SignatureBInvalid,
44    /// `signed_header_a.message.height != signed_header_b.message.height`.
45    SlotMismatch,
46    /// Accused validator was not active at `header.epoch`.
47    ValidatorNotActiveAtEpoch,
48}
49
50/// Proposer-slashing appeal payload.
51///
52/// Traces to [SPEC §3.6.1](../../../docs/resources/SPEC.md).
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
54pub struct ProposerSlashingAppeal {
55    /// Categorical ground for this appeal.
56    pub ground: ProposerAppealGround,
57    /// Ground-specific witness bytes. Many proposer grounds are
58    /// evidence-only (re-derivable from the envelope) — witness is
59    /// allowed to be empty. Grounds that require external state
60    /// (`ValidatorNotActiveAtEpoch`) carry proof bytes here.
61    #[serde(with = "serde_bytes")]
62    pub witness: Vec<u8>,
63}
64
65/// Grounds on which an `AttesterSlashing` can be appealed.
66///
67/// Traces to [SPEC §3.6.2](../../../docs/resources/SPEC.md). Mirrors
68/// DSL-014..017 preconditions + DSL-005 structural check.
69#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
70pub enum AttesterAppealGround {
71    /// Both attestations are byte-equal (DSL-041).
72    AttestationsIdentical,
73    /// Neither double-vote nor surround-vote holds (DSL-042).
74    NotSlashableByPredicate,
75    /// Intersection is empty (DSL-043).
76    EmptyIntersection,
77    /// Aggregate signature A invalid (DSL-044).
78    SignatureAInvalid,
79    /// Aggregate signature B invalid (DSL-045).
80    SignatureBInvalid,
81    /// Structural validation failed (non-ascending indices, cap,
82    /// sig-width) — DSL-046.
83    InvalidIndexedAttestationStructure,
84    /// Named validator not actually in the slashable intersection
85    /// (DSL-047).
86    ValidatorNotInIntersection {
87        /// Index of the validator the appeal argues is not slashable.
88        validator_index: u32,
89    },
90}
91
92/// Attester-slashing appeal payload.
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
94pub struct AttesterSlashingAppeal {
95    /// Categorical ground for this appeal.
96    pub ground: AttesterAppealGround,
97    /// Optional witness bytes.
98    #[serde(with = "serde_bytes")]
99    pub witness: Vec<u8>,
100}
101
102/// Grounds on which an `InvalidBlockProof` can be appealed.
103///
104/// Traces to [SPEC §3.6.3](../../../docs/resources/SPEC.md). Mirrors
105/// DSL-018..020 preconditions.
106#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
107pub enum InvalidBlockAppealGround {
108    /// Block re-executes cleanly per the oracle (DSL-049).
109    BlockActuallyValid,
110    /// Proposer BLS signature invalid (DSL-050).
111    ProposerSignatureInvalid,
112    /// `failure_reason` in the evidence disagrees with the oracle's
113    /// re-execution outcome (DSL-051).
114    FailureReasonMismatch,
115    /// `header.epoch != evidence.epoch` (DSL-052).
116    EvidenceEpochMismatch,
117}
118
119/// Invalid-block appeal payload.
120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
121pub struct InvalidBlockAppeal {
122    /// Categorical ground for this appeal.
123    pub ground: InvalidBlockAppealGround,
124    /// Witness bytes — `BlockActuallyValid` grounds require full
125    /// block body + pre-state for oracle re-execution.
126    #[serde(with = "serde_bytes")]
127    pub witness: Vec<u8>,
128}