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 InventorySettlementOutcome, 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 resolution: InventorySettlementOutcome,
36}
37impl<'a> IcSnapshotUploadSettlementView<'a> {
38 #[must_use]
40 pub const fn outcome(&self) -> ObservationOutcomeRecord {
41 self.resolution.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.resolution.snapshot_index()?)
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 candidates = validate_pair::<IcSnapshotUploadSettlementError>(
92 request.payload().target(),
93 baseline,
94 inventory,
95 &InventorySettlementIdentity {
96 operation: SettlementIdentity {
97 authority: &settlement.authority,
98 mutation_attempt: settlement.mutation_attempt,
99 observation_attempt: settlement.observation_attempt,
100 challenge: &settlement.challenge,
101 },
102 baseline: &settlement.baseline,
103 inventory: &settlement.inventory,
104 observation_evidence: &settlement.observation_evidence,
105 },
106 &SettlementIdentity {
107 authority: &authority,
108 mutation_attempt: request.mutation_attempt(),
109 observation_attempt: request.observation_attempt(),
110 challenge,
111 },
112 &response.input().evidence,
113 )?;
114 if let IcSnapshotUploadAttribution::Applied { snapshot_id, .. } = &settlement.attribution {
115 request.mutation().validate_data_destination(snapshot_id)?;
116 }
117 let resolution = admitted_outcome(
118 inventory,
119 &candidates,
120 InventoryAttribution::from(&settlement.attribution),
121 )
122 .map_err(|()| IcSnapshotUploadSettlementError::NotNewCandidate)?;
123 Ok(IcSnapshotUploadSettlementView {
124 observation,
125 baseline,
126 settlement,
127 resolution,
128 })
129}
130
131#[derive(Debug, Error)]
133pub enum IcSnapshotUploadSettlementError {
134 #[error(transparent)]
136 Observation(#[from] IcObservationAssociationError),
137 #[error(transparent)]
139 Baseline(#[from] SnapshotInventoryDeltaError),
140 #[error(transparent)]
142 Destination(#[from] IcSnapshotUploadError),
143 #[error("metadata settlement requires snapshot inventory")]
145 WrongInventory,
146 #[error(transparent)]
148 Identity(#[from] InventorySettlementMismatch),
149 #[error("metadata settlement ID is not a new candidate")]
151 NotNewCandidate,
152}
153
154#[cfg(test)]
155mod tests;