Skip to main content

ic_backup/model/application_ingress/request/
mod.rs

1//! Passive original application reservations; reconstructing one is never a resend permit.
2
3use super::{ApplicationIngressError, ApplicationIngressPayload};
4use crate::model::attempt_journal::MutationReservation;
5use crate::model::attempt_journal::ObservationReservation;
6use crate::model::{
7    attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
8    operation_plan::OperationPlanRecord,
9};
10
11/// Exact original application mutation after durable reservation.
12///
13/// The caller holds its original guard and independently admits fresh authority,
14/// dependencies, application safety and exclusive never-dispatched custody.
15#[derive(Debug)]
16pub struct ApplicationMutationRequest<'a> {
17    plan: &'a OperationPlanRecord,
18    payload: &'a ApplicationIngressPayload,
19    reservation: MutationReservation,
20}
21impl<'a> ApplicationMutationRequest<'a> {
22    /// Bind original exact opaque bytes to the current pending mutation.
23    /// # Errors
24    /// Rejects changed authority/payload, absent mutation or pending recovery.
25    pub fn new(
26        plan: &'a OperationPlanRecord,
27        sequence: u64,
28        journal: &AttemptJournalRecord,
29        payload: &'a ApplicationIngressPayload,
30    ) -> Result<Self, ApplicationIngressError> {
31        let authority = authority(plan, sequence, journal)?;
32        payload.validate_mutation_binding(authority.binding())?;
33        let reservation = MutationReservation::new(
34            authority,
35            journal,
36            ApplicationIngressError::NoPendingMutation,
37            ApplicationIngressError::AuthorityMismatch,
38            ApplicationIngressError::MutationMismatch,
39            ApplicationIngressError::ObservationPending,
40        )?;
41        Ok(Self {
42            plan,
43            payload,
44            reservation,
45        })
46    }
47    /// Read the complete original plan/context and inventory.
48    #[must_use]
49    pub const fn plan(&self) -> &OperationPlanRecord {
50        self.plan
51    }
52    /// Read exact opaque update fields.
53    #[must_use]
54    pub const fn payload(&self) -> &ApplicationIngressPayload {
55        self.payload
56    }
57    /// Read full original authority and finite allowances.
58    #[must_use]
59    pub const fn authority(&self) -> &AttemptAuthorityRecord {
60        &self.reservation.authority
61    }
62    /// Read the already consumed mutation attempt.
63    #[must_use]
64    pub const fn mutation_attempt(&self) -> u32 {
65        self.reservation.mutation_attempt
66    }
67    /// Recheck current exact original reservation without authorizing dispatch.
68    /// # Errors
69    /// Rejects changed authority, mutation or pending observation.
70    pub fn validate_journal(
71        &self,
72        journal: &AttemptJournalRecord,
73    ) -> Result<(), ApplicationIngressError> {
74        self.reservation.validate(
75            journal,
76            ApplicationIngressError::AuthorityMismatch,
77            ApplicationIngressError::MutationMismatch,
78            ApplicationIngressError::ObservationPending,
79        )
80    }
81}
82
83/// Exact separately reserved application recovery update for an unresolved mutation.
84///
85/// This consumes the observation lane, even when its opaque method reads status.
86/// It never reuses mutation allowance or supplies application settlement semantics.
87#[derive(Debug)]
88pub struct ApplicationObservationRequest<'a> {
89    plan: &'a OperationPlanRecord,
90    payload: &'a ApplicationIngressPayload,
91    reservation: ObservationReservation,
92}
93impl<'a> ApplicationObservationRequest<'a> {
94    /// Bind exact recovery bytes to the independently reserved observation digest.
95    /// # Errors
96    /// Rejects changed authority/target, absent attempts or different reserved bytes.
97    pub fn new(
98        plan: &'a OperationPlanRecord,
99        sequence: u64,
100        journal: &AttemptJournalRecord,
101        payload: &'a ApplicationIngressPayload,
102    ) -> Result<Self, ApplicationIngressError> {
103        let authority = authority(plan, sequence, journal)?;
104        payload.validate_observation_binding(authority.binding())?;
105        let reservation = ObservationReservation::new(authority, journal, payload.digest())
106            .map_err(crate::model::ic_observation::IcObservationRequestError::from)?;
107        Ok(Self {
108            plan,
109            payload,
110            reservation,
111        })
112    }
113    /// Read the complete original mutation plan/context and inventory.
114    #[must_use]
115    pub const fn plan(&self) -> &OperationPlanRecord {
116        self.plan
117    }
118    /// Read exact original recovery update fields.
119    #[must_use]
120    pub const fn payload(&self) -> &ApplicationIngressPayload {
121        self.payload
122    }
123    /// Read original authority; its request still names the unresolved mutation.
124    #[must_use]
125    pub const fn authority(&self) -> &AttemptAuthorityRecord {
126        &self.reservation.authority
127    }
128    /// Read the original unresolved mutation attempt.
129    #[must_use]
130    pub const fn mutation_attempt(&self) -> u32 {
131        self.reservation.mutation_attempt
132    }
133    /// Read the independently consumed observation attempt.
134    #[must_use]
135    pub const fn observation_attempt(&self) -> u32 {
136        self.reservation.observation_attempt
137    }
138    /// Recheck exact current observation identity without a dispatch permit.
139    /// # Errors
140    /// Rejects settled/replaced attempts, changed authority or different reserved bytes.
141    pub fn validate_journal(
142        &self,
143        journal: &AttemptJournalRecord,
144    ) -> Result<(), ApplicationIngressError> {
145        self.reservation
146            .validate(journal)
147            .map_err(crate::model::ic_observation::IcObservationRequestError::from)?;
148        Ok(())
149    }
150}
151
152fn authority(
153    plan: &OperationPlanRecord,
154    sequence: u64,
155    journal: &AttemptJournalRecord,
156) -> Result<AttemptAuthorityRecord, ApplicationIngressError> {
157    let authority = plan.attempt_authority(sequence)?;
158    if journal.authority() != &authority {
159        return Err(ApplicationIngressError::AuthorityMismatch);
160    }
161    Ok(authority)
162}