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
11/// The type of an IC subnet, as reported in the state tree under `/subnet/<subnet_id>/type`.
12///
13/// This leaf is present for certification version V25 and higher.
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum SubnetType {
16 /// The NNS subnet and other system-level subnets.
17 System,
18 /// A standard application subnet.
19 Application,
20 /// A verified application subnet.
21 VerifiedApplication,
22 /// A cloud engine subnet.
23 CloudEngine,
24 /// An unrecognized subnet type string, preserved for forward compatibility.
25 Unknown(String),
26}
27
28/// Information about a subnet, including its public key, member nodes, and assigned canister ranges.
29///
30/// Range information may be incomplete depending on how the subnet was fetched. The lack of a canister ID
31/// within assigned ranges should not be treated immediately as an authorization failure without fetching
32/// fresh data with [`Agent::fetch_subnet_by_canister`](crate::Agent::fetch_subnet_by_canister).
33#[derive(Debug, Clone)]
34pub struct Subnet {
35 pub(crate) id: Principal,
36 // This key is just fetched for completeness. Do not actually use this value as it is not authoritative in case of a rogue subnet.
37 // If a future agent needs to know the subnet key then it should fetch /subnet from the *root* subnet.
38 pub(crate) key: Vec<u8>,
39 pub(crate) node_keys: HashMap<Principal, Vec<u8>>,
40 pub(crate) canister_ranges: RangeInclusiveSet<Principal>,
41 /// Present when the certificate's version is V25 or higher; `None` for older certificates.
42 pub(crate) subnet_type: Option<SubnetType>,
43}
44
45impl Subnet {
46 /// Checks whether the given canister ID is contained within the subnet's assigned canister ranges.
47 pub fn contains_canister(&self, canister_id: &Principal) -> bool {
48 self.canister_ranges.contains(canister_id)
49 }
50 /// Returns an iterator over the known canister ID ranges assigned to this subnet.
51 pub fn iter_canister_ranges(&self) -> CanisterRangesIter<'_> {
52 CanisterRangesIter {
53 inner: self.canister_ranges.iter(),
54 }
55 }
56 /// Returns the self-reported public key of the subnet.
57 ///
58 /// Note that this key is not authoritative if the subnet is rogue.
59 pub fn self_reported_key(&self) -> &[u8] {
60 &self.key
61 }
62 /// Checks whether the given node ID is a member of this subnet.
63 pub fn contains_node(&self, node_id: &Principal) -> bool {
64 self.node_keys.contains_key(node_id)
65 }
66 /// Returns the public key of the given node ID, if it is a member of this subnet.
67 pub fn get_node_key(&self, node_id: &Principal) -> Option<&[u8]> {
68 self.node_keys.get(node_id).map(|k| &k[..])
69 }
70 /// Returns an iterator over the nodes in this subnet.
71 pub fn iter_nodes(&self) -> SubnetNodeIter<'_> {
72 SubnetNodeIter {
73 inner: self.node_keys.keys(),
74 }
75 }
76 /// Returns an iterator over the node IDs and their corresponding public keys in this subnet.
77 pub fn iter_node_keys(&self) -> SubnetKeysIter<'_> {
78 SubnetKeysIter {
79 inner: self.node_keys.iter(),
80 }
81 }
82 /// Returns the subnet's ID.
83 pub fn id(&self) -> Principal {
84 self.id
85 }
86 /// Returns the subnet type if it was present in the state tree certificate.
87 ///
88 /// This is populated when the replica uses certification version V25 or higher.
89 /// Returns `None` for older certificates that do not include the `type` leaf.
90 pub fn subnet_type(&self) -> Option<&SubnetType> {
91 self.subnet_type.as_ref()
92 }
93}
94
95/// Iterator over the canister ID ranges assigned to a subnet.
96pub struct CanisterRangesIter<'a> {
97 inner: rangemap::inclusive_set::Iter<'a, Principal>,
98}
99
100impl Iterator for CanisterRangesIter<'_> {
101 type Item = RangeInclusive<Principal>;
102
103 fn next(&mut self) -> Option<Self::Item> {
104 self.inner.next().cloned()
105 }
106}
107
108/// Iterator over the node IDs in a subnet.
109#[derive(Debug)]
110pub struct SubnetNodeIter<'a> {
111 inner: std::collections::hash_map::Keys<'a, Principal, Vec<u8>>,
112}
113
114impl<'a> Iterator for SubnetNodeIter<'a> {
115 type Item = Principal;
116
117 fn next(&mut self) -> Option<Self::Item> {
118 self.inner.next().copied()
119 }
120}
121
122/// Iterator over the node IDs and their corresponding public keys in a subnet.
123#[derive(Debug)]
124pub struct SubnetKeysIter<'a> {
125 inner: std::collections::hash_map::Iter<'a, Principal, Vec<u8>>,
126}
127
128impl<'a> Iterator for SubnetKeysIter<'a> {
129 type Item = (Principal, &'a [u8]);
130
131 fn next(&mut self) -> Option<Self::Item> {
132 self.inner.next().map(|(k, v)| (*k, &v[..]))
133 }
134}