Skip to main content

ic_query/subnet_catalog/resolver/
canister.rs

1use super::{ResolvedCanisterRoute, ResolvedSubnet, ResolvedSubnetSubject};
2use crate::subnet_catalog::{
3    CatalogError, RawSubnetCatalog, RoutingRange, SubnetInfo, ValidatedSubnetCatalog,
4    parse_principal, principal_bytes,
5};
6use ic_platform::candid::contract::Principal;
7
8impl RawSubnetCatalog {
9    /// Resolve a canister principal through cached routing ranges.
10    pub fn resolve_canister(&self, input_principal: &str) -> Result<ResolvedSubnet, CatalogError> {
11        let canister = parse_principal(input_principal, "canister_principal")?;
12        self.resolve_parsed_canister(&canister)
13    }
14
15    pub(super) fn resolve_parsed_canister(
16        &self,
17        canister: &Principal,
18    ) -> Result<ResolvedSubnet, CatalogError> {
19        let canonical_canister = canister.to_text();
20        let (range, subnet) = self.canister_route(canister)?;
21        Ok(ResolvedSubnet {
22            input_principal: canonical_canister.clone(),
23            resolved_as: ResolvedSubnetSubject::Canister,
24            resolved_from: "routing_range".to_string(),
25            subnet: subnet.clone(),
26            matched_canister_principal: Some(canonical_canister),
27            matched_routing_range: Some(range.clone()),
28            catalog_digest: self.catalog_digest.clone(),
29            provenance: self.provenance.clone(),
30        })
31    }
32
33    fn canister_route(
34        &self,
35        canister: &Principal,
36    ) -> Result<(&RoutingRange, &SubnetInfo), CatalogError> {
37        let range = self
38            .routing_ranges
39            .iter()
40            .find(|range| range_contains_principal(range, canister.as_slice()).unwrap_or(false))
41            .ok_or_else(|| CatalogError::RouteNotFound {
42                canister_principal: canister.to_text(),
43                registry_version: self.provenance.registry_version,
44                catalog_schema_version: self.catalog_schema_version,
45            })?;
46        let subnet = self
47            .subnet_by_principal(&range.subnet_principal)
48            .ok_or_else(|| CatalogError::UnknownRoutingSubnet {
49                subnet_principal: range.subnet_principal.clone(),
50            })?;
51        Ok((range, subnet))
52    }
53}
54
55impl ValidatedSubnetCatalog {
56    /// Resolve one canister to authority evidence bound to this exact catalog.
57    pub fn resolve_canister_route(
58        &self,
59        input_principal: &str,
60    ) -> Result<ResolvedCanisterRoute, CatalogError> {
61        let canister = parse_principal(input_principal, "canister_principal")?;
62        let (matched_range, subnet) = self.raw().canister_route(&canister)?;
63        Ok(ResolvedCanisterRoute {
64            canister,
65            subnet: parse_principal(&matched_range.subnet_principal, "subnet_principal")?,
66            subnet_info: subnet.clone(),
67            matched_range: matched_range.clone(),
68            registry_version: self.provenance().registry_version,
69            catalog_digest: self.catalog_digest(),
70            provenance: self.provenance().clone(),
71        })
72    }
73}
74
75fn range_contains_principal(
76    range: &crate::subnet_catalog::RoutingRange,
77    principal: &[u8],
78) -> Result<bool, CatalogError> {
79    let start = principal_bytes(&range.start_canister_id, "start_canister_id")?;
80    let end = principal_bytes(&range.end_canister_id, "end_canister_id")?;
81    Ok(start.as_slice() <= principal && principal <= end.as_slice())
82}