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::IcSnapshotUploadObservationRequest,
11 },
12 policy::{
13 ic_observation::{
14 IcObservationAssociationError, IcObservationReplyView, IcObservationResponseView,
15 },
16 inventory_settlement::{
17 InventorySettlementIdentity, InventorySettlementMismatch, InventorySettlementOutcome,
18 admitted_outcome, validate_pair,
19 },
20 settlement_identity::SettlementIdentity,
21 snapshot_inventory_delta::SnapshotInventoryDeltaError,
22 },
23};
24use thiserror::Error;
25
26use crate::model::inventory_settlement::IcInventoryAttribution;
27use crate::model::inventory_settlement::IcInventorySettlement;
28
29#[derive(Debug)]
31pub struct IcSnapshotUploadSettlementView<'a> {
32 observation: IcObservationResponseView<'a>,
33 baseline: &'a IcSnapshotReply<'a>,
34 settlement: &'a IcInventorySettlement,
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 IcInventorySettlement {
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 IcInventorySettlement,
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 IcInventoryAttribution::Applied { snapshot_id, .. } = &settlement.attribution {
115 request.mutation().validate_data_destination(snapshot_id)?;
116 }
117 let resolution = admitted_outcome(inventory, &candidates, &settlement.attribution)
118 .map_err(|()| IcSnapshotUploadSettlementError::NotNewCandidate)?;
119 Ok(IcSnapshotUploadSettlementView {
120 observation,
121 baseline,
122 settlement,
123 resolution,
124 })
125}
126
127#[derive(Debug, Error)]
129pub enum IcSnapshotUploadSettlementError {
130 #[error(transparent)]
132 Observation(#[from] IcObservationAssociationError),
133 #[error(transparent)]
135 Baseline(#[from] SnapshotInventoryDeltaError),
136 #[error(transparent)]
138 Destination(#[from] IcSnapshotUploadError),
139 #[error("metadata settlement requires snapshot inventory")]
141 WrongInventory,
142 #[error(transparent)]
144 Identity(#[from] InventorySettlementMismatch),
145 #[error("metadata settlement ID is not a new candidate")]
147 NotNewCandidate,
148}
149
150#[cfg(test)]
151mod tests;