ic_backup/policy/inventory_settlement/
mod.rs1use crate::model::inventory_settlement::IcInventoryAttribution;
4
5use super::{
6 settlement_identity::{SettlementIdentity, SettlementIdentityMismatch},
7 snapshot_inventory_delta::{SnapshotInventoryDeltaError, compare_inventories},
8};
9use crate::model::{
10 artifacts::ArtifactChecksumRecord,
11 attempt_journal::ObservationOutcomeRecord,
12 ic_snapshot_reply::{IcSnapshotInfo, IcSnapshotReply},
13};
14
15pub(crate) struct InventorySettlementIdentity<'a> {
16 pub operation: SettlementIdentity<'a>,
17 pub baseline: &'a ArtifactChecksumRecord,
18 pub inventory: &'a ArtifactChecksumRecord,
19 pub observation_evidence: &'a ArtifactChecksumRecord,
20}
21
22impl InventorySettlementIdentity<'_> {
23 pub fn compare(&self, expected: &Self) -> Result<(), InventorySettlementMismatch> {
24 self.operation.compare(&expected.operation)?;
25 if self.baseline != expected.baseline {
26 return Err(InventorySettlementMismatch::Baseline);
27 }
28 if self.inventory != expected.inventory {
29 return Err(InventorySettlementMismatch::Inventory);
30 }
31 if self.observation_evidence != expected.observation_evidence {
32 return Err(InventorySettlementMismatch::ObservationEvidence);
33 }
34 Ok(())
35 }
36}
37
38#[derive(Debug, thiserror::Error, Eq, PartialEq)]
40pub enum InventorySettlementMismatch {
41 #[error("inventory settlement authority differs")]
43 Authority,
44 #[error("inventory settlement attempts differ")]
46 Attempt,
47 #[error("inventory settlement challenge differs")]
49 Challenge,
50 #[error("inventory settlement baseline differs")]
52 Baseline,
53 #[error("inventory settlement inventory differs")]
55 Inventory,
56 #[error("inventory settlement observation evidence differs")]
58 ObservationEvidence,
59}
60
61impl From<SettlementIdentityMismatch> for InventorySettlementMismatch {
62 fn from(value: SettlementIdentityMismatch) -> Self {
63 match value {
64 SettlementIdentityMismatch::Authority => Self::Authority,
65 SettlementIdentityMismatch::Attempt => Self::Attempt,
66 SettlementIdentityMismatch::Challenge => Self::Challenge,
67 }
68 }
69}
70
71pub(crate) fn validate_pair<'a, E>(
72 target: &str,
73 baseline: &IcSnapshotReply<'_>,
74 inventory: &'a IcSnapshotReply<'_>,
75 claimed: &InventorySettlementIdentity<'_>,
76 operation: &SettlementIdentity<'_>,
77 observation_evidence: &ArtifactChecksumRecord,
78) -> Result<Vec<&'a IcSnapshotInfo>, E>
79where
80 E: From<SnapshotInventoryDeltaError> + From<InventorySettlementMismatch>,
81{
82 let candidates = compare_inventories(target, baseline, inventory)?;
83 claimed.compare(&InventorySettlementIdentity {
84 operation: SettlementIdentity {
85 authority: operation.authority,
86 mutation_attempt: operation.mutation_attempt,
87 observation_attempt: operation.observation_attempt,
88 challenge: operation.challenge,
89 },
90 baseline: &baseline.digest(),
91 inventory: &inventory.digest(),
92 observation_evidence,
93 })?;
94 Ok(candidates)
95}
96
97#[derive(Debug)]
99pub(crate) enum InventorySettlementOutcome {
100 Applied(usize),
101 NotApplied,
102 Uncertain,
103}
104
105impl InventorySettlementOutcome {
106 pub const fn outcome(&self) -> ObservationOutcomeRecord {
107 match self {
108 Self::Applied(_) => ObservationOutcomeRecord::Applied,
109 Self::NotApplied => ObservationOutcomeRecord::NotApplied,
110 Self::Uncertain => ObservationOutcomeRecord::Uncertain,
111 }
112 }
113
114 pub const fn snapshot_index(&self) -> Option<usize> {
115 match self {
116 Self::Applied(index) => Some(*index),
117 Self::NotApplied | Self::Uncertain => None,
118 }
119 }
120}
121
122pub(crate) fn admitted_outcome(
123 inventory: &IcSnapshotReply<'_>,
124 candidates: &[&IcSnapshotInfo],
125 attribution: &IcInventoryAttribution,
126) -> Result<InventorySettlementOutcome, ()> {
127 match attribution {
128 IcInventoryAttribution::Applied { snapshot_id, .. } => {
129 new_candidate_index(inventory, candidates, snapshot_id)
130 .map(InventorySettlementOutcome::Applied)
131 .ok_or(())
132 }
133 IcInventoryAttribution::NotApplied { .. } => Ok(InventorySettlementOutcome::NotApplied),
134 IcInventoryAttribution::Unresolved { .. } => Ok(InventorySettlementOutcome::Uncertain),
135 }
136}
137
138pub(crate) fn new_candidate_index(
139 inventory: &IcSnapshotReply<'_>,
140 candidates: &[&IcSnapshotInfo],
141 snapshot_id: &[u8],
142) -> Option<usize> {
143 if !candidates
144 .iter()
145 .any(|snapshot| snapshot.id() == snapshot_id)
146 {
147 return None;
148 }
149 inventory
150 .snapshots()
151 .binary_search_by(|snapshot| snapshot.id().cmp(snapshot_id))
152 .ok()
153}