Skip to main content

ic_query/subnet_catalog/model/
validation.rs

1use super::{RoutingRange, SubnetCatalog, SubnetInfo};
2use crate::subnet_catalog::{
3    CATALOG_SCHEMA_VERSION, CatalogError, parse_principal, principal_bytes,
4    resolver::routing_range_sorts_after,
5};
6use std::collections::BTreeSet;
7
8impl SubnetCatalog {
9    /// Validate schema, principal syntax, and routing references.
10    pub fn validate(&self) -> Result<(), CatalogError> {
11        if self.catalog_schema_version != CATALOG_SCHEMA_VERSION {
12            return Err(CatalogError::UnsupportedSchemaVersion {
13                found: self.catalog_schema_version,
14                supported: CATALOG_SCHEMA_VERSION,
15            });
16        }
17        if self.subnets.is_empty() {
18            return Err(CatalogError::EmptySubnets);
19        }
20        if self.routing_ranges.is_empty() {
21            return Err(CatalogError::EmptyRoutingRanges);
22        }
23        parse_principal(&self.registry_canister_id, "registry_canister_id")?;
24
25        let mut subnet_principals = BTreeSet::new();
26        for subnet in &self.subnets {
27            parse_principal(&subnet.subnet_principal, "subnet_principal")?;
28            if !subnet_principals.insert(subnet.subnet_principal.clone()) {
29                return Err(CatalogError::DuplicateSubnet {
30                    subnet_principal: subnet.subnet_principal.clone(),
31                });
32            }
33        }
34
35        for range in &self.routing_ranges {
36            if !subnet_principals.contains(&range.subnet_principal) {
37                return Err(CatalogError::UnknownRoutingSubnet {
38                    subnet_principal: range.subnet_principal.clone(),
39                });
40            }
41            let start = principal_bytes(&range.start_canister_id, "start_canister_id")?;
42            let end = principal_bytes(&range.end_canister_id, "end_canister_id")?;
43            parse_principal(&range.subnet_principal, "routing_range.subnet_principal")?;
44            if routing_range_sorts_after(&start, &end) {
45                return Err(CatalogError::InvalidRoutingRange {
46                    subnet_principal: range.subnet_principal.clone(),
47                    start_canister_id: range.start_canister_id.clone(),
48                    end_canister_id: range.end_canister_id.clone(),
49                });
50            }
51        }
52
53        Ok(())
54    }
55
56    #[must_use]
57    pub fn subnet_by_principal(&self, subnet_principal: &str) -> Option<&SubnetInfo> {
58        self.subnets
59            .iter()
60            .find(|subnet| subnet.subnet_principal == subnet_principal)
61    }
62
63    #[must_use]
64    pub fn routing_ranges_for_subnet(&self, subnet_principal: &str) -> Vec<&RoutingRange> {
65        self.routing_ranges
66            .iter()
67            .filter(|range| range.subnet_principal == subnet_principal)
68            .collect()
69    }
70}