gbp_core/state.rs
1//! Finite state machines defined by the state-machine specification.
2//!
3//! Only the enum values and the transition validator live here; the side
4//! effects (timers, retries, etc.) belong in `gbp-node`.
5
6use core::fmt;
7
8/// Group node FSM.
9#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
10pub enum NodeState {
11 /// Initial state, before any transport is opened.
12 Idle,
13 /// QUIC / TLS handshake in progress.
14 Connecting,
15 /// MLS Welcome / ratchet tree exchange in progress.
16 EstablishingGroup,
17 /// Normal operating state.
18 Active,
19 /// `ERR_EPOCH_MISMATCH` (or equivalent) was raised; digest-based resync
20 /// is in progress.
21 Resyncing,
22 /// Fatal error; the node MUST NOT transmit application data.
23 Failed,
24 /// The node performed a graceful shutdown.
25 Closed,
26}
27
28impl NodeState {
29 /// Returns `true` if the transition `self -> next` is allowed by the
30 /// state-machine specification.
31 pub fn can_transition_to(self, next: NodeState) -> bool {
32 use NodeState::*;
33 matches!(
34 (self, next),
35 (Idle, Connecting)
36 | (Idle, Failed)
37 | (Connecting, EstablishingGroup)
38 | (Connecting, Failed)
39 | (EstablishingGroup, Active)
40 | (EstablishingGroup, Failed)
41 | (Active, Resyncing)
42 | (Active, Closed)
43 | (Active, Failed)
44 | (Resyncing, Active)
45 | (Resyncing, Failed)
46 | (_, Closed)
47 )
48 }
49}
50
51impl fmt::Display for NodeState {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 f.write_str(match self {
54 Self::Idle => "IDLE",
55 Self::Connecting => "CONNECTING",
56 Self::EstablishingGroup => "ESTABLISHING_GROUP",
57 Self::Active => "ACTIVE",
58 Self::Resyncing => "RESYNCING",
59 Self::Failed => "FAILED",
60 Self::Closed => "CLOSED",
61 })
62 }
63}
64
65/// Epoch transition FSM.
66#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
67pub enum TransitionState {
68 /// No pending commit.
69 TIdle,
70 /// `PREPARE_TRANSITION` was issued or received.
71 TPrepared,
72 /// MLS commit was processed and the local ratchet was applied.
73 TCommitProcessed,
74 /// Every member acknowledged with `READY_FOR_TRANSITION`.
75 TReady,
76 /// `EXECUTE_TRANSITION` has been applied; epoch was advanced.
77 TExecuted,
78 /// Transition was aborted (`ABORT_TRANSITION` or timeout).
79 TAborted,
80}
81
82impl TransitionState {
83 /// Returns `true` if the transition `self -> next` is allowed by the
84 /// state-machine specification (gbp-state-machine ยง4).
85 pub fn can_transition_to(self, next: TransitionState) -> bool {
86 use TransitionState::*;
87 matches!(
88 (self, next),
89 (TIdle, TPrepared)
90 | (TPrepared, TCommitProcessed)
91 | (TPrepared, TAborted)
92 | (TCommitProcessed, TReady)
93 | (TCommitProcessed, TAborted)
94 | (TReady, TExecuted)
95 | (TReady, TAborted)
96 | (TExecuted, TIdle)
97 | (TAborted, TIdle)
98 )
99 }
100}
101
102/// Sub-protocol activation FSM.
103#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
104pub enum SubprotocolState {
105 /// Sub-protocol is disabled.
106 Disabled,
107 /// Capability negotiation is in progress (`CAPABILITIES_ADVERTISE`).
108 Negotiating,
109 /// Sub-protocol is active.
110 Enabled,
111 /// Sub-protocol is active in degraded mode (e.g. lost FEC).
112 Degraded,
113 /// Sub-protocol is temporarily suspended (`MUTE` / `STREAM_STOP`).
114 Suspended,
115}