use crate::model::{
artifacts::ArtifactChecksumRecord,
download_journal::{ArtifactStateRecord, DownloadJournalRecord},
operation_plan::OperationPlanRecord,
};
use thiserror::Error;
pub fn validate<'a>(
plan: &OperationPlanRecord,
journal: &'a DownloadJournalRecord,
) -> Result<&'a ArtifactChecksumRecord, DownloadIntegrityPolicyError> {
if plan.digest().hash() != journal.intent() {
return Err(DownloadIntegrityPolicyError::IntentMismatch);
}
if plan.selected_targets().len() != journal.artifacts().len()
|| plan
.selected_targets()
.iter()
.zip(journal.artifacts())
.any(|(target, artifact)| target != artifact.canister_id())
{
return Err(DownloadIntegrityPolicyError::TargetSetMismatch);
}
let artifact = &journal.artifacts()[0];
let checksum = artifact
.checksum()
.filter(|_| artifact.state() == ArtifactStateRecord::Durable)
.ok_or_else(|| DownloadIntegrityPolicyError::NonDurableArtifact {
canister_id: artifact.canister_id().to_owned(),
state: artifact.state(),
})?;
Ok(checksum)
}
#[derive(Debug, Error, Eq, PartialEq)]
pub enum DownloadIntegrityPolicyError {
#[error("download journal differs from original plan intent")]
IntentMismatch,
#[error("download journal differs from the exact selected target set")]
TargetSetMismatch,
#[error("artifact for {canister_id} lacks durable checksum evidence ({state:?})")]
NonDurableArtifact {
canister_id: String,
state: ArtifactStateRecord,
},
}
#[cfg(test)]
mod tests;