1use alloc::vec::Vec;
9
10use crate::algebra::WideResourceVector;
11use crate::outcome::ParticipantStateCorruptReason;
12use crate::wire::{
13 AttachSecret, BindingEpoch, CloseCause, ConversationId, DeliverySeq, DetachAttemptToken,
14 Generation, LeaveAttemptToken, LeaveCommitted, ObserverEpoch, ParticipantId, TransactionOrder,
15};
16
17use super::{
18 ActiveBinding, AdmissionOrder, BindingOrigin, BindingState, BoundParticipantCursor,
19 ClaimFrontiers, ClaimFrontiersRestore, ClosureDebt, ClosureState, CommittedBindingTerminal,
20 DebtCompletion, DetachCell, DetachedCredentialRecovery, DetachedMarkerRelease,
21 DetachedMarkerReleaseProvenance, EmptyDetach, EnrollmentFingerprint, Event, FencedAttachCommit,
22 FrontierBinding, IdentityState, LeaveFingerprint, LiveMember, LiveMemberRestore,
23 MarkerDelivery, NonzeroDebtCursorEpisode, ObserverProjection, OrderLedger,
24 OrdinaryBindingAuthority, OrdinaryBindingFate, ParticipantCursorProgress, PendingFinalization,
25 PendingRecoveredCursorRelease, PhysicalCompaction, RecoveredBindingFate,
26 RecoveredBindingFateTransition, RetiredIdentity, SequenceLedger, StoredEdge,
27 binding::{restore_committed_terminal, restore_pending_finalization},
28 claim_frontier::{
29 ClaimFrontiersDescription, HistoricalCausalAuthority, MarkerRecordOccurrence,
30 MarkerRecordRequest, ValidatedConversationHistory, ValidatedMarkerRecord,
31 },
32 cursor_facts::{CursorProgressFact, CursorProgressKey},
33 detach::{
34 restore_committed_detach, restore_pending_detach, restore_terminalized_detach,
35 validate_pending_pair,
36 },
37};
38
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum StorageRestoreError {
42 CommittedBindingTerminal,
44 PendingFinalization,
46 BindingAuthority,
48 MembershipInvariant,
50 LeaveResult,
52 RetiredIdentity,
54 DetachCell,
56 DetachBindingPair,
58 CursorEpisode,
60 ClosureDebt,
62 StoredEdgeProvenance,
64 FencedMarkerLatch,
70}
71
72#[derive(Clone, Debug, PartialEq, Eq)]
74pub enum ConversationStateRestoreError {
75 ClaimFrontier(ParticipantStateCorruptReason),
77 Storage(StorageRestoreError),
79}
80
81#[derive(Debug, PartialEq, Eq)]
88pub(super) struct RestoredConversationState {
89 frontiers: ClaimFrontiers,
90 closure: ClosureState,
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
107pub struct ParticipantConversationRestore<EF, V, LF, D> {
108 pub participants: Vec<ParticipantLifecycleRestore<EF, V, LF, D>>,
110 pub frontiers: ClaimFrontiersRestore,
112 pub sequence_ledger: SequenceLedger,
114 pub order_ledger: OrderLedger,
116 pub closure: ClosureStateRestore,
118}
119
120#[derive(Debug, PartialEq, Eq)]
139pub struct ParticipantConversationState<EF, V, LF, D> {
140 participants: Vec<RestoredParticipantLifecycle<EF, V, LF, D>>,
141 frontiers: ClaimFrontiers,
142 closure: ClosureState,
143}
144
145impl<EF, V, LF, D> ParticipantConversationState<EF, V, LF, D> {
146 #[must_use]
148 pub fn participants(&self) -> &[RestoredParticipantLifecycle<EF, V, LF, D>] {
149 &self.participants
150 }
151
152 #[must_use]
154 pub const fn frontiers(&self) -> &ClaimFrontiers {
155 &self.frontiers
156 }
157
158 #[must_use]
160 pub const fn closure(&self) -> ClosureState {
161 self.closure
162 }
163
164 #[must_use]
171 #[allow(clippy::type_complexity)]
172 pub fn into_parts(
173 self,
174 ) -> (
175 Vec<RestoredParticipantLifecycle<EF, V, LF, D>>,
176 ClaimFrontiers,
177 ClosureState,
178 ) {
179 (self.participants, self.frontiers, self.closure)
180 }
181}
182
183impl<EF: Clone, V: Clone, LF: Clone, D: Clone> ParticipantConversationState<EF, V, LF, D> {
184 #[cfg(test)]
192 pub(super) fn describe(
193 &self,
194 retained_record_limit: u64,
195 ) -> Result<ParticipantConversationDescription<EF, V, LF, D>, ConversationDescribeError> {
196 let views: Vec<_> = self
197 .participants
198 .iter()
199 .map(RestoredParticipantLifecycle::view)
200 .collect();
201 describe_conversation(&views, &self.frontiers, retained_record_limit, self.closure)
202 }
203}
204
205impl RestoredConversationState {
206 #[must_use]
208 #[cfg(test)]
209 pub(crate) const fn frontiers(&self) -> &ClaimFrontiers {
210 &self.frontiers
211 }
212
213 #[must_use]
215 #[cfg(test)]
216 pub(crate) const fn closure(&self) -> ClosureState {
217 self.closure
218 }
219
220 #[must_use]
222 pub(crate) fn into_parts(self) -> (ClaimFrontiers, ClosureState) {
223 (self.frontiers, self.closure)
224 }
225}
226
227#[cfg(test)]
245pub(super) fn restore_conversation_state(
246 frontier_restore: ClaimFrontiersRestore,
247 sequence_ledger: SequenceLedger,
248 order_ledger: OrderLedger,
249 closure_restore: &ClosureStateRestore,
250) -> Result<RestoredConversationState, ConversationStateRestoreError> {
251 let history = ValidatedConversationHistory::empty();
252 restore_conversation_with_history(
253 ClaimFrontiersDescription {
254 restore: frontier_restore,
255 sequence_ledger,
256 order_ledger,
257 fenced_marker_issued: None,
258 },
259 closure_restore,
260 &history,
261 )
262}
263
264fn restore_conversation_with_history(
272 frontier_description: ClaimFrontiersDescription,
273 closure_restore: &ClosureStateRestore,
274 history: &ValidatedConversationHistory,
275) -> Result<RestoredConversationState, ConversationStateRestoreError> {
276 let mut prevalidated = ClaimFrontiers::prevalidate_description(frontier_description, history)
277 .map_err(ConversationStateRestoreError::ClaimFrontier)?;
278 let marker_request = closure_restore.marker_record_request();
279 let ordinary_request = closure_restore.ordinary_binding_request();
280 let closure = match (marker_request, ordinary_request) {
281 (Some(marker_request), None) => {
282 let record = prevalidated.take_marker_record(marker_request).ok_or(
283 ConversationStateRestoreError::Storage(StorageRestoreError::StoredEdgeProvenance),
284 )?;
285 (*closure_restore)
286 .restore_with_marker_record(prevalidated.conversation_id(), record)
287 .map_err(ConversationStateRestoreError::Storage)?
288 }
289 (None, Some(binding)) => {
290 let origin = history
291 .ordinary_origin(
292 binding.conversation_id,
293 binding.participant_id,
294 binding.binding_epoch,
295 )
296 .ok_or(ConversationStateRestoreError::Storage(
297 StorageRestoreError::StoredEdgeProvenance,
298 ))?;
299 (*closure_restore)
300 .restore_with_binding_origin(origin)
301 .map_err(ConversationStateRestoreError::Storage)?
302 }
303 (None, None) => (*closure_restore)
304 .restore()
305 .map_err(ConversationStateRestoreError::Storage)?,
306 (Some(_), Some(_)) => {
307 return Err(ConversationStateRestoreError::Storage(
308 StorageRestoreError::StoredEdgeProvenance,
309 ));
310 }
311 };
312 let current_edge = match closure {
313 ClosureState::Clear => None,
314 ClosureState::Owed { edge, .. } => Some(edge),
315 };
316 let frontiers = prevalidated
317 .finish(current_edge)
318 .map_err(ConversationStateRestoreError::ClaimFrontier)?;
319 if !frontiers.fenced_latch_matches(current_edge) {
320 return Err(ConversationStateRestoreError::Storage(
321 StorageRestoreError::FencedMarkerLatch,
322 ));
323 }
324 Ok(RestoredConversationState { frontiers, closure })
325}
326
327#[derive(Clone, Copy, Debug, PartialEq, Eq)]
329pub struct CommittedBindingTerminalRestore {
330 pub binding: ActiveBinding,
332 pub cause: CloseCause,
334 pub transaction_order: TransactionOrder,
336 pub delivery_seq: DeliverySeq,
338}
339
340impl CommittedBindingTerminalRestore {
341 pub fn restore(self) -> Result<CommittedBindingTerminal, StorageRestoreError> {
348 restore_committed_terminal(
349 self.binding,
350 self.cause,
351 self.transaction_order,
352 self.delivery_seq,
353 )
354 .ok_or(StorageRestoreError::CommittedBindingTerminal)
355 }
356}
357
358#[derive(Clone, Copy, Debug, PartialEq, Eq)]
360pub struct PendingFinalizationRestore {
361 pub binding: ActiveBinding,
363 pub cause: CloseCause,
365 pub transaction_order: TransactionOrder,
367}
368
369impl PendingFinalizationRestore {
370 pub fn restore(self) -> Result<PendingFinalization, StorageRestoreError> {
377 restore_pending_finalization(self.binding, self.cause, self.transaction_order)
378 .ok_or(StorageRestoreError::PendingFinalization)
379 }
380}
381
382#[derive(Clone, Copy, Debug, PartialEq, Eq)]
384pub enum BindingFateTerminalRestore {
385 Committed(CommittedBindingTerminalRestore),
387 Pending(PendingFinalizationRestore),
389}
390
391#[derive(Clone, Copy, Debug, PartialEq, Eq)]
393pub enum RestoredBindingFateTerminal {
394 Committed(CommittedBindingTerminal),
396 Pending(PendingFinalization),
398}
399
400impl BindingFateTerminalRestore {
401 pub fn restore(self) -> Result<RestoredBindingFateTerminal, StorageRestoreError> {
407 match self {
408 Self::Committed(value) => value.restore().map(RestoredBindingFateTerminal::Committed),
409 Self::Pending(value) => value.restore().map(RestoredBindingFateTerminal::Pending),
410 }
411 }
412}
413
414impl RestoredBindingFateTerminal {
415 #[cfg(any(test, feature = "test-support"))]
438 #[allow(clippy::panic, clippy::expect_used)]
439 #[must_use]
440 pub fn committed_for_marker_progress(
441 progress: super::edge::ParticipantCursorProgress,
442 transaction_order: TransactionOrder,
443 delivery_seq: DeliverySeq,
444 ) -> Self {
445 let super::edge::ParticipantCursorProgress::Marker(marker) = progress else {
446 panic!("a continuous cursor witness names no conversation to build a terminal for")
447 };
448 Self::committed_for_test(
449 marker.conversation_id(),
450 marker.participant_id(),
451 marker.binding_epoch(),
452 transaction_order,
453 delivery_seq,
454 )
455 }
456
457 #[cfg(any(test, feature = "test-support"))]
463 #[must_use]
464 pub fn committed_for_marker_delivery(
465 delivery: super::edge::MarkerDelivery,
466 transaction_order: TransactionOrder,
467 delivery_seq: DeliverySeq,
468 ) -> Self {
469 Self::committed_for_test(
470 delivery.conversation_id(),
471 delivery.participant_id(),
472 delivery.binding_epoch(),
473 transaction_order,
474 delivery_seq,
475 )
476 }
477
478 #[cfg(any(test, feature = "test-support"))]
488 #[allow(clippy::expect_used)]
489 #[must_use]
490 pub fn committed_for_test(
491 conversation_id: ConversationId,
492 participant_id: ParticipantId,
493 binding_epoch: BindingEpoch,
494 transaction_order: TransactionOrder,
495 delivery_seq: DeliverySeq,
496 ) -> Self {
497 BindingFateTerminalRestore::Committed(CommittedBindingTerminalRestore {
498 binding: ActiveBinding {
499 participant_id,
500 conversation_id,
501 binding_epoch,
502 },
503 cause: CloseCause::ConnectionLost,
504 transaction_order,
505 delivery_seq,
506 })
507 .restore()
508 .expect("test fate terminal fields form a valid committed terminal")
509 }
510
511 #[must_use]
513 pub const fn storage_description(self) -> BindingFateTerminalRestore {
514 match self {
515 Self::Committed(value) => {
516 BindingFateTerminalRestore::Committed(value.storage_description())
517 }
518 Self::Pending(value) => {
519 BindingFateTerminalRestore::Pending(value.storage_description())
520 }
521 }
522 }
523
524 pub(super) const fn participant_id(self) -> ParticipantId {
525 match self {
526 Self::Committed(value) => value.participant_id(),
527 Self::Pending(value) => value.participant_id(),
528 }
529 }
530
531 pub(super) const fn conversation_id(self) -> ConversationId {
532 match self {
533 Self::Committed(value) => value.conversation_id(),
534 Self::Pending(value) => value.conversation_id(),
535 }
536 }
537
538 pub(super) const fn binding_epoch(self) -> BindingEpoch {
539 match self {
540 Self::Committed(value) => value.binding_epoch(),
541 Self::Pending(value) => value.binding_epoch(),
542 }
543 }
544}
545
546#[derive(Clone, Copy, Debug, PartialEq, Eq)]
548pub enum BindingStateRestore {
549 Detached,
551 Bound(ActiveBinding),
553 PendingFinalization(PendingFinalizationRestore),
555}
556
557impl BindingStateRestore {
558 fn restore_for<EF>(self, member: &LiveMember<EF>) -> Result<BindingState, StorageRestoreError> {
559 let state = match self {
560 Self::Detached => BindingState::Detached,
561 Self::Bound(binding) => BindingState::Bound(binding),
562 Self::PendingFinalization(raw) => BindingState::PendingFinalization(raw.restore()?),
563 };
564 let authority_matches = match state {
565 BindingState::Detached => true,
566 BindingState::Bound(binding) => {
567 binding.participant_id == member.participant_id()
568 && binding.conversation_id == member.conversation_id()
569 && binding.binding_epoch.capability_generation == member.generation()
570 }
571 BindingState::PendingFinalization(pending) => {
572 pending.participant_id() == member.participant_id()
573 && pending.conversation_id() == member.conversation_id()
574 && pending.binding_epoch().capability_generation == member.generation()
575 }
576 };
577 if authority_matches {
578 Ok(state)
579 } else {
580 Err(StorageRestoreError::BindingAuthority)
581 }
582 }
583}
584
585#[derive(Clone, Debug, PartialEq, Eq)]
587pub struct LiveIdentityRestore<EF> {
588 pub participant_id: ParticipantId,
590 pub conversation_id: ConversationId,
592 pub generation: Generation,
594 pub attach_secret: AttachSecret,
596 pub cursor: DeliverySeq,
598 pub enrollment_fingerprint: EnrollmentFingerprint<EF>,
600 pub latest_terminal: Option<CommittedBindingTerminalRestore>,
602}
603
604impl<EF> LiveIdentityRestore<EF> {
605 fn restore(self) -> Result<LiveMember<EF>, StorageRestoreError> {
606 let latest_terminal = self
607 .latest_terminal
608 .map(CommittedBindingTerminalRestore::restore)
609 .transpose()?;
610 LiveMember::restore(LiveMemberRestore {
611 participant_id: self.participant_id,
612 conversation_id: self.conversation_id,
613 generation: self.generation,
614 attach_secret: self.attach_secret,
615 cursor: self.cursor,
616 enrollment_fingerprint: self.enrollment_fingerprint,
617 latest_terminal,
618 })
619 .map_err(|_| StorageRestoreError::MembershipInvariant)
620 }
621}
622
623#[derive(Clone, Copy, Debug, PartialEq, Eq)]
625pub struct LeaveCommittedRestore {
626 pub conversation_id: ConversationId,
628 pub leave_attempt_token: LeaveAttemptToken,
630 pub participant_id: ParticipantId,
632 pub retired_generation: Generation,
634 pub ended_binding_epoch: Option<BindingEpoch>,
636 pub prior_terminal_delivery_seq: Option<DeliverySeq>,
638 pub left_delivery_seq: DeliverySeq,
640}
641
642impl LeaveCommittedRestore {
643 pub fn restore(self) -> Result<LeaveCommitted, StorageRestoreError> {
650 LeaveCommitted::new(
651 self.conversation_id,
652 self.leave_attempt_token,
653 self.participant_id,
654 self.retired_generation,
655 self.ended_binding_epoch,
656 self.prior_terminal_delivery_seq,
657 self.left_delivery_seq,
658 )
659 .ok_or(StorageRestoreError::LeaveResult)
660 }
661}
662
663#[derive(Clone, Debug, PartialEq, Eq)]
665pub struct RetiredIdentityRestore<EF, V, LF> {
666 pub participant_id: ParticipantId,
668 pub conversation_id: ConversationId,
670 pub retired_generation: Generation,
672 pub enrollment_fingerprint: EnrollmentFingerprint<EF>,
674 pub leave_attempt_token: LeaveAttemptToken,
676 pub leave_request_verifier: V,
678 pub leave_fingerprint: LeaveFingerprint<LF>,
680 pub left_transaction_order: TransactionOrder,
682 pub committed_result: LeaveCommittedRestore,
684}
685
686impl<EF, V, LF> RetiredIdentityRestore<EF, V, LF> {
687 fn restore(self) -> Result<RetiredIdentity<EF, V, LF>, StorageRestoreError> {
688 let result = self.committed_result.restore()?;
689 RetiredIdentity::restore(
690 self.participant_id,
691 self.conversation_id,
692 self.retired_generation,
693 self.enrollment_fingerprint,
694 self.leave_attempt_token,
695 self.leave_request_verifier,
696 self.leave_fingerprint,
697 self.left_transaction_order,
698 result,
699 )
700 .map_err(|_| StorageRestoreError::RetiredIdentity)
701 }
702}
703
704#[derive(Clone, Debug, PartialEq, Eq)]
706pub enum DetachCellRestore<V> {
707 Empty,
709 Pending {
711 token: DetachAttemptToken,
713 participant_id: ParticipantId,
715 request_generation: Generation,
717 request_verifier: V,
719 committed_binding_epoch: BindingEpoch,
721 admission_order: AdmissionOrder,
723 refused_epoch: ObserverEpoch,
725 },
726 Committed {
728 token: DetachAttemptToken,
730 participant_id: ParticipantId,
732 request_generation: Generation,
734 request_verifier: V,
736 committed_binding_epoch: BindingEpoch,
738 detached_delivery_seq: DeliverySeq,
740 },
741 Terminalized {
743 token: DetachAttemptToken,
745 participant_id: ParticipantId,
747 request_generation: Generation,
749 request_verifier: V,
751 committed_binding_epoch: BindingEpoch,
753 },
754}
755
756impl<V> DetachCellRestore<V> {
757 fn restore(self) -> Result<DetachCell<V>, StorageRestoreError> {
758 match self {
759 Self::Empty => Ok(DetachCell::Empty(EmptyDetach)),
760 Self::Pending {
761 token,
762 participant_id,
763 request_generation,
764 request_verifier,
765 committed_binding_epoch,
766 admission_order,
767 refused_epoch,
768 } => restore_pending_detach(
769 token,
770 participant_id,
771 request_generation,
772 request_verifier,
773 committed_binding_epoch,
774 admission_order,
775 refused_epoch,
776 )
777 .map(DetachCell::Pending)
778 .ok_or(StorageRestoreError::DetachCell),
779 Self::Committed {
780 token,
781 participant_id,
782 request_generation,
783 request_verifier,
784 committed_binding_epoch,
785 detached_delivery_seq,
786 } => restore_committed_detach(
787 token,
788 participant_id,
789 request_generation,
790 request_verifier,
791 committed_binding_epoch,
792 detached_delivery_seq,
793 )
794 .map(DetachCell::Committed)
795 .ok_or(StorageRestoreError::DetachCell),
796 Self::Terminalized {
797 token,
798 participant_id,
799 request_generation,
800 request_verifier,
801 committed_binding_epoch,
802 } => restore_terminalized_detach(
803 token,
804 participant_id,
805 request_generation,
806 request_verifier,
807 committed_binding_epoch,
808 )
809 .map(DetachCell::Terminalized)
810 .ok_or(StorageRestoreError::DetachCell),
811 }
812 }
813}
814
815#[allow(
817 clippy::large_enum_variant,
818 reason = "the live storage capsule remains inline so its atomic slots cannot be restored separately"
819)]
820#[derive(Clone, Debug, PartialEq, Eq)]
821pub enum ParticipantLifecycleRestore<EF, V, LF, D> {
822 Live {
824 identity: LiveIdentityRestore<EF>,
826 binding: BindingStateRestore,
828 binding_origin: Option<BindingOrigin>,
830 detach_cell: DetachCellRestore<D>,
832 },
833 Retired(RetiredIdentityRestore<EF, V, LF>),
835}
836
837#[allow(
839 clippy::large_enum_variant,
840 reason = "the validated live capsule remains inline as one atomic lifecycle result"
841)]
842#[derive(Clone, Debug, PartialEq, Eq)]
843pub enum RestoredParticipantLifecycle<EF, V, LF, D> {
844 Live {
846 member: LiveMember<EF>,
848 binding: BindingState,
850 binding_origin: Option<BindingOrigin>,
852 detach_cell: DetachCell<D>,
854 },
855 Retired(RetiredIdentity<EF, V, LF>),
857}
858
859impl<EF, V, LF, D> ParticipantLifecycleRestore<EF, V, LF, D> {
860 pub fn restore(
867 self,
868 ) -> Result<RestoredParticipantLifecycle<EF, V, LF, D>, StorageRestoreError> {
869 match self {
870 Self::Retired(identity) => identity
871 .restore()
872 .map(RestoredParticipantLifecycle::Retired),
873 Self::Live {
874 identity,
875 binding,
876 binding_origin,
877 detach_cell,
878 } => {
879 let member = identity.restore()?;
880 let binding = binding.restore_for(&member)?;
881 let binding_origin = binding_origin
882 .map(|origin| validate_binding_origin(origin, &member, binding))
883 .transpose()?;
884 let origin_required = !matches!(binding, BindingState::Detached)
885 || member.latest_terminal().is_some();
886 if origin_required != binding_origin.is_some() {
887 return Err(StorageRestoreError::BindingAuthority);
888 }
889 let detach_cell = detach_cell.restore()?;
890 validate_live_pair(&member, binding, &detach_cell)?;
891 Ok(RestoredParticipantLifecycle::Live {
892 member,
893 binding,
894 binding_origin,
895 detach_cell,
896 })
897 }
898 }
899 }
900}
901
902fn validate_binding_origin<EF>(
903 origin: BindingOrigin,
904 member: &LiveMember<EF>,
905 binding_state: BindingState,
906) -> Result<BindingOrigin, StorageRestoreError> {
907 let expected_epoch = match binding_state {
908 BindingState::Bound(current) => Some(current.binding_epoch),
909 BindingState::PendingFinalization(pending) => Some(pending.binding_epoch()),
910 BindingState::Detached => member
911 .latest_terminal()
912 .map(CommittedBindingTerminal::binding_epoch),
913 };
914 let attached = origin.attached();
915 if origin.participant_id() != member.participant_id()
916 || origin.conversation_id() != member.conversation_id()
917 || expected_epoch != Some(origin.binding_epoch())
918 || attached.participant_id() != member.participant_id()
919 || attached.conversation_id() != member.conversation_id()
920 {
921 Err(StorageRestoreError::BindingAuthority)
922 } else {
923 Ok(origin)
924 }
925}
926
927impl<EF, V, LF, D> RestoredParticipantLifecycle<EF, V, LF, D> {
928 #[must_use]
930 #[allow(clippy::type_complexity)]
931 pub fn into_parts(
932 self,
933 ) -> (
934 IdentityState<EF, V, LF>,
935 Option<BindingState>,
936 Option<DetachCell<D>>,
937 ) {
938 match self {
939 Self::Live {
940 member,
941 binding,
942 binding_origin: _,
943 detach_cell,
944 } => (
945 IdentityState::Live(member),
946 Some(binding),
947 Some(detach_cell),
948 ),
949 Self::Retired(identity) => (IdentityState::Retired(identity), None, None),
950 }
951 }
952}
953
954impl<EF, V, LF, D> ParticipantConversationRestore<EF, V, LF, D> {
955 pub fn restore(
970 self,
971 ) -> Result<ParticipantConversationState<EF, V, LF, D>, ConversationStateRestoreError> {
972 self.restore_with_fenced_latch(None)
973 }
974
975 pub(in crate::lifecycle) fn restore_with_fenced_latch(
994 self,
995 fenced_marker_issued: Option<(ParticipantId, DeliverySeq, BindingEpoch)>,
996 ) -> Result<ParticipantConversationState<EF, V, LF, D>, ConversationStateRestoreError> {
997 let participants = self
998 .participants
999 .into_iter()
1000 .map(ParticipantLifecycleRestore::restore)
1001 .collect::<Result<Vec<_>, _>>()
1002 .map_err(ConversationStateRestoreError::Storage)?;
1003 validate_participant_frontier_projection(
1004 &participants,
1005 self.frontiers.conversation_id,
1006 &self.frontiers.active_identities,
1007 )?;
1008 let history = validated_conversation_history(&participants)?;
1009 let restored = restore_conversation_with_history(
1010 ClaimFrontiersDescription {
1011 restore: self.frontiers,
1012 sequence_ledger: self.sequence_ledger,
1013 order_ledger: self.order_ledger,
1014 fenced_marker_issued,
1015 },
1016 &self.closure,
1017 &history,
1018 )?;
1019 let (frontiers, closure) = restored.into_parts();
1020 Ok(ParticipantConversationState {
1021 participants,
1022 frontiers,
1023 closure,
1024 })
1025 }
1026}
1027
1028#[derive(Debug)]
1034pub enum ParticipantLifecycleView<'a, EF, V, LF, D> {
1035 Live {
1037 member: &'a LiveMember<EF>,
1039 binding: &'a BindingState,
1041 binding_origin: Option<&'a BindingOrigin>,
1044 detach_cell: &'a DetachCell<D>,
1046 },
1047 Retired(&'a RetiredIdentity<EF, V, LF>),
1049}
1050
1051impl<EF, V, LF, D> ParticipantLifecycleView<'_, EF, V, LF, D> {
1052 #[must_use]
1054 pub const fn participant_id(&self) -> ParticipantId {
1055 match self {
1056 Self::Live { member, .. } => member.participant_id(),
1057 Self::Retired(retired) => retired.participant_id(),
1058 }
1059 }
1060}
1061
1062impl<EF: Clone, V: Clone, LF: Clone, D: Clone> ParticipantLifecycleView<'_, EF, V, LF, D> {
1063 pub(super) fn storage_description(&self) -> ParticipantLifecycleRestore<EF, V, LF, D> {
1066 match *self {
1067 Self::Live {
1068 member,
1069 binding,
1070 binding_origin,
1071 detach_cell,
1072 } => ParticipantLifecycleRestore::Live {
1073 identity: LiveIdentityRestore {
1074 participant_id: member.participant_id(),
1075 conversation_id: member.conversation_id(),
1076 generation: member.generation(),
1077 attach_secret: member.attach_secret(),
1078 cursor: member.cursor(),
1079 enrollment_fingerprint: member.enrollment_fingerprint().clone(),
1080 latest_terminal: member
1081 .latest_terminal()
1082 .map(CommittedBindingTerminal::storage_description),
1083 },
1084 binding: describe_binding_state(*binding),
1085 binding_origin: binding_origin.copied(),
1086 detach_cell: detach_cell.storage_description(),
1087 },
1088 Self::Retired(retired) => {
1089 ParticipantLifecycleRestore::Retired(describe_retired(retired))
1090 }
1091 }
1092 }
1093}
1094
1095impl<EF, V, LF, D> RestoredParticipantLifecycle<EF, V, LF, D> {
1096 #[must_use]
1098 pub const fn view(&self) -> ParticipantLifecycleView<'_, EF, V, LF, D> {
1099 match self {
1100 Self::Live {
1101 member,
1102 binding,
1103 binding_origin,
1104 detach_cell,
1105 } => ParticipantLifecycleView::Live {
1106 member,
1107 binding,
1108 binding_origin: binding_origin.as_ref(),
1109 detach_cell,
1110 },
1111 Self::Retired(retired) => ParticipantLifecycleView::Retired(retired),
1112 }
1113 }
1114}
1115
1116const fn describe_binding_state(binding: BindingState) -> BindingStateRestore {
1117 match binding {
1118 BindingState::Detached => BindingStateRestore::Detached,
1119 BindingState::Bound(binding) => BindingStateRestore::Bound(binding),
1120 BindingState::PendingFinalization(pending) => {
1121 BindingStateRestore::PendingFinalization(pending.storage_description())
1122 }
1123 }
1124}
1125
1126fn describe_retired<EF: Clone, V: Clone, LF: Clone>(
1127 retired: &RetiredIdentity<EF, V, LF>,
1128) -> RetiredIdentityRestore<EF, V, LF> {
1129 let result = retired.committed_result();
1130 RetiredIdentityRestore {
1131 participant_id: retired.participant_id(),
1132 conversation_id: retired.conversation_id(),
1133 retired_generation: retired.retired_generation(),
1134 enrollment_fingerprint: retired.enrollment_fingerprint().clone(),
1135 leave_attempt_token: retired.leave_attempt_token(),
1136 leave_request_verifier: retired.leave_request_verifier().clone(),
1137 leave_fingerprint: retired.leave_fingerprint().clone(),
1138 left_transaction_order: retired.left_admission_order().transaction_order(),
1139 committed_result: LeaveCommittedRestore {
1140 conversation_id: result.conversation_id(),
1141 leave_attempt_token: result.leave_attempt_token(),
1142 participant_id: result.participant_id(),
1143 retired_generation: result.retired_generation(),
1144 ended_binding_epoch: result.ended_binding_epoch(),
1145 prior_terminal_delivery_seq: result.prior_terminal_delivery_seq(),
1146 left_delivery_seq: result.left_delivery_seq(),
1147 },
1148 }
1149}
1150
1151#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1154#[non_exhaustive]
1155pub enum ConversationDescribeError {
1156 DetachedCursorReleaseProvenance,
1161 EdgeTerminal,
1165}
1166
1167#[derive(Clone, Debug, PartialEq, Eq)]
1171pub(super) struct ParticipantConversationDescription<EF, V, LF, D> {
1172 pub(super) restore: ParticipantConversationRestore<EF, V, LF, D>,
1175 pub(super) fenced_marker_issued: Option<(ParticipantId, DeliverySeq, BindingEpoch)>,
1177}
1178
1179impl<EF, V, LF, D> ParticipantConversationDescription<EF, V, LF, D> {
1180 pub(super) fn into_state(
1189 self,
1190 ) -> Result<ParticipantConversationState<EF, V, LF, D>, ConversationStateRestoreError> {
1191 self.restore
1192 .restore_with_fenced_latch(self.fenced_marker_issued)
1193 }
1194}
1195
1196pub(super) fn describe_conversation<EF: Clone, V: Clone, LF: Clone, D: Clone>(
1211 participants: &[ParticipantLifecycleView<'_, EF, V, LF, D>],
1212 frontiers: &ClaimFrontiers,
1213 retained_record_limit: u64,
1214 closure: ClosureState,
1215) -> Result<ParticipantConversationDescription<EF, V, LF, D>, ConversationDescribeError> {
1216 let closure = describe_closure(participants, frontiers.conversation_id(), closure)?;
1217 let ClaimFrontiersDescription {
1218 restore,
1219 sequence_ledger,
1220 order_ledger,
1221 fenced_marker_issued,
1222 } = frontiers.describe(retained_record_limit);
1223 Ok(ParticipantConversationDescription {
1224 restore: ParticipantConversationRestore {
1225 participants: participants
1226 .iter()
1227 .map(ParticipantLifecycleView::storage_description)
1228 .collect(),
1229 frontiers: restore,
1230 sequence_ledger,
1231 order_ledger,
1232 closure,
1233 },
1234 fenced_marker_issued,
1235 })
1236}
1237
1238fn describe_closure<EF, V, LF, D>(
1239 participants: &[ParticipantLifecycleView<'_, EF, V, LF, D>],
1240 conversation_id: ConversationId,
1241 closure: ClosureState,
1242) -> Result<ClosureStateRestore, ConversationDescribeError> {
1243 let ClosureState::Owed { debt, edge } = closure else {
1244 return Ok(ClosureStateRestore::Clear);
1245 };
1246 let edge = match edge {
1247 StoredEdge::ObserverProjection(edge) => edge.storage_description(),
1248 StoredEdge::PhysicalCompaction(edge) => edge.storage_description(),
1249 StoredEdge::MarkerDelivery(edge) => edge.storage_description(),
1250 StoredEdge::ParticipantCursorProgress(ParticipantCursorProgress::Continuous(edge)) => {
1251 edge.storage_description(conversation_id)
1252 }
1253 StoredEdge::ParticipantCursorProgress(ParticipantCursorProgress::Marker(edge)) => {
1254 edge.storage_description()
1255 }
1256 StoredEdge::DetachedCredentialRecovery(edge) => {
1257 let terminal = selecting_fate_terminal(
1258 participants,
1259 edge.participant_id(),
1260 edge.prior_binding_epoch(),
1261 )
1262 .ok_or(ConversationDescribeError::EdgeTerminal)?;
1263 edge.storage_description(terminal)
1264 .map_err(|_| ConversationDescribeError::EdgeTerminal)?
1265 }
1266 StoredEdge::DetachedMarkerRelease(edge) => {
1267 let terminal = match edge.provenance() {
1268 DetachedMarkerReleaseProvenance::Fate => Some(
1269 selecting_fate_terminal(
1270 participants,
1271 edge.participant_id(),
1272 edge.last_dead_binding_epoch(),
1273 )
1274 .ok_or(ConversationDescribeError::EdgeTerminal)?,
1275 ),
1276 DetachedMarkerReleaseProvenance::Candidate => None,
1277 };
1278 edge.storage_description(terminal)
1279 .map_err(|_| ConversationDescribeError::EdgeTerminal)?
1280 }
1281 StoredEdge::DetachedCursorRelease(_) => {
1282 return Err(ConversationDescribeError::DetachedCursorReleaseProvenance);
1283 }
1284 };
1285 Ok(ClosureStateRestore::Owed {
1286 debt: debt.value(),
1287 edge,
1288 })
1289}
1290
1291fn selecting_fate_terminal<EF, V, LF, D>(
1295 participants: &[ParticipantLifecycleView<'_, EF, V, LF, D>],
1296 participant_id: ParticipantId,
1297 binding_epoch: BindingEpoch,
1298) -> Option<RestoredBindingFateTerminal> {
1299 participants.iter().find_map(|participant| {
1300 let ParticipantLifecycleView::Live {
1301 member, binding, ..
1302 } = participant
1303 else {
1304 return None;
1305 };
1306 if member.participant_id() != participant_id {
1307 return None;
1308 }
1309 if let BindingState::PendingFinalization(pending) = **binding
1310 && pending.binding_epoch() == binding_epoch
1311 {
1312 return Some(RestoredBindingFateTerminal::Pending(pending));
1313 }
1314 member
1315 .latest_terminal()
1316 .filter(|terminal| terminal.binding_epoch() == binding_epoch)
1317 .map(RestoredBindingFateTerminal::Committed)
1318 })
1319}
1320
1321fn validated_conversation_history<EF, V, LF, D>(
1322 participants: &[RestoredParticipantLifecycle<EF, V, LF, D>],
1323) -> Result<ValidatedConversationHistory, ConversationStateRestoreError> {
1324 let mut causal_authorities = Vec::new();
1325 let mut binding_origins = Vec::new();
1326 let mut seen = Vec::with_capacity(participants.len());
1327 for participant in participants {
1328 let participant_id = match participant {
1329 RestoredParticipantLifecycle::Live {
1330 member,
1331 binding_origin,
1332 ..
1333 } => {
1334 if let Some(terminal) = member.latest_terminal() {
1335 causal_authorities
1336 .push(HistoricalCausalAuthority::from_committed_terminal(terminal));
1337 }
1338 if let Some(origin) = binding_origin {
1339 binding_origins.push(*origin);
1340 }
1341 member.participant_id()
1342 }
1343 RestoredParticipantLifecycle::Retired(retired) => {
1344 causal_authorities.push(HistoricalCausalAuthority::from_retired(retired));
1345 retired.participant_id()
1346 }
1347 };
1348 seen.push(participant_id);
1349 }
1350 seen.sort_unstable();
1354 let has_duplicate = seen
1355 .iter()
1356 .zip(seen.iter().skip(1))
1357 .any(|(current, next)| current == next);
1358 if has_duplicate {
1359 return Err(ConversationStateRestoreError::Storage(
1360 StorageRestoreError::MembershipInvariant,
1361 ));
1362 }
1363 Ok(ValidatedConversationHistory::new(
1364 causal_authorities,
1365 binding_origins,
1366 ))
1367}
1368
1369fn validate_participant_frontier_projection<EF, V, LF, D>(
1370 participants: &[RestoredParticipantLifecycle<EF, V, LF, D>],
1371 conversation_id: ConversationId,
1372 frontier: &[super::FrontierParticipant],
1373) -> Result<(), ConversationStateRestoreError> {
1374 let mut projected = Vec::new();
1375 for participant in participants {
1376 match participant {
1377 RestoredParticipantLifecycle::Live {
1378 member, binding, ..
1379 } => {
1380 if member.conversation_id() != conversation_id {
1381 return Err(ConversationStateRestoreError::Storage(
1382 StorageRestoreError::MembershipInvariant,
1383 ));
1384 }
1385 let binding = match binding {
1386 BindingState::Bound(binding) => FrontierBinding::Bound(binding.binding_epoch),
1387 BindingState::PendingFinalization(pending) => {
1388 FrontierBinding::Detached(pending.binding_epoch())
1389 }
1390 BindingState::Detached => {
1391 let Some(terminal) = member.latest_terminal() else {
1392 return Err(ConversationStateRestoreError::Storage(
1393 StorageRestoreError::BindingAuthority,
1394 ));
1395 };
1396 FrontierBinding::Detached(terminal.binding_epoch())
1397 }
1398 };
1399 projected.push(super::FrontierParticipant::new(
1400 member.participant_id(),
1401 member.cursor(),
1402 binding,
1403 ));
1404 }
1405 RestoredParticipantLifecycle::Retired(retired) => {
1406 if retired.conversation_id() != conversation_id {
1407 return Err(ConversationStateRestoreError::Storage(
1408 StorageRestoreError::MembershipInvariant,
1409 ));
1410 }
1411 }
1412 }
1413 }
1414 projected.sort_by_key(|participant| participant.participant_index());
1415 if projected == frontier {
1416 Ok(())
1417 } else {
1418 Err(ConversationStateRestoreError::Storage(
1419 StorageRestoreError::MembershipInvariant,
1420 ))
1421 }
1422}
1423
1424#[allow(clippy::suspicious_operation_groupings)]
1425fn validate_live_pair<EF, D>(
1426 member: &LiveMember<EF>,
1427 binding: BindingState,
1428 detach_cell: &DetachCell<D>,
1429) -> Result<(), StorageRestoreError> {
1430 match detach_cell {
1431 DetachCell::Empty(_) => Ok(()),
1432 DetachCell::Pending(cell) => {
1433 if cell.participant_id() != member.participant_id()
1434 || cell.request_generation() != member.generation()
1435 || validate_pending_pair(binding, cell, Some(member.conversation_id())).is_err()
1436 {
1437 Err(StorageRestoreError::DetachBindingPair)
1438 } else {
1439 Ok(())
1440 }
1441 }
1442 DetachCell::Committed(cell) => {
1443 let terminal_matches = member.latest_terminal().is_some_and(|terminal| {
1444 terminal.participant_id() == cell.participant_id()
1445 && terminal.conversation_id() == member.conversation_id()
1446 && terminal.binding_epoch() == cell.committed_binding_epoch()
1447 && terminal.delivery_seq() == cell.detached_delivery_seq()
1448 && terminal.detached_cause()
1449 == Some(crate::wire::DetachedCause::CleanDeregister)
1450 });
1451 if cell.participant_id() != member.participant_id()
1452 || cell.request_generation() != member.generation()
1453 || binding != BindingState::Detached
1454 || !terminal_matches
1455 {
1456 Err(StorageRestoreError::DetachBindingPair)
1457 } else {
1458 Ok(())
1459 }
1460 }
1461 DetachCell::Terminalized(cell) => {
1462 if cell.participant_id() != member.participant_id()
1463 || cell.request_generation().get() >= member.generation().get()
1464 {
1465 Err(StorageRestoreError::DetachBindingPair)
1466 } else {
1467 Ok(())
1468 }
1469 }
1470 }
1471}
1472
1473#[derive(Clone, Debug, PartialEq, Eq)]
1475pub struct CursorEpisodeRestore {
1476 pub conversation_id: ConversationId,
1478 pub debt: WideResourceVector,
1480 pub observer_progress: DeliverySeq,
1482 pub candidate_high_watermark: DeliverySeq,
1484 pub current_floor: u128,
1486 pub cap_floor: u128,
1488 pub participants: Vec<BoundParticipantCursor>,
1490 pub facts: Vec<(CursorProgressKey, CursorProgressFact)>,
1492}
1493
1494impl CursorEpisodeRestore {
1495 pub fn restore(self) -> Result<NonzeroDebtCursorEpisode, StorageRestoreError> {
1503 let debt = ClosureDebt::new(self.debt).ok_or(StorageRestoreError::ClosureDebt)?;
1504 NonzeroDebtCursorEpisode::restore(
1505 self.conversation_id,
1506 debt,
1507 self.observer_progress,
1508 self.candidate_high_watermark,
1509 self.current_floor,
1510 self.cap_floor,
1511 self.participants,
1512 self.facts,
1513 )
1514 .ok_or(StorageRestoreError::CursorEpisode)
1515 }
1516}
1517
1518#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1532pub struct OrdinaryBindingAuthorityRestore {
1533 pub binding: ActiveBinding,
1535 pub through_seq: DeliverySeq,
1537}
1538
1539impl OrdinaryBindingAuthorityRestore {
1540 fn restore_with_origin(
1541 self,
1542 origin: &BindingOrigin,
1543 ) -> Result<OrdinaryBindingAuthority, StorageRestoreError> {
1544 if !origin.is_unfenced()
1545 || origin.conversation_id() != self.binding.conversation_id
1546 || origin.participant_id() != self.binding.participant_id
1547 || origin.binding_epoch() != self.binding.binding_epoch
1548 {
1549 return Err(StorageRestoreError::StoredEdgeProvenance);
1550 }
1551 Ok(OrdinaryBindingAuthority::new(
1552 self.binding,
1553 self.through_seq,
1554 ))
1555 }
1556}
1557
1558#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1579pub struct MarkerDeliveryRestore {
1580 pub participant_id: ParticipantId,
1582 pub binding_epoch: BindingEpoch,
1584 pub marker_delivery_seq: DeliverySeq,
1586}
1587
1588impl MarkerDeliveryRestore {
1589 #[cfg(any(test, feature = "test-support"))]
1597 pub(super) fn restore_bound(
1598 self,
1599 conversation_id: ConversationId,
1600 record_authority: ValidatedMarkerRecord,
1601 ) -> Result<MarkerDelivery, StorageRestoreError> {
1602 let restored = self.restore_with_target(
1603 conversation_id,
1604 &record_authority,
1605 MarkerRecordTarget::Bound,
1606 MarkerRecordOccurrence::Undelivered,
1607 );
1608 record_authority.consume();
1609 restored
1610 }
1611
1612 #[cfg(test)]
1614 pub(super) fn restore_detached_delivered_for_test(
1615 self,
1616 conversation_id: ConversationId,
1617 record_authority: ValidatedMarkerRecord,
1618 ) -> Result<MarkerDelivery, StorageRestoreError> {
1619 let restored = self.restore_with_target(
1620 conversation_id,
1621 &record_authority,
1622 MarkerRecordTarget::Detached,
1623 MarkerRecordOccurrence::Delivered,
1624 );
1625 record_authority.consume();
1626 restored
1627 }
1628
1629 fn restore_detached(
1630 self,
1631 conversation_id: ConversationId,
1632 record_authority: &ValidatedMarkerRecord,
1633 ) -> Result<MarkerDelivery, StorageRestoreError> {
1634 self.restore_with_target(
1635 conversation_id,
1636 record_authority,
1637 MarkerRecordTarget::Detached,
1638 MarkerRecordOccurrence::Undelivered,
1639 )
1640 }
1641
1642 fn restore_with_target(
1643 self,
1644 conversation_id: ConversationId,
1645 record_authority: &ValidatedMarkerRecord,
1646 target: MarkerRecordTarget,
1647 occurrence: MarkerRecordOccurrence,
1648 ) -> Result<MarkerDelivery, StorageRestoreError> {
1649 if record_authority.conversation_id() != conversation_id
1650 || !target.matches(record_authority.target_binding(), self.binding_epoch)
1651 || record_authority.occurrence() != occurrence
1652 {
1653 return Err(StorageRestoreError::StoredEdgeProvenance);
1654 }
1655 let delivery = MarkerDelivery::from_validated_record(record_authority);
1656 if delivery.participant_id() != self.participant_id
1657 || delivery.binding_epoch() != self.binding_epoch
1658 || delivery.marker_delivery_seq() != self.marker_delivery_seq
1659 {
1660 return Err(StorageRestoreError::StoredEdgeProvenance);
1661 }
1662 Ok(delivery)
1663 }
1664}
1665
1666#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1667enum MarkerRecordTarget {
1668 Bound,
1669 Detached,
1670}
1671
1672impl MarkerRecordTarget {
1673 fn matches(self, target: super::FrontierBinding, epoch: BindingEpoch) -> bool {
1674 matches!(
1675 (self, target),
1676 (Self::Bound, super::FrontierBinding::Bound(actual))
1677 | (Self::Detached, super::FrontierBinding::Detached(actual))
1678 if actual == epoch
1679 )
1680 }
1681}
1682
1683#[derive(Debug)]
1684enum MarkerRestoreAuthority<'a> {
1685 Absent,
1686 Record {
1687 conversation_id: ConversationId,
1688 record: &'a ValidatedMarkerRecord,
1689 },
1690}
1691
1692impl MarkerRestoreAuthority<'_> {
1693 const fn require_absent(&self) -> Result<(), StorageRestoreError> {
1694 match self {
1695 Self::Absent => Ok(()),
1696 Self::Record { .. } => Err(StorageRestoreError::StoredEdgeProvenance),
1697 }
1698 }
1699
1700 const fn require_record(
1701 &self,
1702 ) -> Result<(ConversationId, &ValidatedMarkerRecord), StorageRestoreError> {
1703 match self {
1704 Self::Record {
1705 conversation_id,
1706 record,
1707 } if record.conversation_id() == *conversation_id => Ok((*conversation_id, record)),
1708 Self::Absent | Self::Record { .. } => Err(StorageRestoreError::StoredEdgeProvenance),
1709 }
1710 }
1711
1712 fn record_for(
1713 &self,
1714 expected_conversation_id: ConversationId,
1715 ) -> Result<&ValidatedMarkerRecord, StorageRestoreError> {
1716 let (conversation_id, record) = self.require_record()?;
1717 if conversation_id == expected_conversation_id {
1718 Ok(record)
1719 } else {
1720 Err(StorageRestoreError::StoredEdgeProvenance)
1721 }
1722 }
1723}
1724
1725#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1727pub struct MarkerCursorProgressRestore {
1728 pub conversation_id: ConversationId,
1730 pub participant_id: ParticipantId,
1732 pub binding_epoch: BindingEpoch,
1734 pub through_seq: DeliverySeq,
1736 pub marker_delivery_seq: DeliverySeq,
1738 pub delivery: MarkerDeliveryRestore,
1740}
1741
1742impl MarkerCursorProgressRestore {
1743 fn restore_with_debt(
1744 self,
1745 debt: ClosureDebt,
1746 record_authority: &ValidatedMarkerRecord,
1747 target: MarkerRecordTarget,
1748 ) -> Result<ParticipantCursorProgress, StorageRestoreError> {
1749 if self.participant_id != self.delivery.participant_id
1750 || self.binding_epoch != self.delivery.binding_epoch
1751 || self.marker_delivery_seq != self.delivery.marker_delivery_seq
1752 || self.through_seq != self.marker_delivery_seq
1753 {
1754 return Err(StorageRestoreError::StoredEdgeProvenance);
1755 }
1756 let marker = self.delivery.restore_with_target(
1757 self.conversation_id,
1758 record_authority,
1759 target,
1760 MarkerRecordOccurrence::Delivered,
1761 )?;
1762 let state = marker
1763 .delivered(
1764 debt,
1765 Event::marker_delivered(
1766 self.participant_id,
1767 self.binding_epoch,
1768 self.marker_delivery_seq,
1769 ),
1770 )
1771 .map_err(|_| StorageRestoreError::StoredEdgeProvenance)?;
1772 match state {
1773 ClosureState::Owed {
1774 edge: StoredEdge::ParticipantCursorProgress(progress),
1775 ..
1776 } => Ok(progress),
1777 ClosureState::Clear | ClosureState::Owed { .. } => {
1778 Err(StorageRestoreError::StoredEdgeProvenance)
1779 }
1780 }
1781 }
1782}
1783
1784#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1786pub struct DetachedCredentialRecoveryRestore {
1787 pub participant_id: ParticipantId,
1789 pub marker_delivery_seq: DeliverySeq,
1791 pub prior_binding_epoch: BindingEpoch,
1793 pub resulting_floor: DeliverySeq,
1795 pub terminal: BindingFateTerminalRestore,
1797 pub progress: MarkerCursorProgressRestore,
1799}
1800
1801impl DetachedCredentialRecoveryRestore {
1802 pub fn restore_description(self) -> Result<DetachedCredentialRecovery, StorageRestoreError> {
1814 if self.participant_id != self.progress.participant_id
1815 || self.marker_delivery_seq != self.progress.marker_delivery_seq
1816 || self.prior_binding_epoch != self.progress.binding_epoch
1817 || self.progress.through_seq != self.marker_delivery_seq
1818 || self.progress.participant_id != self.progress.delivery.participant_id
1819 || self.progress.binding_epoch != self.progress.delivery.binding_epoch
1820 || self.progress.marker_delivery_seq != self.progress.delivery.marker_delivery_seq
1821 {
1822 return Err(StorageRestoreError::StoredEdgeProvenance);
1823 }
1824 let terminal = self.terminal.restore()?;
1825 if terminal.participant_id() != self.participant_id
1826 || terminal.binding_epoch() != self.prior_binding_epoch
1827 || terminal.conversation_id() != self.progress.conversation_id
1828 {
1829 return Err(StorageRestoreError::StoredEdgeProvenance);
1830 }
1831 Ok(DetachedCredentialRecovery::from_storage_description(
1832 self.progress.conversation_id,
1833 self.participant_id,
1834 self.marker_delivery_seq,
1835 self.prior_binding_epoch,
1836 self.resulting_floor,
1837 ))
1838 }
1839
1840 fn restore_with_debt(
1841 self,
1842 debt: ClosureDebt,
1843 record_authority: &ValidatedMarkerRecord,
1844 ) -> Result<DetachedCredentialRecovery, StorageRestoreError> {
1845 if self.participant_id != self.progress.participant_id
1846 || self.marker_delivery_seq != self.progress.marker_delivery_seq
1847 || self.prior_binding_epoch != self.progress.binding_epoch
1848 {
1849 return Err(StorageRestoreError::StoredEdgeProvenance);
1850 }
1851 let terminal = self.terminal.restore()?;
1852 if terminal.participant_id() != self.participant_id
1853 || terminal.binding_epoch() != self.prior_binding_epoch
1854 || terminal.conversation_id() != self.progress.conversation_id
1855 {
1856 return Err(StorageRestoreError::StoredEdgeProvenance);
1857 }
1858 let progress = self.progress.restore_with_debt(
1859 debt,
1860 record_authority,
1861 MarkerRecordTarget::Detached,
1862 )?;
1863 let successor = progress
1864 .binding_fate(
1865 debt,
1866 terminal,
1867 Event::binding_fate_observed(
1868 self.participant_id,
1869 self.prior_binding_epoch,
1870 self.resulting_floor,
1871 ),
1872 )
1873 .map_err(|_| StorageRestoreError::StoredEdgeProvenance)?;
1874 match successor.into_stored_edge() {
1875 StoredEdge::DetachedCredentialRecovery(edge) => Ok(edge),
1876 _ => Err(StorageRestoreError::StoredEdgeProvenance),
1877 }
1878 }
1879}
1880
1881#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1883pub struct DetachedMarkerReleaseRestore {
1884 pub conversation_id: ConversationId,
1886 pub participant_id: ParticipantId,
1888 pub marker_delivery_seq: DeliverySeq,
1890 pub last_dead_binding_epoch: BindingEpoch,
1892 pub delivery: MarkerDeliveryRestore,
1894 pub provenance: DetachedMarkerReleaseProvenanceRestore,
1896}
1897
1898#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1903pub enum DetachedMarkerReleaseProvenanceRestore {
1904 Fate {
1912 terminal: BindingFateTerminalRestore,
1914 },
1915 Candidate,
1918}
1919
1920impl DetachedMarkerReleaseRestore {
1921 fn restore_with_marker_record(
1927 self,
1928 record_authority: &ValidatedMarkerRecord,
1929 ) -> Result<DetachedMarkerRelease, StorageRestoreError> {
1930 if self.participant_id != self.delivery.participant_id
1931 || self.marker_delivery_seq != self.delivery.marker_delivery_seq
1932 || self.last_dead_binding_epoch != self.delivery.binding_epoch
1933 {
1934 return Err(StorageRestoreError::StoredEdgeProvenance);
1935 }
1936 let provenance = match self.provenance {
1937 DetachedMarkerReleaseProvenanceRestore::Fate { terminal } => {
1938 let terminal = terminal.restore()?;
1939 if terminal.participant_id() != self.participant_id
1940 || terminal.binding_epoch() != self.last_dead_binding_epoch
1941 || terminal.conversation_id() != self.conversation_id
1942 {
1943 return Err(StorageRestoreError::StoredEdgeProvenance);
1944 }
1945 DetachedMarkerReleaseProvenance::Fate
1946 }
1947 DetachedMarkerReleaseProvenanceRestore::Candidate => {
1948 DetachedMarkerReleaseProvenance::Candidate
1949 }
1950 };
1951 self.delivery
1952 .restore_detached(self.conversation_id, record_authority)?;
1953 Ok(DetachedMarkerRelease::from_storage_description(
1954 self.conversation_id,
1955 self.participant_id,
1956 self.marker_delivery_seq,
1957 self.last_dead_binding_epoch,
1958 provenance,
1959 ))
1960 }
1961}
1962
1963#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1976pub struct OrdinaryBindingFateRestore {
1977 pub authority: OrdinaryBindingAuthorityRestore,
1979 pub terminal: CommittedBindingTerminalRestore,
1981 pub resulting_floor: DeliverySeq,
1983}
1984
1985impl OrdinaryBindingFateRestore {
1986 fn restore_with_origin(
1993 self,
1994 origin: &BindingOrigin,
1995 ) -> Result<OrdinaryBindingFate, StorageRestoreError> {
1996 let authority = self.authority.restore_with_origin(origin)?;
1997 let terminal = self.terminal.restore()?;
1998 let CommittedBindingTerminal::Died(terminal) = terminal else {
1999 return Err(StorageRestoreError::StoredEdgeProvenance);
2000 };
2001 authority
2002 .binding_fate(terminal, self.resulting_floor)
2003 .map_err(|_| StorageRestoreError::StoredEdgeProvenance)
2004 }
2005}
2006
2007#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2009pub enum DebtCompletionRestore {
2010 Clear,
2012 ObserverProjection {
2014 debt: WideResourceVector,
2016 through_seq: DeliverySeq,
2018 },
2019 PhysicalCompaction {
2021 debt: WideResourceVector,
2023 from_floor: DeliverySeq,
2025 through_seq: DeliverySeq,
2027 },
2028}
2029
2030impl DebtCompletionRestore {
2031 pub fn restore(self) -> Result<DebtCompletion, StorageRestoreError> {
2037 match self {
2038 Self::Clear => Ok(DebtCompletion::clear()),
2039 Self::ObserverProjection { debt, through_seq } => {
2040 let debt = ClosureDebt::new(debt).ok_or(StorageRestoreError::ClosureDebt)?;
2041 Ok(DebtCompletion::observer_projection(
2042 debt,
2043 ObserverProjection::new(through_seq),
2044 ))
2045 }
2046 Self::PhysicalCompaction {
2047 debt,
2048 from_floor,
2049 through_seq,
2050 } => {
2051 let debt = ClosureDebt::new(debt).ok_or(StorageRestoreError::ClosureDebt)?;
2052 let edge = PhysicalCompaction::new(from_floor, through_seq)
2053 .ok_or(StorageRestoreError::StoredEdgeProvenance)?;
2054 Ok(DebtCompletion::physical_compaction(debt, edge))
2055 }
2056 }
2057 }
2058}
2059
2060#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2074pub struct FencedAttachCommitRestore {
2075 pub predecessor: DetachedCredentialRecoveryRestore,
2077 pub predecessor_debt: WideResourceVector,
2079 pub participant_id: ParticipantId,
2081 pub marker_delivery_seq: DeliverySeq,
2083 pub prior_binding_epoch: BindingEpoch,
2085 pub new_binding_epoch: BindingEpoch,
2087 pub resulting_floor: DeliverySeq,
2089 pub successor: DebtCompletionRestore,
2091}
2092
2093impl FencedAttachCommitRestore {
2094 #[cfg(test)]
2101 pub(super) fn restore(
2102 self,
2103 record_authority: ValidatedMarkerRecord,
2104 ) -> Result<FencedAttachCommit, StorageRestoreError> {
2105 let restored = self.restore_with_record(&record_authority);
2106 record_authority.consume();
2107 restored
2108 }
2109
2110 fn restore_with_record(
2111 self,
2112 record_authority: &ValidatedMarkerRecord,
2113 ) -> Result<FencedAttachCommit, StorageRestoreError> {
2114 let debt =
2115 ClosureDebt::new(self.predecessor_debt).ok_or(StorageRestoreError::ClosureDebt)?;
2116 let predecessor = self.predecessor.restore_with_debt(debt, record_authority)?;
2117 FencedAttachCommit::restore_validated(
2118 predecessor,
2119 record_authority,
2120 debt,
2121 Event::fenced_recovery_committed(
2122 self.participant_id,
2123 self.marker_delivery_seq,
2124 self.prior_binding_epoch,
2125 self.new_binding_epoch,
2126 self.resulting_floor,
2127 ),
2128 self.successor.restore()?,
2129 )
2130 .ok_or(StorageRestoreError::StoredEdgeProvenance)
2131 }
2132}
2133
2134#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2144pub struct RecoveredBindingFateRestore {
2145 pub fenced_attach: FencedAttachCommitRestore,
2147 pub participant_id: ParticipantId,
2149 pub binding_epoch: BindingEpoch,
2151 pub resulting_floor: DeliverySeq,
2153}
2154
2155impl RecoveredBindingFateRestore {
2156 #[cfg(test)]
2163 pub(super) fn restore(
2164 self,
2165 record_authority: ValidatedMarkerRecord,
2166 ) -> Result<RecoveredBindingFate, StorageRestoreError> {
2167 let restored = self.restore_with_record(&record_authority);
2168 record_authority.consume();
2169 restored
2170 }
2171
2172 fn restore_with_record(
2173 self,
2174 record_authority: &ValidatedMarkerRecord,
2175 ) -> Result<RecoveredBindingFate, StorageRestoreError> {
2176 let commit = self.fenced_attach.restore_with_record(record_authority)?;
2177 commit
2178 .recovered_binding_fate(Event::binding_fate_observed(
2179 self.participant_id,
2180 self.binding_epoch,
2181 self.resulting_floor,
2182 ))
2183 .map_err(|_| StorageRestoreError::StoredEdgeProvenance)
2184 }
2185}
2186
2187#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2189pub struct PendingRecoveredCursorReleaseRestore {
2190 pub fate: RecoveredBindingFateRestore,
2192 pub resulting_debt: WideResourceVector,
2194}
2195
2196impl PendingRecoveredCursorReleaseRestore {
2197 #[cfg(test)]
2204 pub(super) fn restore(
2205 self,
2206 record_authority: ValidatedMarkerRecord,
2207 ) -> Result<PendingRecoveredCursorRelease, StorageRestoreError> {
2208 let restored = self.restore_with_record(&record_authority);
2209 record_authority.consume();
2210 restored
2211 }
2212
2213 fn restore_with_record(
2214 self,
2215 record_authority: &ValidatedMarkerRecord,
2216 ) -> Result<PendingRecoveredCursorRelease, StorageRestoreError> {
2217 let authority = self.fate.restore_with_record(record_authority)?;
2218 let predecessor_state = authority.predecessor_state();
2219 let resulting_debt =
2220 ClosureDebt::new(self.resulting_debt).ok_or(StorageRestoreError::ClosureDebt)?;
2221 let transition = match predecessor_state {
2222 ClosureState::Owed {
2223 debt,
2224 edge: StoredEdge::ObserverProjection(edge),
2225 } => edge.apply_recovered_binding_fate(debt, resulting_debt, authority),
2226 ClosureState::Owed {
2227 debt,
2228 edge: StoredEdge::PhysicalCompaction(edge),
2229 } => edge.apply_recovered_binding_fate(debt, resulting_debt, authority),
2230 ClosureState::Clear | ClosureState::Owed { .. } => {
2231 return Err(StorageRestoreError::StoredEdgeProvenance);
2232 }
2233 }
2234 .map_err(|_| StorageRestoreError::StoredEdgeProvenance)?;
2235 match transition {
2236 RecoveredBindingFateTransition::PendingStorage(pending) => Ok(pending),
2237 RecoveredBindingFateTransition::DetachedCursorRelease(_) => {
2238 Err(StorageRestoreError::StoredEdgeProvenance)
2239 }
2240 }
2241 }
2242}
2243
2244#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2246pub enum RecoveredStorageCompletionRestore {
2247 ObserverProjection {
2249 through_seq: DeliverySeq,
2251 resulting_debt: Option<WideResourceVector>,
2253 },
2254 PhysicalCompaction {
2256 from_floor: DeliverySeq,
2258 through_seq: DeliverySeq,
2260 resulting_floor: DeliverySeq,
2262 resulting_debt: Option<WideResourceVector>,
2264 },
2265}
2266
2267impl RecoveredStorageCompletionRestore {
2268 fn restore(
2269 self,
2270 pending: PendingRecoveredCursorRelease,
2271 ) -> Result<ClosureState, StorageRestoreError> {
2272 let current = pending.current_state();
2273 match (current, self) {
2274 (
2275 ClosureState::Owed {
2276 edge: StoredEdge::ObserverProjection(edge),
2277 ..
2278 },
2279 Self::ObserverProjection {
2280 through_seq,
2281 resulting_debt,
2282 },
2283 ) => edge
2284 .complete_after_recovered_binding_fate(
2285 Event::projection_completed(through_seq),
2286 optional_debt(resulting_debt)?,
2287 pending,
2288 )
2289 .map_err(|_| StorageRestoreError::StoredEdgeProvenance),
2290 (
2291 ClosureState::Owed {
2292 edge: StoredEdge::PhysicalCompaction(edge),
2293 ..
2294 },
2295 Self::PhysicalCompaction {
2296 from_floor,
2297 through_seq,
2298 resulting_floor,
2299 resulting_debt,
2300 },
2301 ) => {
2302 let event = Event::compaction_completed(from_floor, through_seq, resulting_floor)
2303 .ok_or(StorageRestoreError::StoredEdgeProvenance)?;
2304 edge.complete_after_recovered_binding_fate(
2305 event,
2306 optional_debt(resulting_debt)?,
2307 pending,
2308 )
2309 .map_err(|_| StorageRestoreError::StoredEdgeProvenance)
2310 }
2311 _ => Err(StorageRestoreError::StoredEdgeProvenance),
2312 }
2313 }
2314}
2315
2316#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2318pub enum DetachedCursorReleaseProvenanceRestore {
2319 Ordinary(OrdinaryBindingFateRestore),
2321 RecoveredDirect {
2323 fate: RecoveredBindingFateRestore,
2325 resulting_debt: WideResourceVector,
2327 },
2328 RecoveredAfterStorage {
2330 pending: PendingRecoveredCursorReleaseRestore,
2332 completion: RecoveredStorageCompletionRestore,
2334 },
2335}
2336
2337impl DetachedCursorReleaseProvenanceRestore {
2338 const fn ordinary_binding_request(self) -> Option<ActiveBinding> {
2339 match self {
2340 Self::Ordinary(fate) => Some(fate.authority.binding),
2341 Self::RecoveredDirect { .. } | Self::RecoveredAfterStorage { .. } => None,
2342 }
2343 }
2344
2345 const fn marker_record_request(self) -> Option<MarkerRecordRequest> {
2346 match self {
2347 Self::Ordinary(_) => None,
2348 Self::RecoveredDirect { fate, .. } => Some(MarkerRecordRequest::recovered(
2349 fate.participant_id,
2350 fate.fenced_attach.marker_delivery_seq,
2351 fate.fenced_attach.prior_binding_epoch,
2352 fate.fenced_attach.new_binding_epoch,
2353 )),
2354 Self::RecoveredAfterStorage { pending, .. } => Some(MarkerRecordRequest::recovered(
2355 pending.fate.participant_id,
2356 pending.fate.fenced_attach.marker_delivery_seq,
2357 pending.fate.fenced_attach.prior_binding_epoch,
2358 pending.fate.fenced_attach.new_binding_epoch,
2359 )),
2360 }
2361 }
2362
2363 fn restore_state(
2364 self,
2365 debt: ClosureDebt,
2366 marker_authority: &MarkerRestoreAuthority<'_>,
2367 ordinary_origin: Option<&BindingOrigin>,
2368 ) -> Result<ClosureState, StorageRestoreError> {
2369 match self {
2370 Self::Ordinary(provenance) => {
2371 marker_authority.require_absent()?;
2372 let origin = ordinary_origin.ok_or(StorageRestoreError::StoredEdgeProvenance)?;
2373 Ok(provenance
2374 .restore_with_origin(origin)?
2375 .into_direct_state(debt))
2376 }
2377 Self::RecoveredDirect {
2378 fate,
2379 resulting_debt,
2380 } => {
2381 let (_, record_authority) = marker_authority.require_record()?;
2382 let authority = fate.restore_with_record(record_authority)?;
2383 let predecessor = authority.predecessor_state();
2384 let resulting_debt =
2385 ClosureDebt::new(resulting_debt).ok_or(StorageRestoreError::ClosureDebt)?;
2386 let transition = match predecessor {
2387 ClosureState::Owed {
2388 debt: predecessor_debt,
2389 edge: StoredEdge::PhysicalCompaction(edge),
2390 } => edge.apply_recovered_binding_fate(
2391 predecessor_debt,
2392 resulting_debt,
2393 authority,
2394 ),
2395 ClosureState::Clear | ClosureState::Owed { .. } => {
2396 return Err(StorageRestoreError::StoredEdgeProvenance);
2397 }
2398 }
2399 .map_err(|_| StorageRestoreError::StoredEdgeProvenance)?;
2400 match transition {
2401 RecoveredBindingFateTransition::DetachedCursorRelease(release)
2402 if release.debt() == debt =>
2403 {
2404 Ok(release.into_state())
2405 }
2406 RecoveredBindingFateTransition::PendingStorage(_)
2407 | RecoveredBindingFateTransition::DetachedCursorRelease(_) => {
2408 Err(StorageRestoreError::StoredEdgeProvenance)
2409 }
2410 }
2411 }
2412 Self::RecoveredAfterStorage {
2413 pending,
2414 completion,
2415 } => {
2416 let (_, record_authority) = marker_authority.require_record()?;
2417 let state = completion.restore(pending.restore_with_record(record_authority)?)?;
2418 match state {
2419 ClosureState::Owed {
2420 debt: restored_debt,
2421 edge: StoredEdge::DetachedCursorRelease(_),
2422 } if restored_debt == debt => Ok(state),
2423 ClosureState::Clear | ClosureState::Owed { .. } => {
2424 Err(StorageRestoreError::StoredEdgeProvenance)
2425 }
2426 }
2427 }
2428 }
2429 }
2430}
2431
2432#[allow(clippy::large_enum_variant)]
2434#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2435pub enum StoredEdgeRestore {
2436 ObserverProjection {
2438 through_seq: DeliverySeq,
2440 },
2441 PhysicalCompaction {
2443 from_floor: DeliverySeq,
2445 through_seq: DeliverySeq,
2447 },
2448 MarkerDelivery(MarkerDeliveryRestore),
2450 ParticipantCursorProgressContinuous {
2452 participant_id: ParticipantId,
2454 binding_epoch: BindingEpoch,
2456 through_seq: DeliverySeq,
2458 authority: OrdinaryBindingAuthorityRestore,
2460 },
2461 ParticipantCursorProgressMarker(MarkerCursorProgressRestore),
2463 DetachedCredentialRecovery(DetachedCredentialRecoveryRestore),
2465 DetachedMarkerRelease(DetachedMarkerReleaseRestore),
2467 DetachedCursorRelease {
2469 participant_id: ParticipantId,
2471 last_dead_binding_epoch: BindingEpoch,
2473 provenance: DetachedCursorReleaseProvenanceRestore,
2475 },
2476}
2477
2478impl StoredEdgeRestore {
2479 const fn ordinary_binding_request(self) -> Option<ActiveBinding> {
2480 match self {
2481 Self::ParticipantCursorProgressContinuous { authority, .. } => Some(authority.binding),
2482 Self::DetachedCursorRelease { provenance, .. } => provenance.ordinary_binding_request(),
2483 Self::ObserverProjection { .. }
2484 | Self::PhysicalCompaction { .. }
2485 | Self::MarkerDelivery(_)
2486 | Self::ParticipantCursorProgressMarker(_)
2487 | Self::DetachedCredentialRecovery(_)
2488 | Self::DetachedMarkerRelease(_) => None,
2489 }
2490 }
2491
2492 const fn marker_record_request(self) -> Option<MarkerRecordRequest> {
2493 match self {
2494 Self::MarkerDelivery(value) => Some(MarkerRecordRequest::planned(
2495 value.participant_id,
2496 value.marker_delivery_seq,
2497 super::FrontierBinding::Bound(value.binding_epoch),
2498 )),
2499 Self::ParticipantCursorProgressMarker(value) => Some(MarkerRecordRequest::delivered(
2500 value.participant_id,
2501 value.marker_delivery_seq,
2502 super::FrontierBinding::Bound(value.binding_epoch),
2503 )),
2504 Self::DetachedCredentialRecovery(value) => Some(MarkerRecordRequest::delivered(
2505 value.participant_id,
2506 value.marker_delivery_seq,
2507 super::FrontierBinding::Detached(value.prior_binding_epoch),
2508 )),
2509 Self::DetachedMarkerRelease(value) => Some(MarkerRecordRequest::planned(
2510 value.participant_id,
2511 value.marker_delivery_seq,
2512 super::FrontierBinding::Detached(value.last_dead_binding_epoch),
2513 )),
2514 Self::DetachedCursorRelease { provenance, .. } => provenance.marker_record_request(),
2515 Self::ObserverProjection { .. }
2516 | Self::PhysicalCompaction { .. }
2517 | Self::ParticipantCursorProgressContinuous { .. } => None,
2518 }
2519 }
2520
2521 fn restore_with_debt(
2522 self,
2523 debt: ClosureDebt,
2524 marker_authority: &MarkerRestoreAuthority<'_>,
2525 ordinary_origin: Option<&BindingOrigin>,
2526 ) -> Result<StoredEdge, StorageRestoreError> {
2527 match self {
2528 Self::ObserverProjection { through_seq } => {
2529 marker_authority.require_absent()?;
2530 Ok(StoredEdge::ObserverProjection(ObserverProjection::new(
2531 through_seq,
2532 )))
2533 }
2534 Self::PhysicalCompaction {
2535 from_floor,
2536 through_seq,
2537 } => {
2538 marker_authority.require_absent()?;
2539 PhysicalCompaction::new(from_floor, through_seq)
2540 .map(StoredEdge::PhysicalCompaction)
2541 .ok_or(StorageRestoreError::StoredEdgeProvenance)
2542 }
2543 Self::MarkerDelivery(value) => {
2544 let (conversation_id, record_authority) = marker_authority.require_record()?;
2545 value
2546 .restore_with_target(
2547 conversation_id,
2548 record_authority,
2549 MarkerRecordTarget::Bound,
2550 MarkerRecordOccurrence::Undelivered,
2551 )
2552 .map(StoredEdge::MarkerDelivery)
2553 }
2554 Self::ParticipantCursorProgressContinuous {
2555 participant_id,
2556 binding_epoch,
2557 through_seq,
2558 authority,
2559 } => {
2560 marker_authority.require_absent()?;
2561 let origin = ordinary_origin.ok_or(StorageRestoreError::StoredEdgeProvenance)?;
2562 ParticipantCursorProgress::restore_continuous(
2563 authority.restore_with_origin(origin)?,
2564 participant_id,
2565 binding_epoch,
2566 through_seq,
2567 )
2568 .map(StoredEdge::ParticipantCursorProgress)
2569 .ok_or(StorageRestoreError::StoredEdgeProvenance)
2570 }
2571 Self::ParticipantCursorProgressMarker(value) => {
2572 let record_authority = marker_authority.record_for(value.conversation_id)?;
2573 value
2574 .restore_with_debt(debt, record_authority, MarkerRecordTarget::Bound)
2575 .map(StoredEdge::ParticipantCursorProgress)
2576 }
2577 Self::DetachedCredentialRecovery(value) => {
2578 let record_authority =
2579 marker_authority.record_for(value.progress.conversation_id)?;
2580 value
2581 .restore_with_debt(debt, record_authority)
2582 .map(StoredEdge::DetachedCredentialRecovery)
2583 }
2584 Self::DetachedMarkerRelease(value) => {
2585 let record_authority = marker_authority.record_for(value.conversation_id)?;
2586 value
2587 .restore_with_marker_record(record_authority)
2588 .map(StoredEdge::DetachedMarkerRelease)
2589 }
2590 Self::DetachedCursorRelease {
2591 participant_id,
2592 last_dead_binding_epoch,
2593 provenance,
2594 } => {
2595 let state = provenance.restore_state(debt, marker_authority, ordinary_origin)?;
2596 match state {
2597 ClosureState::Owed {
2598 edge: StoredEdge::DetachedCursorRelease(edge),
2599 ..
2600 } if edge.participant_id() == participant_id
2601 && edge.last_dead_binding_epoch() == last_dead_binding_epoch =>
2602 {
2603 Ok(StoredEdge::DetachedCursorRelease(edge))
2604 }
2605 ClosureState::Clear | ClosureState::Owed { .. } => {
2606 Err(StorageRestoreError::StoredEdgeProvenance)
2607 }
2608 }
2609 }
2610 }
2611 }
2612}
2613
2614#[allow(clippy::large_enum_variant)]
2616#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2617pub enum ClosureStateRestore {
2618 Clear,
2620 Owed {
2622 debt: WideResourceVector,
2624 edge: StoredEdgeRestore,
2626 },
2627}
2628
2629impl ClosureStateRestore {
2630 const fn ordinary_binding_request(self) -> Option<ActiveBinding> {
2631 match self {
2632 Self::Clear => None,
2633 Self::Owed { edge, .. } => edge.ordinary_binding_request(),
2634 }
2635 }
2636
2637 const fn marker_record_request(self) -> Option<MarkerRecordRequest> {
2638 match self {
2639 Self::Clear => None,
2640 Self::Owed { edge, .. } => edge.marker_record_request(),
2641 }
2642 }
2643
2644 pub fn restore(self) -> Result<ClosureState, StorageRestoreError> {
2651 self.restore_with_authority(&MarkerRestoreAuthority::Absent, None)
2652 }
2653
2654 pub(super) fn restore_with_marker_record(
2667 self,
2668 conversation_id: ConversationId,
2669 record: ValidatedMarkerRecord,
2670 ) -> Result<ClosureState, StorageRestoreError> {
2671 let restored = self.restore_with_authority(
2672 &MarkerRestoreAuthority::Record {
2673 conversation_id,
2674 record: &record,
2675 },
2676 None,
2677 );
2678 record.consume();
2679 restored
2680 }
2681
2682 fn restore_with_binding_origin(
2683 self,
2684 origin: &BindingOrigin,
2685 ) -> Result<ClosureState, StorageRestoreError> {
2686 self.restore_with_authority(&MarkerRestoreAuthority::Absent, Some(origin))
2687 }
2688
2689 fn restore_with_authority(
2690 self,
2691 marker_authority: &MarkerRestoreAuthority<'_>,
2692 ordinary_origin: Option<&BindingOrigin>,
2693 ) -> Result<ClosureState, StorageRestoreError> {
2694 match self {
2695 Self::Clear => {
2696 marker_authority.require_absent()?;
2697 Ok(ClosureState::Clear)
2698 }
2699 Self::Owed { debt, edge } => {
2700 let debt = ClosureDebt::new(debt).ok_or(StorageRestoreError::ClosureDebt)?;
2701 Ok(ClosureState::Owed {
2702 debt,
2703 edge: edge.restore_with_debt(debt, marker_authority, ordinary_origin)?,
2704 })
2705 }
2706 }
2707 }
2708}
2709
2710fn optional_debt(
2711 raw: Option<WideResourceVector>,
2712) -> Result<Option<ClosureDebt>, StorageRestoreError> {
2713 raw.map(|value| ClosureDebt::new(value).ok_or(StorageRestoreError::ClosureDebt))
2714 .transpose()
2715}