Skip to main content

liminal_protocol/wire/
primitives.rs

1/// Stable conversation identifier.
2pub type ConversationId = u64;
3
4/// Permanent participant identifier and base identity index.
5pub type ParticipantId = u64;
6
7/// Permanent participant index used by participant-scoped progress accounting.
8pub type ParticipantIndex = u64;
9
10/// Nonzero participant capability or retired generation.
11#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
12pub struct Generation(core::num::NonZeroU64);
13
14impl Generation {
15    /// Canonical first participant generation.
16    pub const ONE: Self = Self(core::num::NonZeroU64::MIN);
17
18    /// Creates a generation, returning `None` for the forbidden zero value.
19    #[must_use]
20    pub const fn new(value: u64) -> Option<Self> {
21        match core::num::NonZeroU64::new(value) {
22            Some(value) => Some(Self(value)),
23            None => None,
24        }
25    }
26
27    /// Returns the nonzero wire value.
28    #[must_use]
29    pub const fn get(self) -> u64 {
30        self.0.get()
31    }
32}
33
34/// Conversation delivery sequence.
35pub type DeliverySeq = u64;
36
37/// Serialized conversation transaction order.
38pub type TransactionOrder = u64;
39
40/// Observer refusal/progress epoch.
41pub type ObserverEpoch = u64;
42
43/// Marker-settlement refusal/clearing epoch.
44///
45/// Deliberately distinct from [`ObserverEpoch`]: the settlement family is
46/// cleared by another participant's record admission or by boot drain, never by
47/// observer progress, so the two epochs are never comparable even though both
48/// serialize as `u64` (participant contract ยง0.16 condition 2).
49pub type SettlementEpoch = u64;
50
51/// Participant protocol version carried in every inner frame prefix.
52#[derive(Clone, Copy, Debug, PartialEq, Eq)]
53pub struct ProtocolVersion {
54    /// Major version.
55    pub major: u16,
56    /// Minor version.
57    pub minor: u16,
58}
59
60impl ProtocolVersion {
61    /// Participant protocol v1.0.
62    pub const V1: Self = Self { major: 1, minor: 0 };
63
64    /// Creates a protocol version.
65    #[must_use]
66    pub const fn new(major: u16, minor: u16) -> Self {
67        Self { major, minor }
68    }
69}
70
71/// Server and connection identity for one accepted connection incarnation.
72#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
73pub struct ConnectionIncarnation {
74    /// Durable server incarnation.
75    pub server_incarnation: u64,
76    /// Connection ordinal within that server incarnation.
77    pub connection_ordinal: u64,
78}
79
80impl ConnectionIncarnation {
81    /// Creates a connection incarnation.
82    #[must_use]
83    pub const fn new(server_incarnation: u64, connection_ordinal: u64) -> Self {
84        Self {
85            server_incarnation,
86            connection_ordinal,
87        }
88    }
89}
90
91/// Immutable participant binding epoch.
92///
93/// It has no order. A participant can rebind on an earlier connection at a
94/// higher capability generation, so no order over both fields means "later".
95/// "Later" compares `capability_generation`; a sort or an ordered collection
96/// keys by [`Self::canonical_sort_key`].
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
98pub struct BindingEpoch {
99    /// Connection that owns the binding.
100    pub connection_incarnation: ConnectionIncarnation,
101    /// Capability generation captured by the binding commit.
102    pub capability_generation: Generation,
103}
104
105impl BindingEpoch {
106    /// Creates a binding epoch.
107    #[must_use]
108    pub const fn new(
109        connection_incarnation: ConnectionIncarnation,
110        capability_generation: Generation,
111    ) -> Self {
112        Self {
113            connection_incarnation,
114            capability_generation,
115        }
116    }
117
118    /// A total order for canonical sorting and ordered-collection keys only:
119    /// connection first, then generation. It does not mean "later"; compare
120    /// `capability_generation` for that.
121    #[must_use]
122    pub const fn canonical_sort_key(self) -> (ConnectionIncarnation, Generation) {
123        (self.connection_incarnation, self.capability_generation)
124    }
125}
126
127macro_rules! fixed_credential {
128    ($(#[$meta:meta])* $name:ident, $length:expr) => {
129        $(#[$meta])*
130        #[derive(Clone, Copy, PartialEq, Eq, Hash)]
131        pub struct $name([u8; $length]);
132
133        /// Names the type and its length, never its bytes: a credential
134        /// reaches logs and error text through `{:?}`.
135        impl core::fmt::Debug for $name {
136            fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137                write!(formatter, "{}({} bytes)", stringify!($name), $length)
138            }
139        }
140
141        impl $name {
142            /// Creates the fixed-width value from its canonical bytes.
143            #[must_use]
144            pub const fn new(bytes: [u8; $length]) -> Self {
145                Self(bytes)
146            }
147
148            /// Returns the canonical fixed-width bytes.
149            #[must_use]
150            pub const fn into_bytes(self) -> [u8; $length] {
151                self.0
152            }
153
154            /// Borrows the canonical fixed-width bytes.
155            #[must_use]
156            pub const fn as_bytes(&self) -> &[u8; $length] {
157                &self.0
158            }
159        }
160    };
161}
162
163fixed_credential!(
164    /// Single-purpose enrollment attempt token.
165    EnrollmentToken,
166    16
167);
168fixed_credential!(
169    /// Single-purpose credential-attach attempt token.
170    AttachAttemptToken,
171    16
172);
173fixed_credential!(
174    /// Single-purpose explicit-detach attempt token.
175    DetachAttemptToken,
176    16
177);
178fixed_credential!(
179    /// Single-purpose terminal Leave attempt token.
180    LeaveAttemptToken,
181    16
182);
183fixed_credential!(
184    /// Single-purpose ordinary record-admission attempt token.
185    RecordAdmissionAttemptToken,
186    16
187);
188fixed_credential!(
189    /// Participant attach secret.
190    AttachSecret,
191    32
192);