ic_backup/model/ic_snapshot_upload/attempt/
mod.rs1use super::IcSnapshotUploadRequest;
4use crate::model::attempt_journal::MutationReservation;
5use crate::model::{
6 attempt_journal::{AttemptAuthorityRecord, AttemptJournalRecord},
7 operation_plan::{OperationPlanError, OperationPlanRecord},
8};
9use thiserror::Error;
10
11#[derive(Debug)]
13pub struct IcSnapshotUploadAttempt<'request, 'source> {
14 plan: &'request OperationPlanRecord,
15 payload: &'request IcSnapshotUploadRequest<'source>,
16 reservation: MutationReservation,
17}
18
19impl<'request, 'source> IcSnapshotUploadAttempt<'request, 'source> {
20 pub fn new(
29 plan: &'request OperationPlanRecord,
30 operation_sequence: u64,
31 journal: &AttemptJournalRecord,
32 payload: &'request IcSnapshotUploadRequest<'source>,
33 ) -> Result<Self, IcSnapshotUploadAttemptError> {
34 let authority = original_authority(plan, operation_sequence, journal, payload)?;
35 let reservation = MutationReservation::new(authority, journal)?;
36 Ok(Self {
37 plan,
38 payload,
39 reservation,
40 })
41 }
42 #[must_use]
44 pub const fn plan(&self) -> &'request OperationPlanRecord {
45 self.plan
46 }
47 #[must_use]
49 pub const fn payload(&self) -> &'request IcSnapshotUploadRequest<'source> {
50 self.payload
51 }
52 #[must_use]
54 pub const fn authority(&self) -> &AttemptAuthorityRecord {
55 &self.reservation.authority
56 }
57 #[must_use]
59 pub const fn mutation_attempt(&self) -> u32 {
60 self.reservation.mutation_attempt
61 }
62 pub fn validate_journal(
66 &self,
67 journal: &AttemptJournalRecord,
68 ) -> Result<(), IcSnapshotUploadAttemptError> {
69 self.reservation.validate(journal).map_err(Into::into)
70 }
71}
72
73pub(crate) fn original_authority(
74 plan: &OperationPlanRecord,
75 operation_sequence: u64,
76 journal: &AttemptJournalRecord,
77 payload: &IcSnapshotUploadRequest<'_>,
78) -> Result<AttemptAuthorityRecord, IcSnapshotUploadAttemptError> {
79 let authority = plan.attempt_authority_for_journal(
80 operation_sequence,
81 journal,
82 IcSnapshotUploadAttemptError::Reservation(
83 crate::model::attempt_journal::MutationReservationError::AuthorityMismatch,
84 ),
85 )?;
86 if payload.target() != authority.binding().target()
87 || payload.binding_digest().hash() != authority.binding().request()
88 {
89 return Err(IcSnapshotUploadAttemptError::PayloadMismatch);
90 }
91 if !source_context_matches(plan, payload) {
92 return Err(IcSnapshotUploadAttemptError::SourceContextMismatch);
93 }
94 Ok(authority)
95}
96
97pub(crate) fn source_context_matches(
98 plan: &OperationPlanRecord,
99 payload: &IcSnapshotUploadRequest<'_>,
100) -> bool {
101 plan.context().network() == payload.source_plan().context().network()
102 && plan.context().release() == payload.source_plan().context().release()
103}
104
105#[derive(Debug, Error)]
107pub enum IcSnapshotUploadAttemptError {
108 #[error("original mutation reservation rejected: {0}")]
110 Reservation(#[from] crate::model::attempt_journal::MutationReservationError),
111 #[error("snapshot upload source network or release differs")]
113 SourceContextMismatch,
114 #[error("snapshot upload original payload mismatch")]
116 PayloadMismatch,
117 #[error(transparent)]
119 Plan(#[from] OperationPlanError),
120}