ic_backup/model/ic_mutation/
mod.rs1use crate::model::ic_update_response::IcUpdateResponseInput;
4use crate::model::{
5 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
6 ic_lifecycle_reply::MAX_IC_LIFECYCLE_REPLY_BYTES,
7 ic_request::{IcManagementRequestRecord, IcRequestError},
8 ic_snapshot_reply::MAX_IC_SNAPSHOT_REPLY_BYTES,
9 operation_plan::{OperationPlanError, OperationPlanRecord},
10};
11use std::fmt;
12use thiserror::Error;
13
14pub const MAX_IC_MUTATION_REPLY_BYTES: usize =
16 if MAX_IC_SNAPSHOT_REPLY_BYTES < MAX_IC_LIFECYCLE_REPLY_BYTES {
17 MAX_IC_SNAPSHOT_REPLY_BYTES
18 } else {
19 MAX_IC_LIFECYCLE_REPLY_BYTES
20 };
21
22#[derive(Debug)]
28pub struct IcMutationRequest<'a> {
29 plan: &'a OperationPlanRecord,
30 payload: &'a IcManagementRequestRecord,
31 authority: AttemptAuthorityRecord,
32 mutation_attempt: u32,
33}
34
35impl<'a> IcMutationRequest<'a> {
36 pub fn new(
44 plan: &'a OperationPlanRecord,
45 operation_sequence: u64,
46 journal: &AttemptJournalRecord,
47 payload: &'a IcManagementRequestRecord,
48 ) -> Result<Self, IcMutationRequestError> {
49 let authority = plan.attempt_authority(operation_sequence)?;
50 if journal.authority() != &authority {
51 return Err(IcMutationRequestError::AuthorityMismatch);
52 }
53 payload.validate_mutation_binding(authority.binding())?;
54 let mutation_attempt = journal
55 .view()
56 .pending_mutation
57 .ok_or(IcMutationRequestError::NoPendingMutation)?;
58 let request = Self {
59 plan,
60 payload,
61 authority,
62 mutation_attempt,
63 };
64 request.validate_journal(journal)?;
65 Ok(request)
66 }
67
68 #[must_use]
70 pub const fn plan(&self) -> &OperationPlanRecord {
71 self.plan
72 }
73 #[must_use]
75 pub const fn payload(&self) -> &'a IcManagementRequestRecord {
76 self.payload
77 }
78 #[must_use]
80 pub const fn authority(&self) -> &AttemptAuthorityRecord {
81 &self.authority
82 }
83 #[must_use]
85 pub const fn mutation_attempt(&self) -> u32 {
86 self.mutation_attempt
87 }
88 pub fn validate_journal(
92 &self,
93 journal: &AttemptJournalRecord,
94 ) -> Result<(), IcMutationRequestError> {
95 if journal.authority() != &self.authority {
96 return Err(IcMutationRequestError::AuthorityMismatch);
97 }
98 let current = journal.view();
99 if current.pending_mutation != Some(self.mutation_attempt) {
100 return Err(IcMutationRequestError::MutationMismatch);
101 }
102 if current.pending_observation.is_some() {
103 return Err(IcMutationRequestError::ObservationPending);
104 }
105 Ok(())
106 }
107}
108
109#[derive(Clone)]
115pub struct IcMutationAcknowledgement {
116 input: IcUpdateResponseInput,
117}
118
119impl IcMutationAcknowledgement {
120 pub fn new(mut input: IcUpdateResponseInput) -> Result<Self, IcMutationAcknowledgementError> {
124 input.admit(
125 MAX_IC_MUTATION_REPLY_BYTES,
126 IcMutationAcknowledgementError::InvalidAttempt,
127 IcMutationAcknowledgementError::ReplyTooLarge,
128 IcMutationAcknowledgementError::InvalidTarget,
129 )?;
130 Ok(Self { input })
131 }
132 #[must_use]
134 pub const fn input(&self) -> &IcUpdateResponseInput {
135 &self.input
136 }
137}
138
139impl fmt::Debug for IcMutationAcknowledgement {
140 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
141 self.input
142 .fmt_redacted(formatter, "IcMutationAcknowledgement")
143 }
144}
145
146#[derive(Debug, Error)]
148pub enum IcMutationRequestError {
149 #[error("IC mutation original authority mismatch")]
151 AuthorityMismatch,
152 #[error("IC mutation requires a pending original mutation")]
154 NoPendingMutation,
155 #[error("IC mutation original attempt mismatch")]
157 MutationMismatch,
158 #[error("IC mutation has a pending recovery observation")]
160 ObservationPending,
161 #[error(transparent)]
163 Payload(#[from] IcRequestError),
164 #[error(transparent)]
166 Plan(#[from] OperationPlanError),
167}
168
169#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
171pub enum IcMutationAcknowledgementError {
172 #[error("invalid IC mutation acknowledgement attempt")]
174 InvalidAttempt,
175 #[error("IC mutation acknowledgement reply too large")]
177 ReplyTooLarge,
178 #[error("invalid IC mutation acknowledgement target")]
180 InvalidTarget,
181}
182
183#[cfg(test)]
184mod tests;