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