1use alloc::boxed::Box;
2
3use crate::algebra::{ResourceVector, WideResourceVector};
4use crate::wire::{BindingEpoch, ConversationId, DeliverySeq, ParticipantId, ParticipantIndex};
5
6use super::{
7 ActiveBinding, CommittedDiedTerminal, ObserverProgressProjection,
8 claim_frontier::{ValidatedMarkerCandidate, ValidatedMarkerRecord},
9 storage::{
10 DetachedCredentialRecoveryRestore, DetachedMarkerReleaseProvenanceRestore,
11 DetachedMarkerReleaseRestore, MarkerCursorProgressRestore, MarkerDeliveryRestore,
12 OrdinaryBindingAuthorityRestore, OrdinaryBindingFateRestore, RestoredBindingFateTerminal,
13 StoredEdgeRestore,
14 },
15};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct ClosureDebt(WideResourceVector);
20
21impl ClosureDebt {
22 #[must_use]
24 pub const fn new(value: WideResourceVector) -> Option<Self> {
25 if value.is_zero() {
26 None
27 } else {
28 Some(Self(value))
29 }
30 }
31
32 #[must_use]
34 pub const fn value(self) -> WideResourceVector {
35 self.0
36 }
37}
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct ObserverProjection {
42 through_seq: DeliverySeq,
43}
44
45impl ObserverProjection {
46 #[must_use]
48 pub const fn new(through_seq: DeliverySeq) -> Self {
49 Self { through_seq }
50 }
51
52 #[must_use]
54 pub const fn through_seq(self) -> DeliverySeq {
55 self.through_seq
56 }
57
58 #[must_use]
60 pub const fn storage_description(self) -> StoredEdgeRestore {
61 StoredEdgeRestore::ObserverProjection {
62 through_seq: self.through_seq,
63 }
64 }
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub struct PhysicalCompaction {
70 from_floor: DeliverySeq,
71 through_seq: DeliverySeq,
72}
73
74impl PhysicalCompaction {
75 #[must_use]
77 pub const fn new(from_floor: DeliverySeq, through_seq: DeliverySeq) -> Option<Self> {
78 if from_floor <= through_seq {
79 Some(Self {
80 from_floor,
81 through_seq,
82 })
83 } else {
84 None
85 }
86 }
87
88 #[must_use]
90 pub const fn from_floor(self) -> DeliverySeq {
91 self.from_floor
92 }
93
94 #[must_use]
96 pub const fn through_seq(self) -> DeliverySeq {
97 self.through_seq
98 }
99
100 #[must_use]
102 pub const fn storage_description(self) -> StoredEdgeRestore {
103 StoredEdgeRestore::PhysicalCompaction {
104 from_floor: self.from_floor,
105 through_seq: self.through_seq,
106 }
107 }
108}
109
110#[allow(clippy::struct_field_names)]
131#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132pub struct MarkerDelivery {
133 conversation_id: ConversationId,
134 participant_id: ParticipantId,
135 binding_epoch: BindingEpoch,
136 marker_delivery_seq: DeliverySeq,
137}
138
139impl MarkerDelivery {
140 #[must_use]
148 pub(super) const fn successor_from_validated_candidate(
149 candidate: ValidatedMarkerCandidate,
150 ) -> StoredEdge {
151 let conversation_id = candidate.conversation_id();
152 let participant_id = candidate.participant_id();
153 let marker_delivery_seq = candidate.delivery_seq();
154 let successor = match candidate.target_binding() {
155 super::FrontierBinding::Bound(binding_epoch) => StoredEdge::MarkerDelivery(Self {
156 conversation_id,
157 participant_id,
158 binding_epoch,
159 marker_delivery_seq,
160 }),
161 super::FrontierBinding::Detached(last_dead_binding_epoch) => {
162 StoredEdge::DetachedMarkerRelease(DetachedMarkerRelease {
163 conversation_id,
164 participant_id,
165 marker_delivery_seq,
166 last_dead_binding_epoch,
167 provenance: DetachedMarkerReleaseProvenance::Candidate,
168 })
169 }
170 };
171 candidate.consume();
172 successor
173 }
174
175 #[must_use]
177 pub(super) const fn from_validated_record(record: &ValidatedMarkerRecord) -> Self {
178 Self {
179 conversation_id: record.conversation_id(),
180 participant_id: record.participant_id(),
181 binding_epoch: record.binding_epoch(),
182 marker_delivery_seq: record.delivery_seq(),
183 }
184 }
185
186 #[must_use]
188 pub const fn conversation_id(self) -> ConversationId {
189 self.conversation_id
190 }
191
192 #[must_use]
194 pub const fn participant_id(self) -> ParticipantId {
195 self.participant_id
196 }
197
198 #[must_use]
200 pub const fn binding_epoch(self) -> BindingEpoch {
201 self.binding_epoch
202 }
203
204 #[must_use]
206 pub const fn marker_delivery_seq(self) -> DeliverySeq {
207 self.marker_delivery_seq
208 }
209
210 #[must_use]
217 pub const fn storage_description(self) -> StoredEdgeRestore {
218 StoredEdgeRestore::MarkerDelivery(MarkerDeliveryRestore {
219 participant_id: self.participant_id,
220 binding_epoch: self.binding_epoch,
221 marker_delivery_seq: self.marker_delivery_seq,
222 })
223 }
224}
225
226#[cfg(any(test, feature = "test-support"))]
227#[allow(clippy::expect_used, clippy::too_many_lines)]
228pub fn validated_marker_record_for_test(
229 conversation_id: crate::wire::ConversationId,
230 participant_id: ParticipantId,
231 target_binding: super::claim_frontier::FrontierBinding,
232 marker_delivery_seq: DeliverySeq,
233 cursor: DeliverySeq,
234) -> ValidatedMarkerRecord {
235 use alloc::{vec, vec::Vec};
236
237 use crate::outcome::CandidatePhase;
238
239 use super::{
240 AdmissionOrder, OrderClaims, OrderHigh, OrderLedger, RecoverySequenceReserve,
241 SequenceClaims, SequenceLedger,
242 claim_frontier::{
243 BindingTerminalOwner, ClaimFrontiers, ClaimFrontiersRestore, FrontierBinding,
244 FrontierParticipant, ImmutableSequenceCandidate, MarkerProvenance, MarkerRecordRequest,
245 MovableOrderClaim, MovableSequenceClaim, OrderClaimFrontierRestore, OrderDirectOwner,
246 RetainedCausalRecord, RetainedCausalRecordKind, SequenceClaimFrontierRestore,
247 SequenceDirectOwner, SequenceProductRangesRestore, TerminalProductRangeRestore,
248 },
249 };
250
251 let identity_slot_limit = participant_id
252 .checked_add(1)
253 .expect("test participant must fit a half-open identity domain");
254 let exit_seq = marker_delivery_seq
255 .checked_add(1)
256 .expect("test marker must leave an exit-claim suffix");
257 let binding_epoch = match target_binding {
258 FrontierBinding::Bound(epoch) | FrontierBinding::Detached(epoch) => epoch,
259 };
260 let terminal_owner = BindingTerminalOwner {
261 participant_index: participant_id,
262 binding_epoch,
263 };
264 let (sequence_claims, sequence_movable, products, order_claims, order_movable) =
265 match target_binding {
266 FrontierBinding::Bound(_) => {
267 let terminal_seq = exit_seq
268 .checked_add(1)
269 .expect("test marker must leave a terminal-claim suffix");
270 let product_seq = terminal_seq
271 .checked_add(1)
272 .expect("test marker must leave a terminal-product suffix");
273 (
274 SequenceClaims::new(1, 1, 0, RecoverySequenceReserve::None),
275 vec![
276 MovableSequenceClaim {
277 delivery_seq: exit_seq,
278 owner: SequenceDirectOwner::MembershipExit {
279 participant_index: participant_id,
280 },
281 },
282 MovableSequenceClaim {
283 delivery_seq: terminal_seq,
284 owner: SequenceDirectOwner::BindingTerminal(terminal_owner),
285 },
286 ],
287 SequenceProductRangesRestore {
288 live_times_terminal: vec![TerminalProductRangeRestore {
289 start: product_seq,
290 length: 1,
291 terminal: terminal_owner,
292 }],
293 live_times_replacement_terminal: None,
294 other_live_times_exit: vec![],
295 },
296 OrderClaims::new(1, 1, false, false)
297 .expect("bound test claims have no torn recovery pair"),
298 vec![
299 MovableOrderClaim {
300 transaction_order: 1,
301 owner: OrderDirectOwner::ActiveBindingTerminal(terminal_owner),
302 },
303 MovableOrderClaim {
304 transaction_order: 2,
305 owner: OrderDirectOwner::MembershipExit {
306 participant_index: participant_id,
307 },
308 },
309 ],
310 )
311 }
312 FrontierBinding::Detached(_) => (
313 SequenceClaims::new(1, 0, 0, RecoverySequenceReserve::None),
314 vec![MovableSequenceClaim {
315 delivery_seq: exit_seq,
316 owner: SequenceDirectOwner::MembershipExit {
317 participant_index: participant_id,
318 },
319 }],
320 SequenceProductRangesRestore::default(),
321 OrderClaims::new(0, 1, false, false)
322 .expect("detached test claims have no torn recovery pair"),
323 vec![MovableOrderClaim {
324 transaction_order: 1,
325 owner: OrderDirectOwner::MembershipExit {
326 participant_index: participant_id,
327 },
328 }],
329 ),
330 };
331 let sequence_ledger = SequenceLedger::try_new(marker_delivery_seq, sequence_claims)
332 .expect("test sequence frontier is within the numeric suffix");
333 let order_ledger = OrderLedger::try_new(OrderHigh::Allocated(0), order_claims)
334 .expect("test order frontier is within the numeric suffix");
335 let admission_order = AdmissionOrder::new(0, CandidatePhase::CompactionMarker, participant_id);
336 let mut prevalidated = ClaimFrontiers::prevalidate(
337 ClaimFrontiersRestore {
338 conversation_id,
339 active_identities: vec![FrontierParticipant::new(
340 participant_id,
341 cursor,
342 target_binding,
343 )],
344 identity_slot_limit,
345 retained_floor: u128::from(marker_delivery_seq),
346 retained_record_limit: 1,
347 retained_records: vec![RetainedCausalRecord {
348 delivery_seq: marker_delivery_seq,
349 admission_order,
350 kind: RetainedCausalRecordKind::CompactionMarker {
351 participant_index: participant_id,
352 provenance: MarkerProvenance::NonProductM,
353 },
354 }],
355 active_marker_anchors: vec![marker_delivery_seq],
356 historical_marker_deliveries: vec![],
357 historical_causal_facts: vec![],
358 sequence: SequenceClaimFrontierRestore {
359 movable_claims: sequence_movable,
360 immutable_candidates: Vec::<ImmutableSequenceCandidate>::new(),
361 products,
362 recovery: None,
363 },
364 order: OrderClaimFrontierRestore {
365 movable_claims: order_movable,
366 immutable_candidates: vec![],
367 recovery: None,
368 },
369 recovery_marker_delivery_seq: None,
370 },
371 sequence_ledger,
372 order_ledger,
373 )
374 .expect("complete test claim frontier must prevalidate");
375 let record = prevalidated
376 .take_marker_record(MarkerRecordRequest::planned(
377 participant_id,
378 marker_delivery_seq,
379 target_binding,
380 ))
381 .expect("prevalidated test frontier retains its exact marker");
382 if cursor >= marker_delivery_seq {
383 record.delivered_for_test()
384 } else {
385 record
386 }
387}
388
389#[cfg(any(test, feature = "test-support"))]
390pub fn marker_delivery_for_test(
391 participant_id: ParticipantId,
392 binding_epoch: BindingEpoch,
393 marker_delivery_seq: DeliverySeq,
394) -> Result<MarkerDelivery, super::storage::StorageRestoreError> {
395 let record = validated_marker_record_for_test(
396 1,
397 participant_id,
398 super::claim_frontier::FrontierBinding::Bound(binding_epoch),
399 marker_delivery_seq,
400 marker_delivery_seq.saturating_sub(1),
401 );
402 super::storage::MarkerDeliveryRestore {
403 participant_id,
404 binding_epoch,
405 marker_delivery_seq,
406 }
407 .restore_bound(1, record)
408}
409
410#[derive(Clone, Copy, Debug, PartialEq, Eq)]
428pub struct CursorProgressContinuous {
429 participant_id: ParticipantId,
430 binding_epoch: BindingEpoch,
431 through_seq: DeliverySeq,
432}
433
434impl CursorProgressContinuous {
435 #[cfg(test)]
437 #[must_use]
438 pub(crate) const fn new(
439 participant_id: ParticipantId,
440 binding_epoch: BindingEpoch,
441 through_seq: DeliverySeq,
442 ) -> Self {
443 Self {
444 participant_id,
445 binding_epoch,
446 through_seq,
447 }
448 }
449
450 #[must_use]
452 pub const fn participant_id(self) -> ParticipantId {
453 self.participant_id
454 }
455
456 #[must_use]
458 pub const fn binding_epoch(self) -> BindingEpoch {
459 self.binding_epoch
460 }
461
462 #[must_use]
464 pub const fn through_seq(self) -> DeliverySeq {
465 self.through_seq
466 }
467
468 #[must_use]
479 pub const fn storage_description(self, conversation_id: ConversationId) -> StoredEdgeRestore {
480 StoredEdgeRestore::ParticipantCursorProgressContinuous {
481 participant_id: self.participant_id,
482 binding_epoch: self.binding_epoch,
483 through_seq: self.through_seq,
484 authority: OrdinaryBindingAuthorityRestore {
485 binding: ActiveBinding {
486 participant_id: self.participant_id,
487 conversation_id,
488 binding_epoch: self.binding_epoch,
489 },
490 through_seq: self.through_seq,
491 },
492 }
493 }
494}
495
496#[derive(Clone, Copy, Debug, PartialEq, Eq)]
503pub struct CursorProgressMarker {
504 conversation_id: ConversationId,
505 participant_id: ParticipantId,
506 binding_epoch: BindingEpoch,
507 through_seq: DeliverySeq,
508 marker_delivery_seq: DeliverySeq,
509}
510
511impl CursorProgressMarker {
512 #[must_use]
514 pub const fn conversation_id(self) -> ConversationId {
515 self.conversation_id
516 }
517
518 #[must_use]
520 pub const fn participant_id(self) -> ParticipantId {
521 self.participant_id
522 }
523
524 #[must_use]
526 pub const fn binding_epoch(self) -> BindingEpoch {
527 self.binding_epoch
528 }
529
530 #[must_use]
532 pub const fn through_seq(self) -> DeliverySeq {
533 self.through_seq
534 }
535
536 #[must_use]
538 pub const fn marker_delivery_seq(self) -> DeliverySeq {
539 self.marker_delivery_seq
540 }
541
542 #[must_use]
548 pub const fn storage_description(self) -> StoredEdgeRestore {
549 StoredEdgeRestore::ParticipantCursorProgressMarker(self.checkpoint_marker_source())
550 }
551
552 pub(in crate::lifecycle) const fn checkpoint_marker_source(
553 self,
554 ) -> MarkerCursorProgressRestore {
555 MarkerCursorProgressRestore {
556 conversation_id: self.conversation_id,
557 participant_id: self.participant_id,
558 binding_epoch: self.binding_epoch,
559 through_seq: self.through_seq,
560 marker_delivery_seq: self.marker_delivery_seq,
561 delivery: MarkerDeliveryRestore {
562 participant_id: self.participant_id,
563 binding_epoch: self.binding_epoch,
564 marker_delivery_seq: self.marker_delivery_seq,
565 },
566 }
567 }
568}
569
570#[derive(Clone, Copy, Debug, PartialEq, Eq)]
586pub enum ParticipantCursorProgress {
587 Continuous(CursorProgressContinuous),
589 Marker(CursorProgressMarker),
591}
592
593impl ParticipantCursorProgress {
594 pub(in crate::lifecycle) const fn checkpoint_marker_source(
595 self,
596 ) -> Option<MarkerCursorProgressRestore> {
597 match self {
598 Self::Continuous(_) => None,
599 Self::Marker(value) => Some(value.checkpoint_marker_source()),
600 }
601 }
602
603 #[cfg(test)]
605 #[must_use]
606 pub(crate) const fn continuous(
607 participant_id: ParticipantId,
608 binding_epoch: BindingEpoch,
609 through_seq: DeliverySeq,
610 ) -> Self {
611 Self::Continuous(CursorProgressContinuous::new(
612 participant_id,
613 binding_epoch,
614 through_seq,
615 ))
616 }
617
618 pub(super) fn restore_continuous(
619 authority: OrdinaryBindingAuthority,
620 participant_id: ParticipantId,
621 binding_epoch: BindingEpoch,
622 through_seq: DeliverySeq,
623 ) -> Option<Self> {
624 if authority.binding.participant_id != participant_id
625 || authority.binding.binding_epoch != binding_epoch
626 || authority.through_seq != through_seq
627 {
628 return None;
629 }
630 Some(Self::Continuous(CursorProgressContinuous {
631 participant_id,
632 binding_epoch,
633 through_seq,
634 }))
635 }
636
637 #[must_use]
639 pub const fn participant_id(self) -> ParticipantId {
640 match self {
641 Self::Continuous(value) => value.participant_id,
642 Self::Marker(value) => value.participant_id,
643 }
644 }
645
646 #[must_use]
648 pub const fn binding_epoch(self) -> BindingEpoch {
649 match self {
650 Self::Continuous(value) => value.binding_epoch,
651 Self::Marker(value) => value.binding_epoch,
652 }
653 }
654
655 #[must_use]
657 pub const fn through_seq(self) -> DeliverySeq {
658 match self {
659 Self::Continuous(value) => value.through_seq,
660 Self::Marker(value) => value.through_seq,
661 }
662 }
663
664 #[must_use]
666 pub const fn marker_delivery_seq(self) -> Option<DeliverySeq> {
667 match self {
668 Self::Continuous(_) => None,
669 Self::Marker(value) => Some(value.marker_delivery_seq),
670 }
671 }
672}
673
674#[derive(Clone, Copy, Debug, PartialEq, Eq)]
679pub struct DetachedCredentialRecovery {
680 conversation_id: ConversationId,
681 participant_id: ParticipantId,
682 marker_delivery_seq: DeliverySeq,
683 prior_binding_epoch: BindingEpoch,
684 resulting_floor: DeliverySeq,
692}
693
694impl DetachedCredentialRecovery {
695 #[must_use]
698 pub const fn resulting_floor(self) -> DeliverySeq {
699 self.resulting_floor
700 }
701
702 pub fn storage_description(
724 self,
725 terminal: RestoredBindingFateTerminal,
726 ) -> Result<StoredEdgeRestore, RestoredBindingFateTerminal> {
727 if terminal.participant_id() != self.participant_id
728 || terminal.binding_epoch() != self.prior_binding_epoch
729 || terminal.conversation_id() != self.conversation_id
730 {
731 return Err(terminal);
732 }
733 Ok(StoredEdgeRestore::DetachedCredentialRecovery(
734 DetachedCredentialRecoveryRestore {
735 participant_id: self.participant_id,
736 marker_delivery_seq: self.marker_delivery_seq,
737 prior_binding_epoch: self.prior_binding_epoch,
738 resulting_floor: self.resulting_floor,
739 terminal: terminal.storage_description(),
740 progress: MarkerCursorProgressRestore {
741 conversation_id: self.conversation_id,
742 participant_id: self.participant_id,
743 binding_epoch: self.prior_binding_epoch,
744 through_seq: self.marker_delivery_seq,
745 marker_delivery_seq: self.marker_delivery_seq,
746 delivery: MarkerDeliveryRestore {
747 participant_id: self.participant_id,
748 binding_epoch: self.prior_binding_epoch,
749 marker_delivery_seq: self.marker_delivery_seq,
750 },
751 },
752 },
753 ))
754 }
755
756 pub(super) const fn from_storage_description(
763 conversation_id: ConversationId,
764 participant_id: ParticipantId,
765 marker_delivery_seq: DeliverySeq,
766 prior_binding_epoch: BindingEpoch,
767 resulting_floor: DeliverySeq,
768 ) -> Self {
769 Self {
770 conversation_id,
771 participant_id,
772 marker_delivery_seq,
773 prior_binding_epoch,
774 resulting_floor,
775 }
776 }
777
778 #[must_use]
780 pub const fn conversation_id(self) -> ConversationId {
781 self.conversation_id
782 }
783
784 #[must_use]
786 pub const fn participant_id(self) -> ParticipantId {
787 self.participant_id
788 }
789
790 #[must_use]
792 pub const fn marker_delivery_seq(self) -> DeliverySeq {
793 self.marker_delivery_seq
794 }
795
796 #[must_use]
798 pub const fn prior_binding_epoch(self) -> BindingEpoch {
799 self.prior_binding_epoch
800 }
801}
802
803#[cfg(any(test, feature = "test-support"))]
804pub fn validated_marker_record_for_recovery_test(
805 recovery: DetachedCredentialRecovery,
806) -> ValidatedMarkerRecord {
807 validated_marker_record_for_test(
808 recovery.conversation_id(),
809 recovery.participant_id(),
810 super::claim_frontier::FrontierBinding::Detached(recovery.prior_binding_epoch()),
811 recovery.marker_delivery_seq(),
812 recovery.marker_delivery_seq(),
813 )
814}
815
816#[derive(Clone, Copy, Debug, PartialEq, Eq)]
821pub struct DetachedMarkerRelease {
822 conversation_id: ConversationId,
830 participant_id: ParticipantId,
831 marker_delivery_seq: DeliverySeq,
832 last_dead_binding_epoch: BindingEpoch,
833 provenance: DetachedMarkerReleaseProvenance,
841}
842
843#[derive(Clone, Copy, Debug, PartialEq, Eq)]
848pub enum DetachedMarkerReleaseProvenance {
849 Fate,
852 Candidate,
856}
857
858impl DetachedMarkerRelease {
859 #[must_use]
861 pub const fn conversation_id(self) -> ConversationId {
862 self.conversation_id
863 }
864
865 #[must_use]
867 pub const fn provenance(self) -> DetachedMarkerReleaseProvenance {
868 self.provenance
869 }
870
871 pub(super) const fn from_storage_description(
875 conversation_id: ConversationId,
876 participant_id: ParticipantId,
877 marker_delivery_seq: DeliverySeq,
878 last_dead_binding_epoch: BindingEpoch,
879 provenance: DetachedMarkerReleaseProvenance,
880 ) -> Self {
881 Self {
882 conversation_id,
883 participant_id,
884 marker_delivery_seq,
885 last_dead_binding_epoch,
886 provenance,
887 }
888 }
889
890 #[must_use]
892 pub const fn participant_id(self) -> ParticipantId {
893 self.participant_id
894 }
895
896 #[must_use]
898 pub const fn marker_delivery_seq(self) -> DeliverySeq {
899 self.marker_delivery_seq
900 }
901
902 #[must_use]
904 pub const fn last_dead_binding_epoch(self) -> BindingEpoch {
905 self.last_dead_binding_epoch
906 }
907
908 pub fn storage_description(
923 self,
924 terminal: Option<RestoredBindingFateTerminal>,
925 ) -> Result<StoredEdgeRestore, Option<RestoredBindingFateTerminal>> {
926 let provenance = match (self.provenance, terminal) {
927 (DetachedMarkerReleaseProvenance::Fate, Some(terminal)) => {
928 if terminal.participant_id() != self.participant_id
929 || terminal.binding_epoch() != self.last_dead_binding_epoch
930 || terminal.conversation_id() != self.conversation_id
931 {
932 return Err(Some(terminal));
933 }
934 DetachedMarkerReleaseProvenanceRestore::Fate {
935 terminal: terminal.storage_description(),
936 }
937 }
938 (DetachedMarkerReleaseProvenance::Candidate, None) => {
939 DetachedMarkerReleaseProvenanceRestore::Candidate
940 }
941 (DetachedMarkerReleaseProvenance::Fate, None)
942 | (DetachedMarkerReleaseProvenance::Candidate, Some(_)) => return Err(terminal),
943 };
944 Ok(StoredEdgeRestore::DetachedMarkerRelease(
945 DetachedMarkerReleaseRestore {
946 conversation_id: self.conversation_id,
947 participant_id: self.participant_id,
948 marker_delivery_seq: self.marker_delivery_seq,
949 last_dead_binding_epoch: self.last_dead_binding_epoch,
950 delivery: MarkerDeliveryRestore {
951 participant_id: self.participant_id,
952 binding_epoch: self.last_dead_binding_epoch,
953 marker_delivery_seq: self.marker_delivery_seq,
954 },
955 provenance,
956 },
957 ))
958 }
959}
960
961#[derive(Clone, Copy, Debug, PartialEq, Eq)]
966pub struct DetachedCursorRelease {
967 participant_id: ParticipantId,
968 last_dead_binding_epoch: BindingEpoch,
969}
970
971impl DetachedCursorRelease {
972 #[must_use]
974 pub const fn participant_id(self) -> ParticipantId {
975 self.participant_id
976 }
977
978 #[must_use]
980 pub const fn last_dead_binding_epoch(self) -> BindingEpoch {
981 self.last_dead_binding_epoch
982 }
983}
984
985#[derive(Clone, Copy, Debug, PartialEq, Eq)]
987pub enum StoredEdge {
988 ObserverProjection(ObserverProjection),
990 PhysicalCompaction(PhysicalCompaction),
992 MarkerDelivery(MarkerDelivery),
994 ParticipantCursorProgress(ParticipantCursorProgress),
996 DetachedCredentialRecovery(DetachedCredentialRecovery),
998 DetachedMarkerRelease(DetachedMarkerRelease),
1000 DetachedCursorRelease(DetachedCursorRelease),
1002}
1003
1004#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1006pub enum ClosureState {
1007 Clear,
1009 Owed {
1011 debt: ClosureDebt,
1013 edge: StoredEdge,
1015 },
1016}
1017
1018#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1024pub struct OrdinaryDetachedAttachAdmission {
1025 _private: (),
1026}
1027
1028impl ClosureState {
1029 pub const fn ordinary_detached_attach_admission(
1036 self,
1037 ) -> Result<OrdinaryDetachedAttachAdmission, Self> {
1038 match self {
1039 Self::Clear => Ok(OrdinaryDetachedAttachAdmission { _private: () }),
1040 Self::Owed { .. } => Err(self),
1041 }
1042 }
1043}
1044
1045#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1051pub struct DebtCompletion(ClosureState);
1052
1053impl DebtCompletion {
1054 #[must_use]
1056 pub const fn clear() -> Self {
1057 Self(ClosureState::Clear)
1058 }
1059
1060 #[must_use]
1062 pub const fn observer_projection(debt: ClosureDebt, edge: ObserverProjection) -> Self {
1063 Self(ClosureState::Owed {
1064 debt,
1065 edge: StoredEdge::ObserverProjection(edge),
1066 })
1067 }
1068
1069 #[must_use]
1071 pub const fn physical_compaction(debt: ClosureDebt, edge: PhysicalCompaction) -> Self {
1072 Self(ClosureState::Owed {
1073 debt,
1074 edge: StoredEdge::PhysicalCompaction(edge),
1075 })
1076 }
1077
1078 #[must_use]
1080 pub const fn into_state(self) -> ClosureState {
1081 self.0
1082 }
1083}
1084
1085#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1128pub struct OrdinaryBindingAuthority {
1129 binding: ActiveBinding,
1130 through_seq: DeliverySeq,
1131}
1132
1133impl OrdinaryBindingAuthority {
1134 pub(crate) const fn new(binding: ActiveBinding, through_seq: DeliverySeq) -> Self {
1135 Self {
1136 binding,
1137 through_seq,
1138 }
1139 }
1140
1141 #[must_use]
1143 pub const fn binding(self) -> ActiveBinding {
1144 self.binding
1145 }
1146
1147 #[must_use]
1149 pub const fn through_seq(self) -> DeliverySeq {
1150 self.through_seq
1151 }
1152
1153 pub(crate) fn cursor_progressed(self, event: Event) -> Result<Self, Self> {
1163 let EventKind::CursorProgressed {
1164 participant_id,
1165 binding_epoch,
1166 progress:
1167 CursorProgressEvent::Normal {
1168 previous_cursor,
1169 through_seq,
1170 },
1171 ..
1172 } = event.0
1173 else {
1174 return Err(self);
1175 };
1176 self.participant_ack_progressed(
1177 self.binding.conversation_id,
1178 participant_id,
1179 binding_epoch,
1180 previous_cursor,
1181 through_seq,
1182 )
1183 }
1184
1185 pub(crate) fn participant_ack_progressed(
1187 self,
1188 conversation_id: ConversationId,
1189 participant_id: ParticipantId,
1190 binding_epoch: BindingEpoch,
1191 previous_cursor: DeliverySeq,
1192 through_seq: DeliverySeq,
1193 ) -> Result<Self, Self> {
1194 if conversation_id != self.binding.conversation_id
1195 || participant_id != self.binding.participant_id
1196 || binding_epoch != self.binding.binding_epoch
1197 || previous_cursor != self.through_seq
1198 || through_seq <= previous_cursor
1199 {
1200 return Err(self);
1201 }
1202 Ok(Self {
1203 through_seq,
1204 ..self
1205 })
1206 }
1207
1208 pub(crate) fn binding_fate(
1215 self,
1216 terminal: CommittedDiedTerminal,
1217 resulting_floor: DeliverySeq,
1218 ) -> Result<OrdinaryBindingFate, Self> {
1219 if terminal.participant_id() != self.binding.participant_id
1220 || terminal.conversation_id() != self.binding.conversation_id
1221 || terminal.binding_epoch() != self.binding.binding_epoch
1222 {
1223 return Err(self);
1224 }
1225 Ok(OrdinaryBindingFate {
1226 conversation_id: self.binding.conversation_id,
1227 through_seq: self.through_seq,
1228 resulting_floor,
1229 terminal,
1230 release: DetachedCursorRelease {
1231 participant_id: self.binding.participant_id,
1232 last_dead_binding_epoch: self.binding.binding_epoch,
1233 },
1234 })
1235 }
1236}
1237
1238#[derive(Clone, Copy, Debug, Eq)]
1245pub struct OrdinaryBindingFate {
1246 conversation_id: ConversationId,
1247 through_seq: DeliverySeq,
1248 resulting_floor: DeliverySeq,
1249 terminal: CommittedDiedTerminal,
1250 release: DetachedCursorRelease,
1251}
1252
1253impl PartialEq for OrdinaryBindingFate {
1254 fn eq(&self, other: &Self) -> bool {
1255 (
1256 self.conversation_id,
1257 self.through_seq,
1258 self.resulting_floor,
1259 self.release,
1260 ) == (
1261 other.conversation_id,
1262 other.through_seq,
1263 other.resulting_floor,
1264 other.release,
1265 )
1266 }
1267}
1268
1269impl OrdinaryBindingFate {
1270 pub(super) const fn storage_description(&self) -> OrdinaryBindingFateRestore {
1271 OrdinaryBindingFateRestore {
1272 authority: OrdinaryBindingAuthorityRestore {
1273 binding: ActiveBinding {
1274 conversation_id: self.conversation_id,
1275 participant_id: self.release.participant_id,
1276 binding_epoch: self.release.last_dead_binding_epoch,
1277 },
1278 through_seq: self.through_seq,
1279 },
1280 terminal: super::CommittedBindingTerminal::Died(self.terminal).storage_description(),
1281 resulting_floor: self.resulting_floor,
1282 }
1283 }
1284
1285 #[must_use]
1287 pub const fn conversation_id(self) -> ConversationId {
1288 self.conversation_id
1289 }
1290
1291 #[must_use]
1293 pub const fn through_seq(self) -> DeliverySeq {
1294 self.through_seq
1295 }
1296
1297 #[must_use]
1299 pub const fn participant_id(self) -> ParticipantId {
1300 self.release.participant_id
1301 }
1302
1303 #[must_use]
1305 pub const fn last_dead_binding_epoch(self) -> BindingEpoch {
1306 self.release.last_dead_binding_epoch
1307 }
1308
1309 #[must_use]
1311 pub const fn resulting_floor(self) -> DeliverySeq {
1312 self.resulting_floor
1313 }
1314 #[must_use]
1316 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
1317 ObserverProgressProjection::sourced(
1318 self.conversation_id,
1319 self.resulting_floor,
1320 super::checkpoint_codec::ObserverProjectionSource::Ordinary {
1321 binding: super::ActiveBinding {
1322 conversation_id: self.conversation_id,
1323 participant_id: self.release.participant_id,
1324 binding_epoch: self.release.last_dead_binding_epoch,
1325 },
1326 through_seq: self.through_seq,
1327 terminal: super::CommittedBindingTerminal::Died(self.terminal)
1328 .storage_description(),
1329 },
1330 )
1331 }
1332
1333 #[must_use]
1335 pub const fn into_direct_state(self, debt: ClosureDebt) -> ClosureState {
1336 owed(debt, StoredEdge::DetachedCursorRelease(self.release))
1337 }
1338}
1339
1340#[derive(Debug, PartialEq, Eq)]
1346pub struct FencedAttachCommit {
1347 conversation_id: ConversationId,
1348 participant_id: ParticipantId,
1349 marker_delivery_seq: DeliverySeq,
1350 prior_binding_epoch: BindingEpoch,
1351 new_binding_epoch: BindingEpoch,
1352 next_state: ClosureState,
1353}
1354
1355impl FencedAttachCommit {
1356 #[must_use]
1358 pub const fn conversation_id(&self) -> ConversationId {
1359 self.conversation_id
1360 }
1361
1362 #[must_use]
1364 pub const fn participant_id(&self) -> ParticipantId {
1365 self.participant_id
1366 }
1367
1368 #[must_use]
1370 pub const fn marker_delivery_seq(&self) -> DeliverySeq {
1371 self.marker_delivery_seq
1372 }
1373
1374 #[must_use]
1376 pub const fn prior_binding_epoch(&self) -> BindingEpoch {
1377 self.prior_binding_epoch
1378 }
1379
1380 #[must_use]
1382 pub const fn new_binding_epoch(&self) -> BindingEpoch {
1383 self.new_binding_epoch
1384 }
1385
1386 #[must_use]
1388 pub const fn next_state(&self) -> ClosureState {
1389 self.next_state
1390 }
1391
1392 pub(super) fn restore_validated(
1397 recovery: DetachedCredentialRecovery,
1398 record_authority: &ValidatedMarkerRecord,
1399 debt: ClosureDebt,
1400 event: Event,
1401 successor: DebtCompletion,
1402 ) -> Option<Self> {
1403 if debt.value().is_zero()
1404 || record_authority.conversation_id() != recovery.conversation_id
1405 || record_authority.participant_id() != recovery.participant_id
1406 || record_authority.delivery_seq() != recovery.marker_delivery_seq
1407 || record_authority.target_binding()
1408 != super::FrontierBinding::Detached(recovery.prior_binding_epoch)
1409 || record_authority.occurrence()
1410 != super::claim_frontier::MarkerRecordOccurrence::Delivered
1411 {
1412 return None;
1413 }
1414 let EventKind::FencedRecoveryCommitted {
1415 participant_id,
1416 marker_delivery_seq,
1417 prior_binding_epoch,
1418 new_binding_epoch,
1419 ..
1420 } = event.0
1421 else {
1422 return None;
1423 };
1424 if participant_id != recovery.participant_id
1425 || marker_delivery_seq != recovery.marker_delivery_seq
1426 || prior_binding_epoch != recovery.prior_binding_epoch
1427 || !is_next_generation(prior_binding_epoch, new_binding_epoch)
1428 {
1429 return None;
1430 }
1431 Some(Self {
1432 conversation_id: recovery.conversation_id,
1433 participant_id,
1434 marker_delivery_seq,
1435 prior_binding_epoch,
1436 new_binding_epoch,
1437 next_state: successor.into_state(),
1438 })
1439 }
1440
1441 pub(super) fn recovered_binding_fate(
1454 self,
1455 event: Event,
1456 ) -> Result<RecoveredBindingFate, Box<Self>> {
1457 let EventKind::BindingFateObserved {
1458 participant_id,
1459 binding_epoch,
1460 resulting_floor,
1461 } = event.0
1462 else {
1463 return Err(Box::new(self));
1464 };
1465 if participant_id != self.participant_id || binding_epoch != self.new_binding_epoch {
1466 return Err(Box::new(self));
1467 }
1468 let ClosureState::Owed { debt, edge } = self.next_state else {
1469 return Err(Box::new(self));
1470 };
1471 let predecessor = match edge {
1472 StoredEdge::ObserverProjection(value) => {
1473 RecoveredStorageEdge::ObserverProjection(value)
1474 }
1475 StoredEdge::PhysicalCompaction(value) => {
1476 RecoveredStorageEdge::PhysicalCompaction(value)
1477 }
1478 _ => return Err(Box::new(self)),
1479 };
1480 Ok(RecoveredBindingFate {
1481 conversation_id: self.conversation_id,
1482 predecessor_debt: debt,
1483 predecessor,
1484 resulting_floor,
1485 release: DetachedCursorRelease {
1486 participant_id,
1487 last_dead_binding_epoch: binding_epoch,
1488 },
1489 })
1490 }
1491}
1492
1493#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1494enum RecoveredStorageEdge {
1495 ObserverProjection(ObserverProjection),
1496 PhysicalCompaction(PhysicalCompaction),
1497}
1498
1499impl RecoveredStorageEdge {
1500 const fn into_stored_edge(self) -> StoredEdge {
1501 match self {
1502 Self::ObserverProjection(value) => StoredEdge::ObserverProjection(value),
1503 Self::PhysicalCompaction(value) => StoredEdge::PhysicalCompaction(value),
1504 }
1505 }
1506}
1507
1508#[derive(Debug, PartialEq, Eq)]
1514pub struct RecoveredBindingFate {
1515 conversation_id: ConversationId,
1516 predecessor_debt: ClosureDebt,
1517 predecessor: RecoveredStorageEdge,
1518 resulting_floor: DeliverySeq,
1519 release: DetachedCursorRelease,
1520}
1521
1522impl RecoveredBindingFate {
1523 #[must_use]
1525 pub const fn conversation_id(&self) -> ConversationId {
1526 self.conversation_id
1527 }
1528
1529 #[must_use]
1531 pub const fn predecessor_state(&self) -> ClosureState {
1532 owed(self.predecessor_debt, self.predecessor.into_stored_edge())
1533 }
1534
1535 #[must_use]
1537 pub const fn participant_id(&self) -> ParticipantId {
1538 self.release.participant_id
1539 }
1540
1541 #[must_use]
1543 pub const fn last_dead_binding_epoch(&self) -> BindingEpoch {
1544 self.release.last_dead_binding_epoch
1545 }
1546
1547 #[must_use]
1549 pub const fn resulting_floor(&self) -> DeliverySeq {
1550 self.resulting_floor
1551 }
1552 #[must_use]
1554 pub const fn observer_progress_projection(&self) -> ObserverProgressProjection {
1555 ObserverProgressProjection::sourced(
1556 self.conversation_id,
1557 self.resulting_floor,
1558 super::checkpoint_codec::ObserverProjectionSource::Recovered {
1559 binding: super::ActiveBinding {
1560 conversation_id: self.conversation_id,
1561 participant_id: self.release.participant_id,
1562 binding_epoch: self.release.last_dead_binding_epoch,
1563 },
1564 predecessor: match self.predecessor {
1565 RecoveredStorageEdge::ObserverProjection(edge) => {
1566 super::DebtCompletionRestore::ObserverProjection {
1567 debt: self.predecessor_debt.value(),
1568 through_seq: edge.through_seq(),
1569 }
1570 }
1571 RecoveredStorageEdge::PhysicalCompaction(edge) => {
1572 super::DebtCompletionRestore::PhysicalCompaction {
1573 debt: self.predecessor_debt.value(),
1574 from_floor: edge.from_floor(),
1575 through_seq: edge.through_seq(),
1576 }
1577 }
1578 },
1579 },
1580 )
1581 }
1582}
1583
1584#[derive(Debug, PartialEq, Eq)]
1591pub struct PendingRecoveredCursorRelease {
1592 debt: ClosureDebt,
1593 predecessor: RecoveredStorageEdge,
1594 release: DetachedCursorRelease,
1595}
1596
1597impl PendingRecoveredCursorRelease {
1598 #[must_use]
1600 pub const fn current_state(&self) -> ClosureState {
1601 owed(self.debt, self.predecessor.into_stored_edge())
1602 }
1603
1604 #[must_use]
1606 pub const fn participant_id(&self) -> ParticipantId {
1607 self.release.participant_id
1608 }
1609
1610 #[must_use]
1612 pub const fn last_dead_binding_epoch(&self) -> BindingEpoch {
1613 self.release.last_dead_binding_epoch
1614 }
1615}
1616
1617#[derive(Debug, PartialEq, Eq)]
1619pub struct RecoveredCursorRelease {
1620 debt: ClosureDebt,
1621 release: DetachedCursorRelease,
1622}
1623
1624impl RecoveredCursorRelease {
1625 #[must_use]
1627 pub const fn debt(&self) -> ClosureDebt {
1628 self.debt
1629 }
1630
1631 #[must_use]
1633 pub const fn edge(&self) -> DetachedCursorRelease {
1634 self.release
1635 }
1636
1637 #[must_use]
1639 pub const fn into_state(self) -> ClosureState {
1640 owed(self.debt, StoredEdge::DetachedCursorRelease(self.release))
1641 }
1642}
1643
1644#[derive(Debug, PartialEq, Eq)]
1646pub enum RecoveredBindingFateTransition {
1647 PendingStorage(PendingRecoveredCursorRelease),
1649 DetachedCursorRelease(RecoveredCursorRelease),
1651}
1652
1653#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1654enum SuccessorUse {
1655 ObserverCompletion,
1656 ObserverMarkerAppend,
1657 ObserverLeave,
1658 PhysicalCompletion,
1659 PhysicalCover,
1660 CursorGreaterAck,
1661}
1662
1663#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1669pub struct ProjectionCompactionSuccessor {
1670 predecessor: StoredEdge,
1671 event: Event,
1672 use_kind: SuccessorUse,
1673 state: ClosureState,
1674}
1675
1676#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1683pub enum CursorFateSuccessor {
1684 DetachedCredentialRecovery(DetachedCredentialRecovery),
1686 DetachedCursorRelease(DetachedCursorRelease),
1688}
1689
1690impl CursorFateSuccessor {
1691 #[must_use]
1693 pub const fn into_stored_edge(self) -> StoredEdge {
1694 match self {
1695 Self::DetachedCredentialRecovery(value) => {
1696 StoredEdge::DetachedCredentialRecovery(value)
1697 }
1698 Self::DetachedCursorRelease(value) => StoredEdge::DetachedCursorRelease(value),
1699 }
1700 }
1701}
1702
1703#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1705pub enum DetachedAttachRefusal {
1706 RecoveryFence,
1708 MarkerNotDelivered,
1710 MarkerMismatch,
1712 DeliveredMarkerAwaitingAck,
1714 EpisodeChurnLimit,
1716 StaleAuthority,
1718 NoBinding,
1720}
1721
1722#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1723enum DetachedClaimTarget {
1724 CredentialRecovery {
1725 marker_delivery_seq: DeliverySeq,
1726 binding_epoch: BindingEpoch,
1727 },
1728 MarkerRelease {
1729 marker_delivery_seq: DeliverySeq,
1730 binding_epoch: BindingEpoch,
1731 },
1732 CursorRelease {
1733 binding_epoch: BindingEpoch,
1734 },
1735}
1736
1737#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1743pub struct KClaimBackedDetachedLeave {
1744 participant_id: ParticipantId,
1745 target: DetachedClaimTarget,
1746 actual_charge: ResourceVector,
1747}
1748
1749impl KClaimBackedDetachedLeave {
1750 #[must_use]
1752 pub const fn participant_id(self) -> ParticipantId {
1753 self.participant_id
1754 }
1755
1756 #[must_use]
1758 pub const fn actual_charge(self) -> ResourceVector {
1759 self.actual_charge
1760 }
1761}
1762
1763#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1772pub struct Event(EventKind);
1773
1774#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1775enum EventKind {
1776 ProjectionCompleted {
1777 through_seq: DeliverySeq,
1778 },
1779 CompactionCompleted {
1780 from_floor: DeliverySeq,
1781 through_seq: DeliverySeq,
1782 resulting_floor: DeliverySeq,
1783 },
1784 MarkerAppended {
1785 marker_delivery_seq: DeliverySeq,
1786 resulting_projection_through: DeliverySeq,
1787 },
1788 MarkerDelivered {
1789 participant_id: ParticipantId,
1790 binding_epoch: BindingEpoch,
1791 marker_delivery_seq: DeliverySeq,
1792 },
1793 CursorProgressed {
1794 participant_index: ParticipantIndex,
1795 participant_id: ParticipantId,
1796 binding_epoch: BindingEpoch,
1797 progress: CursorProgressEvent,
1798 resulting_floor: DeliverySeq,
1799 },
1800 BindingFateObserved {
1801 participant_id: ParticipantId,
1802 binding_epoch: BindingEpoch,
1803 resulting_floor: DeliverySeq,
1804 },
1805 LeaveCommitted {
1806 participant_id: ParticipantId,
1807 authority: LeaveAuthority,
1808 resulting_floor: DeliverySeq,
1809 },
1810 FencedRecoveryCommitted {
1811 participant_id: ParticipantId,
1812 marker_delivery_seq: DeliverySeq,
1813 prior_binding_epoch: BindingEpoch,
1814 new_binding_epoch: BindingEpoch,
1815 resulting_floor: DeliverySeq,
1816 },
1817}
1818
1819#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1820enum CursorProgressEvent {
1821 Normal {
1822 previous_cursor: DeliverySeq,
1823 through_seq: DeliverySeq,
1824 },
1825 Marker {
1826 marker_delivery_seq: DeliverySeq,
1827 },
1828}
1829
1830#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1831enum LeaveAuthority {
1832 Live(BindingEpoch),
1833 Detached,
1834}
1835
1836impl Event {
1837 #[must_use]
1839 pub const fn projection_completed(through_seq: DeliverySeq) -> Self {
1840 Self(EventKind::ProjectionCompleted { through_seq })
1841 }
1842
1843 #[must_use]
1845 pub const fn compaction_completed(
1846 from_floor: DeliverySeq,
1847 through_seq: DeliverySeq,
1848 resulting_floor: DeliverySeq,
1849 ) -> Option<Self> {
1850 if from_floor <= through_seq && resulting_floor > through_seq {
1851 Some(Self(EventKind::CompactionCompleted {
1852 from_floor,
1853 through_seq,
1854 resulting_floor,
1855 }))
1856 } else {
1857 None
1858 }
1859 }
1860
1861 #[must_use]
1863 pub const fn marker_appended(
1864 marker_delivery_seq: DeliverySeq,
1865 resulting_projection_through: DeliverySeq,
1866 ) -> Self {
1867 Self(EventKind::MarkerAppended {
1868 marker_delivery_seq,
1869 resulting_projection_through,
1870 })
1871 }
1872
1873 #[must_use]
1875 pub const fn marker_delivered(
1876 participant_id: ParticipantId,
1877 binding_epoch: BindingEpoch,
1878 marker_delivery_seq: DeliverySeq,
1879 ) -> Self {
1880 Self(EventKind::MarkerDelivered {
1881 participant_id,
1882 binding_epoch,
1883 marker_delivery_seq,
1884 })
1885 }
1886
1887 #[must_use]
1893 pub const fn cursor_progressed(
1894 participant_id: ParticipantId,
1895 binding_epoch: BindingEpoch,
1896 previous_cursor: DeliverySeq,
1897 through_seq: DeliverySeq,
1898 resulting_floor: DeliverySeq,
1899 ) -> Option<Self> {
1900 if through_seq > previous_cursor {
1901 Some(Self(EventKind::CursorProgressed {
1902 participant_index: participant_id,
1903 participant_id,
1904 binding_epoch,
1905 progress: CursorProgressEvent::Normal {
1906 previous_cursor,
1907 through_seq,
1908 },
1909 resulting_floor,
1910 }))
1911 } else {
1912 None
1913 }
1914 }
1915
1916 #[must_use]
1919 pub const fn marker_acknowledged(
1920 participant_id: ParticipantId,
1921 binding_epoch: BindingEpoch,
1922 marker_delivery_seq: DeliverySeq,
1923 resulting_floor: DeliverySeq,
1924 ) -> Self {
1925 Self(EventKind::CursorProgressed {
1926 participant_index: participant_id,
1927 participant_id,
1928 binding_epoch,
1929 progress: CursorProgressEvent::Marker {
1930 marker_delivery_seq,
1931 },
1932 resulting_floor,
1933 })
1934 }
1935
1936 #[must_use]
1938 pub const fn binding_fate_observed(
1939 participant_id: ParticipantId,
1940 binding_epoch: BindingEpoch,
1941 resulting_floor: DeliverySeq,
1942 ) -> Self {
1943 Self(EventKind::BindingFateObserved {
1944 participant_id,
1945 binding_epoch,
1946 resulting_floor,
1947 })
1948 }
1949
1950 #[must_use]
1952 pub const fn live_leave_committed(
1953 participant_id: ParticipantId,
1954 binding_epoch: BindingEpoch,
1955 resulting_floor: DeliverySeq,
1956 ) -> Self {
1957 Self(EventKind::LeaveCommitted {
1958 participant_id,
1959 authority: LeaveAuthority::Live(binding_epoch),
1960 resulting_floor,
1961 })
1962 }
1963
1964 #[must_use]
1966 pub const fn detached_leave_committed(
1967 participant_id: ParticipantId,
1968 resulting_floor: DeliverySeq,
1969 ) -> Self {
1970 Self(EventKind::LeaveCommitted {
1971 participant_id,
1972 authority: LeaveAuthority::Detached,
1973 resulting_floor,
1974 })
1975 }
1976
1977 #[must_use]
1979 pub const fn fenced_recovery_committed(
1980 participant_id: ParticipantId,
1981 marker_delivery_seq: DeliverySeq,
1982 prior_binding_epoch: BindingEpoch,
1983 new_binding_epoch: BindingEpoch,
1984 resulting_floor: DeliverySeq,
1985 ) -> Self {
1986 Self(EventKind::FencedRecoveryCommitted {
1987 participant_id,
1988 marker_delivery_seq,
1989 prior_binding_epoch,
1990 new_binding_epoch,
1991 resulting_floor,
1992 })
1993 }
1994}
1995
1996impl ObserverProjection {
1997 #[must_use]
2003 #[allow(
2004 dead_code,
2005 reason = "the crate-owned binding-fate operation invokes this sealed OP transition"
2006 )]
2007 pub const fn apply_ordinary_binding_fate(
2008 self,
2009 resulting_debt: ClosureDebt,
2010 authority: OrdinaryBindingFate,
2011 ) -> PendingRecoveredCursorRelease {
2012 PendingRecoveredCursorRelease {
2013 debt: resulting_debt,
2014 predecessor: RecoveredStorageEdge::ObserverProjection(self),
2015 release: authority.release,
2016 }
2017 }
2018
2019 pub fn apply_recovered_binding_fate(
2030 self,
2031 debt: ClosureDebt,
2032 resulting_debt: ClosureDebt,
2033 authority: RecoveredBindingFate,
2034 ) -> Result<RecoveredBindingFateTransition, RecoveredBindingFate> {
2035 if authority.predecessor != RecoveredStorageEdge::ObserverProjection(self)
2036 || authority.predecessor_debt != debt
2037 {
2038 return Err(authority);
2039 }
2040 Ok(RecoveredBindingFateTransition::PendingStorage(
2041 PendingRecoveredCursorRelease {
2042 debt: resulting_debt,
2043 predecessor: RecoveredStorageEdge::ObserverProjection(self),
2044 release: authority.release,
2045 },
2046 ))
2047 }
2048
2049 pub fn complete_after_recovered_binding_fate(
2056 self,
2057 event: Event,
2058 resulting_debt: Option<ClosureDebt>,
2059 pending: PendingRecoveredCursorRelease,
2060 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2061 self.complete_after_binding_fate(event, resulting_debt, pending)
2062 }
2063
2064 pub fn complete_after_ordinary_binding_fate(
2071 self,
2072 event: Event,
2073 resulting_debt: Option<ClosureDebt>,
2074 pending: PendingRecoveredCursorRelease,
2075 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2076 self.complete_after_binding_fate(event, resulting_debt, pending)
2077 }
2078
2079 pub(crate) fn complete_after_binding_fate(
2086 self,
2087 event: Event,
2088 resulting_debt: Option<ClosureDebt>,
2089 pending: PendingRecoveredCursorRelease,
2090 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2091 if pending.predecessor != RecoveredStorageEdge::ObserverProjection(self)
2092 || projection_completion_boundary(self, event).is_none()
2093 {
2094 return Err(pending);
2095 }
2096 Ok(preserve_or_clear(
2097 resulting_debt,
2098 StoredEdge::DetachedCursorRelease(pending.release),
2099 ))
2100 }
2101
2102 #[must_use]
2104 pub const fn clear_after_completion(
2105 &self,
2106 event: &Event,
2107 ) -> Option<ProjectionCompactionSuccessor> {
2108 if projection_completion_boundary(*self, *event).is_some() {
2109 Some(ProjectionCompactionSuccessor {
2110 predecessor: StoredEdge::ObserverProjection(*self),
2111 event: *event,
2112 use_kind: SuccessorUse::ObserverCompletion,
2113 state: ClosureState::Clear,
2114 })
2115 } else {
2116 None
2117 }
2118 }
2119
2120 #[must_use]
2122 pub const fn strict_after_completion(
2123 &self,
2124 event: &Event,
2125 debt: ClosureDebt,
2126 edge: StoredEdge,
2127 successor_boundary: DeliverySeq,
2128 ) -> Option<ProjectionCompactionSuccessor> {
2129 let Some(completed_through) = projection_completion_boundary(*self, *event) else {
2130 return None;
2131 };
2132 if successor_boundary <= completed_through
2133 || !strict_edge_matches_boundary(edge, successor_boundary)
2134 {
2135 return None;
2136 }
2137 Some(ProjectionCompactionSuccessor {
2138 predecessor: StoredEdge::ObserverProjection(*self),
2139 event: *event,
2140 use_kind: SuccessorUse::ObserverCompletion,
2141 state: ClosureState::Owed { debt, edge },
2142 })
2143 }
2144
2145 pub fn complete(
2152 self,
2153 debt: ClosureDebt,
2154 event: Event,
2155 successor: ProjectionCompactionSuccessor,
2156 ) -> Result<ClosureState, ClosureState> {
2157 let original = owed(debt, StoredEdge::ObserverProjection(self));
2158 if successor.predecessor == StoredEdge::ObserverProjection(self)
2159 && successor.event == event
2160 && successor.use_kind == SuccessorUse::ObserverCompletion
2161 && projection_completion_boundary(self, event).is_some()
2162 {
2163 Ok(successor.state)
2164 } else {
2165 Err(original)
2166 }
2167 }
2168
2169 #[must_use]
2171 pub const fn later_projection_after_marker(
2172 &self,
2173 event: &Event,
2174 debt: ClosureDebt,
2175 successor: Self,
2176 ) -> Option<ProjectionCompactionSuccessor> {
2177 let EventKind::MarkerAppended {
2178 marker_delivery_seq,
2179 resulting_projection_through,
2180 } = event.0
2181 else {
2182 return None;
2183 };
2184 if marker_delivery_seq <= self.through_seq
2185 || resulting_projection_through < marker_delivery_seq
2186 || successor.through_seq != resulting_projection_through
2187 {
2188 return None;
2189 }
2190 Some(ProjectionCompactionSuccessor {
2191 predecessor: StoredEdge::ObserverProjection(*self),
2192 event: *event,
2193 use_kind: SuccessorUse::ObserverMarkerAppend,
2194 state: owed(debt, StoredEdge::ObserverProjection(successor)),
2195 })
2196 }
2197
2198 pub fn marker_appended(
2205 self,
2206 debt: ClosureDebt,
2207 event: Event,
2208 successor: ProjectionCompactionSuccessor,
2209 ) -> Result<ClosureState, ClosureState> {
2210 let original = owed(debt, StoredEdge::ObserverProjection(self));
2211 if successor.predecessor == StoredEdge::ObserverProjection(self)
2212 && successor.event == event
2213 && successor.use_kind == SuccessorUse::ObserverMarkerAppend
2214 {
2215 Ok(successor.state)
2216 } else {
2217 Err(original)
2218 }
2219 }
2220
2221 #[must_use]
2223 pub const fn later_projection_after_leave(
2224 &self,
2225 event: &Event,
2226 debt: ClosureDebt,
2227 successor: Self,
2228 ) -> Option<ProjectionCompactionSuccessor> {
2229 let EventKind::LeaveCommitted {
2230 resulting_floor, ..
2231 } = event.0
2232 else {
2233 return None;
2234 };
2235 if successor.through_seq <= self.through_seq || successor.through_seq < resulting_floor {
2236 return None;
2237 }
2238 Some(ProjectionCompactionSuccessor {
2239 predecessor: StoredEdge::ObserverProjection(*self),
2240 event: *event,
2241 use_kind: SuccessorUse::ObserverLeave,
2242 state: owed(debt, StoredEdge::ObserverProjection(successor)),
2243 })
2244 }
2245
2246 pub fn leave_with_later_projection(
2253 self,
2254 debt: ClosureDebt,
2255 event: Event,
2256 successor: ProjectionCompactionSuccessor,
2257 ) -> Result<ClosureState, ClosureState> {
2258 let original = owed(debt, StoredEdge::ObserverProjection(self));
2259 if successor.predecessor == StoredEdge::ObserverProjection(self)
2260 && successor.event == event
2261 && successor.use_kind == SuccessorUse::ObserverLeave
2262 {
2263 Ok(successor.state)
2264 } else {
2265 Err(original)
2266 }
2267 }
2268
2269 pub const fn independent_event(
2277 self,
2278 debt: ClosureDebt,
2279 event: Event,
2280 resulting_debt: Option<ClosureDebt>,
2281 ) -> Result<ClosureState, ClosureState> {
2282 let original = owed(debt, StoredEdge::ObserverProjection(self));
2283 if !matches!(
2284 event.0,
2285 EventKind::CursorProgressed { .. }
2286 | EventKind::BindingFateObserved { .. }
2287 | EventKind::LeaveCommitted { .. }
2288 ) {
2289 return Err(original);
2290 }
2291 Ok(preserve_or_clear(
2292 resulting_debt,
2293 StoredEdge::ObserverProjection(self),
2294 ))
2295 }
2296
2297 pub const fn charged_binding_change(
2304 self,
2305 debt: ClosureDebt,
2306 episode_churn_used: u64,
2307 delta_cycles: u64,
2308 episode_churn_limit: u64,
2309 resulting_debt: Option<ClosureDebt>,
2310 ) -> Result<ClosureState, (ClosureState, DetachedAttachRefusal)> {
2311 let original = owed(debt, StoredEdge::ObserverProjection(self));
2312 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2313 return Err((original, DetachedAttachRefusal::EpisodeChurnLimit));
2314 }
2315 Ok(preserve_or_clear(
2316 resulting_debt,
2317 StoredEdge::ObserverProjection(self),
2318 ))
2319 }
2320}
2321
2322impl PhysicalCompaction {
2323 #[must_use]
2325 #[allow(
2326 dead_code,
2327 reason = "the crate-owned binding-fate replay boundary invokes this sealed PC transition"
2328 )]
2329 pub(crate) const fn apply_ordinary_binding_fate(
2330 self,
2331 resulting_debt: ClosureDebt,
2332 authority: OrdinaryBindingFate,
2333 ) -> RecoveredBindingFateTransition {
2334 if authority.resulting_floor > self.through_seq {
2335 RecoveredBindingFateTransition::DetachedCursorRelease(RecoveredCursorRelease {
2336 debt: resulting_debt,
2337 release: authority.release,
2338 })
2339 } else {
2340 RecoveredBindingFateTransition::PendingStorage(PendingRecoveredCursorRelease {
2341 debt: resulting_debt,
2342 predecessor: RecoveredStorageEdge::PhysicalCompaction(self),
2343 release: authority.release,
2344 })
2345 }
2346 }
2347
2348 pub fn apply_recovered_binding_fate(
2359 self,
2360 debt: ClosureDebt,
2361 resulting_debt: ClosureDebt,
2362 authority: RecoveredBindingFate,
2363 ) -> Result<RecoveredBindingFateTransition, RecoveredBindingFate> {
2364 if authority.predecessor != RecoveredStorageEdge::PhysicalCompaction(self)
2365 || authority.predecessor_debt != debt
2366 {
2367 return Err(authority);
2368 }
2369 if authority.resulting_floor > self.through_seq {
2370 Ok(RecoveredBindingFateTransition::DetachedCursorRelease(
2371 RecoveredCursorRelease {
2372 debt: resulting_debt,
2373 release: authority.release,
2374 },
2375 ))
2376 } else {
2377 Ok(RecoveredBindingFateTransition::PendingStorage(
2378 PendingRecoveredCursorRelease {
2379 debt: resulting_debt,
2380 predecessor: RecoveredStorageEdge::PhysicalCompaction(self),
2381 release: authority.release,
2382 },
2383 ))
2384 }
2385 }
2386
2387 pub fn complete_after_recovered_binding_fate(
2394 self,
2395 event: Event,
2396 resulting_debt: Option<ClosureDebt>,
2397 pending: PendingRecoveredCursorRelease,
2398 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2399 self.complete_after_binding_fate(event, resulting_debt, pending)
2400 }
2401
2402 pub(crate) fn complete_after_binding_fate(
2409 self,
2410 event: Event,
2411 resulting_debt: Option<ClosureDebt>,
2412 pending: PendingRecoveredCursorRelease,
2413 ) -> Result<ClosureState, PendingRecoveredCursorRelease> {
2414 if pending.predecessor != RecoveredStorageEdge::PhysicalCompaction(self)
2415 || physical_completion_floor(self, event).is_none()
2416 {
2417 return Err(pending);
2418 }
2419 Ok(preserve_or_clear(
2420 resulting_debt,
2421 StoredEdge::DetachedCursorRelease(pending.release),
2422 ))
2423 }
2424
2425 #[must_use]
2427 pub const fn clear_after_completion(
2428 &self,
2429 event: &Event,
2430 ) -> Option<ProjectionCompactionSuccessor> {
2431 if physical_completion_floor(*self, *event).is_some() {
2432 Some(ProjectionCompactionSuccessor {
2433 predecessor: StoredEdge::PhysicalCompaction(*self),
2434 event: *event,
2435 use_kind: SuccessorUse::PhysicalCompletion,
2436 state: ClosureState::Clear,
2437 })
2438 } else {
2439 None
2440 }
2441 }
2442
2443 #[must_use]
2445 pub const fn strict_after_completion(
2446 &self,
2447 event: &Event,
2448 debt: ClosureDebt,
2449 edge: StoredEdge,
2450 successor_boundary: DeliverySeq,
2451 ) -> Option<ProjectionCompactionSuccessor> {
2452 let Some(resulting_floor) = physical_completion_floor(*self, *event) else {
2453 return None;
2454 };
2455 if successor_boundary < resulting_floor
2456 || !strict_edge_matches_boundary(edge, successor_boundary)
2457 {
2458 return None;
2459 }
2460 Some(ProjectionCompactionSuccessor {
2461 predecessor: StoredEdge::PhysicalCompaction(*self),
2462 event: *event,
2463 use_kind: SuccessorUse::PhysicalCompletion,
2464 state: ClosureState::Owed { debt, edge },
2465 })
2466 }
2467
2468 pub fn complete(
2475 self,
2476 debt: ClosureDebt,
2477 event: Event,
2478 successor: ProjectionCompactionSuccessor,
2479 ) -> Result<ClosureState, ClosureState> {
2480 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2481 if successor.predecessor == StoredEdge::PhysicalCompaction(self)
2482 && successor.event == event
2483 && successor.use_kind == SuccessorUse::PhysicalCompletion
2484 && physical_completion_floor(self, event).is_some()
2485 {
2486 Ok(successor.state)
2487 } else {
2488 Err(original)
2489 }
2490 }
2491
2492 pub const fn marker_appended(
2500 self,
2501 debt: ClosureDebt,
2502 event: Event,
2503 ) -> Result<ClosureState, ClosureState> {
2504 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2505 let EventKind::MarkerAppended {
2506 marker_delivery_seq,
2507 resulting_projection_through,
2508 } = event.0
2509 else {
2510 return Err(original);
2511 };
2512 if marker_delivery_seq <= self.through_seq
2513 || resulting_projection_through < marker_delivery_seq
2514 {
2515 return Err(original);
2516 }
2517 Ok(original)
2518 }
2519
2520 pub const fn preserve_progress(
2528 self,
2529 debt: ClosureDebt,
2530 event: Event,
2531 resulting_debt: ClosureDebt,
2532 ) -> Result<ClosureState, ClosureState> {
2533 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2534 let Some(resulting_floor) = progress_event_floor(event) else {
2535 return Err(original);
2536 };
2537 if resulting_floor > self.through_seq {
2538 return Err(original);
2539 }
2540 Ok(owed(resulting_debt, StoredEdge::PhysicalCompaction(self)))
2541 }
2542
2543 #[must_use]
2545 pub const fn clear_after_progress(
2546 &self,
2547 event: &Event,
2548 ) -> Option<ProjectionCompactionSuccessor> {
2549 let Some(resulting_floor) = progress_event_floor(*event) else {
2550 return None;
2551 };
2552 if resulting_floor <= self.through_seq {
2553 return None;
2554 }
2555 Some(ProjectionCompactionSuccessor {
2556 predecessor: StoredEdge::PhysicalCompaction(*self),
2557 event: *event,
2558 use_kind: SuccessorUse::PhysicalCover,
2559 state: ClosureState::Clear,
2560 })
2561 }
2562
2563 #[must_use]
2565 pub const fn strict_after_progress(
2566 &self,
2567 event: &Event,
2568 debt: ClosureDebt,
2569 edge: StoredEdge,
2570 successor_boundary: DeliverySeq,
2571 ) -> Option<ProjectionCompactionSuccessor> {
2572 let Some(resulting_floor) = progress_event_floor(*event) else {
2573 return None;
2574 };
2575 if resulting_floor <= self.through_seq
2576 || successor_boundary < resulting_floor
2577 || !strict_edge_matches_boundary(edge, successor_boundary)
2578 {
2579 return None;
2580 }
2581 Some(ProjectionCompactionSuccessor {
2582 predecessor: StoredEdge::PhysicalCompaction(*self),
2583 event: *event,
2584 use_kind: SuccessorUse::PhysicalCover,
2585 state: ClosureState::Owed { debt, edge },
2586 })
2587 }
2588
2589 pub fn covered_by_progress(
2596 self,
2597 debt: ClosureDebt,
2598 event: Event,
2599 successor: ProjectionCompactionSuccessor,
2600 ) -> Result<ClosureState, ClosureState> {
2601 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2602 if successor.predecessor == StoredEdge::PhysicalCompaction(self)
2603 && successor.event == event
2604 && successor.use_kind == SuccessorUse::PhysicalCover
2605 {
2606 Ok(successor.state)
2607 } else {
2608 Err(original)
2609 }
2610 }
2611
2612 #[must_use]
2614 pub const fn unchanged(self, debt: ClosureDebt) -> ClosureState {
2615 owed(debt, StoredEdge::PhysicalCompaction(self))
2616 }
2617
2618 pub const fn charged_binding_change_preserving(
2625 self,
2626 debt: ClosureDebt,
2627 episode_churn_used: u64,
2628 delta_cycles: u64,
2629 episode_churn_limit: u64,
2630 resulting_floor: DeliverySeq,
2631 resulting_debt: ClosureDebt,
2632 ) -> Result<ClosureState, (ClosureState, DetachedAttachRefusal)> {
2633 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2634 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2635 return Err((original, DetachedAttachRefusal::EpisodeChurnLimit));
2636 }
2637 if resulting_floor > self.through_seq {
2638 return Err((original, DetachedAttachRefusal::StaleAuthority));
2639 }
2640 Ok(owed(resulting_debt, StoredEdge::PhysicalCompaction(self)))
2641 }
2642
2643 #[allow(clippy::too_many_arguments)]
2650 pub const fn charged_binding_change_covering(
2651 self,
2652 debt: ClosureDebt,
2653 episode_churn_used: u64,
2654 delta_cycles: u64,
2655 episode_churn_limit: u64,
2656 resulting_floor: DeliverySeq,
2657 resulting_debt: ClosureDebt,
2658 edge: StoredEdge,
2659 successor_boundary: DeliverySeq,
2660 ) -> Result<ClosureState, (ClosureState, DetachedAttachRefusal)> {
2661 let original = owed(debt, StoredEdge::PhysicalCompaction(self));
2662 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2663 return Err((original, DetachedAttachRefusal::EpisodeChurnLimit));
2664 }
2665 if resulting_floor <= self.through_seq
2666 || successor_boundary < resulting_floor
2667 || !strict_edge_matches_boundary(edge, successor_boundary)
2668 {
2669 return Err((original, DetachedAttachRefusal::StaleAuthority));
2670 }
2671 Ok(owed(resulting_debt, edge))
2672 }
2673}
2674
2675impl MarkerDelivery {
2676 pub fn delivered_progress(self, event: Event) -> Result<ParticipantCursorProgress, Self> {
2689 let EventKind::MarkerDelivered {
2690 participant_id,
2691 binding_epoch,
2692 marker_delivery_seq,
2693 } = event.0
2694 else {
2695 return Err(self);
2696 };
2697 if participant_id != self.participant_id
2698 || binding_epoch != self.binding_epoch
2699 || marker_delivery_seq != self.marker_delivery_seq
2700 {
2701 return Err(self);
2702 }
2703 Ok(ParticipantCursorProgress::Marker(CursorProgressMarker {
2704 conversation_id: self.conversation_id,
2705 participant_id,
2706 binding_epoch,
2707 through_seq: marker_delivery_seq,
2708 marker_delivery_seq,
2709 }))
2710 }
2711
2712 pub fn delivered(self, debt: ClosureDebt, event: Event) -> Result<ClosureState, ClosureState> {
2722 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2723 let Ok(progress) = self.delivered_progress(event) else {
2724 return Err(original);
2725 };
2726 Ok(owed(debt, StoredEdge::ParticipantCursorProgress(progress)))
2727 }
2728
2729 pub const fn lower_progress(
2737 self,
2738 debt: ClosureDebt,
2739 event: Event,
2740 resulting_debt: Option<ClosureDebt>,
2741 ) -> Result<ClosureState, ClosureState> {
2742 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2743 let is_lower = match event.0 {
2744 EventKind::CursorProgressed {
2745 progress: CursorProgressEvent::Normal { through_seq, .. },
2746 ..
2747 }
2748 | EventKind::ProjectionCompleted { through_seq } => {
2749 through_seq < self.marker_delivery_seq
2750 }
2751 EventKind::CompactionCompleted {
2752 through_seq,
2753 resulting_floor,
2754 ..
2755 } => {
2756 through_seq < self.marker_delivery_seq
2757 && resulting_floor <= self.marker_delivery_seq
2758 }
2759 _ => false,
2760 };
2761 if !is_lower {
2762 return Err(original);
2763 }
2764 Ok(preserve_or_clear(
2765 resulting_debt,
2766 StoredEdge::MarkerDelivery(self),
2767 ))
2768 }
2769
2770 pub fn binding_fate(
2784 self,
2785 debt: ClosureDebt,
2786 terminal: super::storage::RestoredBindingFateTerminal,
2787 event: Event,
2788 ) -> Result<ClosureState, ClosureState> {
2789 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2790 let EventKind::BindingFateObserved {
2791 participant_id,
2792 binding_epoch,
2793 ..
2794 } = event.0
2795 else {
2796 return Err(original);
2797 };
2798 if participant_id != self.participant_id || binding_epoch != self.binding_epoch {
2799 return Err(original);
2800 }
2801 if terminal.participant_id() != self.participant_id
2802 || terminal.binding_epoch() != self.binding_epoch
2803 || terminal.conversation_id() != self.conversation_id
2804 {
2805 return Err(original);
2806 }
2807 Ok(owed(
2808 debt,
2809 StoredEdge::DetachedMarkerRelease(DetachedMarkerRelease {
2810 conversation_id: self.conversation_id,
2811 participant_id,
2812 marker_delivery_seq: self.marker_delivery_seq,
2813 last_dead_binding_epoch: binding_epoch,
2814 provenance: DetachedMarkerReleaseProvenance::Fate,
2815 }),
2816 ))
2817 }
2818
2819 pub const fn retarget(
2826 self,
2827 new_binding_epoch: BindingEpoch,
2828 episode_churn_used: u64,
2829 delta_cycles: u64,
2830 episode_churn_limit: u64,
2831 ) -> Result<Self, (Self, DetachedAttachRefusal)> {
2832 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
2833 return Err((self, DetachedAttachRefusal::EpisodeChurnLimit));
2834 }
2835 if !is_next_generation(self.binding_epoch, new_binding_epoch) {
2836 return Err((self, DetachedAttachRefusal::StaleAuthority));
2837 }
2838 Ok(Self {
2839 binding_epoch: new_binding_epoch,
2840 ..self
2841 })
2842 }
2843
2844 pub fn leave(
2851 self,
2852 debt: ClosureDebt,
2853 event: Event,
2854 successor: DebtCompletion,
2855 ) -> Result<ClosureState, ClosureState> {
2856 let original = owed(debt, StoredEdge::MarkerDelivery(self));
2857 let EventKind::LeaveCommitted {
2858 participant_id,
2859 authority: LeaveAuthority::Live(binding_epoch),
2860 ..
2861 } = event.0
2862 else {
2863 return Err(original);
2864 };
2865 if participant_id != self.participant_id || binding_epoch != self.binding_epoch {
2866 return Err(original);
2867 }
2868 Ok(successor.into_state())
2869 }
2870}
2871
2872impl ParticipantCursorProgress {
2873 pub fn complete_ack(
2881 self,
2882 debt: ClosureDebt,
2883 event: Event,
2884 successor: DebtCompletion,
2885 ) -> Result<ClosureState, ClosureState> {
2886 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
2887 let exact = match (self, event.0) {
2888 (
2889 Self::Continuous(value),
2890 EventKind::CursorProgressed {
2891 participant_id,
2892 binding_epoch,
2893 progress: CursorProgressEvent::Normal { through_seq, .. },
2894 ..
2895 },
2896 ) => {
2897 participant_id == value.participant_id
2898 && binding_epoch == value.binding_epoch
2899 && through_seq == value.through_seq
2900 }
2901 (
2902 Self::Marker(value),
2903 EventKind::CursorProgressed {
2904 participant_id,
2905 binding_epoch,
2906 progress: CursorProgressEvent::Normal { through_seq, .. },
2907 ..
2908 },
2909 ) => {
2910 participant_id == value.participant_id
2911 && binding_epoch == value.binding_epoch
2912 && through_seq == value.through_seq
2913 }
2914 (
2915 Self::Marker(value),
2916 EventKind::CursorProgressed {
2917 participant_id,
2918 binding_epoch,
2919 progress:
2920 CursorProgressEvent::Marker {
2921 marker_delivery_seq,
2922 },
2923 ..
2924 },
2925 ) => {
2926 participant_id == value.participant_id
2927 && binding_epoch == value.binding_epoch
2928 && marker_delivery_seq == value.marker_delivery_seq
2929 }
2930 _ => false,
2931 };
2932 if exact {
2933 Ok(successor.into_state())
2934 } else {
2935 Err(original)
2936 }
2937 }
2938
2939 pub fn lesser_ack(
2946 self,
2947 debt: ClosureDebt,
2948 event: Event,
2949 resulting_debt: ClosureDebt,
2950 ) -> Result<ClosureState, ClosureState> {
2951 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
2952 let EventKind::CursorProgressed {
2953 participant_id,
2954 binding_epoch,
2955 progress: CursorProgressEvent::Normal { through_seq, .. },
2956 ..
2957 } = event.0
2958 else {
2959 return Err(original);
2960 };
2961 if participant_id != self.participant_id()
2962 || binding_epoch != self.binding_epoch()
2963 || through_seq >= self.through_seq()
2964 {
2965 return Err(original);
2966 }
2967 Ok(owed(
2968 resulting_debt,
2969 StoredEdge::ParticipantCursorProgress(self),
2970 ))
2971 }
2972
2973 #[must_use]
2975 pub fn clear_after_greater_ack(&self, event: &Event) -> Option<ProjectionCompactionSuccessor> {
2976 if !greater_ack_matches(*self, *event) {
2977 return None;
2978 }
2979 Some(ProjectionCompactionSuccessor {
2980 predecessor: StoredEdge::ParticipantCursorProgress(*self),
2981 event: *event,
2982 use_kind: SuccessorUse::CursorGreaterAck,
2983 state: ClosureState::Clear,
2984 })
2985 }
2986
2987 #[must_use]
2989 pub fn strict_after_greater_ack(
2990 &self,
2991 event: &Event,
2992 debt: ClosureDebt,
2993 edge: StoredEdge,
2994 successor_boundary: DeliverySeq,
2995 ) -> Option<ProjectionCompactionSuccessor> {
2996 if !greater_ack_matches(*self, *event)
2997 || successor_boundary <= cursor_event_boundary(*event)
2998 || !strict_edge_matches_boundary(edge, successor_boundary)
2999 {
3000 return None;
3001 }
3002 Some(ProjectionCompactionSuccessor {
3003 predecessor: StoredEdge::ParticipantCursorProgress(*self),
3004 event: *event,
3005 use_kind: SuccessorUse::CursorGreaterAck,
3006 state: ClosureState::Owed { debt, edge },
3007 })
3008 }
3009
3010 pub fn greater_ack(
3017 self,
3018 debt: ClosureDebt,
3019 event: Event,
3020 successor: ProjectionCompactionSuccessor,
3021 ) -> Result<ClosureState, ClosureState> {
3022 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
3023 if successor.predecessor == StoredEdge::ParticipantCursorProgress(self)
3024 && successor.event == event
3025 && successor.use_kind == SuccessorUse::CursorGreaterAck
3026 && greater_ack_matches(self, event)
3027 {
3028 Ok(successor.state)
3029 } else {
3030 Err(original)
3031 }
3032 }
3033
3034 #[must_use]
3036 pub const fn unchanged(self, debt: ClosureDebt) -> ClosureState {
3037 owed(debt, StoredEdge::ParticipantCursorProgress(self))
3038 }
3039
3040 pub const fn storage_progress(
3049 self,
3050 debt: ClosureDebt,
3051 event: Event,
3052 resulting_debt: Option<ClosureDebt>,
3053 ) -> Result<ClosureState, ClosureState> {
3054 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
3055 let valid = match event.0 {
3056 EventKind::ProjectionCompleted { .. } => true,
3057 EventKind::CompactionCompleted {
3058 through_seq,
3059 resulting_floor,
3060 ..
3061 } => match self.marker_delivery_seq() {
3062 None => true,
3063 Some(marker) => through_seq < marker && resulting_floor <= marker,
3064 },
3065 _ => false,
3066 };
3067 if !valid {
3068 return Err(original);
3069 }
3070 Ok(preserve_or_clear(
3071 resulting_debt,
3072 StoredEdge::ParticipantCursorProgress(self),
3073 ))
3074 }
3075
3076 pub fn binding_fate(
3088 self,
3089 debt: ClosureDebt,
3090 terminal: super::storage::RestoredBindingFateTerminal,
3091 event: Event,
3092 ) -> Result<CursorFateSuccessor, ClosureState> {
3093 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
3094 let Self::Marker(value) = self else {
3095 return Err(original);
3096 };
3097 let EventKind::BindingFateObserved {
3098 participant_id,
3099 binding_epoch,
3100 resulting_floor,
3101 ..
3102 } = event.0
3103 else {
3104 return Err(original);
3105 };
3106 if participant_id != value.participant_id || binding_epoch != value.binding_epoch {
3107 return Err(original);
3108 }
3109 if terminal.participant_id() != value.participant_id
3110 || terminal.binding_epoch() != value.binding_epoch
3111 || terminal.conversation_id() != value.conversation_id
3112 {
3113 return Err(original);
3114 }
3115 Ok(CursorFateSuccessor::DetachedCredentialRecovery(
3116 DetachedCredentialRecovery {
3117 conversation_id: value.conversation_id,
3118 participant_id: value.participant_id,
3119 marker_delivery_seq: value.marker_delivery_seq,
3120 prior_binding_epoch: value.binding_epoch,
3121 resulting_floor,
3122 },
3123 ))
3124 }
3125
3126 pub const fn retarget(
3133 self,
3134 new_binding_epoch: BindingEpoch,
3135 episode_churn_used: u64,
3136 delta_cycles: u64,
3137 episode_churn_limit: u64,
3138 ) -> Result<Self, (Self, DetachedAttachRefusal)> {
3139 let Self::Continuous(value) = self else {
3140 return Err((self, DetachedAttachRefusal::DeliveredMarkerAwaitingAck));
3141 };
3142 if !charged_churn_fits(episode_churn_used, delta_cycles, episode_churn_limit) {
3143 return Err((self, DetachedAttachRefusal::EpisodeChurnLimit));
3144 }
3145 if !is_next_generation(value.binding_epoch, new_binding_epoch) {
3146 return Err((self, DetachedAttachRefusal::StaleAuthority));
3147 }
3148 Ok(Self::Continuous(CursorProgressContinuous {
3149 binding_epoch: new_binding_epoch,
3150 ..value
3151 }))
3152 }
3153
3154 pub fn leave(
3161 self,
3162 debt: ClosureDebt,
3163 event: Event,
3164 successor: DebtCompletion,
3165 ) -> Result<ClosureState, ClosureState> {
3166 let original = owed(debt, StoredEdge::ParticipantCursorProgress(self));
3167 let EventKind::LeaveCommitted {
3168 participant_id,
3169 authority: LeaveAuthority::Live(binding_epoch),
3170 ..
3171 } = event.0
3172 else {
3173 return Err(original);
3174 };
3175 if participant_id != self.participant_id() || binding_epoch != self.binding_epoch() {
3176 return Err(original);
3177 }
3178 Ok(successor.into_state())
3179 }
3180}
3181
3182pub(super) struct FencedAttachRecordRefusal {
3185 record: ValidatedMarkerRecord,
3186}
3187
3188impl core::fmt::Debug for FencedAttachRecordRefusal {
3189 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
3190 formatter.write_str("FencedAttachRecordRefusal { record: <linear> }")
3191 }
3192}
3193
3194impl FencedAttachRecordRefusal {
3195 pub(super) const fn into_record(self) -> ValidatedMarkerRecord {
3197 self.record
3198 }
3199}
3200
3201impl DetachedCredentialRecovery {
3202 #[must_use]
3204 pub const fn validate_leave_claim(
3205 &self,
3206 participant_id: ParticipantId,
3207 actual_charge: ResourceVector,
3208 remaining_k: ResourceVector,
3209 exit_claims: u64,
3210 ) -> Option<KClaimBackedDetachedLeave> {
3211 validate_detached_claim(
3212 self.participant_id,
3213 DetachedClaimTarget::CredentialRecovery {
3214 marker_delivery_seq: self.marker_delivery_seq,
3215 binding_epoch: self.prior_binding_epoch,
3216 },
3217 participant_id,
3218 actual_charge,
3219 remaining_k,
3220 exit_claims,
3221 )
3222 }
3223
3224 pub(super) fn fenced_attach(
3231 self,
3232 record_authority: ValidatedMarkerRecord,
3233 debt: ClosureDebt,
3234 event: Event,
3235 successor: DebtCompletion,
3236 ) -> Result<FencedAttachCommit, Box<FencedAttachRecordRefusal>> {
3237 let Some(proof) =
3238 FencedAttachCommit::restore_validated(self, &record_authority, debt, event, successor)
3239 else {
3240 return Err(Box::new(FencedAttachRecordRefusal {
3241 record: record_authority,
3242 }));
3243 };
3244 record_authority.consume();
3245 Ok(proof)
3246 }
3247
3248 pub fn detached_leave(
3255 self,
3256 debt: ClosureDebt,
3257 event: Event,
3258 evidence: KClaimBackedDetachedLeave,
3259 successor: DebtCompletion,
3260 ) -> Result<ClosureState, ClosureState> {
3261 let original = owed(debt, StoredEdge::DetachedCredentialRecovery(self));
3262 let EventKind::LeaveCommitted {
3263 participant_id,
3264 authority: LeaveAuthority::Detached,
3265 ..
3266 } = event.0
3267 else {
3268 return Err(original);
3269 };
3270 let target = DetachedClaimTarget::CredentialRecovery {
3271 marker_delivery_seq: self.marker_delivery_seq,
3272 binding_epoch: self.prior_binding_epoch,
3273 };
3274 if participant_id != self.participant_id
3275 || evidence.participant_id != self.participant_id
3276 || evidence.target != target
3277 {
3278 return Err(original);
3279 }
3280 Ok(successor.into_state())
3281 }
3282
3283 #[must_use]
3285 pub const fn ordinary_attach_refusal(self) -> DetachedAttachRefusal {
3286 let _ = self;
3287 DetachedAttachRefusal::RecoveryFence
3288 }
3289
3290 #[must_use]
3292 pub const fn marker_attach_refusal(
3293 self,
3294 presented_marker: DeliverySeq,
3295 ) -> Option<DetachedAttachRefusal> {
3296 if presented_marker == self.marker_delivery_seq {
3297 None
3298 } else {
3299 Some(DetachedAttachRefusal::MarkerMismatch)
3300 }
3301 }
3302
3303 #[must_use]
3305 pub const fn authority_superseded(self) -> (Self, DetachedAttachRefusal) {
3306 (self, DetachedAttachRefusal::StaleAuthority)
3307 }
3308
3309 pub const fn unrelated_event(
3317 self,
3318 debt: ClosureDebt,
3319 event: Event,
3320 resulting_debt: Option<ClosureDebt>,
3321 ) -> Result<ClosureState, ClosureState> {
3322 unrelated_detached_event(
3323 StoredEdge::DetachedCredentialRecovery(self),
3324 self.participant_id,
3325 debt,
3326 event,
3327 resulting_debt,
3328 )
3329 }
3330}
3331
3332mod sealed {
3333 pub trait Sealed {}
3334}
3335
3336pub trait LeaveOnlyEdge: sealed::Sealed + Sized + Copy {
3338 fn participant_id(self) -> ParticipantId;
3340
3341 fn validate_leave_claim(
3344 &self,
3345 participant_id: ParticipantId,
3346 actual_charge: ResourceVector,
3347 remaining_k: ResourceVector,
3348 exit_claims: u64,
3349 ) -> Option<KClaimBackedDetachedLeave>;
3350
3351 fn leave(
3358 self,
3359 debt: ClosureDebt,
3360 event: Event,
3361 evidence: KClaimBackedDetachedLeave,
3362 successor: DebtCompletion,
3363 ) -> Result<ClosureState, ClosureState>;
3364
3365 fn repeat_fate(self, event: Event) -> Result<Self, Self>;
3372
3373 fn authority_superseded(self) -> (Self, DetachedAttachRefusal) {
3375 (self, DetachedAttachRefusal::StaleAuthority)
3376 }
3377
3378 fn binding_required_refusal(self) -> DetachedAttachRefusal {
3380 let _ = self;
3381 DetachedAttachRefusal::NoBinding
3382 }
3383
3384 fn unrelated_event(
3392 self,
3393 debt: ClosureDebt,
3394 event: Event,
3395 resulting_debt: Option<ClosureDebt>,
3396 ) -> Result<ClosureState, ClosureState>;
3397}
3398
3399impl sealed::Sealed for DetachedMarkerRelease {}
3400impl sealed::Sealed for DetachedCursorRelease {}
3401
3402impl LeaveOnlyEdge for DetachedMarkerRelease {
3403 fn participant_id(self) -> ParticipantId {
3404 self.participant_id
3405 }
3406
3407 fn validate_leave_claim(
3408 &self,
3409 participant_id: ParticipantId,
3410 actual_charge: ResourceVector,
3411 remaining_k: ResourceVector,
3412 exit_claims: u64,
3413 ) -> Option<KClaimBackedDetachedLeave> {
3414 validate_detached_claim(
3415 self.participant_id,
3416 DetachedClaimTarget::MarkerRelease {
3417 marker_delivery_seq: self.marker_delivery_seq,
3418 binding_epoch: self.last_dead_binding_epoch,
3419 },
3420 participant_id,
3421 actual_charge,
3422 remaining_k,
3423 exit_claims,
3424 )
3425 }
3426
3427 fn leave(
3428 self,
3429 debt: ClosureDebt,
3430 event: Event,
3431 evidence: KClaimBackedDetachedLeave,
3432 successor: DebtCompletion,
3433 ) -> Result<ClosureState, ClosureState> {
3434 let original = owed(debt, StoredEdge::DetachedMarkerRelease(self));
3435 let EventKind::LeaveCommitted {
3436 participant_id,
3437 authority: LeaveAuthority::Detached,
3438 ..
3439 } = event.0
3440 else {
3441 return Err(original);
3442 };
3443 let target = DetachedClaimTarget::MarkerRelease {
3444 marker_delivery_seq: self.marker_delivery_seq,
3445 binding_epoch: self.last_dead_binding_epoch,
3446 };
3447 if participant_id != self.participant_id
3448 || evidence.participant_id != self.participant_id
3449 || evidence.target != target
3450 {
3451 return Err(original);
3452 }
3453 Ok(successor.into_state())
3454 }
3455
3456 fn repeat_fate(self, event: Event) -> Result<Self, Self> {
3457 let EventKind::BindingFateObserved {
3458 participant_id,
3459 binding_epoch,
3460 ..
3461 } = event.0
3462 else {
3463 return Err(self);
3464 };
3465 if participant_id == self.participant_id && binding_epoch == self.last_dead_binding_epoch {
3466 Ok(self)
3467 } else {
3468 Err(self)
3469 }
3470 }
3471
3472 fn unrelated_event(
3473 self,
3474 debt: ClosureDebt,
3475 event: Event,
3476 resulting_debt: Option<ClosureDebt>,
3477 ) -> Result<ClosureState, ClosureState> {
3478 unrelated_detached_event(
3479 StoredEdge::DetachedMarkerRelease(self),
3480 self.participant_id,
3481 debt,
3482 event,
3483 resulting_debt,
3484 )
3485 }
3486}
3487
3488impl DetachedMarkerRelease {
3489 #[must_use]
3491 pub const fn ordinary_attach_refusal(self) -> DetachedAttachRefusal {
3492 let _ = self;
3493 DetachedAttachRefusal::RecoveryFence
3494 }
3495
3496 #[must_use]
3499 pub const fn marker_attach_refusal(
3500 self,
3501 presented_marker: DeliverySeq,
3502 ) -> DetachedAttachRefusal {
3503 if presented_marker == self.marker_delivery_seq {
3504 DetachedAttachRefusal::MarkerNotDelivered
3505 } else {
3506 DetachedAttachRefusal::MarkerMismatch
3507 }
3508 }
3509}
3510
3511impl LeaveOnlyEdge for DetachedCursorRelease {
3512 fn participant_id(self) -> ParticipantId {
3513 self.participant_id
3514 }
3515
3516 fn validate_leave_claim(
3517 &self,
3518 participant_id: ParticipantId,
3519 actual_charge: ResourceVector,
3520 remaining_k: ResourceVector,
3521 exit_claims: u64,
3522 ) -> Option<KClaimBackedDetachedLeave> {
3523 validate_detached_claim(
3524 self.participant_id,
3525 DetachedClaimTarget::CursorRelease {
3526 binding_epoch: self.last_dead_binding_epoch,
3527 },
3528 participant_id,
3529 actual_charge,
3530 remaining_k,
3531 exit_claims,
3532 )
3533 }
3534
3535 fn leave(
3536 self,
3537 debt: ClosureDebt,
3538 event: Event,
3539 evidence: KClaimBackedDetachedLeave,
3540 successor: DebtCompletion,
3541 ) -> Result<ClosureState, ClosureState> {
3542 let original = owed(debt, StoredEdge::DetachedCursorRelease(self));
3543 let EventKind::LeaveCommitted {
3544 participant_id,
3545 authority: LeaveAuthority::Detached,
3546 ..
3547 } = event.0
3548 else {
3549 return Err(original);
3550 };
3551 let target = DetachedClaimTarget::CursorRelease {
3552 binding_epoch: self.last_dead_binding_epoch,
3553 };
3554 if participant_id != self.participant_id
3555 || evidence.participant_id != self.participant_id
3556 || evidence.target != target
3557 {
3558 return Err(original);
3559 }
3560 Ok(successor.into_state())
3561 }
3562
3563 fn repeat_fate(self, event: Event) -> Result<Self, Self> {
3564 let EventKind::BindingFateObserved {
3565 participant_id,
3566 binding_epoch,
3567 ..
3568 } = event.0
3569 else {
3570 return Err(self);
3571 };
3572 if participant_id == self.participant_id && binding_epoch == self.last_dead_binding_epoch {
3573 Ok(self)
3574 } else {
3575 Err(self)
3576 }
3577 }
3578
3579 fn unrelated_event(
3580 self,
3581 debt: ClosureDebt,
3582 event: Event,
3583 resulting_debt: Option<ClosureDebt>,
3584 ) -> Result<ClosureState, ClosureState> {
3585 unrelated_detached_event(
3586 StoredEdge::DetachedCursorRelease(self),
3587 self.participant_id,
3588 debt,
3589 event,
3590 resulting_debt,
3591 )
3592 }
3593}
3594
3595impl DetachedCursorRelease {
3596 #[must_use]
3598 pub const fn ordinary_attach_refusal(self) -> DetachedAttachRefusal {
3599 let _ = self;
3600 DetachedAttachRefusal::RecoveryFence
3601 }
3602
3603 #[must_use]
3605 pub const fn marker_attach_refusal(self) -> DetachedAttachRefusal {
3606 let _ = self;
3607 DetachedAttachRefusal::MarkerMismatch
3608 }
3609}
3610
3611const fn owed(debt: ClosureDebt, edge: StoredEdge) -> ClosureState {
3612 ClosureState::Owed { debt, edge }
3613}
3614
3615const fn preserve_or_clear(resulting_debt: Option<ClosureDebt>, edge: StoredEdge) -> ClosureState {
3616 match resulting_debt {
3617 Some(debt) => owed(debt, edge),
3618 None => ClosureState::Clear,
3619 }
3620}
3621
3622const fn projection_completion_boundary(
3623 edge: ObserverProjection,
3624 event: Event,
3625) -> Option<DeliverySeq> {
3626 let EventKind::ProjectionCompleted { through_seq } = event.0 else {
3627 return None;
3628 };
3629 if through_seq == edge.through_seq {
3630 Some(through_seq)
3631 } else {
3632 None
3633 }
3634}
3635
3636const fn physical_completion_floor(edge: PhysicalCompaction, event: Event) -> Option<DeliverySeq> {
3637 match event.0 {
3638 EventKind::CompactionCompleted {
3639 from_floor,
3640 through_seq,
3641 resulting_floor,
3642 } if from_floor == edge.from_floor
3643 && through_seq == edge.through_seq
3644 && resulting_floor > edge.through_seq =>
3645 {
3646 Some(resulting_floor)
3647 }
3648 _ => None,
3649 }
3650}
3651
3652const fn progress_event_floor(event: Event) -> Option<DeliverySeq> {
3653 match event.0 {
3654 EventKind::CursorProgressed {
3655 resulting_floor, ..
3656 }
3657 | EventKind::BindingFateObserved {
3658 resulting_floor, ..
3659 }
3660 | EventKind::LeaveCommitted {
3661 resulting_floor, ..
3662 } => Some(resulting_floor),
3663 _ => None,
3664 }
3665}
3666
3667const fn cursor_event_boundary(event: Event) -> DeliverySeq {
3668 match event.0 {
3669 EventKind::CursorProgressed {
3670 progress: CursorProgressEvent::Normal { through_seq, .. },
3671 ..
3672 } => through_seq,
3673 _ => 0,
3674 }
3675}
3676
3677fn greater_ack_matches(edge: ParticipantCursorProgress, event: Event) -> bool {
3678 let EventKind::CursorProgressed {
3679 participant_id,
3680 binding_epoch,
3681 progress:
3682 CursorProgressEvent::Normal {
3683 previous_cursor,
3684 through_seq,
3685 },
3686 ..
3687 } = event.0
3688 else {
3689 return false;
3690 };
3691 participant_id == edge.participant_id()
3692 && binding_epoch == edge.binding_epoch()
3693 && previous_cursor < edge.through_seq()
3694 && through_seq > edge.through_seq()
3695}
3696
3697const fn strict_edge_matches_boundary(edge: StoredEdge, boundary: DeliverySeq) -> bool {
3698 match edge {
3699 StoredEdge::ObserverProjection(value) => value.through_seq == boundary,
3700 StoredEdge::PhysicalCompaction(value) => value.through_seq == boundary,
3701 StoredEdge::MarkerDelivery(value) => value.marker_delivery_seq == boundary,
3702 StoredEdge::ParticipantCursorProgress(value) => value.through_seq() == boundary,
3703 StoredEdge::DetachedCredentialRecovery(_) => false,
3704 StoredEdge::DetachedMarkerRelease(value) => value.marker_delivery_seq == boundary,
3705 StoredEdge::DetachedCursorRelease(_) => true,
3709 }
3710}
3711
3712const fn charged_churn_fits(used: u64, delta: u64, limit: u64) -> bool {
3713 delta > 0 && widen_u64(used) + widen_u64(delta) <= widen_u64(limit)
3714}
3715
3716#[allow(clippy::cast_lossless)]
3717const fn widen_u64(value: u64) -> u128 {
3718 value as u128
3719}
3720
3721const fn is_next_generation(old: BindingEpoch, new: BindingEpoch) -> bool {
3722 match old.capability_generation.get().checked_add(1) {
3723 Some(expected) => new.capability_generation.get() == expected,
3724 None => false,
3725 }
3726}
3727
3728const fn validate_detached_claim(
3729 owner: ParticipantId,
3730 target: DetachedClaimTarget,
3731 participant_id: ParticipantId,
3732 actual_charge: ResourceVector,
3733 remaining_k: ResourceVector,
3734 exit_claims: u64,
3735) -> Option<KClaimBackedDetachedLeave> {
3736 if participant_id != owner
3737 || actual_charge.entries == 0
3738 || actual_charge.bytes == 0
3739 || actual_charge.entries > remaining_k.entries
3740 || actual_charge.bytes > remaining_k.bytes
3741 || exit_claims == 0
3742 {
3743 return None;
3744 }
3745 Some(KClaimBackedDetachedLeave {
3746 participant_id,
3747 target,
3748 actual_charge,
3749 })
3750}
3751
3752const fn event_participant(event: Event) -> Option<ParticipantId> {
3753 match event.0 {
3754 EventKind::MarkerDelivered { participant_id, .. }
3755 | EventKind::CursorProgressed { participant_id, .. }
3756 | EventKind::BindingFateObserved { participant_id, .. }
3757 | EventKind::LeaveCommitted { participant_id, .. }
3758 | EventKind::FencedRecoveryCommitted { participant_id, .. } => Some(participant_id),
3759 EventKind::ProjectionCompleted { .. }
3760 | EventKind::CompactionCompleted { .. }
3761 | EventKind::MarkerAppended { .. } => None,
3762 }
3763}
3764
3765const fn unrelated_detached_event(
3766 edge: StoredEdge,
3767 owner: ParticipantId,
3768 debt: ClosureDebt,
3769 event: Event,
3770 resulting_debt: Option<ClosureDebt>,
3771) -> Result<ClosureState, ClosureState> {
3772 let original = owed(debt, edge);
3773 let Some(participant_id) = event_participant(event) else {
3774 return Err(original);
3775 };
3776 if participant_id == owner {
3777 return Err(original);
3778 }
3779 Ok(preserve_or_clear(resulting_debt, edge))
3780}