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}