liminal_protocol/lifecycle/operations/
marker_ack.rs1use alloc::boxed::Box;
2
3use crate::wire::{
4 BindingEpoch, ConversationId, DeliverySeq, Generation, MarkerAck, MarkerAckCommitted,
5 MarkerAckEnvelope, MarkerAckResponse, ParticipantId,
6};
7
8use super::{
9 super::{
10 BindingRequiredLookupResult, BindingState, LiveMember, ObserverProgressProjection,
11 ParticipantBindingRequest, PresentedIdentity, SealedBindingFateToken,
12 lookup_binding_required,
13 membership::{LiveMemberCursorUpdate, LiveMemberCursorUpdateError},
14 },
15 marker_proof::{
16 MarkerProofDecision, MarkerProofInput, MarkerProofPermit, MarkerProofState,
17 select_marker_proof,
18 },
19};
20
21#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct MarkerAckCommit {
29 outcome: MarkerAckCommitted,
30 proof: MarkerProofPermit,
31 cursor_update: LiveMemberCursorUpdate,
32}
33
34impl MarkerAckCommit {
35 #[must_use]
37 pub const fn outcome(&self) -> &MarkerAckCommitted {
38 &self.outcome
39 }
40
41 #[must_use]
43 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
44 let request = self.outcome.request();
45 let source = match self.proof.progress().checkpoint_marker_source() {
46 Some(progress) => Some(
47 super::super::checkpoint_codec::ObserverProjectionSource::Marker {
48 progress,
49 previous_cursor: self.cursor_update.previous_cursor(),
50 },
51 ),
52 None => None,
53 };
54 ObserverProgressProjection::with_checkpoint_source(
55 request.conversation_id,
56 request.marker_delivery_seq,
57 source,
58 )
59 }
60
61 #[must_use]
63 pub const fn canonical_request(&self) -> MarkerAck {
64 let request = self.outcome.request();
65 MarkerAck {
66 conversation_id: request.conversation_id,
67 participant_id: request.participant_id,
68 capability_generation: request.capability_generation,
69 marker_delivery_seq: request.marker_delivery_seq,
70 }
71 }
72
73 #[must_use]
75 pub const fn receiving_binding_epoch(&self) -> BindingEpoch {
76 self.proof.proof_binding_epoch()
77 }
78
79 #[must_use]
81 pub const fn offered_marker_delivery_seq(&self) -> DeliverySeq {
82 self.proof.expected_marker_delivery_seq()
83 }
84
85 #[must_use]
87 pub const fn delivered_binding_epoch(&self) -> BindingEpoch {
88 self.proof.proof_binding_epoch()
89 }
90
91 #[must_use]
93 pub const fn from_cursor(&self) -> DeliverySeq {
94 self.cursor_update.previous_cursor()
95 }
96
97 #[must_use]
99 pub const fn resulting_cursor(&self) -> DeliverySeq {
100 self.cursor_update.resulting_cursor()
101 }
102
103 #[must_use]
105 pub const fn proof(&self) -> &MarkerProofPermit {
106 &self.proof
107 }
108
109 pub fn progress_binding_fate_token(
125 &self,
126 token: SealedBindingFateToken,
127 ) -> Result<SealedBindingFateToken, Box<SealedBindingFateToken>> {
128 let request = self.outcome.request();
129 token.marker_ack_progressed(
130 request.conversation_id,
131 request.participant_id,
132 self.receiving_binding_epoch(),
133 self.from_cursor(),
134 self.resulting_cursor(),
135 )
136 }
137
138 pub fn apply_to<F>(
150 self,
151 member: &mut LiveMember<F>,
152 ) -> Result<MarkerAckCommitted, MarkerAckCommitError> {
153 member
154 .apply_cursor_update(self.cursor_update)
155 .map_err(MarkerAckCommitError::from_member_error)?;
156 Ok(self.outcome)
157 }
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq)]
162pub enum MarkerAckCommitError {
163 Conversation {
165 expected: ConversationId,
167 actual: ConversationId,
169 },
170 Participant {
172 expected: ParticipantId,
174 actual: ParticipantId,
176 },
177 Generation {
179 expected: Generation,
181 actual: Generation,
183 },
184 NonAdvancing {
186 from_cursor: DeliverySeq,
188 resulting_cursor: DeliverySeq,
190 },
191 CursorPrestate {
193 expected_from_cursor: DeliverySeq,
195 resulting_cursor: DeliverySeq,
197 actual_cursor: DeliverySeq,
199 },
200}
201
202impl MarkerAckCommitError {
203 const fn from_member_error(error: LiveMemberCursorUpdateError) -> Self {
204 match error {
205 LiveMemberCursorUpdateError::Conversation { expected, actual } => {
206 Self::Conversation { expected, actual }
207 }
208 LiveMemberCursorUpdateError::Participant { expected, actual } => {
209 Self::Participant { expected, actual }
210 }
211 LiveMemberCursorUpdateError::Generation { expected, actual } => {
212 Self::Generation { expected, actual }
213 }
214 LiveMemberCursorUpdateError::NonAdvancing {
215 from_cursor,
216 resulting_cursor,
217 } => Self::NonAdvancing {
218 from_cursor,
219 resulting_cursor,
220 },
221 LiveMemberCursorUpdateError::CursorPrestate {
222 expected_from_cursor,
223 resulting_cursor,
224 actual_cursor,
225 } => Self::CursorPrestate {
226 expected_from_cursor,
227 resulting_cursor,
228 actual_cursor,
229 },
230 }
231 }
232}
233
234#[derive(Clone, Debug, PartialEq, Eq)]
236pub enum MarkerAckDecision {
237 Respond(MarkerAckResponse),
239 Commit(MarkerAckCommit),
241}
242
243#[must_use]
253pub fn apply_marker_ack<EF, V, LF>(
254 presented_identity: PresentedIdentity<'_, EF, V, LF>,
255 binding: &BindingState,
256 receiving_binding_epoch: BindingEpoch,
257 request: &MarkerAck,
258 marker_state: &MarkerProofState,
259) -> MarkerAckDecision {
260 let lookup_request = ParticipantBindingRequest::MarkerAck(request.clone());
261 let (member, active_binding) = match lookup_binding_required(
262 presented_identity,
263 binding,
264 Some(receiving_binding_epoch),
265 &lookup_request,
266 ) {
267 BindingRequiredLookupResult::Retired(outcome) => {
268 return MarkerAckDecision::Respond(MarkerAckResponse::from_retired(outcome));
269 }
270 BindingRequiredLookupResult::ParticipantUnknown(outcome) => {
271 return MarkerAckDecision::Respond(MarkerAckResponse::from_participant_unknown(
272 outcome,
273 ));
274 }
275 BindingRequiredLookupResult::StaleAuthority(outcome) => {
276 return MarkerAckDecision::Respond(MarkerAckResponse::from_stale_authority(outcome));
277 }
278 BindingRequiredLookupResult::NoBinding(outcome) => {
279 return MarkerAckDecision::Respond(MarkerAckResponse::from_no_binding(outcome));
280 }
281 BindingRequiredLookupResult::Authorized { member, binding } => (member, binding),
282 };
283
284 let exact_state = MarkerProofState::new(
285 member.cursor(),
286 marker_state.accepted_marker_at_cursor(),
287 marker_state.expected_marker_delivery_seq(),
288 active_binding.binding_epoch,
289 marker_state.delivered_to_proof_epoch(),
290 );
291 match select_marker_proof(&exact_state, MarkerProofInput::marker_ack(request)) {
292 MarkerProofDecision::AckNoOp(outcome) => {
293 MarkerAckDecision::Respond(MarkerAckResponse::from_ack_no_op(outcome))
294 }
295 MarkerProofDecision::MarkerMismatch(outcome) => {
296 MarkerAckDecision::Respond(MarkerAckResponse::from_marker_mismatch(outcome))
297 }
298 MarkerProofDecision::MarkerNotDelivered(outcome) => {
299 MarkerAckDecision::Respond(MarkerAckResponse::from_marker_not_delivered(outcome))
300 }
301 MarkerProofDecision::Permit(proof) => {
302 let envelope = marker_ack_envelope(request);
303 MarkerAckDecision::Commit(MarkerAckCommit {
304 outcome: MarkerAckCommitted::new(envelope),
305 proof,
306 cursor_update: LiveMemberCursorUpdate::new(
307 request.conversation_id,
308 request.participant_id,
309 request.capability_generation,
310 member.cursor(),
311 request.marker_delivery_seq,
312 ),
313 })
314 }
315 }
316}
317
318const fn marker_ack_envelope(request: &MarkerAck) -> MarkerAckEnvelope {
319 MarkerAckEnvelope {
320 conversation_id: request.conversation_id,
321 participant_id: request.participant_id,
322 capability_generation: request.capability_generation,
323 marker_delivery_seq: request.marker_delivery_seq,
324 }
325}