Skip to main content

dig_slashing/evidence/
proposer_slashing.rs

1//! `SignedBlockHeader` + `ProposerSlashing` — proposer-side evidence types.
2//!
3//! Traces to: [SPEC.md §3.4](../../docs/resources/SPEC.md), catalogue rows
4//! [DSL-009](../../docs/requirements/domains/evidence/specs/DSL-009.md) +
5//! [DSL-013](../../docs/requirements/domains/evidence/specs/DSL-013.md).
6//!
7//! # Role
8//!
9//! Carries `dig_block::L2BlockHeader` (the canonical L2 block header) +
10//! its 96-byte BLS G2 signature. Consumed by:
11//!
12//! - `ProposerSlashing` (DSL-013) — equivocation requires TWO `SignedBlockHeader`s
13//!   at the same slot with matching proposer but different messages.
14//! - `InvalidBlockProof` (DSL-018) — invalid-block evidence carries ONE.
15//!
16//! # Scope
17//!
18//! Passive wire carrier. This file is `serde` + `PartialEq` only — NO
19//! cryptographic verification. Signature-width enforcement, BLS verify,
20//! and message re-derivation all live DOWNSTREAM in the verifiers:
21//!
22//! - `verify_proposer_slashing` (DSL-013) runs `chia_bls::Signature::from_bytes`
23//!   which rejects widths != `BLS_SIGNATURE_SIZE`.
24//! - `verify_invalid_block` (DSL-018) runs the same check.
25//!
26//! Keeping the type passive lets construct/serde work uniformly across
27//! valid AND structurally-malformed inputs — critical for fuzzers and
28//! property-based tests (`proptest`).
29//!
30//! # Wire layout
31//!
32//! - `message`: `dig_block::L2BlockHeader` — NOT redefined here. Full
33//!   type identity with `dig-block` so cross-crate consumers (validator,
34//!   mempool) round-trip the same bytes.
35//! - `signature`: `Vec<u8>` annotated `#[serde(with = "serde_bytes")]`.
36//!   JSON emits a byte-string (not a 96-element integer array), keeping
37//!   REMARK payloads (DSL-102, DSL-110) compact.
38
39use dig_block::L2BlockHeader;
40use serde::{Deserialize, Serialize};
41
42/// Block header + BLS signature pair.
43///
44/// Per [SPEC §3.4](../../docs/resources/SPEC.md). Length of `signature`
45/// is NOT enforced here — see module docs for the rationale + the
46/// downstream enforcement points.
47#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
48pub struct SignedBlockHeader {
49    /// Canonical L2 block header.
50    pub message: L2BlockHeader,
51    /// BLS G2 signature over `dig_block::block_signing_message(...)`.
52    /// MUST be exactly `BLS_SIGNATURE_SIZE` (96) bytes when consumed by
53    /// the verifiers (DSL-013, DSL-018).
54    #[serde(with = "serde_bytes")]
55    pub signature: Vec<u8>,
56}
57
58/// Proposer-equivocation evidence: two signed headers at the same slot
59/// from the same proposer with distinct content.
60///
61/// Per [SPEC §3.4](../../docs/resources/SPEC.md). Carried as
62/// `SlashingEvidencePayload::Proposer` in the envelope (DSL-002 / DSL-010).
63///
64/// # Scope
65///
66/// Passive wire carrier. The semantic equivocation predicate —
67/// `same_slot && same_proposer && signed_header_a != signed_header_b` —
68/// is enforced DOWNSTREAM by `verify_proposer_slashing` (DSL-013). This
69/// type will serde-roundtrip ANY pair of `SignedBlockHeader` values,
70/// including identical ones, empty signatures, or mismatched proposers.
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
72pub struct ProposerSlashing {
73    /// First signed header. By convention the one observed first in time,
74    /// but the predicate is symmetric so ordering does not affect slash.
75    pub signed_header_a: SignedBlockHeader,
76    /// Second signed header. MUST differ from `signed_header_a` in at
77    /// least one field for the slashing to be valid (DSL-013 rejects
78    /// `HeadersIdentical`).
79    pub signed_header_b: SignedBlockHeader,
80}