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