Skip to main content

ic_backup/model/control_authority/
mod.rs

1//! Canonical bounded controller identities used by IC status decoding.
2
3use crate::model::attempt_journal::OperationBindingRecord;
4use thiserror::Error;
5
6/// Maximum controllers in the maintained IC controller-set boundary.
7pub const MAX_CONTROLLERS: usize = 10;
8
9/// Canonical bounded known controller set, including an explicitly known empty set.
10///
11/// This validates identities, not observation authenticity. Missing/unknown controller
12/// evidence must fail at the provider boundary, never default into a success result.
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct ControllerSet {
15    principals: Vec<String>,
16}
17impl ControllerSet {
18    /// Normalize and sort exact principals; equivalent duplicate entries reject.
19    /// # Errors
20    /// Rejects excessive counts, malformed principals and canonical duplicates.
21    pub fn new(principals: Vec<String>) -> Result<Self, ControlObservationError> {
22        let principals = super::principal::canonical_set(
23            principals,
24            MAX_CONTROLLERS,
25            || ControlObservationError::TooManyControllers,
26            || ControlObservationError::InvalidPrincipal,
27            || ControlObservationError::DuplicateController,
28        )?;
29        Ok(Self { principals })
30    }
31    /// Read canonical controllers; ordering grants no routing or lifecycle semantics.
32    #[must_use]
33    pub fn principals(&self) -> &[String] {
34        &self.principals
35    }
36    /// Check the exact canonical original caller, without delegating through other controllers.
37    #[must_use]
38    pub fn contains_caller(&self, binding: &OperationBindingRecord) -> bool {
39        self.principals
40            .binary_search_by(|principal| principal.as_str().cmp(binding.caller()))
41            .is_ok()
42    }
43}
44
45/// Invalid bounded controller identities; status decoding retains this public error.
46#[derive(Debug, Eq, Error, PartialEq)]
47pub enum ControlObservationError {
48    /// Controller principal failed bounded canonical admission.
49    #[error("invalid control observation principal")]
50    InvalidPrincipal,
51    /// Full set exceeds the maintained bound.
52    #[error("controller set exceeds {MAX_CONTROLLERS}")]
53    TooManyControllers,
54    /// Equivalent principal appears twice.
55    #[error("duplicate controller principal")]
56    DuplicateController,
57}
58
59#[cfg(test)]
60mod tests;