From c42de52ca00ac396a503ca68f9a7d3ef4f7b7fb4 Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 13:23:30 -0500 Subject: [PATCH 01/10] commit Cargo.lock --- pallets/admin-utils/src/lib.rs | 38 ++++- pallets/subtensor/src/subnets/uids.rs | 143 +++++++++++++++++-- pallets/subtensor/src/utils/rate_limiting.rs | 7 +- 3 files changed, 168 insertions(+), 20 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 0b1498fd44..74357ea675 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -677,12 +677,12 @@ pub mod pallet { ensure!( min_burn < TaoCurrency::from(1_000_000_000), Error::::ValueNotInBounds - ) + ); // Min burn must be less than max burn ensure!( - min_burn > pallet_subtensor::Pallet::::MaxBurn(netuid), + min_burn > pallet_subtensor::Pallet::::get_max_burn(netuid), Error::::ValueNotInBounds - ) + ); pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); log::debug!("MinBurnSet( netuid: {netuid:?} min_burn: {min_burn:?} ) "); Ok(()) @@ -709,12 +709,12 @@ pub mod pallet { ensure!( max_burn > TaoCurrency::from(100_000_000), Error::::ValueNotInBounds - ) + ); // Max burn must be greater than min burn ensure!( - max_burn > pallet_subtensor::Pallet::::MinBurn(netuid), + max_burn > pallet_subtensor::Pallet::::get_min_burn(netuid), Error::::ValueNotInBounds - ) + ); pallet_subtensor::Pallet::::set_max_burn(netuid, max_burn); log::debug!("MaxBurnSet( netuid: {netuid:?} max_burn: {max_burn:?} ) "); Ok(()) @@ -1703,6 +1703,32 @@ pub mod pallet { pallet_subtensor::Pallet::::set_owner_immune_neuron_limit(netuid, immune_neurons)?; Ok(()) } + + /// Sets the number of immune owner neurons + #[pallet::call_index(74)] + #[pallet::weight(Weight::from_parts(15_000_000, 0) + .saturating_add(::DbWeight::get().reads(1_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] + pub fn sudo_trim_to_max_allowed_uids( + origin: OriginFor, + netuid: NetUid, + max_n: u16, + ) -> DispatchResult { + pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; + if let Ok(RawOrigin::Signed(who)) = origin.into() { + ensure!( + pallet_subtensor::Pallet::::passes_rate_limit_on_subnet( + &TransactionType::SetMaxAllowedUIDS, + &who, + netuid, + ), + pallet_subtensor::Error::::TxRateLimitExceeded + ); + } + pallet_subtensor::Pallet::::trim_to_max_allowed_uids(netuid, max_n)?; + Ok(()) + } + } } diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index f5a14c490b..e31701c9cc 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -16,17 +16,6 @@ impl Pallet { } } - /// Resets the trust, emission, consensus, incentive, dividends of the neuron to default - pub fn clear_neuron(netuid: NetUid, neuron_uid: u16) { - let neuron_index: usize = neuron_uid.into(); - Emission::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0.into())); - Trust::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); - Consensus::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); - Incentive::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); - Dividends::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); - Bonds::::remove(netuid, neuron_uid); // Remove bonds for Validator. - } - /// Replace the neuron under this uid. pub fn replace_neuron( netuid: NetUid, @@ -107,6 +96,138 @@ impl Pallet { IsNetworkMember::::insert(new_hotkey.clone(), netuid, true); // Fill network is member. } + /// Appends the uid to the network. + pub fn clear_neuron(netuid: NetUid, neuron_uid: u16) { + let neuron_index: usize = neuron_uid.into(); + Emission::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0.into())); + Trust::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Consensus::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Incentive::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Dividends::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); + Bonds::::remove(netuid, neuron_uid); // Remove bonds for Validator. + } + + pub fn trim_to_max_allowed_uids(netuid: NetUid, max_n: u16) -> DispatchResult { + + // Reasonable limits + ensure!( + Self::if_subnet_exist(netuid), + Error::::SubNetworkDoesNotExist + ); + ensure!( max_n > 16, Error::::InvalidValue ); + ensure!( max_n <= Self::get_max_allowed_uids( netuid ), Error::::InvalidValue ); + + // Set the value. + MaxAllowedUids::::insert(netuid, max_n); + + // Check if we need to trim. + let current_n: u16 = Self::get_subnetwork_n(netuid); + + // We need to trim, get rid of values between max_n and current_n. + if current_n > max_n { + + let ranks: Vec = Rank::::get(netuid); + let trimmed_ranks: Vec = ranks.into_iter().take(max_n as usize).collect(); + Rank::::insert(netuid, trimmed_ranks); + + let trust: Vec = Trust::::get(netuid); + let trimmed_trust: Vec = trust.into_iter().take(max_n as usize).collect(); + Trust::::insert(netuid, trimmed_trust); + + let active: Vec = Active::::get(netuid); + let trimmed_active: Vec = active.into_iter().take(max_n as usize).collect(); + Active::::insert(netuid, trimmed_active); + + let emission: Vec = Emission::::get(netuid); + let trimmed_emission: Vec = emission.into_iter().take(max_n as usize).collect(); + Emission::::insert(netuid, trimmed_emission); + + let consensus: Vec = Consensus::::get(netuid); + let trimmed_consensus: Vec = consensus.into_iter().take(max_n as usize).collect(); + Consensus::::insert(netuid, trimmed_consensus); + + let incentive: Vec = Incentive::::get(netuid); + let trimmed_incentive: Vec = incentive.into_iter().take(max_n as usize).collect(); + Incentive::::insert(netuid, trimmed_incentive); + + let dividends: Vec = Dividends::::get(netuid); + let trimmed_dividends: Vec = dividends.into_iter().take(max_n as usize).collect(); + Dividends::::insert(netuid, trimmed_dividends); + + let lastupdate: Vec = LastUpdate::::get(netuid); + let trimmed_lastupdate: Vec = lastupdate.into_iter().take(max_n as usize).collect(); + LastUpdate::::insert(netuid, trimmed_lastupdate); + + let pruning_scores: Vec = PruningScores::::get(netuid); + let trimmed_pruning_scores: Vec = pruning_scores.into_iter().take(max_n as usize).collect(); + PruningScores::::insert(netuid, trimmed_pruning_scores); + + let vtrust: Vec = ValidatorTrust::::get(netuid); + let trimmed_vtrust: Vec = vtrust.into_iter().take(max_n as usize).collect(); + ValidatorTrust::::insert(netuid, trimmed_vtrust); + + let vpermit: Vec = ValidatorPermit::::get(netuid); + let trimmed_vpermit: Vec = vpermit.into_iter().take(max_n as usize).collect(); + ValidatorPermit::::insert(netuid, trimmed_vpermit); + + let stake_weight: Vec = StakeWeight::::get(netuid); + let trimmed_stake_weight: Vec = stake_weight.into_iter().take(max_n as usize).collect(); + StakeWeight::::insert(netuid, trimmed_stake_weight); + + // Trim UIDs and Keys by removing entries with UID >= max_n (since UIDs are 0-indexed) + // UIDs range from 0 to current_n-1, so we remove UIDs from max_n to current_n-1 + for uid in max_n..current_n { + if let Some(hotkey) = Keys::::try_get(netuid, uid).ok() { + Uids::::remove(netuid, &hotkey); + // Remove IsNetworkMember association for the hotkey + IsNetworkMember::::remove(&hotkey, netuid); + // Remove last hotkey emission for the hotkey + LastHotkeyEmissionOnNetuid::::remove(&hotkey, netuid); + // Remove alpha dividends for the hotkey + AlphaDividendsPerSubnet::::remove(netuid, &hotkey); + // Remove tao dividends for the hotkey + TaoDividendsPerSubnet::::remove(netuid, &hotkey); + } + Keys::::remove(netuid, uid); + // Remove block at registration for the uid + BlockAtRegistration::::remove(netuid, uid); + } + + // Trim weights and bonds for removed UIDs + for uid in max_n..current_n { + Weights::::remove(netuid, uid); + Bonds::::remove(netuid, uid); + } + + // Trim axons, certificates, and prometheus info for removed hotkeys + for uid in max_n..current_n { + if let Some(hotkey) = Keys::::try_get(netuid, uid).ok() { + Axons::::remove(netuid, &hotkey); + NeuronCertificates::::remove(netuid, &hotkey); + Prometheus::::remove(netuid, &hotkey); + } + } + + // Trim weight and bond connections to removed UIDs for remaining neurons + // UIDs 0 to max_n-1 are kept, so we iterate through these valid UIDs + for uid in 0..max_n { + Weights::::mutate(netuid, uid, |weights| { + weights.retain(|(target_uid, _)| *target_uid < max_n); + }); + Bonds::::mutate(netuid, uid, |bonds| { + bonds.retain(|(target_uid, _)| *target_uid < max_n); + }); + } + + // Update the subnetwork size + SubnetworkN::::insert(netuid, max_n); + } + + // --- Ok and done. + Ok(()) + } + + /// Returns true if the uid is set on the network. /// pub fn is_uid_exist_on_network(netuid: NetUid, uid: u16) -> bool { diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index eeb5b96ddb..9cb2d4ffb3 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -11,6 +11,7 @@ pub enum TransactionType { RegisterNetwork, SetWeightsVersionKey, SetSNOwnerHotkey, + SetMaxAllowedUIDS, } /// Implement conversion from TransactionType to u16 @@ -23,6 +24,7 @@ impl From for u16 { TransactionType::RegisterNetwork => 3, TransactionType::SetWeightsVersionKey => 4, TransactionType::SetSNOwnerHotkey => 5, + TransactionType::SetMaxAllowedUIDS => 6, } } } @@ -36,6 +38,7 @@ impl From for TransactionType { 3 => TransactionType::RegisterNetwork, 4 => TransactionType::SetWeightsVersionKey, 5 => TransactionType::SetSNOwnerHotkey, + 6 => TransactionType::SetMaxAllowedUIDS, _ => TransactionType::Unknown, } } @@ -50,7 +53,7 @@ impl Pallet { TransactionType::SetChildren => 150, // 30 minutes TransactionType::SetChildkeyTake => TxChildkeyTakeRateLimit::::get(), TransactionType::RegisterNetwork => NetworkRateLimit::::get(), - + TransactionType::SetMaxAllowedUIDS => 7200 * 30, TransactionType::Unknown => 0, // Default to no limit for unknown types (no limit) _ => 0, } @@ -62,7 +65,6 @@ impl Pallet { TransactionType::SetWeightsVersionKey => (Tempo::::get(netuid) as u64) .saturating_mul(WeightsVersionKeyRateLimit::::get()), TransactionType::SetSNOwnerHotkey => DefaultSetSNOwnerHotkeyRateLimit::::get(), - _ => Self::get_rate_limit(tx_type), } } @@ -89,7 +91,6 @@ impl Pallet { let block: u64 = Self::get_current_block_as_u64(); let limit: u64 = Self::get_rate_limit_on_subnet(tx_type, netuid); let last_block: u64 = Self::get_last_transaction_block_on_subnet(hotkey, netuid, tx_type); - Self::check_passes_rate_limit(limit, block, last_block) } From bf7478ea122b6a500bb25b30d27a9603f583691e Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 13:24:47 -0500 Subject: [PATCH 02/10] cargo clippy --- pallets/subtensor/src/subnets/uids.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index e31701c9cc..fe0380e583 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -177,7 +177,7 @@ impl Pallet { // Trim UIDs and Keys by removing entries with UID >= max_n (since UIDs are 0-indexed) // UIDs range from 0 to current_n-1, so we remove UIDs from max_n to current_n-1 for uid in max_n..current_n { - if let Some(hotkey) = Keys::::try_get(netuid, uid).ok() { + if let Ok(hotkey) = Keys::::try_get(netuid, uid) { Uids::::remove(netuid, &hotkey); // Remove IsNetworkMember association for the hotkey IsNetworkMember::::remove(&hotkey, netuid); @@ -201,7 +201,7 @@ impl Pallet { // Trim axons, certificates, and prometheus info for removed hotkeys for uid in max_n..current_n { - if let Some(hotkey) = Keys::::try_get(netuid, uid).ok() { + if let Ok(hotkey) = Keys::::try_get(netuid, uid) { Axons::::remove(netuid, &hotkey); NeuronCertificates::::remove(netuid, &hotkey); Prometheus::::remove(netuid, &hotkey); From 7e11d96fc75f64c9530f590f12b14833d5877d55 Mon Sep 17 00:00:00 2001 From: unconst Date: Fri, 29 Aug 2025 13:25:49 -0500 Subject: [PATCH 03/10] cargo fmt --- pallets/admin-utils/src/lib.rs | 7 +++---- pallets/subtensor/src/subnets/uids.rs | 28 +++++++++++++++------------ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 74357ea675..38654e0cae 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1710,9 +1710,9 @@ pub mod pallet { .saturating_add(::DbWeight::get().reads(1_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_trim_to_max_allowed_uids( - origin: OriginFor, - netuid: NetUid, - max_n: u16, + origin: OriginFor, + netuid: NetUid, + max_n: u16, ) -> DispatchResult { pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; if let Ok(RawOrigin::Signed(who)) = origin.into() { @@ -1728,7 +1728,6 @@ pub mod pallet { pallet_subtensor::Pallet::::trim_to_max_allowed_uids(netuid, max_n)?; Ok(()) } - } } diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index fe0380e583..69c64b3817 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -106,26 +106,27 @@ impl Pallet { Dividends::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0)); Bonds::::remove(netuid, neuron_uid); // Remove bonds for Validator. } - - pub fn trim_to_max_allowed_uids(netuid: NetUid, max_n: u16) -> DispatchResult { + pub fn trim_to_max_allowed_uids(netuid: NetUid, max_n: u16) -> DispatchResult { // Reasonable limits ensure!( Self::if_subnet_exist(netuid), Error::::SubNetworkDoesNotExist ); - ensure!( max_n > 16, Error::::InvalidValue ); - ensure!( max_n <= Self::get_max_allowed_uids( netuid ), Error::::InvalidValue ); + ensure!(max_n > 16, Error::::InvalidValue); + ensure!( + max_n <= Self::get_max_allowed_uids(netuid), + Error::::InvalidValue + ); // Set the value. MaxAllowedUids::::insert(netuid, max_n); // Check if we need to trim. let current_n: u16 = Self::get_subnetwork_n(netuid); - + // We need to trim, get rid of values between max_n and current_n. if current_n > max_n { - let ranks: Vec = Rank::::get(netuid); let trimmed_ranks: Vec = ranks.into_iter().take(max_n as usize).collect(); Rank::::insert(netuid, trimmed_ranks); @@ -139,7 +140,8 @@ impl Pallet { Active::::insert(netuid, trimmed_active); let emission: Vec = Emission::::get(netuid); - let trimmed_emission: Vec = emission.into_iter().take(max_n as usize).collect(); + let trimmed_emission: Vec = + emission.into_iter().take(max_n as usize).collect(); Emission::::insert(netuid, trimmed_emission); let consensus: Vec = Consensus::::get(netuid); @@ -155,11 +157,13 @@ impl Pallet { Dividends::::insert(netuid, trimmed_dividends); let lastupdate: Vec = LastUpdate::::get(netuid); - let trimmed_lastupdate: Vec = lastupdate.into_iter().take(max_n as usize).collect(); + let trimmed_lastupdate: Vec = + lastupdate.into_iter().take(max_n as usize).collect(); LastUpdate::::insert(netuid, trimmed_lastupdate); let pruning_scores: Vec = PruningScores::::get(netuid); - let trimmed_pruning_scores: Vec = pruning_scores.into_iter().take(max_n as usize).collect(); + let trimmed_pruning_scores: Vec = + pruning_scores.into_iter().take(max_n as usize).collect(); PruningScores::::insert(netuid, trimmed_pruning_scores); let vtrust: Vec = ValidatorTrust::::get(netuid); @@ -171,9 +175,10 @@ impl Pallet { ValidatorPermit::::insert(netuid, trimmed_vpermit); let stake_weight: Vec = StakeWeight::::get(netuid); - let trimmed_stake_weight: Vec = stake_weight.into_iter().take(max_n as usize).collect(); + let trimmed_stake_weight: Vec = + stake_weight.into_iter().take(max_n as usize).collect(); StakeWeight::::insert(netuid, trimmed_stake_weight); - + // Trim UIDs and Keys by removing entries with UID >= max_n (since UIDs are 0-indexed) // UIDs range from 0 to current_n-1, so we remove UIDs from max_n to current_n-1 for uid in max_n..current_n { @@ -226,7 +231,6 @@ impl Pallet { // --- Ok and done. Ok(()) } - /// Returns true if the uid is set on the network. /// From 7ae3dc862f9154d47d9414cd70507c24d7db82d3 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Mon, 1 Sep 2025 16:46:46 +0300 Subject: [PATCH 04/10] Disable Keys::remove check for trim_to_max_allowed_uids --- pallets/subtensor/src/subnets/uids.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 69c64b3817..d2ebd938bf 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -193,6 +193,7 @@ impl Pallet { // Remove tao dividends for the hotkey TaoDividendsPerSubnet::::remove(netuid, &hotkey); } + #[allow(unknown_lints)] Keys::::remove(netuid, uid); // Remove block at registration for the uid BlockAtRegistration::::remove(netuid, uid); From d00cfc1d8f0b1b28e01aa960fa6e4a6f2588c945 Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Mon, 1 Sep 2025 17:08:34 +0300 Subject: [PATCH 05/10] Add benchmark for trim_to_max_uids --- pallets/admin-utils/src/benchmarking.rs | 11 +++++++++++ pallets/subtensor/src/subnets/uids.rs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 61df5d55f8..e3397cfbfc 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -346,5 +346,16 @@ mod benchmarks { _(RawOrigin::Root, 5u16/*version*/)/*sudo_set_commit_reveal_version()*/; } + #[benchmark] + fn sudo_trim_to_max_allowed_uids() { + pallet_subtensor::Pallet::::init_new_network( + 1u16.into(), /*netuid*/ + 1u16, /*sudo_tempo*/ + ); + + #[extrinsic_call] + _(RawOrigin::Root, 1u16.into()/*netuid*/, 4097u16/*max_allowed_uids*/)/*sudo_trim_to_max_allowed_uids()*/; + } + //impl_benchmark_test_suite!(AdminUtils, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index d2ebd938bf..0eb3be2ddb 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -193,7 +193,7 @@ impl Pallet { // Remove tao dividends for the hotkey TaoDividendsPerSubnet::::remove(netuid, &hotkey); } - #[allow(unknown_lints)] + #[allow(unknown_lints)] Keys::::remove(netuid, uid); // Remove block at registration for the uid BlockAtRegistration::::remove(netuid, uid); From 3c0ffb1088182bd22e67669258f796ff2a5201bb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Tue, 2 Sep 2025 10:52:48 -0400 Subject: [PATCH 06/10] Fix wrong way ensure for min burn value, fix outdated comments --- pallets/admin-utils/src/lib.rs | 6 +++--- pallets/subtensor/src/subnets/uids.rs | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 38654e0cae..a511266a7f 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -680,7 +680,7 @@ pub mod pallet { ); // Min burn must be less than max burn ensure!( - min_burn > pallet_subtensor::Pallet::::get_max_burn(netuid), + min_burn <= pallet_subtensor::Pallet::::get_max_burn(netuid), Error::::ValueNotInBounds ); pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); @@ -707,7 +707,7 @@ pub mod pallet { ); // Max burn must be greater than 0.1 TAO. ensure!( - max_burn > TaoCurrency::from(100_000_000), + max_burn >= TaoCurrency::from(100_000_000), Error::::ValueNotInBounds ); // Max burn must be greater than min burn @@ -1704,7 +1704,7 @@ pub mod pallet { Ok(()) } - /// Sets the number of immune owner neurons + /// Sets the maximum allowed UIDs for a subnet #[pallet::call_index(74)] #[pallet::weight(Weight::from_parts(15_000_000, 0) .saturating_add(::DbWeight::get().reads(1_u64)) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 69c64b3817..0a1121aced 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -96,7 +96,8 @@ impl Pallet { IsNetworkMember::::insert(new_hotkey.clone(), netuid, true); // Fill network is member. } - /// Appends the uid to the network. + /// Clears (sets to default) the neuron map values fot a neuron when it is + /// removed from the subnet pub fn clear_neuron(netuid: NetUid, neuron_uid: u16) { let neuron_index: usize = neuron_uid.into(); Emission::::mutate(netuid, |v| Self::set_element_at(v, neuron_index, 0.into())); From 86cce6f473bc63a9afcf2fa3e9fd877665e9a973 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 3 Sep 2025 11:49:22 -0300 Subject: [PATCH 07/10] add test + small refacto --- pallets/admin-utils/src/lib.rs | 29 +------ pallets/admin-utils/src/tests/mod.rs | 108 +++++++++++++++++++++++++- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/subnets/uids.rs | 24 ++---- 4 files changed, 120 insertions(+), 43 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index a511266a7f..da81f2e208 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -107,8 +107,6 @@ pub mod pallet { BondsMovingAverageMaxReached, /// Only root can set negative sigmoid steepness values NegativeSigmoidSteepness, - /// Value not in allowed bounds. - ValueNotInBounds, } /// Enum for specifying the type of precompile operation. #[derive( @@ -667,22 +665,12 @@ pub mod pallet { netuid: NetUid, min_burn: TaoCurrency, ) -> DispatchResult { - pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; - // Allow set min_burn but only up to 1 TAO for spamming. + ensure_root(origin)?; + ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - // Min burn must be less than 1 TAO. - ensure!( - min_burn < TaoCurrency::from(1_000_000_000), - Error::::ValueNotInBounds - ); - // Min burn must be less than max burn - ensure!( - min_burn <= pallet_subtensor::Pallet::::get_max_burn(netuid), - Error::::ValueNotInBounds - ); pallet_subtensor::Pallet::::set_min_burn(netuid, min_burn); log::debug!("MinBurnSet( netuid: {netuid:?} min_burn: {min_burn:?} ) "); Ok(()) @@ -700,21 +688,12 @@ pub mod pallet { netuid: NetUid, max_burn: TaoCurrency, ) -> DispatchResult { - pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin.clone(), netuid)?; + ensure_root(origin)?; + ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist ); - // Max burn must be greater than 0.1 TAO. - ensure!( - max_burn >= TaoCurrency::from(100_000_000), - Error::::ValueNotInBounds - ); - // Max burn must be greater than min burn - ensure!( - max_burn > pallet_subtensor::Pallet::::get_min_burn(netuid), - Error::::ValueNotInBounds - ); pallet_subtensor::Pallet::::set_max_burn(netuid, max_burn); log::debug!("MaxBurnSet( netuid: {netuid:?} max_burn: {max_burn:?} ) "); Ok(()) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 5290d3ddfc..01bd0e68f1 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -5,7 +5,10 @@ use frame_support::{ traits::Hooks, }; use frame_system::Config; -use pallet_subtensor::{Error as SubtensorError, SubnetOwner, Tempo, WeightsVersionKeyRateLimit}; +use pallet_subtensor::{ + Error as SubtensorError, MaxRegistrationsPerBlock, Rank, SubnetOwner, + TargetRegistrationsPerInterval, Tempo, WeightsVersionKeyRateLimit, *, +}; // use pallet_subtensor::{migrations, Event}; use pallet_subtensor::Event; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -1951,3 +1954,106 @@ fn test_sudo_set_commit_reveal_version() { ); }); } + +#[test] +fn test_trim_to_max_allowed_uids() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + add_network(netuid, 10); + MaxRegistrationsPerBlock::::insert(netuid, 256); + TargetRegistrationsPerInterval::::insert(netuid, 256); + + // Add some neurons + let max_n = 32; + for i in 1..=max_n { + let n = i * 1000; + register_ok_neuron(netuid, U256::from(n), U256::from(n + i), 0); + } + + // Run some block to ensure stake weights are set + run_to_block(20); + + // Normal case + let new_max_n = 20; + assert_ok!(AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + new_max_n + )); + + // Ensure storage has been trimmed + assert_eq!(MaxAllowedUids::::get(netuid), new_max_n); + assert_eq!(Rank::::get(netuid).len(), new_max_n as usize); + assert_eq!(Trust::::get(netuid).len(), new_max_n as usize); + assert_eq!(Active::::get(netuid).len(), new_max_n as usize); + assert_eq!(Emission::::get(netuid).len(), new_max_n as usize); + assert_eq!(Consensus::::get(netuid).len(), new_max_n as usize); + assert_eq!(Incentive::::get(netuid).len(), new_max_n as usize); + assert_eq!(Dividends::::get(netuid).len(), new_max_n as usize); + assert_eq!(LastUpdate::::get(netuid).len(), new_max_n as usize); + assert_eq!(PruningScores::::get(netuid).len(), new_max_n as usize); + assert_eq!( + ValidatorTrust::::get(netuid).len(), + new_max_n as usize + ); + assert_eq!( + ValidatorPermit::::get(netuid).len(), + new_max_n as usize + ); + assert_eq!(StakeWeight::::get(netuid).len(), new_max_n as usize); + + for uid in max_n..new_max_n { + assert!(!Keys::::contains_key(netuid, uid)); + assert!(!BlockAtRegistration::::contains_key(netuid, uid)); + assert!(!Weights::::contains_key(netuid, uid)); + assert!(!Bonds::::contains_key(netuid, uid)); + } + + for uid in 0..max_n { + assert!( + Weights::::get(netuid, uid) + .iter() + .all(|(target_uid, _)| *target_uid < new_max_n), + "Found a weight with target_uid >= new_max_n" + ); + assert!( + Bonds::::get(netuid, uid) + .iter() + .all(|(target_uid, _)| *target_uid < new_max_n), + "Found a bond with target_uid >= new_max_n" + ); + } + + assert_eq!(SubnetworkN::::get(netuid), new_max_n); + + // Non existent subnet + assert_err!( + AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + NetUid::from(42), + new_max_n + ), + pallet_subtensor::Error::::SubNetworkDoesNotExist + ); + + // New max n less than lower bound + assert_err!( + AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + 15 + ), + pallet_subtensor::Error::::InvalidValue + ); + + // New max n greater than upper bound + assert_err!( + AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + SubtensorModule::get_max_allowed_uids(netuid) + 1 + ), + pallet_subtensor::Error::::InvalidValue + ); + }); +} diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ef41b07c78..e6d2b658aa 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1486,7 +1486,7 @@ pub mod pallet { /// ==== Subnetwork Consensus Storage ==== /// ======================================= #[pallet::storage] // --- DMAP ( netuid ) --> stake_weight | weight for stake used in YC. - pub(super) type StakeWeight = + pub type StakeWeight = StorageMap<_, Identity, NetUid, Vec, ValueQuery, EmptyU16Vec>; #[pallet::storage] /// --- DMAP ( netuid, hotkey ) --> uid diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index ef7ed1b6ac..cdec803598 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -96,7 +96,7 @@ impl Pallet { IsNetworkMember::::insert(new_hotkey.clone(), netuid, true); // Fill network is member. } - /// Clears (sets to default) the neuron map values fot a neuron when it is + /// Clears (sets to default) the neuron map values fot a neuron when it is /// removed from the subnet pub fn clear_neuron(netuid: NetUid, neuron_uid: u16) { let neuron_index: usize = neuron_uid.into(); @@ -180,9 +180,9 @@ impl Pallet { stake_weight.into_iter().take(max_n as usize).collect(); StakeWeight::::insert(netuid, trimmed_stake_weight); - // Trim UIDs and Keys by removing entries with UID >= max_n (since UIDs are 0-indexed) - // UIDs range from 0 to current_n-1, so we remove UIDs from max_n to current_n-1 for uid in max_n..current_n { + // Trim UIDs and Keys by removing entries with UID >= max_n (since UIDs are 0-indexed) + // UIDs range from 0 to current_n-1, so we remove UIDs from max_n to current_n-1 if let Ok(hotkey) = Keys::::try_get(netuid, uid) { Uids::::remove(netuid, &hotkey); // Remove IsNetworkMember association for the hotkey @@ -193,28 +193,20 @@ impl Pallet { AlphaDividendsPerSubnet::::remove(netuid, &hotkey); // Remove tao dividends for the hotkey TaoDividendsPerSubnet::::remove(netuid, &hotkey); + // Trim axons, certificates, and prometheus info for removed hotkeys + Axons::::remove(netuid, &hotkey); + NeuronCertificates::::remove(netuid, &hotkey); + Prometheus::::remove(netuid, &hotkey); } #[allow(unknown_lints)] Keys::::remove(netuid, uid); // Remove block at registration for the uid BlockAtRegistration::::remove(netuid, uid); - } - - // Trim weights and bonds for removed UIDs - for uid in max_n..current_n { + // Trim weights and bonds for removed UIDs Weights::::remove(netuid, uid); Bonds::::remove(netuid, uid); } - // Trim axons, certificates, and prometheus info for removed hotkeys - for uid in max_n..current_n { - if let Ok(hotkey) = Keys::::try_get(netuid, uid) { - Axons::::remove(netuid, &hotkey); - NeuronCertificates::::remove(netuid, &hotkey); - Prometheus::::remove(netuid, &hotkey); - } - } - // Trim weight and bond connections to removed UIDs for remaining neurons // UIDs 0 to max_n-1 are kept, so we iterate through these valid UIDs for uid in 0..max_n { From 3c8ff8e29689e0f9c7f5d5376d7ab6e225d60675 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 3 Sep 2025 12:42:22 -0300 Subject: [PATCH 08/10] cargo fmt --- pallets/admin-utils/src/tests/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 331d194b15..a131c7e0f3 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2175,4 +2175,3 @@ fn test_trim_to_max_allowed_uids() { ); }); } - From eb001e35291d48fdceb5f28ae4f5ecca6fbd0afd Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 3 Sep 2025 14:36:31 -0300 Subject: [PATCH 09/10] fix misleading comment --- pallets/subtensor/src/subnets/uids.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index e116e98ac4..cfa1770029 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -202,7 +202,7 @@ impl Pallet { Keys::::remove(netuid, uid); // Remove block at registration for the uid BlockAtRegistration::::remove(netuid, uid); - // Trim weights and bonds for removed UIDs + // Remove entire weights and bonds entries for removed UIDs Weights::::remove(netuid, uid); Bonds::::remove(netuid, uid); } From b96c60c418d298a1d0c64e32c31f82c899e0dff2 Mon Sep 17 00:00:00 2001 From: Loris Moulin Date: Wed, 3 Sep 2025 14:45:12 -0300 Subject: [PATCH 10/10] fix merge issue --- pallets/admin-utils/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 64e4a54c8b..a4d8ff9061 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1648,6 +1648,7 @@ pub mod pallet { ); } pallet_subtensor::Pallet::::trim_to_max_allowed_uids(netuid, max_n)?; + Ok(()) } } }