Skip to main content

liminal_protocol/lifecycle/admission/
observer.rs

1use crate::wire::{
2    AttachEnvelope, BindingEpoch, DeliverySeq, DetachEnvelope, EnrollmentEnvelope, LeaveEnvelope,
3    ObserverBackpressure, ObserverBackpressureState, RecordAdmissionEnvelope,
4};
5
6/// Operations whose candidate floor is checked against hard observer retention.
7///
8/// The variants carry every operation-specific field required by the frozen
9/// `ObserverBackpressure` register. Normal and marker acknowledgements are
10/// deliberately absent because they can never return this outcome.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum ObserverCheckedOperation {
13    /// Participant enrollment.
14    Enrollment(EnrollmentEnvelope),
15    /// Credential attach or supersession.
16    CredentialAttach(AttachEnvelope),
17    /// Initial detach of the exact committed binding epoch.
18    InitialDetach {
19        /// Exact detach request envelope.
20        request: DetachEnvelope,
21        /// Binding epoch the detach would terminalize.
22        committed_binding_epoch: BindingEpoch,
23    },
24    /// Terminal Leave.
25    Leave {
26        /// Exact Leave request envelope.
27        request: LeaveEnvelope,
28        /// Whether an earlier binding-terminal cell already exists.
29        prior_terminal_cell_exists: bool,
30    },
31    /// Ordinary record admission.
32    RecordAdmission(RecordAdmissionEnvelope),
33}
34
35/// Opaque proof that one protocol-computed candidate floor passed stage 11.
36///
37/// Construction is private. A consuming server can persist and commit the
38/// exact checked values, but cannot create a permit without running the shared
39/// protocol selector.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub struct ObserverFloorPermit {
42    observer_progress: DeliverySeq,
43    cap_floor: u128,
44}
45
46impl ObserverFloorPermit {
47    /// Returns the observer progress against which the floor was checked.
48    #[must_use]
49    pub const fn observer_progress(self) -> DeliverySeq {
50        self.observer_progress
51    }
52
53    /// Returns the exact protocol-computed candidate capacity floor.
54    #[must_use]
55    pub const fn cap_floor(self) -> u128 {
56        self.cap_floor
57    }
58}
59
60/// Stage-11 observer hard-retention decision.
61#[derive(Clone, Debug, PartialEq, Eq)]
62pub enum ObserverFloorDecision {
63    /// `cap_floor <= observer_progress + 1`; commit may continue with this proof.
64    Eligible(ObserverFloorPermit),
65    /// The candidate would remove a sequence above hard observer progress.
66    Respond(ObserverBackpressure),
67}
68
69/// Applies the observer hard-retention selector to a protocol-computed floor.
70///
71/// The comparison is widened before checked one-past progress is formed.
72/// Equality passes. A strict excess returns the exact operation-specific
73/// [`ObserverBackpressure`] with an initial refusal epoch equal to current
74/// observer progress.
75#[must_use]
76pub fn check_observer_floor(
77    operation: ObserverCheckedOperation,
78    observer_progress: DeliverySeq,
79    cap_floor: u128,
80) -> ObserverFloorDecision {
81    let observer_limit = u128::from(observer_progress) + 1;
82    if cap_floor <= observer_limit {
83        return ObserverFloorDecision::Eligible(ObserverFloorPermit {
84            observer_progress,
85            cap_floor,
86        });
87    }
88
89    let state = ObserverBackpressureState::initial(observer_progress);
90    let response = match operation {
91        ObserverCheckedOperation::Enrollment(request) => {
92            ObserverBackpressure::Enrollment { request, state }
93        }
94        ObserverCheckedOperation::CredentialAttach(request) => {
95            ObserverBackpressure::CredentialAttach { request, state }
96        }
97        ObserverCheckedOperation::InitialDetach {
98            request,
99            committed_binding_epoch,
100        } => ObserverBackpressure::Detach {
101            request,
102            committed_binding_epoch,
103            state,
104        },
105        ObserverCheckedOperation::Leave {
106            request,
107            prior_terminal_cell_exists,
108        } => ObserverBackpressure::Leave {
109            request,
110            state,
111            prior_terminal_cell_exists,
112        },
113        ObserverCheckedOperation::RecordAdmission(request) => {
114            ObserverBackpressure::RecordAdmission { request, state }
115        }
116    };
117    ObserverFloorDecision::Respond(response)
118}