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}