From baf5fc6722507922decbcf7f14f1218d18f94308 Mon Sep 17 00:00:00 2001 From: 0xsid0703 Date: Wed, 17 Dec 2025 14:30:19 +0100 Subject: [PATCH] feat: Add get_submetagraphs method for filtering by netuid (#2050) --- pallets/subtensor/rpc/src/lib.rs | 17 ++ pallets/subtensor/runtime-api/src/lib.rs | 1 + pallets/subtensor/src/rpc_info/metagraph.rs | 12 ++ pallets/subtensor/src/tests/mod.rs | 1 + pallets/subtensor/src/tests/submetagraph.rs | 219 ++++++++++++++++++++ runtime/src/lib.rs | 4 + 6 files changed, 254 insertions(+) create mode 100644 pallets/subtensor/src/tests/submetagraph.rs diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index b5749988ee..e179538d09 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -81,6 +81,8 @@ pub trait SubtensorCustomApi { mecid: MechId, at: Option, ) -> RpcResult>; + #[method(name = "subnetInfo_getSubmetagraphs")] + fn get_submetagraphs(&self, netuid: NetUid, at: Option) -> RpcResult>; #[method(name = "subnetInfo_getSubnetState")] fn get_subnet_state(&self, netuid: NetUid, at: Option) -> RpcResult>; #[method(name = "subnetInfo_getLockCost")] @@ -405,6 +407,21 @@ where } } + fn get_submetagraphs( + &self, + netuid: NetUid, + at: Option<::Hash>, + ) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + match api.get_submetagraphs(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get submetagraphs: {e:?}")).into()) + } + } + } + fn get_subnet_state( &self, netuid: NetUid, diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index f1107bad08..bdbf64f927 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -42,6 +42,7 @@ sp_api::decl_runtime_apis! { fn get_metagraph(netuid: NetUid) -> Option>; fn get_all_mechagraphs() -> Vec>>; fn get_mechagraph(netuid: NetUid, mecid: MechId) -> Option>; + fn get_submetagraphs(netuid: NetUid) -> Vec>>; fn get_dynamic_info(netuid: NetUid) -> Option>; fn get_subnet_state(netuid: NetUid) -> Option>; fn get_selective_metagraph(netuid: NetUid, metagraph_indexes: Vec) -> Option>; diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index ea24657aeb..249f71cbb8 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -847,6 +847,18 @@ impl Pallet { metagraphs } + pub fn get_submetagraphs(netuid: NetUid) -> Vec>> { + if !Self::if_subnet_exist(netuid) { + return Vec::new(); + } + let mechanism_count = u8::from(MechanismCountCurrent::::get(netuid)); + let mut metagraphs = Vec::>>::new(); + for mecid in 0..mechanism_count { + metagraphs.push(Self::get_mechagraph(netuid, MechId::from(mecid))); + } + metagraphs + } + pub fn get_selective_metagraph( netuid: NetUid, metagraph_indexes: Vec, diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index bbaf25af58..f5a91f8b86 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -24,6 +24,7 @@ mod registration; mod serving; mod staking; mod staking2; +mod submetagraph; mod subnet; mod subnet_emissions; mod swap_coldkey; diff --git a/pallets/subtensor/src/tests/submetagraph.rs b/pallets/subtensor/src/tests/submetagraph.rs new file mode 100644 index 0000000000..5c697671e5 --- /dev/null +++ b/pallets/subtensor/src/tests/submetagraph.rs @@ -0,0 +1,219 @@ +#![allow( + clippy::arithmetic_side_effects, + clippy::expect_used, + clippy::indexing_slicing, + clippy::unwrap_used +)] + +// Run tests with: +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::submetagraph --show-output + +use super::mock::*; +use crate::*; +use sp_core::U256; +use subtensor_runtime_common::{MechId, NetUid}; + +#[test] +fn test_get_submetagraphs_nonexistent_subnet() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(999u16); // Non-existent subnet + + let submetagraphs = SubtensorModule::get_submetagraphs(netuid); + assert_eq!(submetagraphs.len(), 0); + }); +} + +#[test] +fn test_get_submetagraphs_empty_mechanisms() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + let tempo: u16 = 2; + let modality: u16 = 2; + + // Add network + add_network(netuid, tempo, modality); + + // Explicitly set mechanism count to 0 + NetworksAdded::::insert(netuid, true); + MechanismCountCurrent::::insert(netuid, MechId::from(0u8)); + + let submetagraphs = SubtensorModule::get_submetagraphs(netuid); + assert_eq!(submetagraphs.len(), 0); + }); +} + +#[test] +fn test_get_submetagraphs_single_mechanism() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1u16); + let tempo: u16 = 2; + let modality: u16 = 2; + + add_network(netuid, tempo, modality); + + // Set mechanism count to 1 + NetworksAdded::::insert(netuid, true); + MechanismCountCurrent::::insert(netuid, MechId::from(1u8)); + + let submetagraphs = SubtensorModule::get_submetagraphs(netuid); + assert_eq!(submetagraphs.len(), 1); + + // Verify we can get the mechagraph directly + let mechagraph = SubtensorModule::get_mechagraph(netuid, MechId::from(0u8)); + assert!(mechagraph.is_some()); + }); +} + +#[test] +fn test_get_submetagraphs_multiple_mechanisms() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(2u16); + let tempo: u16 = 2; + let modality: u16 = 2; + + add_network(netuid, tempo, modality); + + // Set mechanism count to 3 + NetworksAdded::::insert(netuid, true); + MechanismCountCurrent::::insert(netuid, MechId::from(3u8)); + + let submetagraphs = SubtensorModule::get_submetagraphs(netuid); + assert_eq!(submetagraphs.len(), 3); + + // Verify each mechagraph exists + for mecid in 0..3 { + let mechagraph = SubtensorModule::get_mechagraph(netuid, MechId::from(mecid)); + assert!( + mechagraph.is_some(), + "Mechagraph for mecid {} should exist", + mecid + ); + } + }); +} + +#[test] +fn test_get_submetagraphs_filters_by_netuid() { + new_test_ext(1).execute_with(|| { + let netuid1 = NetUid::from(10u16); + let netuid2 = NetUid::from(20u16); + let tempo: u16 = 2; + let modality: u16 = 2; + + // Add two networks + add_network(netuid1, tempo, modality); + add_network(netuid2, tempo, modality); + + // Set different mechanism counts for each + NetworksAdded::::insert(netuid1, true); + NetworksAdded::::insert(netuid2, true); + MechanismCountCurrent::::insert(netuid1, MechId::from(2u8)); + MechanismCountCurrent::::insert(netuid2, MechId::from(4u8)); + + // Get submetagraphs for each netuid + let submetagraphs1 = SubtensorModule::get_submetagraphs(netuid1); + let submetagraphs2 = SubtensorModule::get_submetagraphs(netuid2); + + assert_eq!(submetagraphs1.len(), 2); + assert_eq!(submetagraphs2.len(), 4); + + // Verify filtering - netuid1 should only have 2, netuid2 should only have 4 + for (idx, metagraph) in submetagraphs1.iter().enumerate() { + assert!( + metagraph.is_some(), + "Mechagraph at index {} should exist", + idx + ); + assert!(idx < 2); + } + + for (idx, metagraph) in submetagraphs2.iter().enumerate() { + assert!( + metagraph.is_some(), + "Mechagraph at index {} should exist", + idx + ); + assert!(idx < 4); + } + }); +} + +#[test] +fn test_get_submetagraphs_vs_get_all_mechagraphs() { + new_test_ext(1).execute_with(|| { + let netuid1 = NetUid::from(30u16); + let netuid2 = NetUid::from(31u16); + let tempo: u16 = 2; + let modality: u16 = 2; + + // Add two networks with different mechanism counts + add_network(netuid1, tempo, modality); + add_network(netuid2, tempo, modality); + + NetworksAdded::::insert(netuid1, true); + NetworksAdded::::insert(netuid2, true); + MechanismCountCurrent::::insert(netuid1, MechId::from(2u8)); + MechanismCountCurrent::::insert(netuid2, MechId::from(3u8)); + + // Get all mechagraphs (should include both netuids) + let all_mechagraphs = SubtensorModule::get_all_mechagraphs(); + + // Get submetagraphs for each netuid + let submetagraphs1 = SubtensorModule::get_submetagraphs(netuid1); + let submetagraphs2 = SubtensorModule::get_submetagraphs(netuid2); + + // Verify that get_submetagraphs returns only the mechagraphs for the specific netuid + assert_eq!(submetagraphs1.len(), 2); + assert_eq!(submetagraphs2.len(), 3); + + // The sum should match (or be less if there are other subnets) + // At minimum, our two subnets should be included + assert!(all_mechagraphs.len() >= 5); // 2 + 3 = 5 + + // Verify that get_submetagraphs for netuid1 returns the same as filtering all_mechagraphs + // by checking that each mechagraph in submetagraphs1 exists in all_mechagraphs + // (This is a simplified check - in practice you'd compare the actual data) + assert_eq!(submetagraphs1.len(), 2); + assert_eq!(submetagraphs2.len(), 3); + }); +} + +#[test] +fn test_get_submetagraphs_with_registered_neurons() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(40u16); + let tempo: u16 = 2; + let modality: u16 = 2; + + add_network(netuid, tempo, modality); + NetworksAdded::::insert(netuid, true); + MechanismCountCurrent::::insert(netuid, MechId::from(2u8)); + + // Register some neurons + let hotkey1 = U256::from(1); + let coldkey1 = U256::from(1); + let hotkey2 = U256::from(2); + let coldkey2 = U256::from(2); + + register_ok_neuron(netuid, hotkey1, coldkey1, 39420842); + register_ok_neuron(netuid, hotkey2, coldkey2, 39420843); + + let submetagraphs = SubtensorModule::get_submetagraphs(netuid); + assert_eq!(submetagraphs.len(), 2); + + // Verify that mechagraphs exist and contain neuron data + // We verify by comparing with direct get_mechagraph calls + for (idx, metagraph_opt) in submetagraphs.iter().enumerate() { + assert!( + metagraph_opt.is_some(), + "Mechagraph at index {} should exist", + idx + ); + + // Verify it matches the direct call + let direct_mechagraph = + SubtensorModule::get_mechagraph(netuid, MechId::from(idx as u8)); + assert_eq!(metagraph_opt.is_some(), direct_mechagraph.is_some()); + } + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f3dea51c84..c016719b90 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2394,6 +2394,10 @@ impl_runtime_apis! { SubtensorModule::get_all_mechagraphs() } + fn get_submetagraphs(netuid: NetUid) -> Vec>> { + SubtensorModule::get_submetagraphs(netuid) + } + fn get_all_dynamic_info() -> Vec>> { SubtensorModule::get_all_dynamic_info() }