ic_backup/model/control_authority/
mod.rs1use crate::model::{
4 artifacts::ArtifactChecksumRecord,
5 attempt_journal::OperationBindingRecord,
6 ic_request::{IcManagementRequestRecord, IcRequestError},
7 operation_plan::{OperationPlanError, OperationPlanRecord, PlanContextRecord},
8};
9use thiserror::Error;
10
11pub const MAX_CONTROLLERS: usize = 10;
13pub const MAX_CONTROL_REMOTE_OBSERVATIONS: u32 = 1024;
15
16#[derive(Clone, Debug, Eq, PartialEq)]
21pub struct ControllerSet {
22 principals: Vec<String>,
23}
24impl ControllerSet {
25 pub fn new(principals: Vec<String>) -> Result<Self, ControlObservationError> {
29 let principals = super::principal::canonical_set(
30 principals,
31 MAX_CONTROLLERS,
32 || ControlObservationError::TooManyControllers,
33 || ControlObservationError::InvalidPrincipal,
34 || ControlObservationError::DuplicateController,
35 )?;
36 Ok(Self { principals })
37 }
38 #[must_use]
40 pub fn principals(&self) -> &[String] {
41 &self.principals
42 }
43 #[must_use]
45 pub fn contains_caller(&self, binding: &OperationBindingRecord) -> bool {
46 self.principals
47 .binary_search_by(|principal| principal.as_str().cmp(binding.caller()))
48 .is_ok()
49 }
50}
51
52#[derive(Clone, Debug)]
58pub struct ControlObservationRequest<'a> {
59 binding: OperationBindingRecord,
60 wire: &'a IcManagementRequestRecord,
61 challenge: ArtifactChecksumRecord,
62 max_remote_observations: u32,
63}
64impl<'a> ControlObservationRequest<'a> {
65 pub fn new(
69 plan: &OperationPlanRecord,
70 sequence: u64,
71 wire: &'a IcManagementRequestRecord,
72 challenge: ArtifactChecksumRecord,
73 max_remote_observations: u32,
74 ) -> Result<Self, ControlRequestError> {
75 if max_remote_observations > MAX_CONTROL_REMOTE_OBSERVATIONS {
76 return Err(ControlRequestError::ObservationLimitTooLarge);
77 }
78 let binding = plan.attempt_authority(sequence)?.binding().clone();
79 wire.validate_mutation_binding(&binding)?;
80 Ok(Self {
81 binding,
82 wire,
83 challenge,
84 max_remote_observations,
85 })
86 }
87 #[must_use]
89 pub const fn binding(&self) -> &OperationBindingRecord {
90 &self.binding
91 }
92 #[must_use]
94 pub const fn wire(&self) -> &IcManagementRequestRecord {
95 self.wire
96 }
97 #[must_use]
99 pub const fn challenge(&self) -> &ArtifactChecksumRecord {
100 &self.challenge
101 }
102 #[must_use]
104 pub const fn max_remote_observations(&self) -> u32 {
105 self.max_remote_observations
106 }
107 #[must_use]
113 pub fn digest(&self) -> ArtifactChecksumRecord {
114 let mut bytes = b"ic-backup/control-observation/v1\0".to_vec();
115 bytes.extend_from_slice(self.binding.intent().as_bytes());
116 bytes.extend_from_slice(&self.binding.operation_sequence().to_be_bytes());
117 bytes.extend_from_slice(self.wire.digest().hash().as_bytes());
118 bytes.extend_from_slice(self.challenge.hash().as_bytes());
119 bytes.extend_from_slice(&self.max_remote_observations.to_be_bytes());
120 ArtifactChecksumRecord::from_bytes(&bytes)
121 }
122}
123
124#[derive(Clone, Debug)]
126pub struct ControlObservationInput {
127 pub request: ArtifactChecksumRecord,
129 pub context: PlanContextRecord,
131 pub target: String,
133 pub controllers: ControllerSet,
135 pub evidence: ArtifactChecksumRecord,
137 pub remote_observations: u32,
139}
140#[derive(Clone, Debug)]
142pub struct ControlObservation {
143 input: ControlObservationInput,
144}
145impl ControlObservation {
146 pub fn new(mut input: ControlObservationInput) -> Result<Self, ControlObservationError> {
150 input.target = super::principal::canonical_text(&input.target)
151 .ok_or(ControlObservationError::InvalidPrincipal)?;
152 Ok(Self { input })
153 }
154 #[must_use]
156 pub const fn request(&self) -> &ArtifactChecksumRecord {
157 &self.input.request
158 }
159 #[must_use]
161 pub const fn context(&self) -> &PlanContextRecord {
162 &self.input.context
163 }
164 #[must_use]
166 pub fn target(&self) -> &str {
167 &self.input.target
168 }
169 #[must_use]
171 pub const fn controllers(&self) -> &ControllerSet {
172 &self.input.controllers
173 }
174 #[must_use]
176 pub const fn evidence(&self) -> &ArtifactChecksumRecord {
177 &self.input.evidence
178 }
179 #[must_use]
181 pub const fn remote_observations(&self) -> u32 {
182 self.input.remote_observations
183 }
184}
185
186#[derive(Debug, Eq, Error, PartialEq)]
188pub enum ControlObservationError {
189 #[error("invalid control observation principal")]
191 InvalidPrincipal,
192 #[error("controller set exceeds {MAX_CONTROLLERS}")]
194 TooManyControllers,
195 #[error("duplicate controller principal")]
197 DuplicateController,
198}
199#[derive(Debug, Error)]
201pub enum ControlRequestError {
202 #[error("control observation ceiling exceeds {MAX_CONTROL_REMOTE_OBSERVATIONS}")]
204 ObservationLimitTooLarge,
205 #[error(transparent)]
207 Plan(#[from] OperationPlanError),
208 #[error(transparent)]
210 Payload(#[from] IcRequestError),
211}
212
213#[cfg(test)]
214mod tests;