ic_backup/model/ic_snapshot_upload_observation/
mod.rs1use crate::model::{
4 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
5 ic_observation::{IcObservationRequestError, ObservationReservation},
6 ic_request::{IcManagementMethodRecord, IcManagementRequestRecord},
7 ic_snapshot_upload::{
8 IcSnapshotUploadAttemptError, IcSnapshotUploadKind, IcSnapshotUploadRequest,
9 original_authority,
10 },
11 operation_plan::OperationPlanRecord,
12};
13use thiserror::Error;
14
15mod settlement;
16pub use settlement::{IcSnapshotUploadAttribution, IcSnapshotUploadSettlement};
17
18#[derive(Debug)]
25pub struct IcSnapshotUploadObservationRequest<'request, 'source> {
26 plan: &'request OperationPlanRecord,
27 mutation: &'request IcSnapshotUploadRequest<'source>,
28 payload: &'request IcManagementRequestRecord,
29 authority: AttemptAuthorityRecord,
30 mutation_attempt: u32,
31 observation_attempt: u32,
32}
33
34impl<'request, 'source> IcSnapshotUploadObservationRequest<'request, 'source> {
35 pub fn new(
42 plan: &'request OperationPlanRecord,
43 operation_sequence: u64,
44 journal: &AttemptJournalRecord,
45 mutation: &'request IcSnapshotUploadRequest<'source>,
46 payload: &'request IcManagementRequestRecord,
47 ) -> Result<Self, IcSnapshotUploadObservationError> {
48 if !matches!(mutation.kind(), IcSnapshotUploadKind::Metadata) {
49 return Err(IcSnapshotUploadObservationError::UnsupportedUpload);
50 }
51 if payload.method() != IcManagementMethodRecord::ListCanisterSnapshots {
52 return Err(IcSnapshotUploadObservationError::UnsupportedObservation);
53 }
54 let authority = original_authority(plan, operation_sequence, journal, mutation)?;
55 payload
56 .validate_observation_binding(authority.binding(), &payload.digest())
57 .map_err(IcObservationRequestError::from)?;
58 let current = journal.view();
59 let request = Self {
60 plan,
61 mutation,
62 payload,
63 authority,
64 mutation_attempt: current
65 .pending_mutation
66 .ok_or(IcObservationRequestError::NoPendingMutation)?,
67 observation_attempt: current
68 .pending_observation
69 .ok_or(IcObservationRequestError::NoPendingObservation)?,
70 };
71 request.validate_journal(journal)?;
72 Ok(request)
73 }
74
75 #[must_use]
77 pub const fn plan(&self) -> &OperationPlanRecord {
78 self.plan
79 }
80 #[must_use]
82 pub const fn mutation(&self) -> &'request IcSnapshotUploadRequest<'source> {
83 self.mutation
84 }
85 #[must_use]
87 pub const fn payload(&self) -> &'request IcManagementRequestRecord {
88 self.payload
89 }
90 #[must_use]
92 pub const fn authority(&self) -> &AttemptAuthorityRecord {
93 &self.authority
94 }
95 #[must_use]
97 pub const fn mutation_attempt(&self) -> u32 {
98 self.mutation_attempt
99 }
100 #[must_use]
102 pub const fn observation_attempt(&self) -> u32 {
103 self.observation_attempt
104 }
105 pub fn validate_journal(
109 &self,
110 journal: &AttemptJournalRecord,
111 ) -> Result<(), IcObservationRequestError> {
112 ObservationReservation {
113 authority: &self.authority,
114 mutation_attempt: self.mutation_attempt,
115 observation_attempt: self.observation_attempt,
116 request: self.payload.digest(),
117 }
118 .validate(journal)
119 }
120}
121
122#[derive(Debug, Error)]
124pub enum IcSnapshotUploadObservationError {
125 #[error("snapshot upload observation requires metadata intent")]
127 UnsupportedUpload,
128 #[error("snapshot upload observation requires snapshot list")]
130 UnsupportedObservation,
131 #[error(transparent)]
133 Upload(#[from] IcSnapshotUploadAttemptError),
134 #[error(transparent)]
136 Observation(#[from] IcObservationRequestError),
137}
138
139#[cfg(test)]
140mod tests;