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