Skip to main content

dig_slashing/
system.rs

1//! `SlashingSystem` genesis bootstrap.
2//!
3//! Traces to: [SPEC §11](../docs/resources/SPEC.md).
4//!
5//! # Role
6//!
7//! Bundles the three long-lived state trackers
8//! (`SlashingManager`, `ParticipationTracker`,
9//! `InactivityScoreTracker`) into one aggregate that the
10//! embedder can construct via [`SlashingSystem::genesis`] at
11//! chain birth and step forward via [`crate::run_epoch_boundary`]
12//! at every epoch boundary.
13//!
14//! The three sub-components are independently useful (tests in
15//! earlier phases construct them directly), so this aggregate is
16//! intentionally a thin wrapper — it owns zero logic beyond the
17//! constructor.
18
19use dig_protocol::Bytes32;
20
21use crate::inactivity::InactivityScoreTracker;
22use crate::manager::SlashingManager;
23use crate::participation::ParticipationTracker;
24
25/// Parameters required at chain genesis to initialise the
26/// slashing system.
27///
28/// `network_id` is carried for future domain-separation /
29/// signature-binding work (currently unused by `genesis` itself
30/// but required by embedders who persist the aggregate across
31/// chain forks).
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct GenesisParameters {
34    /// Epoch the chain starts at. Typically `0`; non-zero only
35    /// for test-fixtures or sidechains forked from a live chain.
36    pub genesis_epoch: u64,
37    /// Number of validators present at genesis. Drives
38    /// tracker sizing; subsequent validator-set changes go
39    /// through DSL-127's step-7 resize.
40    pub initial_validator_count: usize,
41    /// Network identifier. Reserved for downstream
42    /// domain-separated signatures; not consulted by `genesis`
43    /// itself.
44    pub network_id: Bytes32,
45}
46
47/// Aggregate of the three long-lived slashing-state trackers
48/// an embedder carries across blocks.
49///
50/// Each field is independently exported by the crate; the
51/// aggregate exists only to give embedders a single struct to
52/// serialise / snapshot / pass into `run_epoch_boundary`.
53///
54/// # `network_id`
55///
56/// Stored privately under DSL-170. The accessor
57/// [`SlashingSystem::network_id`] returns a borrow. Consumed by
58/// downstream admission flows (DSL-168
59/// `process_block_admissions`) that need domain-separated
60/// signature verification without requiring every embedder call
61/// site to thread `network_id` through its arg list.
62#[derive(Debug)]
63pub struct SlashingSystem {
64    pub manager: SlashingManager,
65    pub participation: ParticipationTracker,
66    pub inactivity: InactivityScoreTracker,
67    /// Network identifier captured at genesis. See DSL-170 for
68    /// the rationale for carrying this on the aggregate rather
69    /// than reconstructing it per call.
70    network_id: Bytes32,
71}
72
73impl SlashingSystem {
74    /// Construct the at-genesis state per SPEC §11.
75    ///
76    /// Implements [DSL-128](../docs/requirements/domains/orchestration/specs/DSL-128.md).
77    ///
78    /// # Post-conditions
79    ///
80    ///   - `manager.processed.is_empty()` and
81    ///     `manager.book().is_empty()` — no slashes yet.
82    ///   - `manager.current_epoch() == params.genesis_epoch`.
83    ///   - `participation.current_epoch_number() == params.genesis_epoch`
84    ///     and both the previous- and current-epoch flag
85    ///     vectors are zero-initialised with
86    ///     `initial_validator_count` entries.
87    ///   - `inactivity.validator_count() == initial_validator_count`
88    ///     and every score is `0`.
89    ///   - `network_id()` returns the exact `params.network_id`
90    ///     (DSL-170).
91    #[must_use]
92    pub fn genesis(params: &GenesisParameters) -> Self {
93        Self {
94            manager: SlashingManager::new(params.genesis_epoch),
95            participation: ParticipationTracker::new(
96                params.initial_validator_count,
97                params.genesis_epoch,
98            ),
99            inactivity: InactivityScoreTracker::new(params.initial_validator_count),
100            network_id: params.network_id,
101        }
102    }
103
104    /// Network identifier captured at genesis.
105    ///
106    /// Implements [DSL-170](../docs/requirements/domains/orchestration/specs/DSL-170.md).
107    /// Traces to SPEC §11.
108    ///
109    /// # Lifetime
110    ///
111    /// Returns `&Bytes32` borrowed from `&self`. Callers that need
112    /// an owned value copy by `*system.network_id()` — `Bytes32`
113    /// is a fixed-width byte wrapper that implements `Copy`.
114    ///
115    /// # Consumers
116    ///
117    /// - DSL-168 `process_block_admissions` reads this so the
118    ///   admission pipeline can reconstruct signing-message
119    ///   domain-separated digests without a per-call arg.
120    /// - Embedders persisting the aggregate across chain forks
121    ///   use this to detect cross-fork replay at load time.
122    #[must_use]
123    pub fn network_id(&self) -> &Bytes32 {
124        &self.network_id
125    }
126}