Skip to main content

ic_backup/policy/download_integrity/
mod.rs

1//! Pure original-plan admission of durable local download declarations.
2
3use crate::model::{
4    artifacts::ArtifactChecksumRecord,
5    download_journal::{ArtifactStateRecord, DownloadJournalRecord},
6    operation_plan::OperationPlanRecord,
7};
8use thiserror::Error;
9
10/// Require original intent, exact selected-target coverage and durable checksums.
11///
12/// The canonical record owners already bound/normalize entries and reject duplicates.
13/// This comparison neither observes bytes nor authenticates retained declarations.
14/// Snapshot tokens/timestamp/size remain exactly those retained by the journal;
15/// the pre-capture plan cannot establish an eventual captured snapshot's identity.
16///
17/// # Errors
18/// Rejects changed full-plan intent, missing/extra/different selected targets and
19/// any artifact without retained durable publication and checksum.
20pub fn validate<'a>(
21    plan: &OperationPlanRecord,
22    journal: &'a DownloadJournalRecord,
23) -> Result<&'a ArtifactChecksumRecord, DownloadIntegrityPolicyError> {
24    if plan.digest().hash() != journal.intent() {
25        return Err(DownloadIntegrityPolicyError::IntentMismatch);
26    }
27    if plan.selected_targets().len() != journal.artifacts().len()
28        || plan
29            .selected_targets()
30            .iter()
31            .zip(journal.artifacts())
32            .any(|(target, artifact)| target != artifact.canister_id())
33    {
34        return Err(DownloadIntegrityPolicyError::TargetSetMismatch);
35    }
36    let artifact = &journal.artifacts()[0];
37    let checksum = artifact
38        .checksum()
39        .filter(|_| artifact.state() == ArtifactStateRecord::Durable)
40        .ok_or_else(|| DownloadIntegrityPolicyError::NonDurableArtifact {
41            canister_id: artifact.canister_id().to_owned(),
42            state: artifact.state(),
43        })?;
44    Ok(checksum)
45}
46
47/// Typed structural mismatch, before any artifact IO or remote effect.
48#[derive(Debug, Error, Eq, PartialEq)]
49pub enum DownloadIntegrityPolicyError {
50    /// The journal is not bound to the full original plan digest.
51    #[error("download journal differs from original plan intent")]
52    IntentMismatch,
53    /// Journal targets are not exactly the original selected physical set.
54    #[error("download journal differs from the exact selected target set")]
55    TargetSetMismatch,
56    /// An exact artifact lacks retained durable publication and its checksum.
57    #[error("artifact for {canister_id} lacks durable checksum evidence ({state:?})")]
58    NonDurableArtifact {
59        /// Canonical exact physical target.
60        canister_id: String,
61        /// Actually retained local state.
62        state: ArtifactStateRecord,
63    },
64}
65
66#[cfg(test)]
67mod tests;