1use alloc::boxed::Box;
2
3use crate::{
4 algebra::{floor_transition, marker_clamped_floor},
5 wire::DeliverySeq,
6};
7
8use super::{LiveFrontierError, LiveFrontierOwner, live_frontier::BindingFateOwnerPlan};
9use crate::lifecycle::{
10 CommittedDiedTerminal, Event, FrontierBinding, ObserverProgressProjection, OrdinaryBindingFate,
11 RecoveredBindingFate, SealedBindingFateIntent, SealedBindingFateToken,
12};
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum BindingFateTerminal {
17 Ordinary(CommittedDiedTerminal),
19 Recovered,
21 RecoveredAndReserveFinalizer,
23}
24
25#[derive(Debug, PartialEq, Eq)]
27pub enum MeasuredBindingFate {
28 Ordinary(OrdinaryBindingFate),
30 Recovered(RecoveredBindingFate),
32}
33
34impl MeasuredBindingFate {
35 #[must_use]
37 pub const fn resulting_floor(&self) -> DeliverySeq {
38 match self {
39 Self::Ordinary(fate) => fate.resulting_floor(),
40 Self::Recovered(fate) => fate.resulting_floor(),
41 }
42 }
43
44 #[must_use]
46 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
47 match self {
48 Self::Ordinary(fate) => fate.observer_progress_projection(),
49 Self::Recovered(fate) => fate.observer_progress_projection(),
50 }
51 }
52}
53
54#[derive(Debug, PartialEq, Eq)]
56pub struct PreparedBindingFate {
57 owner: LiveFrontierOwner,
58 fate: MeasuredBindingFate,
59 event: Event,
60}
61
62impl PreparedBindingFate {
63 #[must_use]
65 pub const fn fate(&self) -> &MeasuredBindingFate {
66 &self.fate
67 }
68
69 #[must_use]
71 pub const fn event(&self) -> Event {
72 self.event
73 }
74
75 #[must_use]
77 pub fn into_parts(self) -> (LiveFrontierOwner, MeasuredBindingFate, Event) {
78 (self.owner, self.fate, self.event)
79 }
80}
81
82#[derive(Debug, PartialEq, Eq)]
84pub struct PendingDiedOrdinaryFinalizer {
85 resulting_floor: DeliverySeq,
86 authority: FinalizerAuthority,
87 provenance: Box<crate::lifecycle::storage::OrdinaryBindingFateRestore>,
88}
89
90#[derive(Debug, PartialEq, Eq)]
91struct FinalizerAuthority;
92
93#[derive(Debug, PartialEq, Eq)]
95pub struct PreparedPendingDiedOrdinaryFinalizer {
96 owner: LiveFrontierOwner,
97 fate: OrdinaryBindingFate,
98 finalizer: PendingDiedOrdinaryFinalizer,
99}
100
101impl PreparedPendingDiedOrdinaryFinalizer {
102 #[must_use]
104 pub fn into_parts(
105 self,
106 ) -> (
107 LiveFrontierOwner,
108 OrdinaryBindingFate,
109 PendingDiedOrdinaryFinalizer,
110 ) {
111 (self.owner, self.fate, self.finalizer)
112 }
113}
114
115impl LiveFrontierOwner {
116 pub fn complete_pending_died_ordinary_finalizer(
123 self,
124 finalizer: PendingDiedOrdinaryFinalizer,
125 ) -> Result<Self, LiveFrontierError> {
126 let PendingDiedOrdinaryFinalizer {
127 resulting_floor,
128 authority,
129 provenance,
130 } = finalizer;
131 let FinalizerAuthority = authority;
132 let mut owner = self.install_finalized_binding_fate_floor(resulting_floor)?;
133 owner.retain_ordinary_cursor_release(provenance);
134 Ok(owner)
135 }
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub enum BindingFateMeasurementError {
141 Token,
143 Conversation,
145 Participant,
147 Binding,
149 Terminal,
151 ObserverProgress,
153 ResultingFloor,
155 OwnerTransition(LiveFrontierError),
163}
164
165#[derive(Debug, PartialEq, Eq)]
167pub struct BindingFateMeasurementRefused {
168 owner: LiveFrontierOwner,
169 token: SealedBindingFateToken,
170 terminal: BindingFateTerminal,
171 error: BindingFateMeasurementError,
172}
173
174impl BindingFateMeasurementRefused {
175 #[must_use]
177 pub const fn error(&self) -> BindingFateMeasurementError {
178 self.error
179 }
180
181 #[must_use]
183 pub fn into_parts(
184 self,
185 ) -> (
186 LiveFrontierOwner,
187 SealedBindingFateToken,
188 BindingFateTerminal,
189 ) {
190 (self.owner, self.token, self.terminal)
191 }
192}
193
194struct ValidatedBindingFateMeasurement {
195 participant_id: crate::wire::ParticipantId,
196 binding_epoch: crate::wire::BindingEpoch,
197 resulting_floor: DeliverySeq,
198 owner_plan: BindingFateOwnerPlan,
199}
200
201struct ValidatedBindingFateFloor {
202 participant_id: crate::wire::ParticipantId,
203 binding_epoch: crate::wire::BindingEpoch,
204 resulting_floor: DeliverySeq,
205}
206
207impl LiveFrontierOwner {
208 pub fn prepare_binding_fate(
220 self,
221 token: SealedBindingFateToken,
222 terminal: BindingFateTerminal,
223 hard_observer_progress: DeliverySeq,
224 ) -> Result<PreparedBindingFate, Box<BindingFateMeasurementRefused>> {
225 let measurement = match validate_binding_fate_measurement(
226 &self,
227 &token,
228 terminal,
229 hard_observer_progress,
230 ) {
231 Ok(measurement) => measurement,
232 Err(error) => return refusal(self, token, terminal, error),
233 };
234 let event = Event::binding_fate_observed(
235 measurement.participant_id,
236 measurement.binding_epoch,
237 measurement.resulting_floor,
238 );
239 let fate = match terminal {
240 BindingFateTerminal::Ordinary(terminal) => token
241 .ordinary_binding_fate(terminal, measurement.resulting_floor)
242 .map(MeasuredBindingFate::Ordinary),
243 BindingFateTerminal::Recovered | BindingFateTerminal::RecoveredAndReserveFinalizer => {
244 token
245 .recovered_binding_fate_measured(measurement.resulting_floor)
246 .map(MeasuredBindingFate::Recovered)
247 }
248 };
249 match fate {
250 Ok(fate) => {
251 let mut owner = self.install_binding_fate_transition(
252 measurement.owner_plan,
253 measurement.resulting_floor,
254 );
255 if let MeasuredBindingFate::Ordinary(source) = &fate {
256 owner.retain_ordinary_cursor_release(Box::new(source.storage_description()));
257 }
258 Ok(PreparedBindingFate { owner, fate, event })
259 }
260 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
261 owner: self,
262 token: *token,
263 terminal,
264 error: BindingFateMeasurementError::Terminal,
265 })),
266 }
267 }
268
269 pub fn prepare_pending_died_ordinary_finalizer(
277 self,
278 token: SealedBindingFateToken,
279 terminal: CommittedDiedTerminal,
280 hard_observer_progress: DeliverySeq,
281 ) -> Result<PreparedPendingDiedOrdinaryFinalizer, Box<BindingFateMeasurementRefused>> {
282 let terminal_input = BindingFateTerminal::Ordinary(terminal);
283 let measurement = match validate_binding_fate_measurement(
284 &self,
285 &token,
286 terminal_input,
287 hard_observer_progress,
288 ) {
289 Ok(measurement) => measurement,
290 Err(error) => {
291 return Err(Box::new(BindingFateMeasurementRefused {
292 owner: self,
293 token,
294 terminal: terminal_input,
295 error,
296 }));
297 }
298 };
299 let resulting_floor = measurement.resulting_floor;
300 match token.ordinary_binding_fate(terminal, resulting_floor) {
301 Ok(fate) => Ok(PreparedPendingDiedOrdinaryFinalizer {
302 owner: self,
303 fate,
304 finalizer: PendingDiedOrdinaryFinalizer {
305 resulting_floor,
306 authority: FinalizerAuthority,
307 provenance: Box::new(fate.storage_description()),
308 },
309 }),
310 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
311 owner: self,
312 token: *token,
313 terminal: terminal_input,
314 error: BindingFateMeasurementError::Terminal,
315 })),
316 }
317 }
318
319 pub fn prepare_pending_died_ordinary_after_fenced_proof(
330 self,
331 token: SealedBindingFateToken,
332 terminal: CommittedDiedTerminal,
333 hard_observer_progress: DeliverySeq,
334 ) -> Result<PreparedPendingDiedOrdinaryFinalizer, Box<BindingFateMeasurementRefused>> {
335 let terminal_input = BindingFateTerminal::Ordinary(terminal);
336 let measurement = match validate_binding_fate_floor(
337 &self,
338 &token,
339 terminal_input,
340 hard_observer_progress,
341 ) {
342 Ok(measurement) => measurement,
343 Err(error) => {
344 return Err(Box::new(BindingFateMeasurementRefused {
345 owner: self,
346 token,
347 terminal: terminal_input,
348 error,
349 }));
350 }
351 };
352 let resulting_floor = measurement.resulting_floor;
353 match token.ordinary_binding_fate(terminal, resulting_floor) {
354 Ok(fate) => Ok(PreparedPendingDiedOrdinaryFinalizer {
355 owner: self,
356 fate,
357 finalizer: PendingDiedOrdinaryFinalizer {
358 resulting_floor,
359 authority: FinalizerAuthority,
360 provenance: Box::new(fate.storage_description()),
361 },
362 }),
363 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
364 owner: self,
365 token: *token,
366 terminal: terminal_input,
367 error: BindingFateMeasurementError::Terminal,
368 })),
369 }
370 }
371}
372
373fn validate_binding_fate_measurement(
374 owner: &LiveFrontierOwner,
375 token: &SealedBindingFateToken,
376 terminal: BindingFateTerminal,
377 hard_observer_progress: DeliverySeq,
378) -> Result<ValidatedBindingFateMeasurement, BindingFateMeasurementError> {
379 let floor = validate_binding_fate_floor(owner, token, terminal, hard_observer_progress)?;
380 let owner_plan = owner
381 .prepare_binding_fate_transition(
382 floor.participant_id,
383 floor.binding_epoch,
384 token
385 .measurement_context()
386 .ok_or(BindingFateMeasurementError::Token)?
387 .cursor,
388 floor.resulting_floor,
389 terminal == BindingFateTerminal::RecoveredAndReserveFinalizer,
390 )
391 .map_err(BindingFateMeasurementError::OwnerTransition)?;
392 Ok(ValidatedBindingFateMeasurement {
393 participant_id: floor.participant_id,
394 binding_epoch: floor.binding_epoch,
395 resulting_floor: floor.resulting_floor,
396 owner_plan,
397 })
398}
399
400fn validate_binding_fate_floor(
401 owner: &LiveFrontierOwner,
402 token: &SealedBindingFateToken,
403 terminal: BindingFateTerminal,
404 hard_observer_progress: DeliverySeq,
405) -> Result<ValidatedBindingFateFloor, BindingFateMeasurementError> {
406 let Some(context) = token.measurement_context() else {
407 return Err(BindingFateMeasurementError::Token);
408 };
409 if context.conversation_id != owner.frontiers().conversation_id() {
410 return Err(BindingFateMeasurementError::Conversation);
411 }
412 let Some(participant) = owner
413 .frontiers()
414 .active_identities()
415 .participants()
416 .iter()
417 .find(|participant| participant.participant_index() == context.participant_id)
418 else {
419 return Err(BindingFateMeasurementError::Participant);
420 };
421 if participant.cursor() != context.cursor
422 || participant.binding() != FrontierBinding::Bound(context.binding_epoch)
423 && participant.binding() != FrontierBinding::Detached(context.binding_epoch)
424 {
425 return Err(BindingFateMeasurementError::Binding);
426 }
427 let terminal_matches = match (token.intent(), terminal) {
428 (Some(SealedBindingFateIntent::Ordinary), BindingFateTerminal::Ordinary(died)) => {
429 died.conversation_id() == context.conversation_id
430 && died.participant_id() == context.participant_id
431 && died.binding_epoch() == context.binding_epoch
432 }
433 (
434 Some(SealedBindingFateIntent::Recovered { .. }),
435 BindingFateTerminal::Recovered | BindingFateTerminal::RecoveredAndReserveFinalizer,
436 ) => true,
437 _ => false,
438 };
439 if !terminal_matches {
440 return Err(BindingFateMeasurementError::Terminal);
441 }
442 let candidate_high_watermark = owner.frontiers().sequence().ledger().high_watermark();
443 if hard_observer_progress > candidate_high_watermark {
444 return Err(BindingFateMeasurementError::ObserverProgress);
445 }
446 let minimum_remaining_cursor = owner
447 .frontiers()
448 .active_identities()
449 .participants()
450 .iter()
451 .filter(|participant| participant.participant_index() != context.participant_id)
452 .map(|participant| participant.cursor())
453 .min();
454 let measured = floor_transition(
455 owner.frontiers().retained_floor(),
456 minimum_remaining_cursor,
457 candidate_high_watermark,
458 hard_observer_progress,
459 owner.frontiers().retained_floor(),
460 );
461 let lowest_retained_marker_seq = owner
515 .frontiers()
516 .retained_marker_records()
517 .iter()
518 .map(|record| record.delivery_seq)
519 .min();
520 let clamped_floor = marker_clamped_floor(measured.resulting_floor, lowest_retained_marker_seq);
521 let Ok(resulting_floor) = DeliverySeq::try_from(clamped_floor) else {
522 return Err(BindingFateMeasurementError::ResultingFloor);
523 };
524 Ok(ValidatedBindingFateFloor {
525 participant_id: context.participant_id,
526 binding_epoch: context.binding_epoch,
527 resulting_floor,
528 })
529}
530
531fn refusal(
532 owner: LiveFrontierOwner,
533 token: SealedBindingFateToken,
534 terminal: BindingFateTerminal,
535 error: BindingFateMeasurementError,
536) -> Result<PreparedBindingFate, Box<BindingFateMeasurementRefused>> {
537 Err(Box::new(BindingFateMeasurementRefused {
538 owner,
539 token,
540 terminal,
541 error,
542 }))
543}
544
545impl PendingDiedOrdinaryFinalizer {
546 #[must_use]
548 pub const fn encode_checkpoint(&self) -> [u8; 8] {
549 self.resulting_floor.to_be_bytes()
550 }
551
552 pub fn restore_checkpoint(
560 body: &[u8],
561 fate: &OrdinaryBindingFate,
562 ) -> Result<Self, crate::lifecycle::OwnerCheckpointRestoreError> {
563 let bytes: [u8; 8] = body.try_into().map_err(|_| {
564 crate::lifecycle::OwnerCheckpointRestoreError::Field("pending_died_finalizer.framing")
565 })?;
566 let resulting_floor = u64::from_be_bytes(bytes);
567 if resulting_floor != fate.resulting_floor() {
568 return Err(crate::lifecycle::OwnerCheckpointRestoreError::Field(
569 "pending_died_finalizer.floor",
570 ));
571 }
572 Ok(Self {
573 resulting_floor,
574 authority: FinalizerAuthority,
575 provenance: Box::new(fate.storage_description()),
576 })
577 }
578}