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}
88
89#[derive(Debug, PartialEq, Eq)]
90struct FinalizerAuthority;
91
92#[derive(Debug, PartialEq, Eq)]
94pub struct PreparedPendingDiedOrdinaryFinalizer {
95 owner: LiveFrontierOwner,
96 fate: OrdinaryBindingFate,
97 finalizer: PendingDiedOrdinaryFinalizer,
98}
99
100impl PreparedPendingDiedOrdinaryFinalizer {
101 #[must_use]
103 pub fn into_parts(
104 self,
105 ) -> (
106 LiveFrontierOwner,
107 OrdinaryBindingFate,
108 PendingDiedOrdinaryFinalizer,
109 ) {
110 (self.owner, self.fate, self.finalizer)
111 }
112}
113
114impl LiveFrontierOwner {
115 pub fn complete_pending_died_ordinary_finalizer(
122 self,
123 finalizer: PendingDiedOrdinaryFinalizer,
124 ) -> Result<Self, LiveFrontierError> {
125 let PendingDiedOrdinaryFinalizer {
126 resulting_floor,
127 authority,
128 } = finalizer;
129 let FinalizerAuthority = authority;
130 self.install_finalized_binding_fate_floor(resulting_floor)
131 }
132}
133
134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
136pub enum BindingFateMeasurementError {
137 Token,
139 Conversation,
141 Participant,
143 Binding,
145 Terminal,
147 ObserverProgress,
149 ResultingFloor,
151 OwnerTransition(LiveFrontierError),
159}
160
161#[derive(Debug, PartialEq, Eq)]
163pub struct BindingFateMeasurementRefused {
164 owner: LiveFrontierOwner,
165 token: SealedBindingFateToken,
166 terminal: BindingFateTerminal,
167 error: BindingFateMeasurementError,
168}
169
170impl BindingFateMeasurementRefused {
171 #[must_use]
173 pub const fn error(&self) -> BindingFateMeasurementError {
174 self.error
175 }
176
177 #[must_use]
179 pub fn into_parts(
180 self,
181 ) -> (
182 LiveFrontierOwner,
183 SealedBindingFateToken,
184 BindingFateTerminal,
185 ) {
186 (self.owner, self.token, self.terminal)
187 }
188}
189
190struct ValidatedBindingFateMeasurement {
191 participant_id: crate::wire::ParticipantId,
192 binding_epoch: crate::wire::BindingEpoch,
193 resulting_floor: DeliverySeq,
194 owner_plan: BindingFateOwnerPlan,
195}
196
197struct ValidatedBindingFateFloor {
198 participant_id: crate::wire::ParticipantId,
199 binding_epoch: crate::wire::BindingEpoch,
200 resulting_floor: DeliverySeq,
201}
202
203impl LiveFrontierOwner {
204 pub fn prepare_binding_fate(
216 self,
217 token: SealedBindingFateToken,
218 terminal: BindingFateTerminal,
219 hard_observer_progress: DeliverySeq,
220 ) -> Result<PreparedBindingFate, Box<BindingFateMeasurementRefused>> {
221 let measurement = match validate_binding_fate_measurement(
222 &self,
223 &token,
224 terminal,
225 hard_observer_progress,
226 ) {
227 Ok(measurement) => measurement,
228 Err(error) => return refusal(self, token, terminal, error),
229 };
230 let event = Event::binding_fate_observed(
231 measurement.participant_id,
232 measurement.binding_epoch,
233 measurement.resulting_floor,
234 );
235 let fate = match terminal {
236 BindingFateTerminal::Ordinary(terminal) => token
237 .ordinary_binding_fate(terminal, measurement.resulting_floor)
238 .map(MeasuredBindingFate::Ordinary),
239 BindingFateTerminal::Recovered | BindingFateTerminal::RecoveredAndReserveFinalizer => {
240 token
241 .recovered_binding_fate_measured(measurement.resulting_floor)
242 .map(MeasuredBindingFate::Recovered)
243 }
244 };
245 match fate {
246 Ok(fate) => {
247 let owner = self.install_binding_fate_transition(
248 measurement.owner_plan,
249 measurement.resulting_floor,
250 );
251 Ok(PreparedBindingFate { owner, fate, event })
252 }
253 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
254 owner: self,
255 token: *token,
256 terminal,
257 error: BindingFateMeasurementError::Terminal,
258 })),
259 }
260 }
261
262 pub fn prepare_pending_died_ordinary_finalizer(
270 self,
271 token: SealedBindingFateToken,
272 terminal: CommittedDiedTerminal,
273 hard_observer_progress: DeliverySeq,
274 ) -> Result<PreparedPendingDiedOrdinaryFinalizer, Box<BindingFateMeasurementRefused>> {
275 let terminal_input = BindingFateTerminal::Ordinary(terminal);
276 let measurement = match validate_binding_fate_measurement(
277 &self,
278 &token,
279 terminal_input,
280 hard_observer_progress,
281 ) {
282 Ok(measurement) => measurement,
283 Err(error) => {
284 return Err(Box::new(BindingFateMeasurementRefused {
285 owner: self,
286 token,
287 terminal: terminal_input,
288 error,
289 }));
290 }
291 };
292 let resulting_floor = measurement.resulting_floor;
293 match token.ordinary_binding_fate(terminal, resulting_floor) {
294 Ok(fate) => Ok(PreparedPendingDiedOrdinaryFinalizer {
295 owner: self,
296 fate,
297 finalizer: PendingDiedOrdinaryFinalizer {
298 resulting_floor,
299 authority: FinalizerAuthority,
300 },
301 }),
302 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
303 owner: self,
304 token: *token,
305 terminal: terminal_input,
306 error: BindingFateMeasurementError::Terminal,
307 })),
308 }
309 }
310
311 pub fn prepare_pending_died_ordinary_after_fenced_proof(
322 self,
323 token: SealedBindingFateToken,
324 terminal: CommittedDiedTerminal,
325 hard_observer_progress: DeliverySeq,
326 ) -> Result<PreparedPendingDiedOrdinaryFinalizer, Box<BindingFateMeasurementRefused>> {
327 let terminal_input = BindingFateTerminal::Ordinary(terminal);
328 let measurement = match validate_binding_fate_floor(
329 &self,
330 &token,
331 terminal_input,
332 hard_observer_progress,
333 ) {
334 Ok(measurement) => measurement,
335 Err(error) => {
336 return Err(Box::new(BindingFateMeasurementRefused {
337 owner: self,
338 token,
339 terminal: terminal_input,
340 error,
341 }));
342 }
343 };
344 let resulting_floor = measurement.resulting_floor;
345 match token.ordinary_binding_fate(terminal, resulting_floor) {
346 Ok(fate) => Ok(PreparedPendingDiedOrdinaryFinalizer {
347 owner: self,
348 fate,
349 finalizer: PendingDiedOrdinaryFinalizer {
350 resulting_floor,
351 authority: FinalizerAuthority,
352 },
353 }),
354 Err(token) => Err(Box::new(BindingFateMeasurementRefused {
355 owner: self,
356 token: *token,
357 terminal: terminal_input,
358 error: BindingFateMeasurementError::Terminal,
359 })),
360 }
361 }
362}
363
364fn validate_binding_fate_measurement(
365 owner: &LiveFrontierOwner,
366 token: &SealedBindingFateToken,
367 terminal: BindingFateTerminal,
368 hard_observer_progress: DeliverySeq,
369) -> Result<ValidatedBindingFateMeasurement, BindingFateMeasurementError> {
370 let floor = validate_binding_fate_floor(owner, token, terminal, hard_observer_progress)?;
371 let owner_plan = owner
372 .prepare_binding_fate_transition(
373 floor.participant_id,
374 floor.binding_epoch,
375 token
376 .measurement_context()
377 .ok_or(BindingFateMeasurementError::Token)?
378 .cursor,
379 floor.resulting_floor,
380 terminal == BindingFateTerminal::RecoveredAndReserveFinalizer,
381 )
382 .map_err(BindingFateMeasurementError::OwnerTransition)?;
383 Ok(ValidatedBindingFateMeasurement {
384 participant_id: floor.participant_id,
385 binding_epoch: floor.binding_epoch,
386 resulting_floor: floor.resulting_floor,
387 owner_plan,
388 })
389}
390
391fn validate_binding_fate_floor(
392 owner: &LiveFrontierOwner,
393 token: &SealedBindingFateToken,
394 terminal: BindingFateTerminal,
395 hard_observer_progress: DeliverySeq,
396) -> Result<ValidatedBindingFateFloor, BindingFateMeasurementError> {
397 let Some(context) = token.measurement_context() else {
398 return Err(BindingFateMeasurementError::Token);
399 };
400 if context.conversation_id != owner.frontiers().conversation_id() {
401 return Err(BindingFateMeasurementError::Conversation);
402 }
403 let Some(participant) = owner
404 .frontiers()
405 .active_identities()
406 .participants()
407 .iter()
408 .find(|participant| participant.participant_index() == context.participant_id)
409 else {
410 return Err(BindingFateMeasurementError::Participant);
411 };
412 if participant.cursor() != context.cursor
413 || participant.binding() != FrontierBinding::Bound(context.binding_epoch)
414 && participant.binding() != FrontierBinding::Detached(context.binding_epoch)
415 {
416 return Err(BindingFateMeasurementError::Binding);
417 }
418 let terminal_matches = match (token.intent(), terminal) {
419 (Some(SealedBindingFateIntent::Ordinary), BindingFateTerminal::Ordinary(died)) => {
420 died.conversation_id() == context.conversation_id
421 && died.participant_id() == context.participant_id
422 && died.binding_epoch() == context.binding_epoch
423 }
424 (
425 Some(SealedBindingFateIntent::Recovered { .. }),
426 BindingFateTerminal::Recovered | BindingFateTerminal::RecoveredAndReserveFinalizer,
427 ) => true,
428 _ => false,
429 };
430 if !terminal_matches {
431 return Err(BindingFateMeasurementError::Terminal);
432 }
433 let candidate_high_watermark = owner.frontiers().sequence().ledger().high_watermark();
434 if hard_observer_progress > candidate_high_watermark {
435 return Err(BindingFateMeasurementError::ObserverProgress);
436 }
437 let minimum_remaining_cursor = owner
438 .frontiers()
439 .active_identities()
440 .participants()
441 .iter()
442 .filter(|participant| participant.participant_index() != context.participant_id)
443 .map(|participant| participant.cursor())
444 .min();
445 let measured = floor_transition(
446 owner.frontiers().retained_floor(),
447 minimum_remaining_cursor,
448 candidate_high_watermark,
449 hard_observer_progress,
450 owner.frontiers().retained_floor(),
451 );
452 let lowest_retained_marker_seq = owner
506 .frontiers()
507 .retained_marker_records()
508 .iter()
509 .map(|record| record.delivery_seq)
510 .min();
511 let clamped_floor = marker_clamped_floor(measured.resulting_floor, lowest_retained_marker_seq);
512 let Ok(resulting_floor) = DeliverySeq::try_from(clamped_floor) else {
513 return Err(BindingFateMeasurementError::ResultingFloor);
514 };
515 Ok(ValidatedBindingFateFloor {
516 participant_id: context.participant_id,
517 binding_epoch: context.binding_epoch,
518 resulting_floor,
519 })
520}
521
522fn refusal(
523 owner: LiveFrontierOwner,
524 token: SealedBindingFateToken,
525 terminal: BindingFateTerminal,
526 error: BindingFateMeasurementError,
527) -> Result<PreparedBindingFate, Box<BindingFateMeasurementRefused>> {
528 Err(Box::new(BindingFateMeasurementRefused {
529 owner,
530 token,
531 terminal,
532 error,
533 }))
534}