ic_agent/agent/
subnet.rs

1//! Information about IC subnets.
2//!
3//! Fetch subnet information via [`Agent::fetch_subnet_by_id`](crate::Agent::fetch_subnet_by_id) or
4//! [`Agent::get_subnet_by_canister`](crate::Agent::get_subnet_by_canister).
5
6use std::{collections::HashMap, ops::RangeInclusive};
7
8use candid::Principal;
9use rangemap::RangeInclusiveSet;
10
11use crate::agent::PrincipalStep;
12
13/// Information about a subnet, including its public key, member nodes, and assigned canister ranges.
14///
15/// Range information may be incomplete depending on how the subnet was fetched. The lack of a canister ID
16/// within assigned ranges should not be treated immediately as an authorization failure without fetching
17/// fresh data with [`Agent::fetch_subnet_by_canister`](crate::Agent::fetch_subnet_by_canister).
18#[derive(Clone)]
19pub struct Subnet {
20    pub(crate) id: Principal,
21    // This key is just fetched for completeness. Do not actually use this value as it is not authoritative in case of a rogue subnet.
22    // If a future agent needs to know the subnet key then it should fetch /subnet from the *root* subnet.
23    pub(crate) key: Vec<u8>,
24    pub(crate) node_keys: HashMap<Principal, Vec<u8>>,
25    pub(crate) canister_ranges: RangeInclusiveSet<Principal, PrincipalStep>,
26}
27
28impl Subnet {
29    /// Checks whether the given canister ID is contained within the subnet's assigned canister ranges.
30    pub fn contains_canister(&self, canister_id: &Principal) -> bool {
31        self.canister_ranges.contains(canister_id)
32    }
33    /// Returns an iterator over the known canister ID ranges assigned to this subnet.
34    pub fn iter_canister_ranges(&self) -> CanisterRangesIter<'_> {
35        CanisterRangesIter {
36            inner: self.canister_ranges.iter(),
37        }
38    }
39    /// Returns the self-reported public key of the subnet.
40    ///
41    /// Note that this key is not authoritative if the subnet is rogue.
42    pub fn self_reported_key(&self) -> &[u8] {
43        &self.key
44    }
45    /// Checks whether the given node ID is a member of this subnet.
46    pub fn contains_node(&self, node_id: &Principal) -> bool {
47        self.node_keys.contains_key(node_id)
48    }
49    /// Returns the public key of the given node ID, if it is a member of this subnet.
50    pub fn get_node_key(&self, node_id: &Principal) -> Option<&[u8]> {
51        self.node_keys.get(node_id).map(|k| &k[..])
52    }
53    /// Returns an iterator over the nodes in this subnet.
54    pub fn iter_nodes(&self) -> SubnetNodeIter<'_> {
55        SubnetNodeIter {
56            inner: self.node_keys.keys(),
57        }
58    }
59    /// Returns an iterator over the node IDs and their corresponding public keys in this subnet.
60    pub fn iter_node_keys(&self) -> SubnetKeysIter<'_> {
61        SubnetKeysIter {
62            inner: self.node_keys.iter(),
63        }
64    }
65    /// Returns the subnet's ID.
66    pub fn id(&self) -> Principal {
67        self.id
68    }
69}
70
71/// Iterator over the canister ID ranges assigned to a subnet.
72pub struct CanisterRangesIter<'a> {
73    inner: rangemap::inclusive_set::Iter<'a, Principal>,
74}
75
76impl Iterator for CanisterRangesIter<'_> {
77    type Item = RangeInclusive<Principal>;
78
79    fn next(&mut self) -> Option<Self::Item> {
80        self.inner.next().cloned()
81    }
82}
83
84/// Iterator over the node IDs in a subnet.
85pub struct SubnetNodeIter<'a> {
86    inner: std::collections::hash_map::Keys<'a, Principal, Vec<u8>>,
87}
88
89impl<'a> Iterator for SubnetNodeIter<'a> {
90    type Item = Principal;
91
92    fn next(&mut self) -> Option<Self::Item> {
93        self.inner.next().copied()
94    }
95}
96
97/// Iterator over the node IDs and their corresponding public keys in a subnet.
98pub struct SubnetKeysIter<'a> {
99    inner: std::collections::hash_map::Iter<'a, Principal, Vec<u8>>,
100}
101
102impl<'a> Iterator for SubnetKeysIter<'a> {
103    type Item = (Principal, &'a [u8]);
104
105    fn next(&mut self) -> Option<Self::Item> {
106        self.inner.next().map(|(k, v)| (*k, &v[..]))
107    }
108}