use super::{
IcSnapshotUploadDataObservationAssociationError, IcSnapshotUploadDataObservationView,
validate_response,
};
use crate::model::{
artifacts::ArtifactChecksumRecord,
attempt_journal::{AttemptJournalRecord, ObservationOutcomeRecord},
ic_snapshot_upload_data_observation::{
IcSnapshotUploadDataAttribution, IcSnapshotUploadDataObservationRequest,
IcSnapshotUploadDataObservationResponse, IcSnapshotUploadDataSettlement,
},
};
use thiserror::Error;
#[derive(Debug)]
pub struct IcSnapshotUploadDataSettlementView<'request, 'metadata> {
observation: IcSnapshotUploadDataObservationView<'request, 'metadata>,
settlement: &'request IcSnapshotUploadDataSettlement,
outcome: ObservationOutcomeRecord,
}
impl<'request, 'metadata> IcSnapshotUploadDataSettlementView<'request, 'metadata> {
#[must_use]
pub const fn outcome(&self) -> ObservationOutcomeRecord {
self.outcome
}
#[must_use]
pub const fn observation(&self) -> &IcSnapshotUploadDataObservationView<'request, 'metadata> {
&self.observation
}
#[must_use]
pub const fn settlement(&self) -> &'request IcSnapshotUploadDataSettlement {
self.settlement
}
}
pub fn validate_settlement<'request, 'metadata>(
request: &IcSnapshotUploadDataObservationRequest<'request, '_, 'metadata>,
journal: &AttemptJournalRecord,
response: &'request IcSnapshotUploadDataObservationResponse,
challenge: &ArtifactChecksumRecord,
settlement: &'request IcSnapshotUploadDataSettlement,
) -> Result<
IcSnapshotUploadDataSettlementView<'request, 'metadata>,
IcSnapshotUploadDataSettlementError,
> {
let observation = validate_response(request, journal, response)?;
if settlement.authority != request.authority().digest() {
return Err(IcSnapshotUploadDataSettlementError::AuthorityMismatch);
}
if settlement.mutation_attempt != request.mutation_attempt()
|| settlement.observation_attempt != request.observation_attempt()
{
return Err(IcSnapshotUploadDataSettlementError::AttemptMismatch);
}
if &settlement.challenge != challenge {
return Err(IcSnapshotUploadDataSettlementError::ChallengeMismatch);
}
if settlement.readback != observation.reply().digest() {
return Err(IcSnapshotUploadDataSettlementError::ReadbackMismatch);
}
if settlement.observation_evidence != response.input().evidence {
return Err(IcSnapshotUploadDataSettlementError::ObservationEvidenceMismatch);
}
let outcome = match &settlement.attribution {
IcSnapshotUploadDataAttribution::Applied { .. } => {
if !observation.matches_original_chunk() {
return Err(IcSnapshotUploadDataSettlementError::AppliedBytesMismatch);
}
ObservationOutcomeRecord::Applied
}
IcSnapshotUploadDataAttribution::NotApplied { .. } => ObservationOutcomeRecord::NotApplied,
IcSnapshotUploadDataAttribution::Unresolved { .. } => ObservationOutcomeRecord::Uncertain,
};
Ok(IcSnapshotUploadDataSettlementView {
observation,
settlement,
outcome,
})
}
#[derive(Debug, Error)]
pub enum IcSnapshotUploadDataSettlementError {
#[error(transparent)]
Observation(#[from] IcSnapshotUploadDataObservationAssociationError),
#[error("data upload settlement original authority differs")]
AuthorityMismatch,
#[error("data upload settlement attempt identities differ")]
AttemptMismatch,
#[error("data upload settlement challenge differs")]
ChallengeMismatch,
#[error("data upload settlement readback differs")]
ReadbackMismatch,
#[error("data upload settlement observation evidence differs")]
ObservationEvidenceMismatch,
#[error("data upload settlement applied bytes differ from original intent")]
AppliedBytesMismatch,
}
#[cfg(test)]
mod tests;