ic_backup/model/ic_snapshot_transfer_read/
mod.rs1use crate::model::ic_update_response::IcUpdateResponseInput;
4use crate::model::{
5 artifacts::ArtifactChecksumRecord,
6 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
7 ic_snapshot_data::{IcSnapshotDataRequest, MAX_IC_SNAPSHOT_DATA_REPLY_BYTES},
8 ic_snapshot_metadata::IcSnapshotMetadataRequest,
9 operation_plan::{OperationPlanError, OperationPlanRecord},
10};
11use std::fmt;
12use thiserror::Error;
13
14#[derive(Clone, Copy, Debug)]
16pub enum IcSnapshotTransferReadPayload<'request, 'metadata> {
17 Metadata(&'request IcSnapshotMetadataRequest),
19 Data(&'request IcSnapshotDataRequest<'metadata>),
21}
22
23impl IcSnapshotTransferReadPayload<'_, '_> {
24 pub(crate) fn validate_binding(
25 &self,
26 authority: &AttemptAuthorityRecord,
27 ) -> Result<(), IcSnapshotTransferReadError> {
28 if self.target() != authority.binding().target()
29 || self.digest().hash() != authority.binding().request()
30 {
31 return Err(IcSnapshotTransferReadError::PayloadMismatch);
32 }
33 Ok(())
34 }
35 #[must_use]
37 pub fn target(&self) -> &str {
38 match self {
39 Self::Metadata(payload) => payload.target(),
40 Self::Data(payload) => payload.target(),
41 }
42 }
43 #[must_use]
45 pub const fn receiver(&self) -> &'static str {
46 match self {
47 Self::Metadata(payload) => payload.receiver(),
48 Self::Data(payload) => payload.receiver(),
49 }
50 }
51 #[must_use]
53 pub const fn method(&self) -> &'static str {
54 match self {
55 Self::Metadata(payload) => payload.method(),
56 Self::Data(payload) => payload.method(),
57 }
58 }
59 #[must_use]
61 pub fn arguments(&self) -> &[u8] {
62 match self {
63 Self::Metadata(payload) => payload.arguments(),
64 Self::Data(payload) => payload.arguments(),
65 }
66 }
67 #[must_use]
69 pub fn digest(&self) -> ArtifactChecksumRecord {
70 match self {
71 Self::Metadata(payload) => payload.digest(),
72 Self::Data(payload) => payload.digest(),
73 }
74 }
75}
76
77#[derive(Debug)]
83pub struct IcSnapshotTransferReadRequest<'request, 'metadata> {
84 plan: &'request OperationPlanRecord,
85 payload: IcSnapshotTransferReadPayload<'request, 'metadata>,
86 authority: AttemptAuthorityRecord,
87 mutation_attempt: u32,
88}
89
90impl<'request, 'metadata> IcSnapshotTransferReadRequest<'request, 'metadata> {
91 pub fn new(
96 plan: &'request OperationPlanRecord,
97 operation_sequence: u64,
98 journal: &AttemptJournalRecord,
99 payload: IcSnapshotTransferReadPayload<'request, 'metadata>,
100 ) -> Result<Self, IcSnapshotTransferReadError> {
101 let authority = plan.attempt_authority(operation_sequence)?;
102 if journal.authority() != &authority {
103 return Err(IcSnapshotTransferReadError::AuthorityMismatch);
104 }
105 payload.validate_binding(&authority)?;
106 let mutation_attempt = journal
107 .view()
108 .pending_mutation
109 .ok_or(IcSnapshotTransferReadError::NoPendingMutation)?;
110 let request = Self {
111 plan,
112 payload,
113 authority,
114 mutation_attempt,
115 };
116 request.validate_journal(journal)?;
117 Ok(request)
118 }
119 #[must_use]
121 pub const fn plan(&self) -> &'request OperationPlanRecord {
122 self.plan
123 }
124 #[must_use]
126 pub const fn payload(&self) -> IcSnapshotTransferReadPayload<'request, 'metadata> {
127 self.payload
128 }
129 #[must_use]
131 pub const fn authority(&self) -> &AttemptAuthorityRecord {
132 &self.authority
133 }
134 #[must_use]
136 pub const fn mutation_attempt(&self) -> u32 {
137 self.mutation_attempt
138 }
139 pub fn validate_journal(
143 &self,
144 journal: &AttemptJournalRecord,
145 ) -> Result<(), IcSnapshotTransferReadError> {
146 if journal.authority() != &self.authority {
147 return Err(IcSnapshotTransferReadError::AuthorityMismatch);
148 }
149 let view = journal.view();
150 if view.pending_mutation != Some(self.mutation_attempt) {
151 return Err(IcSnapshotTransferReadError::MutationMismatch);
152 }
153 if view.pending_observation.is_some() {
154 return Err(IcSnapshotTransferReadError::ObservationPending);
155 }
156 Ok(())
157 }
158}
159
160#[derive(Clone)]
162pub struct IcSnapshotTransferReadResponse {
163 input: IcUpdateResponseInput,
164}
165
166impl IcSnapshotTransferReadResponse {
167 pub fn new(mut input: IcUpdateResponseInput) -> Result<Self, IcSnapshotTransferReadError> {
171 input.admit(
172 MAX_IC_SNAPSHOT_DATA_REPLY_BYTES,
173 IcSnapshotTransferReadError::InvalidAttempt,
174 IcSnapshotTransferReadError::ReplyTooLarge,
175 IcSnapshotTransferReadError::InvalidTarget,
176 )?;
177 Ok(Self { input })
178 }
179 #[must_use]
181 pub const fn input(&self) -> &IcUpdateResponseInput {
182 &self.input
183 }
184}
185
186impl fmt::Debug for IcSnapshotTransferReadResponse {
187 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
188 self.input
189 .fmt_redacted(formatter, "IcSnapshotTransferReadResponse")
190 }
191}
192
193#[derive(Debug, Error)]
195pub enum IcSnapshotTransferReadError {
196 #[error("snapshot transfer read authority differs")]
198 AuthorityMismatch,
199 #[error("snapshot transfer read payload differs")]
201 PayloadMismatch,
202 #[error("snapshot transfer read requires a pending original update")]
204 NoPendingMutation,
205 #[error("snapshot transfer read original attempt differs")]
207 MutationMismatch,
208 #[error("snapshot transfer read recovery observation is pending")]
210 ObservationPending,
211 #[error("invalid snapshot transfer read attempt")]
213 InvalidAttempt,
214 #[error("snapshot transfer read reply too large")]
216 ReplyTooLarge,
217 #[error("invalid snapshot transfer read target")]
219 InvalidTarget,
220 #[error(transparent)]
222 Plan(#[from] OperationPlanError),
223}