ic_backup/model/ic_snapshot_upload_data_observation/
mod.rs1use crate::model::attempt_journal::ObservationReservation;
4use crate::model::{
5 artifacts::ArtifactChecksumRecord,
6 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
7 ic_observation::{
8 IcObservationRequestError, IcObservationResponseError, IcObservationResponseInput,
9 fmt_response_input, validate_response_input,
10 },
11 ic_snapshot_data::{IcSnapshotDataRequest, MAX_IC_SNAPSHOT_DATA_REPLY_BYTES},
12 ic_snapshot_upload::{
13 IcSnapshotUploadAttemptError, IcSnapshotUploadKind, IcSnapshotUploadRequest,
14 original_authority,
15 },
16 operation_plan::OperationPlanRecord,
17};
18use std::fmt;
19use thiserror::Error;
20
21mod settlement;
22pub use settlement::{IcSnapshotUploadDataAttribution, IcSnapshotUploadDataSettlement};
23
24#[derive(Debug)]
30pub struct IcSnapshotUploadDataObservationRequest<'request, 'source, 'metadata> {
31 plan: &'request OperationPlanRecord,
32 mutation: &'request IcSnapshotUploadRequest<'source>,
33 payload: &'request IcSnapshotDataRequest<'metadata>,
34 reservation: ObservationReservation,
35 original_chunk_checksum: &'request ArtifactChecksumRecord,
36}
37
38impl<'request, 'source, 'metadata>
39 IcSnapshotUploadDataObservationRequest<'request, 'source, 'metadata>
40{
41 pub fn new(
49 plan: &'request OperationPlanRecord,
50 operation_sequence: u64,
51 journal: &AttemptJournalRecord,
52 mutation: &'request IcSnapshotUploadRequest<'source>,
53 payload: &'request IcSnapshotDataRequest<'metadata>,
54 ) -> Result<Self, IcSnapshotUploadDataObservationError> {
55 let IcSnapshotUploadKind::Data {
56 snapshot_id,
57 source_kind,
58 chunk_checksum,
59 ..
60 } = mutation.kind()
61 else {
62 return Err(IcSnapshotUploadDataObservationError::DataUploadRequired);
63 };
64 let authority = original_authority(plan, operation_sequence, journal, mutation)?;
65 if payload.target() != mutation.target()
66 || payload.snapshot_id() != snapshot_id
67 || !payload.matches_kind(source_kind)
68 {
69 return Err(IcSnapshotUploadDataObservationError::ReadbackMismatch);
70 }
71 let actual = payload.metadata().metadata();
72 if !matches!(
73 actual.source,
74 Some(ic_platform::management_canister_types::SnapshotSource::MetadataUpload(_))
75 ) || payload.metadata().region_sizes() != mutation.source().region_sizes()
76 {
77 return Err(IcSnapshotUploadDataObservationError::DestinationMetadataMismatch);
78 }
79 let reservation = ObservationReservation::new(authority, journal, payload.digest())
80 .map_err(crate::model::ic_observation::IcObservationRequestError::from)?;
81 let request = Self {
82 plan,
83 mutation,
84 payload,
85 reservation,
86 original_chunk_checksum: chunk_checksum,
87 };
88 Ok(request)
89 }
90
91 #[must_use]
93 pub const fn plan(&self) -> &OperationPlanRecord {
94 self.plan
95 }
96 #[must_use]
98 pub const fn mutation(&self) -> &'request IcSnapshotUploadRequest<'source> {
99 self.mutation
100 }
101 #[must_use]
103 pub const fn payload(&self) -> &'request IcSnapshotDataRequest<'metadata> {
104 self.payload
105 }
106 #[must_use]
108 pub const fn authority(&self) -> &AttemptAuthorityRecord {
109 &self.reservation.authority
110 }
111 #[must_use]
113 pub const fn mutation_attempt(&self) -> u32 {
114 self.reservation.mutation_attempt
115 }
116 #[must_use]
118 pub const fn observation_attempt(&self) -> u32 {
119 self.reservation.observation_attempt
120 }
121 #[must_use]
123 pub const fn original_chunk_checksum(&self) -> &ArtifactChecksumRecord {
124 self.original_chunk_checksum
125 }
126 pub fn validate_journal(
130 &self,
131 journal: &AttemptJournalRecord,
132 ) -> Result<(), IcObservationRequestError> {
133 self.reservation
134 .validate(journal)
135 .map_err(IcObservationRequestError::from)
136 }
137}
138
139#[derive(Debug, Error)]
141pub enum IcSnapshotUploadDataObservationError {
142 #[error("data upload observation requires original data intent")]
144 DataUploadRequired,
145 #[error("data upload observation readback differs")]
147 ReadbackMismatch,
148 #[error("data upload observation destination metadata differs")]
150 DestinationMetadataMismatch,
151 #[error(transparent)]
153 Upload(#[from] IcSnapshotUploadAttemptError),
154 #[error(transparent)]
156 Observation(#[from] IcObservationRequestError),
157}
158
159#[derive(Clone)]
164pub struct IcSnapshotUploadDataObservationResponse {
165 input: IcObservationResponseInput,
166}
167
168impl IcSnapshotUploadDataObservationResponse {
169 pub fn new(mut input: IcObservationResponseInput) -> Result<Self, IcObservationResponseError> {
173 validate_response_input(&mut input, MAX_IC_SNAPSHOT_DATA_REPLY_BYTES)?;
174 Ok(Self { input })
175 }
176 #[must_use]
178 pub const fn input(&self) -> &IcObservationResponseInput {
179 &self.input
180 }
181}
182impl fmt::Debug for IcSnapshotUploadDataObservationResponse {
183 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
184 fmt_response_input(
185 &self.input,
186 formatter,
187 "IcSnapshotUploadDataObservationResponse",
188 )
189 }
190}
191
192#[cfg(test)]
193mod tests;