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(mut principals: Vec<String>) -> Result<Self, ControlObservationError> {
29 if principals.len() > MAX_CONTROLLERS {
30 return Err(ControlObservationError::TooManyControllers);
31 }
32 for principal in &mut principals {
33 *principal = super::principal::canonical_text(principal)
34 .ok_or(ControlObservationError::InvalidPrincipal)?;
35 }
36 principals.sort();
37 if principals.windows(2).any(|pair| pair[0] == pair[1]) {
38 return Err(ControlObservationError::DuplicateController);
39 }
40 Ok(Self { principals })
41 }
42 #[must_use]
44 pub fn principals(&self) -> &[String] {
45 &self.principals
46 }
47 #[must_use]
49 pub fn contains_caller(&self, binding: &OperationBindingRecord) -> bool {
50 self.principals
51 .binary_search_by(|principal| principal.as_str().cmp(binding.caller()))
52 .is_ok()
53 }
54}
55
56#[derive(Clone, Debug)]
62pub struct ControlObservationRequest<'a> {
63 binding: OperationBindingRecord,
64 wire: &'a IcManagementRequestRecord,
65 challenge: ArtifactChecksumRecord,
66 max_remote_observations: u32,
67}
68impl<'a> ControlObservationRequest<'a> {
69 pub fn new(
73 plan: &OperationPlanRecord,
74 sequence: u64,
75 wire: &'a IcManagementRequestRecord,
76 challenge: ArtifactChecksumRecord,
77 max_remote_observations: u32,
78 ) -> Result<Self, ControlRequestError> {
79 if max_remote_observations > MAX_CONTROL_REMOTE_OBSERVATIONS {
80 return Err(ControlRequestError::ObservationLimitTooLarge);
81 }
82 let binding = plan.attempt_authority(sequence)?.binding().clone();
83 wire.validate_mutation_binding(&binding)?;
84 Ok(Self {
85 binding,
86 wire,
87 challenge,
88 max_remote_observations,
89 })
90 }
91 #[must_use]
93 pub const fn binding(&self) -> &OperationBindingRecord {
94 &self.binding
95 }
96 #[must_use]
98 pub const fn wire(&self) -> &IcManagementRequestRecord {
99 self.wire
100 }
101 #[must_use]
103 pub const fn challenge(&self) -> &ArtifactChecksumRecord {
104 &self.challenge
105 }
106 #[must_use]
108 pub const fn max_remote_observations(&self) -> u32 {
109 self.max_remote_observations
110 }
111 #[must_use]
117 pub fn digest(&self) -> ArtifactChecksumRecord {
118 let mut bytes = b"ic-backup/control-observation/v1\0".to_vec();
119 bytes.extend_from_slice(self.binding.intent().as_bytes());
120 bytes.extend_from_slice(&self.binding.operation_sequence().to_be_bytes());
121 bytes.extend_from_slice(self.wire.digest().hash().as_bytes());
122 bytes.extend_from_slice(self.challenge.hash().as_bytes());
123 bytes.extend_from_slice(&self.max_remote_observations.to_be_bytes());
124 ArtifactChecksumRecord::from_bytes(&bytes)
125 }
126}
127
128#[derive(Clone, Debug)]
130pub struct ControlObservationInput {
131 pub request: ArtifactChecksumRecord,
133 pub context: PlanContextRecord,
135 pub target: String,
137 pub controllers: ControllerSet,
139 pub evidence: ArtifactChecksumRecord,
141 pub remote_observations: u32,
143}
144#[derive(Clone, Debug)]
146pub struct ControlObservation {
147 input: ControlObservationInput,
148}
149impl ControlObservation {
150 pub fn new(mut input: ControlObservationInput) -> Result<Self, ControlObservationError> {
154 input.target = super::principal::canonical_text(&input.target)
155 .ok_or(ControlObservationError::InvalidPrincipal)?;
156 Ok(Self { input })
157 }
158 #[must_use]
160 pub const fn request(&self) -> &ArtifactChecksumRecord {
161 &self.input.request
162 }
163 #[must_use]
165 pub const fn context(&self) -> &PlanContextRecord {
166 &self.input.context
167 }
168 #[must_use]
170 pub fn target(&self) -> &str {
171 &self.input.target
172 }
173 #[must_use]
175 pub const fn controllers(&self) -> &ControllerSet {
176 &self.input.controllers
177 }
178 #[must_use]
180 pub const fn evidence(&self) -> &ArtifactChecksumRecord {
181 &self.input.evidence
182 }
183 #[must_use]
185 pub const fn remote_observations(&self) -> u32 {
186 self.input.remote_observations
187 }
188}
189
190#[derive(Debug, Eq, Error, PartialEq)]
192pub enum ControlObservationError {
193 #[error("invalid control observation principal")]
195 InvalidPrincipal,
196 #[error("controller set exceeds {MAX_CONTROLLERS}")]
198 TooManyControllers,
199 #[error("duplicate controller principal")]
201 DuplicateController,
202}
203#[derive(Debug, Error)]
205pub enum ControlRequestError {
206 #[error("control observation ceiling exceeds {MAX_CONTROL_REMOTE_OBSERVATIONS}")]
208 ObservationLimitTooLarge,
209 #[error(transparent)]
211 Plan(#[from] OperationPlanError),
212 #[error(transparent)]
214 Payload(#[from] IcRequestError),
215}
216
217#[cfg(test)]
218mod tests;