ic_backup/policy/ic_snapshot_upload_observation/settlement/
mod.rs1use super::validate_response;
3use crate::{
4 model::{
5 artifacts::ArtifactChecksumRecord,
6 attempt_journal::{AttemptJournalRecord, ObservationOutcomeRecord},
7 ic_observation::IcObservationResponse,
8 ic_snapshot_reply::{IcSnapshotInfo, IcSnapshotReply},
9 ic_snapshot_upload::IcSnapshotUploadError,
10 ic_snapshot_upload_observation::{
11 IcSnapshotUploadAttribution, IcSnapshotUploadObservationRequest,
12 IcSnapshotUploadSettlement,
13 },
14 },
15 policy::{
16 ic_observation::{
17 IcObservationAssociationError, IcObservationReplyView, IcObservationResponseView,
18 },
19 inventory_settlement::{
20 InventoryAttribution, InventorySettlementIdentity, InventorySettlementMismatch,
21 admitted_outcome, validate_pair,
22 },
23 snapshot_inventory_delta::SnapshotInventoryDeltaError,
24 },
25};
26use thiserror::Error;
27
28#[derive(Debug)]
30pub struct IcSnapshotUploadSettlementView<'a> {
31 observation: IcObservationResponseView<'a>,
32 baseline: &'a IcSnapshotReply<'a>,
33 settlement: &'a IcSnapshotUploadSettlement,
34 allocated_snapshot: Option<usize>,
35 outcome: ObservationOutcomeRecord,
36}
37impl<'a> IcSnapshotUploadSettlementView<'a> {
38 #[must_use]
40 pub const fn outcome(&self) -> ObservationOutcomeRecord {
41 self.outcome
42 }
43 #[must_use]
45 pub const fn baseline(&self) -> &'a IcSnapshotReply<'a> {
46 self.baseline
47 }
48 #[must_use]
50 pub const fn observation(&self) -> &IcObservationResponseView<'a> {
51 &self.observation
52 }
53 #[must_use]
55 pub const fn settlement(&self) -> &'a IcSnapshotUploadSettlement {
56 self.settlement
57 }
58 #[must_use]
60 pub fn allocated_snapshot(&self) -> Option<&IcSnapshotInfo> {
61 self.observation
62 .inventory_snapshot(self.allocated_snapshot?)
63 }
64}
65
66pub fn validate_settlement<'a>(
79 request: &IcSnapshotUploadObservationRequest<'a, '_>,
80 journal: &AttemptJournalRecord,
81 baseline: &'a IcSnapshotReply<'a>,
82 response: &'a IcObservationResponse,
83 challenge: &ArtifactChecksumRecord,
84 settlement: &'a IcSnapshotUploadSettlement,
85) -> Result<IcSnapshotUploadSettlementView<'a>, IcSnapshotUploadSettlementError> {
86 let observation = validate_response(request, journal, response)?;
87 let IcObservationReplyView::Inventory(inventory) = observation.reply() else {
88 return Err(IcSnapshotUploadSettlementError::WrongInventory);
89 };
90 let authority = request.authority().digest();
91 let baseline_digest = baseline.digest();
92 let inventory_digest = inventory.digest();
93 let candidates = validate_pair::<IcSnapshotUploadSettlementError>(
94 request.payload().target(),
95 baseline,
96 inventory,
97 &InventorySettlementIdentity {
98 authority: &settlement.authority,
99 mutation_attempt: settlement.mutation_attempt,
100 observation_attempt: settlement.observation_attempt,
101 challenge: &settlement.challenge,
102 baseline: &settlement.baseline,
103 inventory: &settlement.inventory,
104 observation_evidence: &settlement.observation_evidence,
105 },
106 &InventorySettlementIdentity {
107 authority: &authority,
108 mutation_attempt: request.mutation_attempt(),
109 observation_attempt: request.observation_attempt(),
110 challenge,
111 baseline: &baseline_digest,
112 inventory: &inventory_digest,
113 observation_evidence: &response.input().evidence,
114 },
115 )?;
116 if let IcSnapshotUploadAttribution::Applied { snapshot_id, .. } = &settlement.attribution {
117 request.mutation().validate_data_destination(snapshot_id)?;
118 }
119 let (outcome, allocated_snapshot) = admitted_outcome(
120 inventory,
121 &candidates,
122 InventoryAttribution::from(&settlement.attribution),
123 )
124 .map_err(|()| IcSnapshotUploadSettlementError::NotNewCandidate)?;
125 Ok(IcSnapshotUploadSettlementView {
126 observation,
127 baseline,
128 settlement,
129 allocated_snapshot,
130 outcome,
131 })
132}
133
134#[derive(Debug, Error)]
136pub enum IcSnapshotUploadSettlementError {
137 #[error(transparent)]
139 Observation(#[from] IcObservationAssociationError),
140 #[error(transparent)]
142 Baseline(#[from] SnapshotInventoryDeltaError),
143 #[error(transparent)]
145 Destination(#[from] IcSnapshotUploadError),
146 #[error("metadata settlement requires snapshot inventory")]
148 WrongInventory,
149 #[error("metadata settlement authority differs")]
151 AuthorityMismatch,
152 #[error("metadata settlement attempts differ")]
154 AttemptMismatch,
155 #[error("metadata settlement challenge differs")]
157 ChallengeMismatch,
158 #[error("metadata settlement baseline differs")]
160 BaselineMismatch,
161 #[error("metadata settlement inventory differs")]
163 InventoryMismatch,
164 #[error("metadata settlement observation evidence differs")]
166 ObservationEvidenceMismatch,
167 #[error("metadata settlement ID is not a new candidate")]
169 NotNewCandidate,
170}
171
172impl From<InventorySettlementMismatch> for IcSnapshotUploadSettlementError {
173 fn from(value: InventorySettlementMismatch) -> Self {
174 match value {
175 InventorySettlementMismatch::Authority => Self::AuthorityMismatch,
176 InventorySettlementMismatch::Attempt => Self::AttemptMismatch,
177 InventorySettlementMismatch::Challenge => Self::ChallengeMismatch,
178 InventorySettlementMismatch::Baseline => Self::BaselineMismatch,
179 InventorySettlementMismatch::Inventory => Self::InventoryMismatch,
180 InventorySettlementMismatch::ObservationEvidence => Self::ObservationEvidenceMismatch,
181 }
182 }
183}
184
185#[cfg(test)]
186mod tests;