Skip to main content

ic_backup/policy/ic_snapshot_upload/
mod.rs

1//! Pure current-reservation and passive upload-reply association; no IO or settlement.
2
3#[cfg(test)]
4mod tests;
5
6use super::response_identity::{ResponseIdentity, ResponseIdentityMismatch};
7use crate::model::{
8    attempt_journal::AttemptJournalRecord,
9    ic_mutation::IcMutationAcknowledgement,
10    ic_snapshot_upload::{
11        IcSnapshotUploadAttempt, IcSnapshotUploadAttemptError, IcSnapshotUploadError,
12        IcSnapshotUploadReply,
13    },
14};
15use thiserror::Error;
16
17/// Read-only retained original acknowledgement and bounded source-bound upload reply.
18#[derive(Debug)]
19pub struct IcSnapshotUploadAcknowledgementView<'request, 'source> {
20    acknowledgement: &'request IcMutationAcknowledgement,
21    reply: IcSnapshotUploadReply<'request, 'source>,
22}
23impl<'request, 'source> IcSnapshotUploadAcknowledgementView<'request, 'source> {
24    /// Read exact claimed context/target/authority/attempt/raw bytes/evidence.
25    #[must_use]
26    pub const fn acknowledgement(&self) -> &'request IcMutationAcknowledgement {
27        self.acknowledgement
28    }
29    /// Read passive bounded metadata ID or data acknowledgement; no Applied receipt.
30    #[must_use]
31    pub const fn reply(&self) -> &IcSnapshotUploadReply<'request, 'source> {
32        &self.reply
33    }
34}
35
36/// Recheck exact original pending upload and passive actual association fields.
37///
38/// Reuses the existing bounded acknowledgement owner, with tighter upload decoding.
39/// Actual authentication, exclusive original allocation/write attribution, freshness,
40/// controller/byte custody and independent per-call accounting remain integration-owned.
41/// Lost replies stay pending. Zero/one/multiple inventory entries or absent data never
42/// establish an outcome. Only separately qualified settled observations can reconcile;
43/// no observation, receipt, retry, refund, completion or release is produced here.
44/// # Errors
45/// Rejects original reservation drift, mismatching actual claims and invalid reply shape.
46pub fn validate_acknowledgement<'request, 'source>(
47    request: &IcSnapshotUploadAttempt<'request, 'source>,
48    journal: &AttemptJournalRecord,
49    acknowledgement: &'request IcMutationAcknowledgement,
50) -> Result<IcSnapshotUploadAcknowledgementView<'request, 'source>, IcSnapshotUploadAssociationError>
51{
52    request.validate_journal(journal)?;
53    let input = acknowledgement.input();
54    ResponseIdentity {
55        authority: &input.authority,
56        mutation_attempt: input.mutation_attempt,
57        context: &input.context,
58        target: &input.target,
59    }
60    .compare(&ResponseIdentity {
61        authority: &request.authority().digest(),
62        mutation_attempt: request.mutation_attempt(),
63        context: request.plan().context(),
64        target: request.payload().target(),
65    })
66    .map_err(|mismatch| match mismatch {
67        ResponseIdentityMismatch::Authority => IcSnapshotUploadAssociationError::AuthorityMismatch,
68        ResponseIdentityMismatch::Attempt => IcSnapshotUploadAssociationError::AttemptMismatch,
69        ResponseIdentityMismatch::Context => IcSnapshotUploadAssociationError::ContextMismatch,
70        ResponseIdentityMismatch::Target => IcSnapshotUploadAssociationError::TargetMismatch,
71    })?;
72    let reply = IcSnapshotUploadReply::decode(request.payload(), &input.reply)?;
73    Ok(IcSnapshotUploadAcknowledgementView {
74        acknowledgement,
75        reply,
76    })
77}
78
79/// Typed passive association denial; all original obligations and counters remain retained.
80#[derive(Debug, Error)]
81pub enum IcSnapshotUploadAssociationError {
82    /// Exact current original mutation/reservation no longer matches.
83    #[error(transparent)]
84    Reservation(#[from] IcSnapshotUploadAttemptError),
85    /// Claimed full original authority differs.
86    #[error("snapshot upload acknowledgement authority mismatch")]
87    AuthorityMismatch,
88    /// Claimed already allocated mutation differs.
89    #[error("snapshot upload acknowledgement attempt mismatch")]
90    AttemptMismatch,
91    /// Claimed actual network/caller/release differs.
92    #[error("snapshot upload acknowledgement context mismatch")]
93    ContextMismatch,
94    /// Claimed actual target differs.
95    #[error("snapshot upload acknowledgement target mismatch")]
96    TargetMismatch,
97    /// Canonical bounded upload decoder rejected the raw reply.
98    #[error(transparent)]
99    Reply(#[from] IcSnapshotUploadError),
100}