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