Skip to main content

ic_backup/policy/ic_snapshot_upload_data_observation/
mod.rs

1//! Passive exact data-upload readback association; byte equality is no receipt.
2
3use crate::{
4    model::{
5        attempt_journal::AttemptJournalRecord,
6        ic_snapshot_data::{IcSnapshotDataError, IcSnapshotDataReply},
7        ic_snapshot_upload_data_observation::{
8            IcSnapshotUploadDataObservationRequest, IcSnapshotUploadDataObservationResponse,
9        },
10    },
11    policy::ic_observation::{IcObservationAssociationError, ObservationClaims, validate_claims},
12};
13use thiserror::Error;
14
15mod settlement;
16pub use settlement::{
17    IcSnapshotUploadDataSettlementError, IcSnapshotUploadDataSettlementView, validate_settlement,
18};
19
20/// Read-only exact data evidence and original-chunk equality; no write outcome.
21#[derive(Debug)]
22pub struct IcSnapshotUploadDataObservationView<'request, 'metadata> {
23    response: &'request IcSnapshotUploadDataObservationResponse,
24    reply: IcSnapshotDataReply<'request, 'metadata>,
25    matches_original_chunk: bool,
26}
27impl<'request, 'metadata> IcSnapshotUploadDataObservationView<'request, 'metadata> {
28    /// Read original passive claims and raw reply evidence.
29    #[must_use]
30    pub const fn response(&self) -> &'request IcSnapshotUploadDataObservationResponse {
31        self.response
32    }
33    /// Read the canonical bounded data decoder's exact byte/metadata projection.
34    #[must_use]
35    pub const fn reply(&self) -> &IcSnapshotDataReply<'request, 'metadata> {
36        &self.reply
37    }
38    /// Compare actual chunk SHA-256 with originally encoded upload bytes.
39    ///
40    /// Equal bytes may predate this upload or come from another writer. Different
41    /// bytes or absent/failed observations cannot alone establish `NotApplied`.
42    #[must_use]
43    pub const fn matches_original_chunk(&self) -> bool {
44        self.matches_original_chunk
45    }
46}
47
48/// Recheck exact pending reservations and passive claims, then decode exact read bytes.
49///
50/// Authentication, observation chronology, exclusive write attribution, original
51/// allocation and stable destination custody remain integration-owned. This performs
52/// no IO, observation, receipt, retry, refund or terminal/reference release.
53/// # Errors
54/// Rejects current reservation/claim drift and existing bounded data codec failures.
55pub fn validate_response<'request, 'metadata>(
56    request: &IcSnapshotUploadDataObservationRequest<'request, '_, 'metadata>,
57    journal: &AttemptJournalRecord,
58    response: &'request IcSnapshotUploadDataObservationResponse,
59) -> Result<
60    IcSnapshotUploadDataObservationView<'request, 'metadata>,
61    IcSnapshotUploadDataObservationAssociationError,
62> {
63    request
64        .validate_journal(journal)
65        .map_err(IcObservationAssociationError::from)?;
66    validate_claims(
67        &ObservationClaims {
68            authority: request.authority(),
69            mutation_attempt: request.mutation_attempt(),
70            observation_attempt: request.observation_attempt(),
71            request: request.payload().digest(),
72            context: request.plan().context(),
73            target: request.payload().target(),
74        },
75        response.input(),
76    )?;
77    let reply = IcSnapshotDataReply::decode(request.payload(), &response.input().reply)?;
78    let matches_original_chunk = reply.chunk_checksum() == request.original_chunk_checksum();
79    Ok(IcSnapshotUploadDataObservationView {
80        response,
81        reply,
82        matches_original_chunk,
83    })
84}
85
86/// Canonical passive-claim or data-codec denial, with no spending/outcome change.
87#[derive(Debug, Error)]
88pub enum IcSnapshotUploadDataObservationAssociationError {
89    /// Existing current reservation/actual claim association failed.
90    #[error(transparent)]
91    Association(#[from] IcObservationAssociationError),
92    /// Existing exact data reply decoding failed.
93    #[error(transparent)]
94    Data(#[from] IcSnapshotDataError),
95}
96
97#[cfg(test)]
98mod tests;