ic_backup/model/ic_snapshot_upload_data_observation/
mod.rs1use crate::model::attempt_journal::{ObservationReservation, ObservationReservationError};
4use crate::model::{
5 artifacts::ArtifactChecksumRecord,
6 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
7 ic_observation::{
8 IcObservationResponseError, IcObservationResponseInput, fmt_response_input,
9 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::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 let request = Self {
81 plan,
82 mutation,
83 payload,
84 reservation,
85 original_chunk_checksum: chunk_checksum,
86 };
87 Ok(request)
88 }
89
90 #[must_use]
92 pub const fn plan(&self) -> &OperationPlanRecord {
93 self.plan
94 }
95 #[must_use]
97 pub const fn mutation(&self) -> &'request IcSnapshotUploadRequest<'source> {
98 self.mutation
99 }
100 #[must_use]
102 pub const fn payload(&self) -> &'request IcSnapshotDataRequest<'metadata> {
103 self.payload
104 }
105 #[must_use]
107 pub const fn authority(&self) -> &AttemptAuthorityRecord {
108 &self.reservation.authority
109 }
110 #[must_use]
112 pub const fn mutation_attempt(&self) -> u32 {
113 self.reservation.mutation_attempt
114 }
115 #[must_use]
117 pub const fn observation_attempt(&self) -> u32 {
118 self.reservation.observation_attempt
119 }
120 #[must_use]
122 pub const fn original_chunk_checksum(&self) -> &ArtifactChecksumRecord {
123 self.original_chunk_checksum
124 }
125 pub fn validate_journal(
129 &self,
130 journal: &AttemptJournalRecord,
131 ) -> Result<(), ObservationReservationError> {
132 self.reservation.validate(journal)
133 }
134}
135
136#[derive(Debug, Error)]
138pub enum IcSnapshotUploadDataObservationError {
139 #[error("data upload observation requires original data intent")]
141 DataUploadRequired,
142 #[error("data upload observation readback differs")]
144 ReadbackMismatch,
145 #[error("data upload observation destination metadata differs")]
147 DestinationMetadataMismatch,
148 #[error(transparent)]
150 Upload(#[from] IcSnapshotUploadAttemptError),
151 #[error(transparent)]
153 Reservation(#[from] ObservationReservationError),
154}
155
156#[derive(Clone)]
161pub struct IcSnapshotUploadDataObservationResponse {
162 input: IcObservationResponseInput,
163}
164
165impl IcSnapshotUploadDataObservationResponse {
166 pub fn new(mut input: IcObservationResponseInput) -> Result<Self, IcObservationResponseError> {
170 validate_response_input(&mut input, MAX_IC_SNAPSHOT_DATA_REPLY_BYTES)?;
171 Ok(Self { input })
172 }
173 #[must_use]
175 pub const fn input(&self) -> &IcObservationResponseInput {
176 &self.input
177 }
178}
179impl fmt::Debug for IcSnapshotUploadDataObservationResponse {
180 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
181 fmt_response_input(
182 &self.input,
183 formatter,
184 "IcSnapshotUploadDataObservationResponse",
185 )
186 }
187}
188
189#[cfg(test)]
190mod tests;