Skip to main content

ic_backup/model/ic_snapshot_upload_observation/
mod.rs

1//! Exact reserved inventory observations of unresolved metadata uploads.
2
3use crate::model::{
4    attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
5    ic_observation::{IcObservationRequestError, ObservationReservation},
6    ic_request::{IcManagementMethodRecord, IcManagementRequestRecord},
7    ic_snapshot_upload::{
8        IcSnapshotUploadAttemptError, IcSnapshotUploadKind, IcSnapshotUploadRequest,
9        original_authority,
10    },
11    operation_plan::OperationPlanRecord,
12};
13use thiserror::Error;
14
15mod settlement;
16pub use settlement::{IcSnapshotUploadAttribution, IcSnapshotUploadSettlement};
17
18/// Original metadata-upload intent and its already spent exact list observation.
19///
20/// This declaration supplies no dispatch permission, authentication or allocation
21/// attribution. Integrations retain original bytes and qualify fresh read access,
22/// chronology, command custody and proof of no prior observation dispatch.
23/// Lost observations stay pending; reconstruction permits no repeated call.
24#[derive(Debug)]
25pub struct IcSnapshotUploadObservationRequest<'request, 'source> {
26    plan: &'request OperationPlanRecord,
27    mutation: &'request IcSnapshotUploadRequest<'source>,
28    payload: &'request IcManagementRequestRecord,
29    reservation: ObservationReservation,
30}
31
32impl<'request, 'source> IcSnapshotUploadObservationRequest<'request, 'source> {
33    /// Bind exact original metadata intent and an independently reserved list payload.
34    ///
35    /// Data uploads and status observations have no admission through this boundary.
36    /// No journal is created, reserved, reset or settled by construction.
37    /// # Errors
38    /// Rejects unsupported methods, changed original bindings and missing reservations.
39    pub fn new(
40        plan: &'request OperationPlanRecord,
41        operation_sequence: u64,
42        journal: &AttemptJournalRecord,
43        mutation: &'request IcSnapshotUploadRequest<'source>,
44        payload: &'request IcManagementRequestRecord,
45    ) -> Result<Self, IcSnapshotUploadObservationError> {
46        if !matches!(mutation.kind(), IcSnapshotUploadKind::Metadata) {
47            return Err(IcSnapshotUploadObservationError::UnsupportedUpload);
48        }
49        if payload.method() != IcManagementMethodRecord::ListCanisterSnapshots {
50            return Err(IcSnapshotUploadObservationError::UnsupportedObservation);
51        }
52        let authority = original_authority(plan, operation_sequence, journal, mutation)?;
53        payload
54            .validate_observation_binding(authority.binding(), &payload.digest())
55            .map_err(IcObservationRequestError::from)?;
56        let reservation = ObservationReservation::new(authority, journal, payload.digest())?;
57        let request = Self {
58            plan,
59            mutation,
60            payload,
61            reservation,
62        };
63        Ok(request)
64    }
65
66    /// Read the complete original upload plan and immutable allowances.
67    #[must_use]
68    pub const fn plan(&self) -> &OperationPlanRecord {
69        self.plan
70    }
71    /// Read exact original metadata upload bytes and retained source binding.
72    #[must_use]
73    pub const fn mutation(&self) -> &'request IcSnapshotUploadRequest<'source> {
74        self.mutation
75    }
76    /// Read the original accounted list receiver, target, method and Candid bytes.
77    #[must_use]
78    pub const fn payload(&self) -> &'request IcManagementRequestRecord {
79        self.payload
80    }
81    /// Read original plan-derived authority, without fresh spending permission.
82    #[must_use]
83    pub const fn authority(&self) -> &AttemptAuthorityRecord {
84        &self.reservation.authority
85    }
86    /// Read the original unresolved metadata-upload attempt.
87    #[must_use]
88    pub const fn mutation_attempt(&self) -> u32 {
89        self.reservation.mutation_attempt
90    }
91    /// Read the already consumed inventory-observation attempt.
92    #[must_use]
93    pub const fn observation_attempt(&self) -> u32 {
94        self.reservation.observation_attempt
95    }
96    /// Recheck exact current authority, both pending attempts and reserved list bytes.
97    /// # Errors
98    /// Rejects changed/settled reservations or different original authority/payload.
99    pub fn validate_journal(
100        &self,
101        journal: &AttemptJournalRecord,
102    ) -> Result<(), IcObservationRequestError> {
103        self.reservation.validate(journal)
104    }
105}
106
107/// Structural upload/list admission failure; no outcome or spending transition.
108#[derive(Debug, Error)]
109pub enum IcSnapshotUploadObservationError {
110    /// This inventory boundary admits metadata allocation only.
111    #[error("snapshot upload observation requires metadata intent")]
112    UnsupportedUpload,
113    /// Only the exact original list payload is supported.
114    #[error("snapshot upload observation requires snapshot list")]
115    UnsupportedObservation,
116    /// Original upload authority, bytes or source context differs.
117    #[error(transparent)]
118    Upload(#[from] IcSnapshotUploadAttemptError),
119    /// Existing original observation reservation admission failed.
120    #[error(transparent)]
121    Observation(#[from] IcObservationRequestError),
122}
123
124#[cfg(test)]
125mod tests;