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 let original = mutation.source().metadata();
73 let actual_sizes = [
74 actual.wasm_module_size,
75 actual.wasm_memory_size,
76 actual.stable_memory_size,
77 ];
78 let original_sizes = [
79 original.wasm_module_size,
80 original.wasm_memory_size,
81 original.stable_memory_size,
82 ];
83 if !matches!(
84 actual.source,
85 Some(ic_platform::management_canister_types::SnapshotSource::MetadataUpload(_))
86 ) || actual_sizes != original_sizes
87 {
88 return Err(IcSnapshotUploadDataObservationError::DestinationMetadataMismatch);
89 }
90 let reservation = ObservationReservation::new(authority, journal, payload.digest())
91 .map_err(crate::model::ic_observation::IcObservationRequestError::from)?;
92 let request = Self {
93 plan,
94 mutation,
95 payload,
96 reservation,
97 original_chunk_checksum: chunk_checksum,
98 };
99 Ok(request)
100 }
101
102 #[must_use]
104 pub const fn plan(&self) -> &OperationPlanRecord {
105 self.plan
106 }
107 #[must_use]
109 pub const fn mutation(&self) -> &'request IcSnapshotUploadRequest<'source> {
110 self.mutation
111 }
112 #[must_use]
114 pub const fn payload(&self) -> &'request IcSnapshotDataRequest<'metadata> {
115 self.payload
116 }
117 #[must_use]
119 pub const fn authority(&self) -> &AttemptAuthorityRecord {
120 &self.reservation.authority
121 }
122 #[must_use]
124 pub const fn mutation_attempt(&self) -> u32 {
125 self.reservation.mutation_attempt
126 }
127 #[must_use]
129 pub const fn observation_attempt(&self) -> u32 {
130 self.reservation.observation_attempt
131 }
132 #[must_use]
134 pub const fn original_chunk_checksum(&self) -> &ArtifactChecksumRecord {
135 self.original_chunk_checksum
136 }
137 pub fn validate_journal(
141 &self,
142 journal: &AttemptJournalRecord,
143 ) -> Result<(), IcObservationRequestError> {
144 self.reservation
145 .validate(journal)
146 .map_err(IcObservationRequestError::from)
147 }
148}
149
150#[derive(Debug, Error)]
152pub enum IcSnapshotUploadDataObservationError {
153 #[error("data upload observation requires original data intent")]
155 DataUploadRequired,
156 #[error("data upload observation readback differs")]
158 ReadbackMismatch,
159 #[error("data upload observation destination metadata differs")]
161 DestinationMetadataMismatch,
162 #[error(transparent)]
164 Upload(#[from] IcSnapshotUploadAttemptError),
165 #[error(transparent)]
167 Observation(#[from] IcObservationRequestError),
168}
169
170#[derive(Clone)]
175pub struct IcSnapshotUploadDataObservationResponse {
176 input: IcObservationResponseInput,
177}
178
179impl IcSnapshotUploadDataObservationResponse {
180 pub fn new(mut input: IcObservationResponseInput) -> Result<Self, IcObservationResponseError> {
184 validate_response_input(&mut input, MAX_IC_SNAPSHOT_DATA_REPLY_BYTES)?;
185 Ok(Self { input })
186 }
187 #[must_use]
189 pub const fn input(&self) -> &IcObservationResponseInput {
190 &self.input
191 }
192}
193impl fmt::Debug for IcSnapshotUploadDataObservationResponse {
194 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
195 fmt_response_input(
196 &self.input,
197 formatter,
198 "IcSnapshotUploadDataObservationResponse",
199 )
200 }
201}
202
203#[cfg(test)]
204mod tests;