1use alloc::boxed::Box;
2
3use crate::{algebra::floor_transition, wire::DeliverySeq};
4
5use super::{LiveFrontierError, LiveFrontierOwner, live_frontier::BindingFateOwnerPlan};
6use crate::lifecycle::{
7 CommittedDiedTerminal, Event, FrontierBinding, ObserverProgressProjection, OrdinaryBindingFate,
8 RecoveredBindingFate, SealedBindingFateIntent, SealedBindingFateToken,
9};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum BindingFateTerminal {
14 Ordinary(CommittedDiedTerminal),
16 Recovered,
18 RecoveredAndReserveFinalizer,
20}
21
22#[derive(Debug, PartialEq, Eq)]
24pub enum MeasuredBindingFate {
25 Ordinary(OrdinaryBindingFate),
27 Recovered(RecoveredBindingFate),
29}
30
31impl MeasuredBindingFate {
32 #[must_use]
34 pub const fn resulting_floor(&self) -> DeliverySeq {
35 match self {
36 Self::Ordinary(fate) => fate.resulting_floor(),
37 Self::Recovered(fate) => fate.resulting_floor(),
38 }
39 }
40
41 #[must_use]
43 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
44 match self {
45 Self::Ordinary(fate) => fate.observer_progress_projection(),
46 Self::Recovered(fate) => fate.observer_progress_projection(),
47 }
48 }
49}
50
51#[derive(Debug, PartialEq, Eq)]
53pub struct PreparedBindingFate {
54 owner: LiveFrontierOwner,
55 fate: MeasuredBindingFate,
56 event: Event,
57}
58
59impl PreparedBindingFate {
60 #[must_use]
62 pub const fn fate(&self) -> &MeasuredBindingFate {
63 &self.fate
64 }
65
66 #[must_use]
68 pub const fn event(&self) -> Event {
69 self.event
70 }
71
72 #[must_use]
74 pub fn into_parts(self) -> (LiveFrontierOwner, MeasuredBindingFate, Event) {
75 (self.owner, self.fate, self.event)
76 }
77}
78
79#[derive(Debug, PartialEq, Eq)]
81pub struct PendingDiedOrdinaryFinalizer {
82 resulting_floor: DeliverySeq,
83 authority: FinalizerAuthority,
84}
85
86#[derive(Debug, PartialEq, Eq)]
87struct FinalizerAuthority;
88
89#[derive(Debug, PartialEq, Eq)]
91pub struct PreparedPendingDiedOrdinaryFinalizer {
92 owner: LiveFrontierOwner,
93 fate: OrdinaryBindingFate,
94 finalizer: PendingDiedOrdinaryFinalizer,
95}
96
97impl PreparedPendingDiedOrdinaryFinalizer {
98 #[must_use]
100 pub fn into_parts(
101 self,
102 ) -> (
103 LiveFrontierOwner,
104 OrdinaryBindingFate,
105 PendingDiedOrdinaryFinalizer,
106 ) {
107 (self.owner, self.fate, self.finalizer)
108 }
109}
110
111impl LiveFrontierOwner {
112 pub fn complete_pending_died_ordinary_finalizer(
119 self,
120 finalizer: PendingDiedOrdinaryFinalizer,
121 ) -> Result<Self, LiveFrontierError> {
122 let PendingDiedOrdinaryFinalizer {
123 resulting_floor,
124 authority,
125 } = finalizer;
126 let FinalizerAuthority = authority;
127 self.install_finalized_binding_fate_floor(resulting_floor)
128 }
129}
130
131#[derive(Clone, Copy, Debug, PartialEq, Eq)]
133pub enum BindingFateMeasurementError {
134 Token,
136 Conversation,
138 Participant,
140 Binding,
142 Terminal,
144 ObserverProgress,
146 ResultingFloor,
148 OwnerTransition,
150}
151
152#[derive(Debug, PartialEq, Eq)]
154pub struct BindingFateMeasurementRefused {
155 owner: LiveFrontierOwner,
156 token: SealedBindingFateToken,
157 terminal: BindingFateTerminal,
158 error: BindingFateMeasurementError,
159}
160
161impl BindingFateMeasurementRefused {
162 #[must_use]
164 pub const fn error(&self) -> BindingFateMeasurementError {
165 self.error
166 }
167
168 #[must_use]
170 pub fn into_parts(
171 self,
172 ) -> (
173 LiveFrontierOwner,
174 SealedBindingFateToken,
175 BindingFateTerminal,
176 ) {
177 (self.owner, self.token, self.terminal)
178 }
179}
180
181struct ValidatedBindingFateMeasurement {
182 participant_id: crate::wire::ParticipantId,
183 binding_epoch: crate::wire::BindingEpoch,
184 resulting_floor: DeliverySeq,
185 owner_plan: BindingFateOwnerPlan,
186}
187
188struct ValidatedBindingFateFloor {
189 participant_id: crate::wire::ParticipantId,
190 binding_epoch: crate::wire::BindingEpoch,
191 resulting_floor: DeliverySeq,
192}
193
194impl LiveFrontierOwner {
195 pub fn prepare_binding_fate(
207 self,
208 token: SealedBindingFateToken,
209 terminal: BindingFateTerminal,
210 hard_observer_progress: DeliverySeq,
211 ) -> Result<PreparedBindingFate, Box<BindingFateMeasurementRefused>> {
212 let measurement = match validate_binding_fate_measurement(
213 &self,
214 &token,
215 terminal,
216 hard_observer_progress,
217 ) {
218 Ok(measurement) => measurement,
219 Err(error) => return refusal(self, token, terminal, error),
220 };
221 let event = Event::binding_fate_observed(
222 measurement.participant_id,
223 measurement.binding_epoch,
224 measurement.resulting_floor,
225 );
226 let fate = match terminal {
227 BindingFateTerminal::Ordinary(terminal) => token
228 .ordinary_binding_fate(terminal, measurement.resulting_floor)
229 .map(MeasuredBindingFate::Ordinary),
230 BindingFateTerminal::Recovered | BindingFateTerminal::RecoveredAndReserveFinalizer => {
231 token
232 .recovered_binding_fate_measured(measurement.resulting_floor)
233 .map(MeasuredBindingFate::Recovered)
234 }
235 };
236 match fate {
237 Ok(fate) => {
238 let owner = self.install_binding_fate_transition(
239 measurement.owner_plan,
240 measurement.resulting_floor,
241 );
242 Ok(PreparedBindingFate { owner, fate, event })
243 }
244 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
245 owner: self,
246 token: *token,
247 terminal,
248 error: BindingFateMeasurementError::Terminal,
249 })),
250 }
251 }
252
253 pub fn prepare_pending_died_ordinary_finalizer(
261 self,
262 token: SealedBindingFateToken,
263 terminal: CommittedDiedTerminal,
264 hard_observer_progress: DeliverySeq,
265 ) -> Result<PreparedPendingDiedOrdinaryFinalizer, Box<BindingFateMeasurementRefused>> {
266 let terminal_input = BindingFateTerminal::Ordinary(terminal);
267 let measurement = match validate_binding_fate_measurement(
268 &self,
269 &token,
270 terminal_input,
271 hard_observer_progress,
272 ) {
273 Ok(measurement) => measurement,
274 Err(error) => {
275 return Err(Box::new(BindingFateMeasurementRefused {
276 owner: self,
277 token,
278 terminal: terminal_input,
279 error,
280 }));
281 }
282 };
283 let resulting_floor = measurement.resulting_floor;
284 match token.ordinary_binding_fate(terminal, resulting_floor) {
285 Ok(fate) => Ok(PreparedPendingDiedOrdinaryFinalizer {
286 owner: self,
287 fate,
288 finalizer: PendingDiedOrdinaryFinalizer {
289 resulting_floor,
290 authority: FinalizerAuthority,
291 },
292 }),
293 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
294 owner: self,
295 token: *token,
296 terminal: terminal_input,
297 error: BindingFateMeasurementError::Terminal,
298 })),
299 }
300 }
301
302 pub fn prepare_pending_died_ordinary_after_fenced_proof(
313 self,
314 token: SealedBindingFateToken,
315 terminal: CommittedDiedTerminal,
316 hard_observer_progress: DeliverySeq,
317 ) -> Result<PreparedPendingDiedOrdinaryFinalizer, Box<BindingFateMeasurementRefused>> {
318 let terminal_input = BindingFateTerminal::Ordinary(terminal);
319 let measurement = match validate_binding_fate_floor(
320 &self,
321 &token,
322 terminal_input,
323 hard_observer_progress,
324 ) {
325 Ok(measurement) => measurement,
326 Err(error) => {
327 return Err(Box::new(BindingFateMeasurementRefused {
328 owner: self,
329 token,
330 terminal: terminal_input,
331 error,
332 }));
333 }
334 };
335 let resulting_floor = measurement.resulting_floor;
336 match token.ordinary_binding_fate(terminal, resulting_floor) {
337 Ok(fate) => Ok(PreparedPendingDiedOrdinaryFinalizer {
338 owner: self,
339 fate,
340 finalizer: PendingDiedOrdinaryFinalizer {
341 resulting_floor,
342 authority: FinalizerAuthority,
343 },
344 }),
345 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
346 owner: self,
347 token: *token,
348 terminal: terminal_input,
349 error: BindingFateMeasurementError::Terminal,
350 })),
351 }
352 }
353}
354
355fn validate_binding_fate_measurement(
356 owner: &LiveFrontierOwner,
357 token: &SealedBindingFateToken,
358 terminal: BindingFateTerminal,
359 hard_observer_progress: DeliverySeq,
360) -> Result<ValidatedBindingFateMeasurement, BindingFateMeasurementError> {
361 let floor = validate_binding_fate_floor(owner, token, terminal, hard_observer_progress)?;
362 let owner_plan = owner
363 .prepare_binding_fate_transition(
364 floor.participant_id,
365 floor.binding_epoch,
366 token
367 .measurement_context()
368 .ok_or(BindingFateMeasurementError::Token)?
369 .cursor,
370 floor.resulting_floor,
371 terminal == BindingFateTerminal::RecoveredAndReserveFinalizer,
372 )
373 .map_err(|_| BindingFateMeasurementError::OwnerTransition)?;
374 Ok(ValidatedBindingFateMeasurement {
375 participant_id: floor.participant_id,
376 binding_epoch: floor.binding_epoch,
377 resulting_floor: floor.resulting_floor,
378 owner_plan,
379 })
380}
381
382fn validate_binding_fate_floor(
383 owner: &LiveFrontierOwner,
384 token: &SealedBindingFateToken,
385 terminal: BindingFateTerminal,
386 hard_observer_progress: DeliverySeq,
387) -> Result<ValidatedBindingFateFloor, BindingFateMeasurementError> {
388 let Some(context) = token.measurement_context() else {
389 return Err(BindingFateMeasurementError::Token);
390 };
391 if context.conversation_id != owner.frontiers().conversation_id() {
392 return Err(BindingFateMeasurementError::Conversation);
393 }
394 let Some(participant) = owner
395 .frontiers()
396 .active_identities()
397 .participants()
398 .iter()
399 .find(|participant| participant.participant_index() == context.participant_id)
400 else {
401 return Err(BindingFateMeasurementError::Participant);
402 };
403 if participant.cursor() != context.cursor
404 || participant.binding() != FrontierBinding::Bound(context.binding_epoch)
405 && participant.binding() != FrontierBinding::Detached(context.binding_epoch)
406 {
407 return Err(BindingFateMeasurementError::Binding);
408 }
409 let terminal_matches = match (token.intent(), terminal) {
410 (Some(SealedBindingFateIntent::Ordinary), BindingFateTerminal::Ordinary(died)) => {
411 died.conversation_id() == context.conversation_id
412 && died.participant_id() == context.participant_id
413 && died.binding_epoch() == context.binding_epoch
414 }
415 (
416 Some(SealedBindingFateIntent::Recovered { .. }),
417 BindingFateTerminal::Recovered | BindingFateTerminal::RecoveredAndReserveFinalizer,
418 ) => true,
419 _ => false,
420 };
421 if !terminal_matches {
422 return Err(BindingFateMeasurementError::Terminal);
423 }
424 let candidate_high_watermark = owner.frontiers().sequence().ledger().high_watermark();
425 if hard_observer_progress > candidate_high_watermark {
426 return Err(BindingFateMeasurementError::ObserverProgress);
427 }
428 let minimum_remaining_cursor = owner
429 .frontiers()
430 .active_identities()
431 .participants()
432 .iter()
433 .filter(|participant| participant.participant_index() != context.participant_id)
434 .map(|participant| participant.cursor())
435 .min();
436 let measured = floor_transition(
437 owner.frontiers().retained_floor(),
438 minimum_remaining_cursor,
439 candidate_high_watermark,
440 hard_observer_progress,
441 owner.frontiers().retained_floor(),
442 );
443 let Ok(resulting_floor) = DeliverySeq::try_from(measured.resulting_floor) else {
444 return Err(BindingFateMeasurementError::ResultingFloor);
445 };
446 Ok(ValidatedBindingFateFloor {
447 participant_id: context.participant_id,
448 binding_epoch: context.binding_epoch,
449 resulting_floor,
450 })
451}
452
453fn refusal(
454 owner: LiveFrontierOwner,
455 token: SealedBindingFateToken,
456 terminal: BindingFateTerminal,
457 error: BindingFateMeasurementError,
458) -> Result<PreparedBindingFate, Box<BindingFateMeasurementRefused>> {
459 Err(Box::new(BindingFateMeasurementRefused {
460 owner,
461 token,
462 terminal,
463 error,
464 }))
465}