ic_backup/model/control_authority/
mod.rs1use crate::model::evidence_digest::EvidenceDigest;
4use crate::model::{
5 artifacts::ArtifactChecksumRecord,
6 attempt_journal::OperationBindingRecord,
7 ic_request::{IcManagementRequestRecord, IcRequestError},
8 operation_plan::{OperationPlanError, OperationPlanRecord, PlanContextRecord},
9};
10use thiserror::Error;
11
12pub const MAX_CONTROLLERS: usize = 10;
14pub const MAX_CONTROL_REMOTE_OBSERVATIONS: u32 = 1024;
16
17#[derive(Clone, Debug, Eq, PartialEq)]
22pub struct ControllerSet {
23 principals: Vec<String>,
24}
25impl ControllerSet {
26 pub fn new(principals: Vec<String>) -> Result<Self, ControlObservationError> {
30 let principals = super::principal::canonical_set(
31 principals,
32 MAX_CONTROLLERS,
33 || ControlObservationError::TooManyControllers,
34 || ControlObservationError::InvalidPrincipal,
35 || ControlObservationError::DuplicateController,
36 )?;
37 Ok(Self { principals })
38 }
39 #[must_use]
41 pub fn principals(&self) -> &[String] {
42 &self.principals
43 }
44 #[must_use]
46 pub fn contains_caller(&self, binding: &OperationBindingRecord) -> bool {
47 self.principals
48 .binary_search_by(|principal| principal.as_str().cmp(binding.caller()))
49 .is_ok()
50 }
51}
52
53#[derive(Clone, Debug)]
59pub struct ControlObservationRequest<'a> {
60 binding: OperationBindingRecord,
61 wire: &'a IcManagementRequestRecord,
62 challenge: ArtifactChecksumRecord,
63 max_remote_observations: u32,
64}
65impl<'a> ControlObservationRequest<'a> {
66 pub fn new(
70 plan: &OperationPlanRecord,
71 sequence: u64,
72 wire: &'a IcManagementRequestRecord,
73 challenge: ArtifactChecksumRecord,
74 max_remote_observations: u32,
75 ) -> Result<Self, ControlRequestError> {
76 if max_remote_observations > MAX_CONTROL_REMOTE_OBSERVATIONS {
77 return Err(ControlRequestError::ObservationLimitTooLarge);
78 }
79 let binding = plan.attempt_authority(sequence)?.binding().clone();
80 wire.validate_mutation_binding(&binding)?;
81 Ok(Self {
82 binding,
83 wire,
84 challenge,
85 max_remote_observations,
86 })
87 }
88 #[must_use]
90 pub const fn binding(&self) -> &OperationBindingRecord {
91 &self.binding
92 }
93 #[must_use]
95 pub const fn wire(&self) -> &IcManagementRequestRecord {
96 self.wire
97 }
98 #[must_use]
100 pub const fn challenge(&self) -> &ArtifactChecksumRecord {
101 &self.challenge
102 }
103 #[must_use]
105 pub const fn max_remote_observations(&self) -> u32 {
106 self.max_remote_observations
107 }
108 #[must_use]
114 pub fn digest(&self) -> ArtifactChecksumRecord {
115 let mut bytes = EvidenceDigest::new(b"ic-backup/control-observation/v1\0");
116 bytes.append(self.binding.intent().as_bytes());
117 bytes.append(&self.binding.operation_sequence().to_be_bytes());
118 bytes.append(self.wire.digest().hash().as_bytes());
119 bytes.append(self.challenge.hash().as_bytes());
120 bytes.append(&self.max_remote_observations.to_be_bytes());
121 bytes.finish()
122 }
123}
124
125#[derive(Clone, Debug)]
127pub struct ControlObservationInput {
128 pub request: ArtifactChecksumRecord,
130 pub context: PlanContextRecord,
132 pub target: String,
134 pub controllers: ControllerSet,
136 pub evidence: ArtifactChecksumRecord,
138 pub remote_observations: u32,
140}
141#[derive(Clone, Debug)]
143pub struct ControlObservation {
144 input: ControlObservationInput,
145}
146impl ControlObservation {
147 pub fn new(mut input: ControlObservationInput) -> Result<Self, ControlObservationError> {
151 input.target = super::principal::canonical_text(&input.target)
152 .ok_or(ControlObservationError::InvalidPrincipal)?;
153 Ok(Self { input })
154 }
155 #[must_use]
157 pub const fn request(&self) -> &ArtifactChecksumRecord {
158 &self.input.request
159 }
160 #[must_use]
162 pub const fn context(&self) -> &PlanContextRecord {
163 &self.input.context
164 }
165 #[must_use]
167 pub fn target(&self) -> &str {
168 &self.input.target
169 }
170 #[must_use]
172 pub const fn controllers(&self) -> &ControllerSet {
173 &self.input.controllers
174 }
175 #[must_use]
177 pub const fn evidence(&self) -> &ArtifactChecksumRecord {
178 &self.input.evidence
179 }
180 #[must_use]
182 pub const fn remote_observations(&self) -> u32 {
183 self.input.remote_observations
184 }
185}
186
187#[derive(Debug, Eq, Error, PartialEq)]
189pub enum ControlObservationError {
190 #[error("invalid control observation principal")]
192 InvalidPrincipal,
193 #[error("controller set exceeds {MAX_CONTROLLERS}")]
195 TooManyControllers,
196 #[error("duplicate controller principal")]
198 DuplicateController,
199}
200#[derive(Debug, Error)]
202pub enum ControlRequestError {
203 #[error("control observation ceiling exceeds {MAX_CONTROL_REMOTE_OBSERVATIONS}")]
205 ObservationLimitTooLarge,
206 #[error(transparent)]
208 Plan(#[from] OperationPlanError),
209 #[error(transparent)]
211 Payload(#[from] IcRequestError),
212}
213
214#[cfg(test)]
215mod tests;