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) const fn measurement_context(
110 &self,
111 ) -> Option<BindingFateMeasurementContext> {
112 match (&self.ordinary, &self.recovered) {
113 (Some(authority), None) if authority.through_seq() == self.cursor => {
114 let binding = authority.binding();
115 Some(BindingFateMeasurementContext {
116 conversation_id: binding.conversation_id,
117 participant_id: binding.participant_id,
118 binding_epoch: binding.binding_epoch,
119 cursor: self.cursor,
120 })
121 }
122 (None, Some(proof)) => Some(BindingFateMeasurementContext {
123 conversation_id: proof.conversation_id(),
124 participant_id: proof.participant_id(),
125 binding_epoch: proof.new_binding_epoch(),
126 cursor: self.cursor,
127 }),
128 (None | Some(_), None) | (Some(_), Some(_)) => None,
129 }
130 }
131
132 pub(in crate::lifecycle) fn ordinary_binding_fate(
134 mut self,
135 terminal: CommittedDiedTerminal,
136 resulting_floor: DeliverySeq,
137 ) -> Result<OrdinaryBindingFate, Box<Self>> {
138 if self.recovered.is_some() {
139 return Err(Box::new(self));
140 }
141 let Some(authority) = self.ordinary.take() else {
142 return Err(Box::new(self));
143 };
144 match authority.binding_fate(terminal, resulting_floor) {
145 Ok(fate) => Ok(fate),
146 Err(authority) => {
147 self.ordinary = Some(authority);
148 Err(Box::new(self))
149 }
150 }
151 }
152
153 pub(in crate::lifecycle) fn recovered_binding_fate_measured(
155 self,
156 resulting_floor: DeliverySeq,
157 ) -> Result<RecoveredBindingFate, Box<Self>> {
158 let Some(context) = self.measurement_context() else {
159 return Err(Box::new(self));
160 };
161 self.recovered_binding_fate(Event::binding_fate_observed(
162 context.participant_id,
163 context.binding_epoch,
164 resulting_floor,
165 ))
166 }
167
168 pub(in crate::lifecycle) fn recovered_binding_fate(
169 mut self,
170 event: Event,
171 ) -> Result<RecoveredBindingFate, Box<Self>> {
172 if self.ordinary.is_some() {
173 return Err(Box::new(self));
174 }
175 let Some(proof) = self.recovered.take() else {
176 return Err(Box::new(self));
177 };
178 match proof.recovered_binding_fate(event) {
179 Ok(fate) => Ok(fate),
180 Err(proof) => {
181 self.recovered = Some(*proof);
182 Err(Box::new(self))
183 }
184 }
185 }
186}