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 .pending_mutation()
56 .ok_or(IcMutationRequestError::NoPendingMutation)?;
57 let request = Self {
58 plan,
59 payload,
60 authority,
61 mutation_attempt,
62 };
63 request.validate_journal(journal)?;
64 Ok(request)
65 }
66
67 #[must_use]
69 pub const fn plan(&self) -> &OperationPlanRecord {
70 self.plan
71 }
72 #[must_use]
74 pub const fn payload(&self) -> &'a IcManagementRequestRecord {
75 self.payload
76 }
77 #[must_use]
79 pub const fn authority(&self) -> &AttemptAuthorityRecord {
80 &self.authority
81 }
82 #[must_use]
84 pub const fn mutation_attempt(&self) -> u32 {
85 self.mutation_attempt
86 }
87 pub fn validate_journal(
91 &self,
92 journal: &AttemptJournalRecord,
93 ) -> Result<(), IcMutationRequestError> {
94 journal.validate_pending_mutation(
95 &self.authority,
96 self.mutation_attempt,
97 IcMutationRequestError::AuthorityMismatch,
98 IcMutationRequestError::MutationMismatch,
99 IcMutationRequestError::ObservationPending,
100 )
101 }
102}
103
104#[derive(Clone)]
110pub struct IcMutationAcknowledgement {
111 input: IcUpdateResponseInput,
112}
113
114impl IcMutationAcknowledgement {
115 pub fn new(mut input: IcUpdateResponseInput) -> Result<Self, IcMutationAcknowledgementError> {
119 input.admit(
120 MAX_IC_MUTATION_REPLY_BYTES,
121 IcMutationAcknowledgementError::InvalidAttempt,
122 IcMutationAcknowledgementError::ReplyTooLarge,
123 IcMutationAcknowledgementError::InvalidTarget,
124 )?;
125 Ok(Self { input })
126 }
127 #[must_use]
129 pub const fn input(&self) -> &IcUpdateResponseInput {
130 &self.input
131 }
132}
133
134impl fmt::Debug for IcMutationAcknowledgement {
135 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
136 self.input
137 .fmt_redacted(formatter, "IcMutationAcknowledgement")
138 }
139}
140
141#[derive(Debug, Error)]
143pub enum IcMutationRequestError {
144 #[error("IC mutation original authority mismatch")]
146 AuthorityMismatch,
147 #[error("IC mutation requires a pending original mutation")]
149 NoPendingMutation,
150 #[error("IC mutation original attempt mismatch")]
152 MutationMismatch,
153 #[error("IC mutation has a pending recovery observation")]
155 ObservationPending,
156 #[error(transparent)]
158 Payload(#[from] IcRequestError),
159 #[error(transparent)]
161 Plan(#[from] OperationPlanError),
162}
163
164#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
166pub enum IcMutationAcknowledgementError {
167 #[error("invalid IC mutation acknowledgement attempt")]
169 InvalidAttempt,
170 #[error("IC mutation acknowledgement reply too large")]
172 ReplyTooLarge,
173 #[error("invalid IC mutation acknowledgement target")]
175 InvalidTarget,
176}
177
178#[cfg(test)]
179mod tests;