bittensor_rs/queries/
subnet.rs

1//! # Subnet Queries
2//!
3//! Query subnet information from the Bittensor network.
4
5use crate::api::api;
6use crate::error::BittensorError;
7use subxt::OnlineClient;
8use subxt::PolkadotConfig;
9
10/// Subnet information
11#[derive(Debug, Clone)]
12pub struct SubnetInfo {
13    /// Subnet netuid
14    pub netuid: u16,
15    /// Subnet tempo (blocks per epoch)
16    pub tempo: u16,
17    /// Number of neurons
18    pub n: u16,
19    /// Maximum number of neurons
20    pub max_n: u16,
21    /// Immunity period
22    pub immunity_period: u16,
23    /// Registration allowed
24    pub registration_allowed: bool,
25}
26
27/// Subnet hyperparameters
28#[derive(Debug, Clone)]
29pub struct SubnetHyperparameters {
30    /// Blocks per epoch
31    pub tempo: u16,
32    /// Maximum neurons
33    pub max_n: u16,
34    /// Minimum allowed weights
35    pub min_allowed_weights: u16,
36    /// Maximum allowed weights
37    pub max_allowed_weights: u16,
38    /// Weights version key
39    pub weights_version_key: u64,
40    /// Weights rate limit
41    pub weights_rate_limit: u64,
42    /// Registration allowed
43    pub registration_allowed: bool,
44    /// Adjustment interval
45    pub adjustment_interval: u16,
46    /// Target registration per interval
47    pub target_regs_per_interval: u16,
48    /// Immunity period
49    pub immunity_period: u16,
50}
51
52/// Get information about a subnet
53pub async fn get_subnet_info(
54    client: &OnlineClient<PolkadotConfig>,
55    netuid: u16,
56) -> Result<SubnetInfo, BittensorError> {
57    let storage = client
58        .storage()
59        .at_latest()
60        .await
61        .map_err(|e| BittensorError::RpcError {
62            message: format!("Failed to get storage: {}", e),
63        })?;
64
65    let tempo = storage
66        .fetch(&api::storage().subtensor_module().tempo(netuid))
67        .await
68        .map_err(|e| BittensorError::StorageQueryError {
69            key: "tempo".to_string(),
70            message: e.to_string(),
71        })?
72        .ok_or(BittensorError::SubnetNotFound { netuid })?;
73
74    let n = storage
75        .fetch(&api::storage().subtensor_module().subnetwork_n(netuid))
76        .await
77        .map_err(|e| BittensorError::StorageQueryError {
78            key: "n".to_string(),
79            message: e.to_string(),
80        })?
81        .unwrap_or(0);
82
83    let max_n = storage
84        .fetch(&api::storage().subtensor_module().max_allowed_uids(netuid))
85        .await
86        .map_err(|e| BittensorError::StorageQueryError {
87            key: "max_n".to_string(),
88            message: e.to_string(),
89        })?
90        .unwrap_or(0);
91
92    let immunity_period = storage
93        .fetch(&api::storage().subtensor_module().immunity_period(netuid))
94        .await
95        .map_err(|e| BittensorError::StorageQueryError {
96            key: "immunity_period".to_string(),
97            message: e.to_string(),
98        })?
99        .unwrap_or(0);
100
101    let registration_allowed = storage
102        .fetch(
103            &api::storage()
104                .subtensor_module()
105                .network_registration_allowed(netuid),
106        )
107        .await
108        .map_err(|e| BittensorError::StorageQueryError {
109            key: "registration_allowed".to_string(),
110            message: e.to_string(),
111        })?
112        .unwrap_or(false);
113
114    Ok(SubnetInfo {
115        netuid,
116        tempo,
117        n,
118        max_n,
119        immunity_period,
120        registration_allowed,
121    })
122}
123
124/// Get hyperparameters for a subnet
125pub async fn get_subnet_hyperparameters(
126    client: &OnlineClient<PolkadotConfig>,
127    netuid: u16,
128) -> Result<SubnetHyperparameters, BittensorError> {
129    let storage = client
130        .storage()
131        .at_latest()
132        .await
133        .map_err(|e| BittensorError::RpcError {
134            message: format!("Failed to get storage: {}", e),
135        })?;
136
137    let tempo = storage
138        .fetch(&api::storage().subtensor_module().tempo(netuid))
139        .await
140        .map_err(|e| BittensorError::StorageQueryError {
141            key: "tempo".to_string(),
142            message: e.to_string(),
143        })?
144        .ok_or(BittensorError::SubnetNotFound { netuid })?;
145
146    let max_n = storage
147        .fetch(&api::storage().subtensor_module().max_allowed_uids(netuid))
148        .await
149        .map_err(|e| BittensorError::StorageQueryError {
150            key: "max_n".to_string(),
151            message: e.to_string(),
152        })?
153        .unwrap_or(0);
154
155    let min_allowed_weights = storage
156        .fetch(
157            &api::storage()
158                .subtensor_module()
159                .min_allowed_weights(netuid),
160        )
161        .await
162        .map_err(|e| BittensorError::StorageQueryError {
163            key: "min_allowed_weights".to_string(),
164            message: e.to_string(),
165        })?
166        .unwrap_or(0);
167
168    let max_allowed_weights = storage
169        .fetch(&api::storage().subtensor_module().max_weights_limit(netuid))
170        .await
171        .map_err(|e| BittensorError::StorageQueryError {
172            key: "max_allowed_weights".to_string(),
173            message: e.to_string(),
174        })?
175        .unwrap_or(0);
176
177    let weights_version_key = storage
178        .fetch(
179            &api::storage()
180                .subtensor_module()
181                .weights_version_key(netuid),
182        )
183        .await
184        .map_err(|e| BittensorError::StorageQueryError {
185            key: "weights_version_key".to_string(),
186            message: e.to_string(),
187        })?
188        .unwrap_or(0);
189
190    let weights_rate_limit = storage
191        .fetch(
192            &api::storage()
193                .subtensor_module()
194                .weights_set_rate_limit(netuid),
195        )
196        .await
197        .map_err(|e| BittensorError::StorageQueryError {
198            key: "weights_rate_limit".to_string(),
199            message: e.to_string(),
200        })?
201        .unwrap_or(0);
202
203    let registration_allowed = storage
204        .fetch(
205            &api::storage()
206                .subtensor_module()
207                .network_registration_allowed(netuid),
208        )
209        .await
210        .map_err(|e| BittensorError::StorageQueryError {
211            key: "registration_allowed".to_string(),
212            message: e.to_string(),
213        })?
214        .unwrap_or(false);
215
216    let adjustment_interval = storage
217        .fetch(
218            &api::storage()
219                .subtensor_module()
220                .adjustment_interval(netuid),
221        )
222        .await
223        .map_err(|e| BittensorError::StorageQueryError {
224            key: "adjustment_interval".to_string(),
225            message: e.to_string(),
226        })?
227        .unwrap_or(0);
228
229    let target_regs_per_interval = storage
230        .fetch(
231            &api::storage()
232                .subtensor_module()
233                .target_registrations_per_interval(netuid),
234        )
235        .await
236        .map_err(|e| BittensorError::StorageQueryError {
237            key: "target_regs_per_interval".to_string(),
238            message: e.to_string(),
239        })?
240        .unwrap_or(0);
241
242    let immunity_period = storage
243        .fetch(&api::storage().subtensor_module().immunity_period(netuid))
244        .await
245        .map_err(|e| BittensorError::StorageQueryError {
246            key: "immunity_period".to_string(),
247            message: e.to_string(),
248        })?
249        .unwrap_or(0);
250
251    Ok(SubnetHyperparameters {
252        tempo,
253        max_n,
254        min_allowed_weights,
255        max_allowed_weights,
256        weights_version_key,
257        weights_rate_limit,
258        registration_allowed,
259        adjustment_interval,
260        target_regs_per_interval,
261        immunity_period,
262    })
263}
264
265/// Get the number of active subnets
266pub async fn get_total_subnets(
267    client: &OnlineClient<PolkadotConfig>,
268) -> Result<u16, BittensorError> {
269    let storage = client
270        .storage()
271        .at_latest()
272        .await
273        .map_err(|e| BittensorError::RpcError {
274            message: format!("Failed to get storage: {}", e),
275        })?;
276
277    let count = storage
278        .fetch(&api::storage().subtensor_module().total_networks())
279        .await
280        .map_err(|e| BittensorError::StorageQueryError {
281            key: "total_networks".to_string(),
282            message: e.to_string(),
283        })?
284        .unwrap_or(0);
285
286    Ok(count)
287}
288
289/// Check if a subnet exists
290pub async fn subnet_exists(
291    client: &OnlineClient<PolkadotConfig>,
292    netuid: u16,
293) -> Result<bool, BittensorError> {
294    let storage = client
295        .storage()
296        .at_latest()
297        .await
298        .map_err(|e| BittensorError::RpcError {
299            message: format!("Failed to get storage: {}", e),
300        })?;
301
302    let exists = storage
303        .fetch(&api::storage().subtensor_module().networks_added(netuid))
304        .await
305        .map_err(|e| BittensorError::StorageQueryError {
306            key: "networks_added".to_string(),
307            message: e.to_string(),
308        })?
309        .unwrap_or(false);
310
311    Ok(exists)
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317
318    #[test]
319    fn test_subnet_info_struct() {
320        let info = SubnetInfo {
321            netuid: 1,
322            tempo: 360,
323            n: 256,
324            max_n: 4096,
325            immunity_period: 100,
326            registration_allowed: true,
327        };
328
329        assert_eq!(info.netuid, 1);
330        assert_eq!(info.tempo, 360);
331    }
332
333    #[test]
334    fn test_hyperparameters_struct() {
335        let params = SubnetHyperparameters {
336            tempo: 360,
337            max_n: 4096,
338            min_allowed_weights: 0,
339            max_allowed_weights: 65535,
340            weights_version_key: 0,
341            weights_rate_limit: 100,
342            registration_allowed: true,
343            adjustment_interval: 112,
344            target_regs_per_interval: 2,
345            immunity_period: 100,
346        };
347
348        assert_eq!(params.tempo, 360);
349        assert!(params.registration_allowed);
350    }
351}