ic_backup/policy/ic_snapshot_upload_data_observation/settlement/
mod.rs1use super::{
4 IcSnapshotUploadDataObservationAssociationError, IcSnapshotUploadDataObservationView,
5 validate_response,
6};
7use crate::model::{
8 artifacts::ArtifactChecksumRecord,
9 attempt_journal::{AttemptJournalRecord, ObservationOutcomeRecord},
10 ic_snapshot_upload_data_observation::{
11 IcSnapshotUploadDataAttribution, IcSnapshotUploadDataObservationRequest,
12 IcSnapshotUploadDataObservationResponse, IcSnapshotUploadDataSettlement,
13 },
14};
15use crate::policy::settlement_identity::{SettlementIdentity, SettlementIdentityMismatch};
16use thiserror::Error;
17
18#[derive(Debug)]
20pub struct IcSnapshotUploadDataSettlementView<'request, 'metadata> {
21 observation: IcSnapshotUploadDataObservationView<'request, 'metadata>,
22 settlement: &'request IcSnapshotUploadDataSettlement,
23 outcome: ObservationOutcomeRecord,
24}
25impl<'request, 'metadata> IcSnapshotUploadDataSettlementView<'request, 'metadata> {
26 #[must_use]
28 pub const fn outcome(&self) -> ObservationOutcomeRecord {
29 self.outcome
30 }
31 #[must_use]
33 pub const fn observation(&self) -> &IcSnapshotUploadDataObservationView<'request, 'metadata> {
34 &self.observation
35 }
36 #[must_use]
38 pub const fn settlement(&self) -> &'request IcSnapshotUploadDataSettlement {
39 self.settlement
40 }
41}
42
43pub fn validate_settlement<'request, 'metadata>(
57 request: &IcSnapshotUploadDataObservationRequest<'request, '_, 'metadata>,
58 journal: &AttemptJournalRecord,
59 response: &'request IcSnapshotUploadDataObservationResponse,
60 challenge: &ArtifactChecksumRecord,
61 settlement: &'request IcSnapshotUploadDataSettlement,
62) -> Result<
63 IcSnapshotUploadDataSettlementView<'request, 'metadata>,
64 IcSnapshotUploadDataSettlementError,
65> {
66 let observation = validate_response(request, journal, response)?;
67 SettlementIdentity {
68 authority: &settlement.authority,
69 mutation_attempt: settlement.mutation_attempt,
70 observation_attempt: settlement.observation_attempt,
71 challenge: &settlement.challenge,
72 }
73 .compare(&SettlementIdentity {
74 authority: &request.authority().digest(),
75 mutation_attempt: request.mutation_attempt(),
76 observation_attempt: request.observation_attempt(),
77 challenge,
78 })?;
79 if settlement.readback != observation.reply().digest() {
80 return Err(IcSnapshotUploadDataSettlementError::ReadbackMismatch);
81 }
82 if settlement.observation_evidence != response.input().evidence {
83 return Err(IcSnapshotUploadDataSettlementError::ObservationEvidenceMismatch);
84 }
85 let outcome = match &settlement.attribution {
86 IcSnapshotUploadDataAttribution::Applied { .. } => {
87 if !observation.matches_original_chunk() {
88 return Err(IcSnapshotUploadDataSettlementError::AppliedBytesMismatch);
89 }
90 ObservationOutcomeRecord::Applied
91 }
92 IcSnapshotUploadDataAttribution::NotApplied { .. } => ObservationOutcomeRecord::NotApplied,
93 IcSnapshotUploadDataAttribution::Unresolved { .. } => ObservationOutcomeRecord::Uncertain,
94 };
95 Ok(IcSnapshotUploadDataSettlementView {
96 observation,
97 settlement,
98 outcome,
99 })
100}
101
102#[derive(Debug, Error)]
104pub enum IcSnapshotUploadDataSettlementError {
105 #[error(transparent)]
107 Observation(#[from] IcSnapshotUploadDataObservationAssociationError),
108 #[error(transparent)]
110 Identity(#[from] SettlementIdentityMismatch),
111 #[error("data upload settlement readback differs")]
113 ReadbackMismatch,
114 #[error("data upload settlement observation evidence differs")]
116 ObservationEvidenceMismatch,
117 #[error("data upload settlement applied bytes differ from original intent")]
119 AppliedBytesMismatch,
120}
121
122#[cfg(test)]
123mod tests;