ic-query 0.97.0

Internet Computer query library for CloudEngine, NNS, SNS, ICRC, system canisters, and public network metadata
Documentation
use crate::{
    ic_registry::{
        MainnetRegistryFetchRequest, RegistryFetchError,
        projection::subnet_kind_from_registry,
        relations::{RegistryRelationInventory, ResolvedNodeRelation, resolved_node_relations},
    },
    nns::topology::{
        NNS_SUBNET_TOPOLOGY_REPORT_SCHEMA_VERSION, NnsSubnetNodeProviderRow,
        NnsSubnetTopologyReport, NnsSubnetTopologyRow,
    },
    subnet_catalog::{MAINNET_NETWORK, MAINNET_REGISTRY_CANISTER_ID},
};
use ic_host_tools::registry::proto::SubnetRecord;
use ic_platform::candid::contract::Principal;
use std::collections::BTreeMap;

pub(in crate::ic_registry) fn subnet_topology_from_inventory(
    request: &MainnetRegistryFetchRequest,
    inventory: &RegistryRelationInventory,
    registry_version: u64,
) -> Result<NnsSubnetTopologyReport, RegistryFetchError> {
    let mut node_assignments = BTreeMap::<String, String>::new();
    let node_relations = resolved_node_relations(
        &inventory.node_principals,
        &inventory.node_operator_principals,
        &inventory.node_operator_records,
    )?;
    let mut subnets = Vec::with_capacity(inventory.subnet_records.len());

    for (subnet_principal, subnet_record) in &inventory.subnet_records {
        u32::try_from(subnet_record.membership.len()).map_err(|_| {
            RegistryFetchError::CountOverflow {
                field: "subnet_record.membership",
            }
        })?;
        let node_providers = subnet_node_providers(
            subnet_principal,
            subnet_record,
            &node_relations,
            &mut node_assignments,
        )?;
        subnets.push(NnsSubnetTopologyRow {
            subnet_principal: subnet_principal.clone(),
            subnet_kind: subnet_kind_from_registry(subnet_record.subnet_type),
            node_providers,
        });
    }

    Ok(NnsSubnetTopologyReport {
        schema_version: NNS_SUBNET_TOPOLOGY_REPORT_SCHEMA_VERSION,
        network: MAINNET_NETWORK.to_string(),
        registry_canister_id: MAINNET_REGISTRY_CANISTER_ID.to_string(),
        registry_version,
        fetched_at: request.fetched_at.clone(),
        fetched_by: request.fetched_by.clone(),
        source_endpoint: request.endpoint.clone(),
        subnets,
    })
}

fn subnet_node_providers(
    subnet_principal: &str,
    subnet_record: &SubnetRecord,
    node_relations: &BTreeMap<String, ResolvedNodeRelation>,
    node_assignments: &mut BTreeMap<String, String>,
) -> Result<Vec<NnsSubnetNodeProviderRow>, RegistryFetchError> {
    let mut provider_counts = BTreeMap::<String, u32>::new();
    for node_raw in &subnet_record.membership {
        let node_principal = Principal::try_from_slice(node_raw)
            .map(|principal| principal.to_text())
            .map_err(|err| RegistryFetchError::InvalidPrincipal {
                field: "subnet_record.membership",
                reason: err.to_string(),
            })?;
        if let Some(first_subnet_principal) =
            node_assignments.insert(node_principal.clone(), subnet_principal.to_string())
        {
            return Err(RegistryFetchError::DuplicateNodeAssignment {
                node_principal,
                first_subnet_principal,
                second_subnet_principal: subnet_principal.to_string(),
            });
        }
        let relation = node_relations.get(&node_principal).ok_or_else(|| {
            RegistryFetchError::MissingNodeRecord {
                node_principal: node_principal.clone(),
            }
        })?;
        let count = provider_counts
            .entry(relation.node_provider_principal.clone())
            .or_default();
        *count = count
            .checked_add(1)
            .ok_or(RegistryFetchError::CountOverflow {
                field: "node_provider.node_count",
            })?;
    }

    Ok(provider_counts
        .into_iter()
        .map(
            |(node_provider_principal, node_count)| NnsSubnetNodeProviderRow {
                node_provider_principal,
                node_count,
            },
        )
        .collect())
}

#[cfg(test)]
mod tests;