liminal_protocol/lifecycle/operations/
marker_ack.rs1use crate::wire::{
2 BindingEpoch, ConversationId, DeliverySeq, Generation, MarkerAck, MarkerAckCommitted,
3 MarkerAckEnvelope, MarkerAckResponse, ParticipantId,
4};
5
6use super::{
7 super::{
8 BindingRequiredLookupResult, BindingState, LiveMember, ObserverProgressProjection,
9 ParticipantBindingRequest, PresentedIdentity, lookup_binding_required,
10 membership::{LiveMemberCursorUpdate, LiveMemberCursorUpdateError},
11 },
12 marker_proof::{
13 MarkerProofDecision, MarkerProofInput, MarkerProofPermit, MarkerProofState,
14 select_marker_proof,
15 },
16};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct MarkerAckCommit {
26 outcome: MarkerAckCommitted,
27 proof: MarkerProofPermit,
28 cursor_update: LiveMemberCursorUpdate,
29}
30
31impl MarkerAckCommit {
32 #[must_use]
34 pub const fn outcome(&self) -> &MarkerAckCommitted {
35 &self.outcome
36 }
37
38 #[must_use]
40 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
41 let request = self.outcome.request();
42 ObserverProgressProjection::new(request.conversation_id, request.marker_delivery_seq)
43 }
44
45 #[must_use]
47 pub const fn canonical_request(&self) -> MarkerAck {
48 let request = self.outcome.request();
49 MarkerAck {
50 conversation_id: request.conversation_id,
51 participant_id: request.participant_id,
52 capability_generation: request.capability_generation,
53 marker_delivery_seq: request.marker_delivery_seq,
54 }
55 }
56
57 #[must_use]
59 pub const fn receiving_binding_epoch(&self) -> BindingEpoch {
60 self.proof.proof_binding_epoch()
61 }
62
63 #[must_use]
65 pub const fn offered_marker_delivery_seq(&self) -> DeliverySeq {
66 self.proof.expected_marker_delivery_seq()
67 }
68
69 #[must_use]
71 pub const fn delivered_binding_epoch(&self) -> BindingEpoch {
72 self.proof.proof_binding_epoch()
73 }
74
75 #[must_use]
77 pub const fn from_cursor(&self) -> DeliverySeq {
78 self.cursor_update.previous_cursor()
79 }
80
81 #[must_use]
83 pub const fn resulting_cursor(&self) -> DeliverySeq {
84 self.cursor_update.resulting_cursor()
85 }
86
87 #[must_use]
89 pub const fn proof(&self) -> &MarkerProofPermit {
90 &self.proof
91 }
92
93 pub fn apply_to<F>(
105 self,
106 member: &mut LiveMember<F>,
107 ) -> Result<MarkerAckCommitted, MarkerAckCommitError> {
108 member
109 .apply_cursor_update(self.cursor_update)
110 .map_err(MarkerAckCommitError::from_member_error)?;
111 Ok(self.outcome)
112 }
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq)]
117pub enum MarkerAckCommitError {
118 Conversation {
120 expected: ConversationId,
122 actual: ConversationId,
124 },
125 Participant {
127 expected: ParticipantId,
129 actual: ParticipantId,
131 },
132 Generation {
134 expected: Generation,
136 actual: Generation,
138 },
139 NonAdvancing {
141 from_cursor: DeliverySeq,
143 resulting_cursor: DeliverySeq,
145 },
146 CursorPrestate {
148 expected_from_cursor: DeliverySeq,
150 resulting_cursor: DeliverySeq,
152 actual_cursor: DeliverySeq,
154 },
155}
156
157impl MarkerAckCommitError {
158 const fn from_member_error(error: LiveMemberCursorUpdateError) -> Self {
159 match error {
160 LiveMemberCursorUpdateError::Conversation { expected, actual } => {
161 Self::Conversation { expected, actual }
162 }
163 LiveMemberCursorUpdateError::Participant { expected, actual } => {
164 Self::Participant { expected, actual }
165 }
166 LiveMemberCursorUpdateError::Generation { expected, actual } => {
167 Self::Generation { expected, actual }
168 }
169 LiveMemberCursorUpdateError::NonAdvancing {
170 from_cursor,
171 resulting_cursor,
172 } => Self::NonAdvancing {
173 from_cursor,
174 resulting_cursor,
175 },
176 LiveMemberCursorUpdateError::CursorPrestate {
177 expected_from_cursor,
178 resulting_cursor,
179 actual_cursor,
180 } => Self::CursorPrestate {
181 expected_from_cursor,
182 resulting_cursor,
183 actual_cursor,
184 },
185 }
186 }
187}
188
189#[derive(Clone, Debug, PartialEq, Eq)]
191pub enum MarkerAckDecision {
192 Respond(MarkerAckResponse),
194 Commit(MarkerAckCommit),
196}
197
198#[must_use]
208pub fn apply_marker_ack<EF, V, LF>(
209 presented_identity: PresentedIdentity<'_, EF, V, LF>,
210 binding: &BindingState,
211 receiving_binding_epoch: BindingEpoch,
212 request: &MarkerAck,
213 marker_state: &MarkerProofState,
214) -> MarkerAckDecision {
215 let lookup_request = ParticipantBindingRequest::MarkerAck(request.clone());
216 let (member, active_binding) = match lookup_binding_required(
217 presented_identity,
218 binding,
219 Some(receiving_binding_epoch),
220 &lookup_request,
221 ) {
222 BindingRequiredLookupResult::Retired(outcome) => {
223 return MarkerAckDecision::Respond(MarkerAckResponse::from_retired(outcome));
224 }
225 BindingRequiredLookupResult::ParticipantUnknown(outcome) => {
226 return MarkerAckDecision::Respond(MarkerAckResponse::from_participant_unknown(
227 outcome,
228 ));
229 }
230 BindingRequiredLookupResult::StaleAuthority(outcome) => {
231 return MarkerAckDecision::Respond(MarkerAckResponse::from_stale_authority(outcome));
232 }
233 BindingRequiredLookupResult::NoBinding(outcome) => {
234 return MarkerAckDecision::Respond(MarkerAckResponse::from_no_binding(outcome));
235 }
236 BindingRequiredLookupResult::Authorized { member, binding } => (member, binding),
237 };
238
239 let exact_state = MarkerProofState::new(
240 member.cursor(),
241 marker_state.accepted_marker_at_cursor(),
242 marker_state.expected_marker_delivery_seq(),
243 active_binding.binding_epoch,
244 marker_state.delivered_to_proof_epoch(),
245 );
246 match select_marker_proof(&exact_state, MarkerProofInput::marker_ack(request)) {
247 MarkerProofDecision::AckNoOp(outcome) => {
248 MarkerAckDecision::Respond(MarkerAckResponse::from_ack_no_op(outcome))
249 }
250 MarkerProofDecision::MarkerMismatch(outcome) => {
251 MarkerAckDecision::Respond(MarkerAckResponse::from_marker_mismatch(outcome))
252 }
253 MarkerProofDecision::MarkerNotDelivered(outcome) => {
254 MarkerAckDecision::Respond(MarkerAckResponse::from_marker_not_delivered(outcome))
255 }
256 MarkerProofDecision::Permit(proof) => {
257 let envelope = marker_ack_envelope(request);
258 MarkerAckDecision::Commit(MarkerAckCommit {
259 outcome: MarkerAckCommitted::new(envelope),
260 proof,
261 cursor_update: LiveMemberCursorUpdate::new(
262 request.conversation_id,
263 request.participant_id,
264 request.capability_generation,
265 member.cursor(),
266 request.marker_delivery_seq,
267 ),
268 })
269 }
270 }
271}
272
273const fn marker_ack_envelope(request: &MarkerAck) -> MarkerAckEnvelope {
274 MarkerAckEnvelope {
275 conversation_id: request.conversation_id,
276 participant_id: request.participant_id,
277 capability_generation: request.capability_generation,
278 marker_delivery_seq: request.marker_delivery_seq,
279 }
280}