ic_query/subnet_catalog/model/
validation.rs1use 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 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}