ic_backup/model/ic_snapshot_transfer_read/
mod.rs1use crate::model::{
4 artifacts::ArtifactChecksumRecord,
5 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord, MAX_OPERATION_ATTEMPTS},
6 ic_snapshot_data::{IcSnapshotDataRequest, MAX_IC_SNAPSHOT_DATA_REPLY_BYTES},
7 ic_snapshot_metadata::IcSnapshotMetadataRequest,
8 operation_plan::{OperationPlanError, OperationPlanRecord, PlanContextRecord},
9};
10use std::fmt;
11use thiserror::Error;
12
13#[derive(Clone, Copy, Debug)]
15pub enum IcSnapshotTransferReadPayload<'request, 'metadata> {
16 Metadata(&'request IcSnapshotMetadataRequest),
18 Data(&'request IcSnapshotDataRequest<'metadata>),
20}
21
22impl IcSnapshotTransferReadPayload<'_, '_> {
23 pub(crate) fn validate_binding(
24 &self,
25 authority: &AttemptAuthorityRecord,
26 ) -> Result<(), IcSnapshotTransferReadError> {
27 if self.target() != authority.binding().target()
28 || self.digest().hash() != authority.binding().request()
29 {
30 return Err(IcSnapshotTransferReadError::PayloadMismatch);
31 }
32 Ok(())
33 }
34 #[must_use]
36 pub fn target(&self) -> &str {
37 match self {
38 Self::Metadata(payload) => payload.target(),
39 Self::Data(payload) => payload.target(),
40 }
41 }
42 #[must_use]
44 pub const fn receiver(&self) -> &'static str {
45 match self {
46 Self::Metadata(payload) => payload.receiver(),
47 Self::Data(payload) => payload.receiver(),
48 }
49 }
50 #[must_use]
52 pub const fn method(&self) -> &'static str {
53 match self {
54 Self::Metadata(payload) => payload.method(),
55 Self::Data(payload) => payload.method(),
56 }
57 }
58 #[must_use]
60 pub fn arguments(&self) -> &[u8] {
61 match self {
62 Self::Metadata(payload) => payload.arguments(),
63 Self::Data(payload) => payload.arguments(),
64 }
65 }
66 #[must_use]
68 pub fn digest(&self) -> ArtifactChecksumRecord {
69 match self {
70 Self::Metadata(payload) => payload.digest(),
71 Self::Data(payload) => payload.digest(),
72 }
73 }
74}
75
76#[derive(Debug)]
82pub struct IcSnapshotTransferReadRequest<'request, 'metadata> {
83 plan: &'request OperationPlanRecord,
84 payload: IcSnapshotTransferReadPayload<'request, 'metadata>,
85 authority: AttemptAuthorityRecord,
86 mutation_attempt: u32,
87}
88
89impl<'request, 'metadata> IcSnapshotTransferReadRequest<'request, 'metadata> {
90 pub fn new(
95 plan: &'request OperationPlanRecord,
96 operation_sequence: u64,
97 journal: &AttemptJournalRecord,
98 payload: IcSnapshotTransferReadPayload<'request, 'metadata>,
99 ) -> Result<Self, IcSnapshotTransferReadError> {
100 let authority = plan.attempt_authority(operation_sequence)?;
101 if journal.authority() != &authority {
102 return Err(IcSnapshotTransferReadError::AuthorityMismatch);
103 }
104 payload.validate_binding(&authority)?;
105 let mutation_attempt = journal
106 .view()
107 .pending_mutation
108 .ok_or(IcSnapshotTransferReadError::NoPendingMutation)?;
109 let request = Self {
110 plan,
111 payload,
112 authority,
113 mutation_attempt,
114 };
115 request.validate_journal(journal)?;
116 Ok(request)
117 }
118 #[must_use]
120 pub const fn plan(&self) -> &'request OperationPlanRecord {
121 self.plan
122 }
123 #[must_use]
125 pub const fn payload(&self) -> IcSnapshotTransferReadPayload<'request, 'metadata> {
126 self.payload
127 }
128 #[must_use]
130 pub const fn authority(&self) -> &AttemptAuthorityRecord {
131 &self.authority
132 }
133 #[must_use]
135 pub const fn mutation_attempt(&self) -> u32 {
136 self.mutation_attempt
137 }
138 pub fn validate_journal(
142 &self,
143 journal: &AttemptJournalRecord,
144 ) -> Result<(), IcSnapshotTransferReadError> {
145 if journal.authority() != &self.authority {
146 return Err(IcSnapshotTransferReadError::AuthorityMismatch);
147 }
148 let view = journal.view();
149 if view.pending_mutation != Some(self.mutation_attempt) {
150 return Err(IcSnapshotTransferReadError::MutationMismatch);
151 }
152 if view.pending_observation.is_some() {
153 return Err(IcSnapshotTransferReadError::ObservationPending);
154 }
155 Ok(())
156 }
157}
158
159#[derive(Clone)]
161pub struct IcSnapshotTransferReadResponseInput {
162 pub authority: ArtifactChecksumRecord,
164 pub mutation_attempt: u32,
166 pub context: PlanContextRecord,
168 pub target: String,
170 pub reply: Vec<u8>,
172 pub evidence: ArtifactChecksumRecord,
174}
175
176#[derive(Clone)]
178pub struct IcSnapshotTransferReadResponse {
179 input: IcSnapshotTransferReadResponseInput,
180}
181
182impl IcSnapshotTransferReadResponse {
183 pub fn new(
187 mut input: IcSnapshotTransferReadResponseInput,
188 ) -> Result<Self, IcSnapshotTransferReadError> {
189 if input.mutation_attempt == 0 || input.mutation_attempt > MAX_OPERATION_ATTEMPTS {
190 return Err(IcSnapshotTransferReadError::InvalidAttempt);
191 }
192 if input.reply.len() > MAX_IC_SNAPSHOT_DATA_REPLY_BYTES {
193 return Err(IcSnapshotTransferReadError::ReplyTooLarge);
194 }
195 input.target = crate::model::principal::canonical_text(&input.target)
196 .ok_or(IcSnapshotTransferReadError::InvalidTarget)?;
197 Ok(Self { input })
198 }
199 #[must_use]
201 pub const fn input(&self) -> &IcSnapshotTransferReadResponseInput {
202 &self.input
203 }
204}
205
206impl fmt::Debug for IcSnapshotTransferReadResponse {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 f.debug_struct("IcSnapshotTransferReadResponse")
209 .field("authority", &self.input.authority)
210 .field("mutation_attempt", &self.input.mutation_attempt)
211 .field("target", &self.input.target)
212 .field("reply_bytes", &self.input.reply.len())
213 .finish_non_exhaustive()
214 }
215}
216
217#[derive(Debug, Error)]
219pub enum IcSnapshotTransferReadError {
220 #[error("snapshot transfer read authority differs")]
222 AuthorityMismatch,
223 #[error("snapshot transfer read payload differs")]
225 PayloadMismatch,
226 #[error("snapshot transfer read requires a pending original update")]
228 NoPendingMutation,
229 #[error("snapshot transfer read original attempt differs")]
231 MutationMismatch,
232 #[error("snapshot transfer read recovery observation is pending")]
234 ObservationPending,
235 #[error("invalid snapshot transfer read attempt")]
237 InvalidAttempt,
238 #[error("snapshot transfer read reply too large")]
240 ReplyTooLarge,
241 #[error("invalid snapshot transfer read target")]
243 InvalidTarget,
244 #[error(transparent)]
246 Plan(#[from] OperationPlanError),
247}