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 settlement_identity::SettlementIdentity,
24 snapshot_inventory_delta::SnapshotInventoryDeltaError,
25 },
26};
27use thiserror::Error;
28
29#[derive(Debug)]
31pub struct IcSnapshotUploadSettlementView<'a> {
32 observation: IcObservationResponseView<'a>,
33 baseline: &'a IcSnapshotReply<'a>,
34 settlement: &'a IcSnapshotUploadSettlement,
35 allocated_snapshot: Option<usize>,
36 outcome: ObservationOutcomeRecord,
37}
38impl<'a> IcSnapshotUploadSettlementView<'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 IcSnapshotUploadSettlement {
57 self.settlement
58 }
59 #[must_use]
61 pub fn allocated_snapshot(&self) -> Option<&IcSnapshotInfo> {
62 self.observation
63 .inventory_snapshot(self.allocated_snapshot?)
64 }
65}
66
67pub fn validate_settlement<'a>(
80 request: &IcSnapshotUploadObservationRequest<'a, '_>,
81 journal: &AttemptJournalRecord,
82 baseline: &'a IcSnapshotReply<'a>,
83 response: &'a IcObservationResponse,
84 challenge: &ArtifactChecksumRecord,
85 settlement: &'a IcSnapshotUploadSettlement,
86) -> Result<IcSnapshotUploadSettlementView<'a>, IcSnapshotUploadSettlementError> {
87 let observation = validate_response(request, journal, response)?;
88 let IcObservationReplyView::Inventory(inventory) = observation.reply() else {
89 return Err(IcSnapshotUploadSettlementError::WrongInventory);
90 };
91 let authority = request.authority().digest();
92 let baseline_digest = baseline.digest();
93 let inventory_digest = inventory.digest();
94 let candidates = validate_pair::<IcSnapshotUploadSettlementError>(
95 request.payload().target(),
96 baseline,
97 inventory,
98 &InventorySettlementIdentity {
99 operation: SettlementIdentity {
100 authority: &settlement.authority,
101 mutation_attempt: settlement.mutation_attempt,
102 observation_attempt: settlement.observation_attempt,
103 challenge: &settlement.challenge,
104 },
105 baseline: &settlement.baseline,
106 inventory: &settlement.inventory,
107 observation_evidence: &settlement.observation_evidence,
108 },
109 &InventorySettlementIdentity {
110 operation: SettlementIdentity {
111 authority: &authority,
112 mutation_attempt: request.mutation_attempt(),
113 observation_attempt: request.observation_attempt(),
114 challenge,
115 },
116 baseline: &baseline_digest,
117 inventory: &inventory_digest,
118 observation_evidence: &response.input().evidence,
119 },
120 )?;
121 if let IcSnapshotUploadAttribution::Applied { snapshot_id, .. } = &settlement.attribution {
122 request.mutation().validate_data_destination(snapshot_id)?;
123 }
124 let (outcome, allocated_snapshot) = admitted_outcome(
125 inventory,
126 &candidates,
127 InventoryAttribution::from(&settlement.attribution),
128 )
129 .map_err(|()| IcSnapshotUploadSettlementError::NotNewCandidate)?;
130 Ok(IcSnapshotUploadSettlementView {
131 observation,
132 baseline,
133 settlement,
134 allocated_snapshot,
135 outcome,
136 })
137}
138
139#[derive(Debug, Error)]
141pub enum IcSnapshotUploadSettlementError {
142 #[error(transparent)]
144 Observation(#[from] IcObservationAssociationError),
145 #[error(transparent)]
147 Baseline(#[from] SnapshotInventoryDeltaError),
148 #[error(transparent)]
150 Destination(#[from] IcSnapshotUploadError),
151 #[error("metadata settlement requires snapshot inventory")]
153 WrongInventory,
154 #[error(transparent)]
156 Identity(#[from] InventorySettlementMismatch),
157 #[error("metadata settlement ID is not a new candidate")]
159 NotNewCandidate,
160}
161
162#[cfg(test)]
163mod tests;