liminal_protocol/lifecycle/
binding_fate.rs1use alloc::boxed::Box;
2
3use crate::wire::{BindingEpoch, ConversationId, DeliverySeq, ParticipantId};
4
5#[cfg(test)]
6use super::FencedAttachCommit;
7use super::{
8 CommittedDiedTerminal, Event, OrdinaryBindingFate, RecoveredBindingFate, SealedBindingFateToken,
9};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum SealedBindingFateIntent {
17 Ordinary,
19 Recovered {
21 prior_binding_epoch: BindingEpoch,
23 marker_delivery_seq: DeliverySeq,
25 },
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub(in crate::lifecycle) struct BindingFateMeasurementContext {
31 pub(in crate::lifecycle) conversation_id: ConversationId,
32 pub(in crate::lifecycle) participant_id: ParticipantId,
33 pub(in crate::lifecycle) binding_epoch: BindingEpoch,
34 pub(in crate::lifecycle) cursor: DeliverySeq,
35}
36
37impl SealedBindingFateToken {
38 #[must_use]
40 pub const fn is_recovered(&self) -> bool {
41 self.recovered.is_some()
42 }
43
44 #[must_use]
46 pub const fn intent(&self) -> Option<SealedBindingFateIntent> {
47 match (&self.ordinary, &self.recovered) {
48 (Some(_), None) => Some(SealedBindingFateIntent::Ordinary),
49 (None, Some(proof)) => Some(SealedBindingFateIntent::Recovered {
50 prior_binding_epoch: proof.prior_binding_epoch(),
51 marker_delivery_seq: proof.marker_delivery_seq(),
52 }),
53 (None, None) | (Some(_), Some(_)) => None,
54 }
55 }
56
57 #[cfg(test)]
58 pub(in crate::lifecycle) const fn from_recovered_for_test(
59 recovered: FencedAttachCommit,
60 ) -> Self {
61 let cursor = recovered.marker_delivery_seq();
62 Self {
63 ordinary: None,
64 recovered: Some(recovered),
65 cursor,
66 }
67 }
68
69 pub(in crate::lifecycle) fn participant_ack_progressed(
71 mut self,
72 conversation_id: ConversationId,
73 participant_id: ParticipantId,
74 binding_epoch: BindingEpoch,
75 previous_cursor: DeliverySeq,
76 through_seq: DeliverySeq,
77 ) -> Result<Self, Box<Self>> {
78 if previous_cursor != self.cursor || through_seq <= previous_cursor {
79 return Err(Box::new(self));
80 }
81 match (self.ordinary.take(), self.recovered.as_ref()) {
82 (Some(authority), None) => match authority.participant_ack_progressed(
83 conversation_id,
84 participant_id,
85 binding_epoch,
86 previous_cursor,
87 through_seq,
88 ) {
89 Ok(authority) => self.ordinary = Some(authority),
90 Err(authority) => {
91 self.ordinary = Some(authority);
92 return Err(Box::new(self));
93 }
94 },
95 (None, Some(proof))
96 if proof.conversation_id() == conversation_id
97 && proof.participant_id() == participant_id
98 && proof.new_binding_epoch() == binding_epoch => {}
99 (ordinary, _) => {
100 self.ordinary = ordinary;
101 return Err(Box::new(self));
102 }
103 }
104 self.cursor = through_seq;
105 Ok(self)
106 }
107
108 pub(in crate::lifecycle) fn marker_ack_progressed(
133 self,
134 conversation_id: ConversationId,
135 participant_id: ParticipantId,
136 binding_epoch: BindingEpoch,
137 previous_cursor: DeliverySeq,
138 through_seq: DeliverySeq,
139 ) -> Result<Self, Box<Self>> {
140 if self.cursor == through_seq && previous_cursor < through_seq {
146 return Ok(self);
147 }
148 self.participant_ack_progressed(
149 conversation_id,
150 participant_id,
151 binding_epoch,
152 previous_cursor,
153 through_seq,
154 )
155 }
156
157 pub(in crate::lifecycle) const fn measurement_context(
159 &self,
160 ) -> Option<BindingFateMeasurementContext> {
161 match (&self.ordinary, &self.recovered) {
162 (Some(authority), None) if authority.through_seq() == self.cursor => {
163 let binding = authority.binding();
164 Some(BindingFateMeasurementContext {
165 conversation_id: binding.conversation_id,
166 participant_id: binding.participant_id,
167 binding_epoch: binding.binding_epoch,
168 cursor: self.cursor,
169 })
170 }
171 (None, Some(proof)) => Some(BindingFateMeasurementContext {
172 conversation_id: proof.conversation_id(),
173 participant_id: proof.participant_id(),
174 binding_epoch: proof.new_binding_epoch(),
175 cursor: self.cursor,
176 }),
177 (None | Some(_), None) | (Some(_), Some(_)) => None,
178 }
179 }
180
181 pub(in crate::lifecycle) fn ordinary_binding_fate(
183 mut self,
184 terminal: CommittedDiedTerminal,
185 resulting_floor: DeliverySeq,
186 ) -> Result<OrdinaryBindingFate, Box<Self>> {
187 if self.recovered.is_some() {
188 return Err(Box::new(self));
189 }
190 let Some(authority) = self.ordinary.take() else {
191 return Err(Box::new(self));
192 };
193 match authority.binding_fate(terminal, resulting_floor) {
194 Ok(fate) => Ok(fate),
195 Err(authority) => {
196 self.ordinary = Some(authority);
197 Err(Box::new(self))
198 }
199 }
200 }
201
202 pub(in crate::lifecycle) fn recovered_binding_fate_measured(
204 self,
205 resulting_floor: DeliverySeq,
206 ) -> Result<RecoveredBindingFate, Box<Self>> {
207 let Some(context) = self.measurement_context() else {
208 return Err(Box::new(self));
209 };
210 self.recovered_binding_fate(Event::binding_fate_observed(
211 context.participant_id,
212 context.binding_epoch,
213 resulting_floor,
214 ))
215 }
216
217 pub(in crate::lifecycle) fn recovered_binding_fate(
218 mut self,
219 event: Event,
220 ) -> Result<RecoveredBindingFate, Box<Self>> {
221 if self.ordinary.is_some() {
222 return Err(Box::new(self));
223 }
224 let Some(proof) = self.recovered.take() else {
225 return Err(Box::new(self));
226 };
227 match proof.recovered_binding_fate(event) {
228 Ok(fate) => Ok(fate),
229 Err(proof) => {
230 self.recovered = Some(*proof);
231 Err(Box::new(self))
232 }
233 }
234 }
235}