ic_backup/policy/ic_observation/capture_settlement/
mod.rs1use super::{
4 IcObservationAssociationError, IcObservationReplyView, IcObservationResponseView,
5 validate_response,
6};
7use crate::{
8 model::{
9 artifacts::ArtifactChecksumRecord,
10 attempt_journal::{AttemptJournalRecord, ObservationOutcomeRecord},
11 ic_observation::{IcCaptureSettlement, IcObservationRequest, IcObservationResponse},
12 ic_request::IcManagementMethodRecord as Method,
13 ic_snapshot_reply::{IcSnapshotInfo, IcSnapshotReply},
14 },
15 policy::{
16 inventory_settlement::{
17 InventoryAttribution, InventorySettlementIdentity, InventorySettlementMismatch,
18 admitted_outcome, validate_pair,
19 },
20 settlement_identity::SettlementIdentity,
21 snapshot_inventory_delta::SnapshotInventoryDeltaError,
22 },
23};
24use thiserror::Error;
25
26#[cfg(test)]
27use crate::model::ic_observation::IcCaptureAttribution;
28
29#[derive(Debug)]
31pub struct IcCaptureSettlementView<'a> {
32 observation: IcObservationResponseView<'a>,
33 baseline: &'a IcSnapshotReply<'a>,
34 settlement: &'a IcCaptureSettlement,
35 captured_snapshot: Option<usize>,
36 outcome: ObservationOutcomeRecord,
37}
38impl<'a> IcCaptureSettlementView<'a> {
39 #[must_use]
41 pub const fn outcome(&self) -> ObservationOutcomeRecord {
42 self.outcome
43 }
44 #[must_use]
46 pub const fn baseline(&self) -> &'a IcSnapshotReply<'a> {
47 self.baseline
48 }
49 #[must_use]
51 pub const fn observation(&self) -> &IcObservationResponseView<'a> {
52 &self.observation
53 }
54 #[must_use]
56 pub const fn settlement(&self) -> &'a IcCaptureSettlement {
57 self.settlement
58 }
59 #[must_use]
61 pub fn captured_snapshot(&self) -> Option<&IcSnapshotInfo> {
62 self.observation.inventory_snapshot(self.captured_snapshot?)
63 }
64}
65
66pub fn validate_capture_settlement<'a>(
82 request: &IcObservationRequest<'a>,
83 journal: &AttemptJournalRecord,
84 baseline: &'a IcSnapshotReply<'a>,
85 response: &'a IcObservationResponse,
86 challenge: &ArtifactChecksumRecord,
87 settlement: &'a IcCaptureSettlement,
88) -> Result<IcCaptureSettlementView<'a>, IcCaptureSettlementError> {
89 if request.mutation().method() != Method::TakeCanisterSnapshot
90 || request.payload().method() != Method::ListCanisterSnapshots
91 {
92 return Err(IcCaptureSettlementError::UnsupportedMethod);
93 }
94 let observation = validate_response(request, journal, response)?;
95 let IcObservationReplyView::Inventory(inventory) = observation.reply() else {
96 return Err(IcCaptureSettlementError::UnsupportedMethod);
97 };
98 let authority = request.authority().digest();
99 let candidates = validate_pair::<IcCaptureSettlementError>(
100 request.mutation().target(),
101 baseline,
102 inventory,
103 &InventorySettlementIdentity {
104 operation: SettlementIdentity {
105 authority: &settlement.authority,
106 mutation_attempt: settlement.mutation_attempt,
107 observation_attempt: settlement.observation_attempt,
108 challenge: &settlement.challenge,
109 },
110 baseline: &settlement.baseline,
111 inventory: &settlement.inventory,
112 observation_evidence: &settlement.observation_evidence,
113 },
114 &SettlementIdentity {
115 authority: &authority,
116 mutation_attempt: request.mutation_attempt(),
117 observation_attempt: request.observation_attempt(),
118 challenge,
119 },
120 &response.input().evidence,
121 )?;
122 let (outcome, captured_snapshot) = admitted_outcome(
123 inventory,
124 &candidates,
125 InventoryAttribution::from(&settlement.attribution),
126 )
127 .map_err(|()| IcCaptureSettlementError::NotNewCandidate)?;
128 Ok(IcCaptureSettlementView {
129 observation,
130 baseline,
131 settlement,
132 captured_snapshot,
133 outcome,
134 })
135}
136
137#[derive(Debug, Error)]
139pub enum IcCaptureSettlementError {
140 #[error("capture settlement requires original capture and list observation")]
142 UnsupportedMethod,
143 #[error(transparent)]
145 Observation(#[from] IcObservationAssociationError),
146 #[error(transparent)]
148 Baseline(#[from] SnapshotInventoryDeltaError),
149 #[error(transparent)]
151 Identity(#[from] InventorySettlementMismatch),
152 #[error("capture settlement ID is not a new candidate")]
154 NotNewCandidate,
155}
156
157#[cfg(test)]
158mod tests;