Skip to main content

ic_query/subnet_catalog/resolver/
canister.rs

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