diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 6a06e754b..67fba62b1 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -11,6 +11,8 @@ use frame_benchmarking::v1::account; use frame_benchmarking::v2::*; use frame_support::BoundedVec; use frame_system::RawOrigin; +use pallet_subtensor::SubnetworkN; +use subtensor_runtime_common::NetUid; use super::*; @@ -238,6 +240,19 @@ mod benchmarks { _(RawOrigin::Root, 1u16.into()/*netuid*/, 3u16/*kappa*/)/*set_kappa*/; } + #[benchmark] + fn sudo_set_min_allowed_uids() { + let netuid = NetUid::from(1); + pallet_subtensor::Pallet::::set_admin_freeze_window(0); + pallet_subtensor::Pallet::::init_new_network(netuid, 1u16 /*tempo*/); + + // Artificially set that some neurons are already registered + SubnetworkN::::set(netuid, 32); + + #[extrinsic_call] + _(RawOrigin::Root, netuid, 16u16/*min_allowed_uids*/)/*sudo_set_min_allowed_uids*/; + } + #[benchmark] fn sudo_set_max_allowed_uids() { // disable admin freeze window @@ -432,5 +447,17 @@ mod benchmarks { _(RawOrigin::Root, 1u16.into()/*netuid*/, 5u16/*immune_neurons*/)/*sudo_set_owner_immune_neuron_limit()*/; } + #[benchmark] + fn sudo_trim_to_max_allowed_uids() { + pallet_subtensor::Pallet::::set_admin_freeze_window(0); + pallet_subtensor::Pallet::::init_new_network( + 1u16.into(), /*netuid*/ + 1u16, /*sudo_tempo*/ + ); + + #[extrinsic_call] + _(RawOrigin::Root, 1u16.into()/*netuid*/, 256u16/*max_n*/)/*sudo_trim_to_max_allowed_uids()*/; + } + //impl_benchmark_test_suite!(AdminUtils, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 84de882ac..8b4f7204c 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -106,6 +106,10 @@ pub mod pallet { NegativeSigmoidSteepness, /// Value not in allowed bounds. ValueNotInBounds, + /// The minimum allowed UIDs must be less than the current number of UIDs in the subnet. + MinAllowedUidsGreaterThanCurrentUids, + /// The minimum allowed UIDs must be less than the maximum allowed UIDs. + MinAllowedUidsGreaterThanMaxAllowedUids, } /// Enum for specifying the type of precompile operation. #[derive( @@ -1902,6 +1906,70 @@ pub mod pallet { ); Ok(()) } + + /// Trims the maximum number of UIDs for a subnet. + /// + /// The trimming is done by sorting the UIDs by emission descending and then trimming + /// the lowest emitters while preserving temporally and owner immune UIDs. The UIDs are + /// then compressed to the left and storage is migrated to the new compressed UIDs. + #[pallet::call_index(78)] + #[pallet::weight(Weight::from_parts(32_880_000, 0) + .saturating_add(::DbWeight::get().reads(6_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] + pub fn sudo_trim_to_max_allowed_uids( + origin: OriginFor, + netuid: NetUid, + max_n: u16, + ) -> DispatchResult { + let maybe_owner = pallet_subtensor::Pallet::::ensure_sn_owner_or_root_with_limits( + origin.clone(), + netuid, + &[TransactionType::MaxUidsTrimming], + )?; + + pallet_subtensor::Pallet::::trim_to_max_allowed_uids(netuid, max_n)?; + + pallet_subtensor::Pallet::::record_owner_rl( + maybe_owner, + netuid, + &[TransactionType::MaxUidsTrimming], + ); + Ok(()) + } + + /// The extrinsic sets the minimum allowed UIDs for a subnet. + /// It is only callable by the root account. + #[pallet::call_index(79)] + #[pallet::weight(Weight::from_parts(31_550_000, 0) + .saturating_add(::DbWeight::get().reads(5_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] + pub fn sudo_set_min_allowed_uids( + origin: OriginFor, + netuid: NetUid, + min_allowed_uids: u16, + ) -> DispatchResult { + pallet_subtensor::Pallet::::ensure_root_with_rate_limit(origin, netuid)?; + + ensure!( + pallet_subtensor::Pallet::::if_subnet_exist(netuid), + Error::::SubnetDoesNotExist + ); + ensure!( + min_allowed_uids < pallet_subtensor::Pallet::::get_max_allowed_uids(netuid), + Error::::MinAllowedUidsGreaterThanMaxAllowedUids + ); + ensure!( + min_allowed_uids < pallet_subtensor::Pallet::::get_subnetwork_n(netuid), + Error::::MinAllowedUidsGreaterThanCurrentUids + ); + + pallet_subtensor::Pallet::::set_min_allowed_uids(netuid, min_allowed_uids); + + log::debug!( + "MinAllowedUidsSet( netuid: {netuid:?} min_allowed_uids: {min_allowed_uids:?} ) " + ); + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 954ef9144..558aba865 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -13,7 +13,7 @@ use sp_consensus_grandpa::AuthorityList as GrandpaAuthorityList; use sp_core::U256; use sp_core::{ConstU64, H256}; use sp_runtime::{ - BuildStorage, KeyTypeId, Perbill, + BuildStorage, KeyTypeId, Perbill, Percent, testing::TestXt, traits::{BlakeTwo256, ConstU32, IdentityLookup}, }; @@ -91,7 +91,8 @@ parameter_types! { pub const InitialTempo: u16 = 0; pub const SelfOwnership: u64 = 2; pub const InitialImmunityPeriod: u16 = 2; - pub const InitialMaxAllowedUids: u16 = 2; + pub const InitialMinAllowedUids: u16 = 2; + pub const InitialMaxAllowedUids: u16 = 4; pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialBondsPenalty: u16 = u16::MAX; pub const InitialBondsResetOn: bool = false; @@ -129,7 +130,6 @@ parameter_types! { pub const InitialRAORecycledForRegistration: u64 = 0; pub const InitialSenateRequiredStakePercentage: u64 = 2; // 2 percent of total stake pub const InitialNetworkImmunityPeriod: u64 = 7200 * 7; - pub const InitialNetworkMinAllowedUids: u16 = 128; pub const InitialNetworkMinLockCost: u64 = 100_000_000_000; pub const InitialSubnetOwnerCut: u16 = 0; // 0%. 100% of rewards go to validators + miners. pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks. @@ -151,6 +151,7 @@ parameter_types! { pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; pub const HotkeySwapOnSubnetInterval: u64 = 7 * 24 * 60 * 60 / 12; // 7 days pub const LeaseDividendsDistributionInterval: u32 = 100; // 100 blocks + pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); } impl pallet_subtensor::Config for Test { @@ -174,6 +175,7 @@ impl pallet_subtensor::Config for Test { type InitialRho = InitialRho; type InitialAlphaSigmoidSteepness = InitialAlphaSigmoidSteepness; type InitialKappa = InitialKappa; + type InitialMinAllowedUids = InitialMinAllowedUids; type InitialMaxAllowedUids = InitialMaxAllowedUids; type InitialValidatorPruneLen = InitialValidatorPruneLen; type InitialScalingLawPower = InitialScalingLawPower; @@ -205,7 +207,6 @@ impl pallet_subtensor::Config for Test { type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = InitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; - type InitialNetworkMinAllowedUids = InitialNetworkMinAllowedUids; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; type InitialSubnetOwnerCut = InitialSubnetOwnerCut; type InitialNetworkLockReductionInterval = InitialNetworkLockReductionInterval; @@ -228,6 +229,7 @@ impl pallet_subtensor::Config for Test { type ProxyInterface = (); type LeaseDividendsDistributionInterval = LeaseDividendsDistributionInterval; type GetCommitments = (); + type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; } parameter_types! { diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 124efc859..3b68db2a9 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, utils::rate_limiting::TransactionType}; use sp_consensus_grandpa::AuthorityId as GrandpaId; @@ -2417,3 +2420,404 @@ fn test_sudo_set_subsubnet_count_and_emissions() { ); }); } + +#[test] +fn test_trim_to_max_allowed_uids() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + let sn_owner = U256::from(1); + let sn_owner_hotkey1 = U256::from(2); + let sn_owner_hotkey2 = U256::from(3); + add_network(netuid, 10); + SubnetOwner::::insert(netuid, sn_owner); + SubnetOwnerHotkey::::insert(netuid, sn_owner_hotkey1); + MaxRegistrationsPerBlock::::insert(netuid, 256); + TargetRegistrationsPerInterval::::insert(netuid, 256); + ImmuneOwnerUidsLimit::::insert(netuid, 2); + // We set a low value here to make testing easier + MinAllowedUids::::set(netuid, 4); + // We define 4 subsubnets + let subsubnet_count = SubId::from(4); + SubsubnetCountCurrent::::insert(netuid, subsubnet_count); + + // Add some neurons + let max_n = 16; + 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 and that we are past the immunity period + // for all neurons + run_to_block((ImmunityPeriod::::get(netuid) + 1).into()); + + // Set some randomized values that we can keep track of + let values = vec![ + 17u16, 42u16, 8u16, 56u16, 23u16, 91u16, 34u16, // owner owned + 77u16, // temporally immune + 12u16, 65u16, 3u16, 88u16, // owner owned + 29u16, 51u16, 74u16, // temporally immune + 39u16, + ]; + let bool_values = vec![ + false, false, false, true, false, true, true, // owner owned + true, // temporally immune + false, true, false, true, // owner owned + false, true, true, // temporally immune + false, + ]; + let alpha_values = values.iter().map(|&v| (v as u64).into()).collect(); + let u64_values: Vec = values.iter().map(|&v| v as u64).collect(); + + Emission::::set(netuid, alpha_values); + Rank::::insert(netuid, values.clone()); + Trust::::insert(netuid, values.clone()); + Consensus::::insert(netuid, values.clone()); + Dividends::::insert(netuid, values.clone()); + PruningScores::::insert(netuid, values.clone()); + ValidatorTrust::::insert(netuid, values.clone()); + StakeWeight::::insert(netuid, values.clone()); + ValidatorPermit::::insert(netuid, bool_values.clone()); + Active::::insert(netuid, bool_values); + + for subid in 0..subsubnet_count.into() { + let netuid_index = + SubtensorModule::get_subsubnet_storage_index(netuid, SubId::from(subid)); + Incentive::::insert(netuid_index, values.clone()); + LastUpdate::::insert(netuid_index, u64_values.clone()); + } + + // We set some owner immune uids + let now = frame_system::Pallet::::block_number(); + BlockAtRegistration::::set(netuid, 6, now); + BlockAtRegistration::::set(netuid, 11, now); + + // And some temporally immune uids + Keys::::insert(netuid, 7, sn_owner_hotkey1); + Uids::::insert(netuid, sn_owner_hotkey1, 7); + Keys::::insert(netuid, 14, sn_owner_hotkey2); + Uids::::insert(netuid, sn_owner_hotkey2, 14); + + // Populate Weights and Bonds storage items to test trimming + // Create weights and bonds that span across the range that will be trimmed + for uid in 0..max_n { + let mut weights = Vec::new(); + let mut bonds = Vec::new(); + + // Add connections to all other uids, including those that will be trimmed + for target_uid in 0..max_n { + if target_uid != uid { + // Use some non-zero values to make the test more meaningful + let weight_value = (uid + target_uid) % 1000; + let bond_value = (uid * target_uid) % 1000; + weights.push((target_uid, weight_value)); + bonds.push((target_uid, bond_value)); + } + } + + for subid in 0..subsubnet_count.into() { + let netuid_index = + SubtensorModule::get_subsubnet_storage_index(netuid, SubId::from(subid)); + Weights::::insert(netuid_index, uid, weights.clone()); + Bonds::::insert(netuid_index, uid, bonds.clone()); + } + } + + // Normal case + let new_max_n = 8; + assert_ok!(AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + new_max_n + )); + + // Ensure the max allowed uids has been set correctly + assert_eq!(MaxAllowedUids::::get(netuid), new_max_n); + + // Ensure the emission has been trimmed correctly, keeping the highest emitters + // and immune and compressed to the left + assert_eq!( + Emission::::get(netuid), + vec![ + 56.into(), + 91.into(), + 34.into(), + 77.into(), + 65.into(), + 88.into(), + 51.into(), + 74.into() + ] + ); + // Ensure rest of storage has been trimmed correctly + let expected_values = vec![56, 91, 34, 77, 65, 88, 51, 74]; + let expected_bools = vec![true, true, true, true, true, true, true, true]; + let expected_u64_values = vec![56, 91, 34, 77, 65, 88, 51, 74]; + assert_eq!(Rank::::get(netuid), expected_values); + assert_eq!(Trust::::get(netuid), expected_values); + assert_eq!(Active::::get(netuid), expected_bools); + assert_eq!(Consensus::::get(netuid), expected_values); + assert_eq!(Dividends::::get(netuid), expected_values); + assert_eq!(PruningScores::::get(netuid), expected_values); + assert_eq!(ValidatorTrust::::get(netuid), expected_values); + assert_eq!(ValidatorPermit::::get(netuid), expected_bools); + assert_eq!(StakeWeight::::get(netuid), expected_values); + + for subid in 0..subsubnet_count.into() { + let netuid_index = + SubtensorModule::get_subsubnet_storage_index(netuid, SubId::from(subid)); + assert_eq!(Incentive::::get(netuid_index), expected_values); + assert_eq!(LastUpdate::::get(netuid_index), expected_u64_values); + } + + // Ensure trimmed uids related storage has been cleared + for uid in new_max_n..max_n { + assert!(!Keys::::contains_key(netuid, uid)); + assert!(!BlockAtRegistration::::contains_key(netuid, uid)); + for subid in 0..subsubnet_count.into() { + let netuid_index = + SubtensorModule::get_subsubnet_storage_index(netuid, SubId::from(subid)); + assert!(!Weights::::contains_key(netuid_index, uid)); + assert!(!Bonds::::contains_key(netuid_index, uid)); + } + } + + // Ensure trimmed uids hotkey related storage has been cleared + let trimmed_hotkeys = vec![ + U256::from(1000), + U256::from(2000), + U256::from(3000), + U256::from(5000), + U256::from(9000), + U256::from(11000), + U256::from(13000), + U256::from(16000), + ]; + for hotkey in trimmed_hotkeys { + assert!(!Uids::::contains_key(netuid, hotkey)); + assert!(!IsNetworkMember::::contains_key(hotkey, netuid)); + assert!(!LastHotkeyEmissionOnNetuid::::contains_key( + hotkey, netuid + )); + assert!(!AlphaDividendsPerSubnet::::contains_key( + netuid, hotkey + )); + assert!(!TaoDividendsPerSubnet::::contains_key(netuid, hotkey)); + assert!(!Axons::::contains_key(netuid, hotkey)); + assert!(!NeuronCertificates::::contains_key(netuid, hotkey)); + assert!(!Prometheus::::contains_key(netuid, hotkey)); + } + + // Ensure trimmed uids weights and bonds connections have been trimmed correctly + for uid in 0..new_max_n { + for subid in 0..subsubnet_count.into() { + let netuid_index = + SubtensorModule::get_subsubnet_storage_index(netuid, SubId::from(subid)); + assert!( + Weights::::get(netuid_index, uid) + .iter() + .all(|(target_uid, _)| *target_uid < new_max_n), + "Found a weight with target_uid >= new_max_n" + ); + assert!( + Bonds::::get(netuid_index, uid) + .iter() + .all(|(target_uid, _)| *target_uid < new_max_n), + "Found a bond with target_uid >= new_max_n" + ); + } + } + + // Actual number of neurons on the network updated after trimming + 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, + 2 + ), + 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 + ); + }); +} + +#[test] +fn test_trim_to_max_allowed_uids_too_many_immune() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + let sn_owner = U256::from(1); + add_network(netuid, 10); + SubnetOwner::::insert(netuid, sn_owner); + MaxRegistrationsPerBlock::::insert(netuid, 256); + TargetRegistrationsPerInterval::::insert(netuid, 256); + ImmuneOwnerUidsLimit::::insert(netuid, 2); + MinAllowedUids::::set(netuid, 4); + + // Add 5 neurons + let max_n = 5; + for i in 1..=max_n { + let n = i * 1000; + register_ok_neuron(netuid, U256::from(n), U256::from(n + i), 0); + } + + // Run some blocks to ensure stake weights are set + run_to_block((ImmunityPeriod::::get(netuid) + 1).into()); + + // Set owner immune uids (2 UIDs) by adding them to OwnedHotkeys + let owner_hotkey1 = U256::from(1000); + let owner_hotkey2 = U256::from(2000); + OwnedHotkeys::::insert(sn_owner, vec![owner_hotkey1, owner_hotkey2]); + Keys::::insert(netuid, 0, owner_hotkey1); + Uids::::insert(netuid, owner_hotkey1, 0); + Keys::::insert(netuid, 1, owner_hotkey2); + Uids::::insert(netuid, owner_hotkey2, 1); + + // Set temporally immune uids (2 UIDs) to make total immune count 4 out of 5 (80%) + // Set their registration block to current block to make them temporally immune + let current_block = frame_system::Pallet::::block_number(); + for uid in 2..4 { + let hotkey = U256::from(uid * 1000 + 1000); + Keys::::insert(netuid, uid, hotkey); + Uids::::insert(netuid, hotkey, uid); + BlockAtRegistration::::insert(netuid, uid, current_block); + } + + // Try to trim to 4 UIDs - this should fail because 4/4 = 100% immune (>= 80%) + assert_err!( + AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + 4 + ), + pallet_subtensor::Error::::InvalidValue + ); + + // Try to trim to 3 UIDs - this should also fail because 4/3 > 80% immune (>= 80%) + assert_err!( + AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + 3 + ), + pallet_subtensor::Error::::InvalidValue + ); + + // Now test a scenario where trimming should succeed + // Remove one immune UID to make it 3 immune out of 4 total + let uid_to_remove = 3; + let hotkey_to_remove = U256::from(uid_to_remove * 1000 + 1000); + #[allow(unknown_lints)] + Keys::::remove(netuid, uid_to_remove); + Uids::::remove(netuid, hotkey_to_remove); + BlockAtRegistration::::remove(netuid, uid_to_remove); + + // Now we have 3 immune out of 4 total UIDs + // Try to trim to 3 UIDs - this should succeed because 3/3 = 100% immune, but that's exactly 80% + // Wait, 100% is > 80%, so this should fail. Let me test with a scenario where we have fewer immune UIDs + + // Remove another immune UID to make it 2 immune out of 3 total + let uid_to_remove2 = 2; + let hotkey_to_remove2 = U256::from(uid_to_remove2 * 1000 + 1000); + #[allow(unknown_lints)] + Keys::::remove(netuid, uid_to_remove2); + Uids::::remove(netuid, hotkey_to_remove2); + BlockAtRegistration::::remove(netuid, uid_to_remove2); + + // Now we have 2 immune out of 2 total UIDs + // Try to trim to 1 UID - this should fail because 2/1 is impossible, but the check prevents it + assert_err!( + AdminUtils::sudo_trim_to_max_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + 1 + ), + pallet_subtensor::Error::::InvalidValue + ); + }); +} + +#[test] +fn test_sudo_set_min_allowed_uids() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + let to_be_set: u16 = 8; + add_network(netuid, 10); + MaxRegistrationsPerBlock::::insert(netuid, 256); + TargetRegistrationsPerInterval::::insert(netuid, 256); + + // Register some neurons + for i in 0..=16 { + register_ok_neuron(netuid, U256::from(i * 1000), U256::from(i * 1000 + i), 0); + } + + // Normal case + assert_ok!(AdminUtils::sudo_set_min_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + to_be_set + )); + assert_eq!(SubtensorModule::get_min_allowed_uids(netuid), to_be_set); + + // Non root + assert_err!( + AdminUtils::sudo_set_min_allowed_uids( + <::RuntimeOrigin>::signed(U256::from(0)), + netuid, + to_be_set + ), + DispatchError::BadOrigin + ); + + // Non existent subnet + assert_err!( + AdminUtils::sudo_set_min_allowed_uids( + <::RuntimeOrigin>::root(), + NetUid::from(42), + to_be_set + ), + Error::::SubnetDoesNotExist + ); + + // Min allowed uids greater than max allowed uids + assert_err!( + AdminUtils::sudo_set_min_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + SubtensorModule::get_max_allowed_uids(netuid) + 1 + ), + Error::::MinAllowedUidsGreaterThanMaxAllowedUids + ); + + // Min allowed uids greater than current uids + assert_err!( + AdminUtils::sudo_set_min_allowed_uids( + <::RuntimeOrigin>::root(), + netuid, + SubtensorModule::get_subnetwork_n(netuid) + 1 + ), + Error::::MinAllowedUidsGreaterThanCurrentUids + ); + }); +} diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index cdee675b0..00fab25ae 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -523,11 +523,6 @@ pub mod pallet { T::InitialNetworkImmunityPeriod::get() } #[pallet::type_value] - /// Default value for network min allowed UIDs. - pub fn DefaultNetworkMinAllowedUids() -> u16 { - T::InitialNetworkMinAllowedUids::get() - } - #[pallet::type_value] /// Default value for network min lock cost. pub fn DefaultNetworkMinLockCost() -> TaoCurrency { T::InitialNetworkMinLockCost::get().into() @@ -613,11 +608,21 @@ pub mod pallet { T::InitialKappa::get() } #[pallet::type_value] + /// Default value for network min allowed UIDs. + pub fn DefaultMinAllowedUids() -> u16 { + T::InitialMinAllowedUids::get() + } + #[pallet::type_value] /// Default maximum allowed UIDs. pub fn DefaultMaxAllowedUids() -> u16 { T::InitialMaxAllowedUids::get() } #[pallet::type_value] + /// -- Rate limit for set max allowed UIDs + pub fn MaxUidsTrimmingRateLimit() -> u64 { + prod_or_fast!(30 * 7200, 1) + } + #[pallet::type_value] /// Default immunity period. pub fn DefaultImmunityPeriod() -> u16 { T::InitialImmunityPeriod::get() @@ -1342,6 +1347,10 @@ pub mod pallet { pub type BurnRegistrationsThisInterval = StorageMap<_, Identity, NetUid, u16, ValueQuery>; #[pallet::storage] + /// --- MAP ( netuid ) --> min_allowed_uids + pub type MinAllowedUids = + StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMinAllowedUids>; + #[pallet::storage] /// --- MAP ( netuid ) --> max_allowed_uids pub type MaxAllowedUids = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultMaxAllowedUids>; @@ -1505,7 +1514,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/macros/config.rs b/pallets/subtensor/src/macros/config.rs index eebff46c6..cb6af2972 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -132,7 +132,10 @@ mod config { /// Kappa constant. #[pallet::constant] type InitialKappa: Get; - /// Max UID constant. + /// Initial minimum allowed network UIDs + #[pallet::constant] + type InitialMinAllowedUids: Get; + /// Initial maximum allowed network UIDs #[pallet::constant] type InitialMaxAllowedUids: Get; /// Initial validator context pruning length. @@ -195,9 +198,6 @@ mod config { /// Initial network immunity period #[pallet::constant] type InitialNetworkImmunityPeriod: Get; - /// Initial minimum allowed network UIDs - #[pallet::constant] - type InitialNetworkMinAllowedUids: Get; /// Initial network minimum burn cost #[pallet::constant] type InitialNetworkMinLockCost: Get; @@ -254,5 +254,8 @@ mod config { /// Number of blocks between dividends distribution. #[pallet::constant] type LeaseDividendsDistributionInterval: Get>; + /// Maximum percentage of immune UIDs. + #[pallet::constant] + type MaxImmuneUidsPercentage: Get; } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index a8b981048..f4583c071 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -882,7 +882,7 @@ mod dispatches { /// - Attempting to set prometheus information withing the rate limit min. /// #[pallet::call_index(40)] - #[pallet::weight((Weight::from_parts(41_240_000, 0) + #[pallet::weight((Weight::from_parts(32_510_000, 0) .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::No))] pub fn serve_axon_tls( diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index bb41c063e..a5de146e3 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -439,5 +439,8 @@ mod events { /// UID-indexed array of miner incentive alpha; index equals UID. emissions: Vec, }, + + /// The minimum allowed UIDs for a subnet have been set. + MinAllowedUidsSet(NetUid, u16), } } diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index 6f11921db..a620755f2 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -389,6 +389,20 @@ impl Pallet { } fn get_immune_owner_hotkeys(netuid: NetUid, coldkey: &T::AccountId) -> Vec { + Self::get_immune_owner_tuples(netuid, coldkey) + .into_iter() + .map(|(_, hk)| hk) + .collect() + } + + pub fn get_immune_owner_uids(netuid: NetUid, coldkey: &T::AccountId) -> Vec { + Self::get_immune_owner_tuples(netuid, coldkey) + .into_iter() + .map(|(uid, _)| uid) + .collect() + } + + fn get_immune_owner_tuples(netuid: NetUid, coldkey: &T::AccountId) -> Vec<(u16, T::AccountId)> { // Gather (block, uid, hotkey) only for hotkeys that have a UID and a registration block. let mut triples: Vec<(u64, u16, T::AccountId)> = OwnedHotkeys::::get(coldkey) .into_iter() @@ -411,22 +425,24 @@ impl Pallet { triples.truncate(limit); } - // Project to just hotkeys - let mut immune_hotkeys: Vec = - triples.into_iter().map(|(_, _, hk)| hk).collect(); + // Project to uid/hotkey tuple + let mut immune_tuples: Vec<(u16, T::AccountId)> = + triples.into_iter().map(|(_, uid, hk)| (uid, hk)).collect(); // Insert subnet owner hotkey in the beginning of the list if valid and not // already present if let Ok(owner_hk) = SubnetOwnerHotkey::::try_get(netuid) { - if Uids::::get(netuid, &owner_hk).is_some() && !immune_hotkeys.contains(&owner_hk) { - immune_hotkeys.insert(0, owner_hk); - if immune_hotkeys.len() > limit { - immune_hotkeys.truncate(limit); + if let Some(owner_uid) = Uids::::get(netuid, &owner_hk) { + if !immune_tuples.contains(&(owner_uid, owner_hk.clone())) { + immune_tuples.insert(0, (owner_uid, owner_hk.clone())); + if immune_tuples.len() > limit { + immune_tuples.truncate(limit); + } } } } - immune_hotkeys + immune_tuples } /// Determine which peer to prune from the network by finding the element with the lowest pruning score out of diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 2ec6869ba..e01f17cad 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -1,6 +1,8 @@ use super::*; use frame_support::storage::IterableStorageDoubleMap; -use sp_std::vec; +use sp_runtime::Percent; +use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; +use sp_std::{cmp, vec}; use subtensor_runtime_common::NetUid; impl Pallet { @@ -129,6 +131,237 @@ impl Pallet { IsNetworkMember::::insert(new_hotkey.clone(), netuid, true); // Fill network is member. } + 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 >= MinAllowedUids::::get(netuid), + Error::::InvalidValue + ); + ensure!( + max_n <= MaxAllowedUids::::get(netuid), + Error::::InvalidValue + ); + + MaxAllowedUids::::insert(netuid, max_n); + + let current_n = Self::get_subnetwork_n(netuid); + if current_n > max_n { + let owner = SubnetOwner::::get(netuid); + let owner_uids = BTreeSet::from_iter(Self::get_immune_owner_uids(netuid, &owner)); + + // Count the number of immune UIDs + let mut immune_count: u16 = 0; + for uid in 0..current_n { + if owner_uids.contains(&{ uid }) || Self::get_neuron_is_immune(netuid, uid) { + immune_count = immune_count.saturating_add(1); + } + } + + // Ensure the number of immune UIDs is less than 80% + let immune_percentage = Percent::from_rational(immune_count, max_n); + ensure!( + immune_percentage < T::MaxImmuneUidsPercentage::get(), + Error::::InvalidValue + ); + + // Get all emissions with their UIDs and sort by emission (descending) + // This ensures we keep the highest emitters and remove the lowest ones + let mut emissions = Emission::::get(netuid) + .into_iter() + .enumerate() + .collect::>(); + emissions.sort_by_key(|(_, emission)| cmp::Reverse(*emission)); + + let mut removed_uids = BTreeSet::new(); + let mut uids_left_to_process = current_n; + let subsubnets_count = SubsubnetCountCurrent::::get(netuid).into(); + + // Iterate from the end (lowest emitters) to the beginning + for i in (0..current_n).rev() { + if uids_left_to_process == max_n { + break; // We've reached the target number of UIDs + } + + if let Some((uid, _)) = emissions.get(i as usize).cloned() { + let neuron_uid = uid as u16; + + // Skip subnet owner's or temporally immune uids + if owner_uids.contains(&neuron_uid) + || Self::get_neuron_is_immune(netuid, neuron_uid) + { + continue; + } + + // Remove hotkey related storage items if hotkey exists + if let Ok(hotkey) = Keys::::try_get(netuid, neuron_uid) { + Uids::::remove(netuid, &hotkey); + IsNetworkMember::::remove(&hotkey, netuid); + LastHotkeyEmissionOnNetuid::::remove(&hotkey, netuid); + AlphaDividendsPerSubnet::::remove(netuid, &hotkey); + TaoDividendsPerSubnet::::remove(netuid, &hotkey); + Axons::::remove(netuid, &hotkey); + NeuronCertificates::::remove(netuid, &hotkey); + Prometheus::::remove(netuid, &hotkey); + } + + // Remove all storage items associated with this uid + #[allow(unknown_lints)] + Keys::::remove(netuid, neuron_uid); + BlockAtRegistration::::remove(netuid, neuron_uid); + for subid in 0..subsubnets_count { + let netuid_index = Self::get_subsubnet_storage_index(netuid, subid.into()); + Weights::::remove(netuid_index, neuron_uid); + Bonds::::remove(netuid_index, neuron_uid); + } + + // Remove from emissions array and track as removed + emissions.remove(i.into()); + removed_uids.insert(uid); + uids_left_to_process = uids_left_to_process.saturating_sub(1); + } + } + + // Sort remaining emissions by uid to compress uids to the left + // This ensures consecutive uid indices in the final arrays + emissions.sort_by_key(|(uid, _)| *uid); + + // Extract the final uids and emissions after trimming and sorting + let (trimmed_uids, trimmed_emissions): (Vec, Vec) = + emissions.into_iter().unzip(); + + // Get all current arrays from storage + let ranks = Rank::::get(netuid); + let trust = Trust::::get(netuid); + let active = Active::::get(netuid); + let consensus = Consensus::::get(netuid); + let dividends = Dividends::::get(netuid); + let pruning_scores = PruningScores::::get(netuid); + let vtrust = ValidatorTrust::::get(netuid); + let vpermit = ValidatorPermit::::get(netuid); + let stake_weight = StakeWeight::::get(netuid); + + // Create trimmed arrays by extracting values for kept uids only + // Pre-allocate vectors with exact capacity for efficiency + let len = trimmed_uids.len(); + let mut trimmed_ranks = Vec::with_capacity(len); + let mut trimmed_trust = Vec::with_capacity(len); + let mut trimmed_active = Vec::with_capacity(len); + let mut trimmed_consensus = Vec::with_capacity(len); + let mut trimmed_dividends = Vec::with_capacity(len); + let mut trimmed_pruning_scores = Vec::with_capacity(len); + let mut trimmed_vtrust = Vec::with_capacity(len); + let mut trimmed_vpermit = Vec::with_capacity(len); + let mut trimmed_stake_weight = Vec::with_capacity(len); + + // Single iteration to extract values for all kept uids + for &uid in &trimmed_uids { + trimmed_ranks.push(ranks.get(uid).cloned().unwrap_or_default()); + trimmed_trust.push(trust.get(uid).cloned().unwrap_or_default()); + trimmed_active.push(active.get(uid).cloned().unwrap_or_default()); + trimmed_consensus.push(consensus.get(uid).cloned().unwrap_or_default()); + trimmed_dividends.push(dividends.get(uid).cloned().unwrap_or_default()); + trimmed_pruning_scores.push(pruning_scores.get(uid).cloned().unwrap_or_default()); + trimmed_vtrust.push(vtrust.get(uid).cloned().unwrap_or_default()); + trimmed_vpermit.push(vpermit.get(uid).cloned().unwrap_or_default()); + trimmed_stake_weight.push(stake_weight.get(uid).cloned().unwrap_or_default()); + } + + // Update storage with trimmed arrays + Emission::::insert(netuid, trimmed_emissions); + Rank::::insert(netuid, trimmed_ranks); + Trust::::insert(netuid, trimmed_trust); + Active::::insert(netuid, trimmed_active); + Consensus::::insert(netuid, trimmed_consensus); + Dividends::::insert(netuid, trimmed_dividends); + PruningScores::::insert(netuid, trimmed_pruning_scores); + ValidatorTrust::::insert(netuid, trimmed_vtrust); + ValidatorPermit::::insert(netuid, trimmed_vpermit); + StakeWeight::::insert(netuid, trimmed_stake_weight); + + // Update incentives/lastupdates for subsubnets + for subid in 0..subsubnets_count { + let netuid_index = Self::get_subsubnet_storage_index(netuid, subid.into()); + let incentive = Incentive::::get(netuid_index); + let lastupdate = LastUpdate::::get(netuid_index); + let mut trimmed_incentive = Vec::with_capacity(trimmed_uids.len()); + let mut trimmed_lastupdate = Vec::with_capacity(trimmed_uids.len()); + + for uid in &trimmed_uids { + trimmed_incentive.push(incentive.get(*uid).cloned().unwrap_or_default()); + trimmed_lastupdate.push(lastupdate.get(*uid).cloned().unwrap_or_default()); + } + + Incentive::::insert(netuid_index, trimmed_incentive); + LastUpdate::::insert(netuid_index, trimmed_lastupdate); + } + + // Create mapping from old uid to new compressed uid + // This is needed to update connections (weights and bonds) with correct uid references + let old_to_new_uid: BTreeMap = trimmed_uids + .iter() + .enumerate() + .map(|(new_uid, &old_uid)| (old_uid, new_uid)) + .collect(); + + // Update connections (weights and bonds) for each kept uid + // This involves three operations per uid: + // 1. Swap the uid storage to the new compressed position + // 2. Update all connections to reference the new compressed uids + // 3. Clear the connections to the trimmed uids + for (old_uid, new_uid) in &old_to_new_uid { + let old_neuron_uid = *old_uid as u16; + let new_neuron_uid = *new_uid as u16; + + // Swap uid specific storage items to new compressed positions + Keys::::swap(netuid, old_neuron_uid, netuid, new_neuron_uid); + BlockAtRegistration::::swap(netuid, old_neuron_uid, netuid, new_neuron_uid); + + for subid in 0..subsubnets_count { + let netuid_index = Self::get_subsubnet_storage_index(netuid, subid.into()); + + // Swap to new position and remap all target uids + Weights::::swap(netuid_index, old_neuron_uid, netuid_index, new_neuron_uid); + Weights::::mutate(netuid_index, new_neuron_uid, |weights| { + weights.retain_mut(|(target_uid, _weight)| { + if let Some(new_target_uid) = + old_to_new_uid.get(&(*target_uid as usize)) + { + *target_uid = *new_target_uid as u16; + true + } else { + false + } + }) + }); + + // Swap to new position and remap all target uids + Bonds::::swap(netuid_index, old_neuron_uid, netuid_index, new_neuron_uid); + Bonds::::mutate(netuid_index, new_neuron_uid, |bonds| { + bonds.retain_mut(|(target_uid, _bond)| { + if let Some(new_target_uid) = + old_to_new_uid.get(&(*target_uid as usize)) + { + *target_uid = *new_target_uid as u16; + true + } else { + false + } + }) + }); + } + } + + // Update the subnet's uid count to reflect the new maximum + SubnetworkN::::insert(netuid, max_n); + } + + 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/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 672b1e44d..3f2ba40ef 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -19,7 +19,7 @@ use pallet_collective::MemberCount; use sp_core::{ConstU64, Get, H256, U256, offchain::KeyTypeId}; use sp_runtime::Perbill; use sp_runtime::{ - BuildStorage, + BuildStorage, Percent, traits::{BlakeTwo256, IdentityLookup}, }; use sp_std::{cell::RefCell, cmp::Ordering}; @@ -164,7 +164,8 @@ parameter_types! { pub const InitialTempo: u16 = 360; pub const SelfOwnership: u64 = 2; pub const InitialImmunityPeriod: u16 = 2; - pub const InitialMaxAllowedUids: u16 = 2; + pub const InitialMinAllowedUids: u16 = 2; + pub const InitialMaxAllowedUids: u16 = 4; pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialBondsPenalty:u16 = u16::MAX; pub const InitialBondsResetOn: bool = false; @@ -202,7 +203,6 @@ parameter_types! { pub const InitialRAORecycledForRegistration: u64 = 0; pub const InitialSenateRequiredStakePercentage: u64 = 2; // 2 percent of total stake pub const InitialNetworkImmunityPeriod: u64 = 7200 * 7; - pub const InitialNetworkMinAllowedUids: u16 = 128; pub const InitialNetworkMinLockCost: u64 = 100_000_000_000; pub const InitialSubnetOwnerCut: u16 = 0; // 0%. 100% of rewards go to validators + miners. pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks. @@ -223,6 +223,7 @@ parameter_types! { pub const HotkeySwapOnSubnetInterval: u64 = 15; // 15 block, should be bigger than subnet number, then trigger clean up for all subnets pub const MaxContributorsPerLeaseToRemove: u32 = 3; pub const LeaseDividendsDistributionInterval: u32 = 100; + pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); } // Configure collective pallet for council @@ -405,6 +406,7 @@ impl crate::Config for Test { type InitialRho = InitialRho; type InitialAlphaSigmoidSteepness = InitialAlphaSigmoidSteepness; type InitialKappa = InitialKappa; + type InitialMinAllowedUids = InitialMinAllowedUids; type InitialMaxAllowedUids = InitialMaxAllowedUids; type InitialValidatorPruneLen = InitialValidatorPruneLen; type InitialScalingLawPower = InitialScalingLawPower; @@ -436,7 +438,6 @@ impl crate::Config for Test { type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = InitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; - type InitialNetworkMinAllowedUids = InitialNetworkMinAllowedUids; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; type InitialSubnetOwnerCut = InitialSubnetOwnerCut; type InitialNetworkLockReductionInterval = InitialNetworkLockReductionInterval; @@ -459,6 +460,7 @@ impl crate::Config for Test { type ProxyInterface = FakeProxier; type LeaseDividendsDistributionInterval = LeaseDividendsDistributionInterval; type GetCommitments = (); + type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; } // Swap-related parameter types diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 7ab96d65c..f8f4fe57e 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -561,6 +561,14 @@ impl Pallet { Self::deposit_event(Event::MinAllowedWeightSet(netuid, min_allowed_weights)); } + pub fn get_min_allowed_uids(netuid: NetUid) -> u16 { + MinAllowedUids::::get(netuid) + } + pub fn set_min_allowed_uids(netuid: NetUid, min_allowed: u16) { + MinAllowedUids::::insert(netuid, min_allowed); + Self::deposit_event(Event::MinAllowedUidsSet(netuid, min_allowed)); + } + pub fn get_max_allowed_uids(netuid: NetUid) -> u16 { MaxAllowedUids::::get(netuid) } diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index 58fcab01d..190634212 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -15,6 +15,7 @@ pub enum TransactionType { OwnerHyperparamUpdate(Hyperparameter), SubsubnetCountUpdate, SubsubnetEmission, + MaxUidsTrimming, } impl TransactionType { @@ -26,7 +27,7 @@ impl TransactionType { Self::RegisterNetwork => NetworkRateLimit::::get(), Self::SubsubnetCountUpdate => SubsubnetCountSetRateLimit::::get(), Self::SubsubnetEmission => SubsubnetEmissionRateLimit::::get(), - + Self::MaxUidsTrimming => MaxUidsTrimmingRateLimit::::get(), Self::Unknown => 0, // Default to no limit for unknown types (no limit) _ => 0, } @@ -139,6 +140,7 @@ impl From for u16 { TransactionType::OwnerHyperparamUpdate(_) => 6, TransactionType::SubsubnetCountUpdate => 7, TransactionType::SubsubnetEmission => 8, + TransactionType::MaxUidsTrimming => 9, } } } @@ -155,6 +157,7 @@ impl From for TransactionType { 6 => TransactionType::OwnerHyperparamUpdate(Hyperparameter::Unknown), 7 => TransactionType::SubsubnetCountUpdate, 8 => TransactionType::SubsubnetEmission, + 9 => TransactionType::MaxUidsTrimming, _ => TransactionType::Unknown, } } diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index 8aca06dc5..8c6a064ef 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -15,7 +15,7 @@ pub use pallet_subtensor::*; pub use sp_core::U256; use sp_core::{ConstU64, H256}; use sp_runtime::{ - BuildStorage, KeyTypeId, Perbill, + BuildStorage, KeyTypeId, Perbill, Percent, testing::TestXt, traits::{BlakeTwo256, ConstU32, IdentityLookup, One}, }; @@ -156,7 +156,8 @@ parameter_types! { pub const InitialTempo: u16 = 0; pub const SelfOwnership: u64 = 2; pub const InitialImmunityPeriod: u16 = 2; - pub const InitialMaxAllowedUids: u16 = 2; + pub const InitialMinAllowedUids: u16 = 2; + pub const InitialMaxAllowedUids: u16 = 4; pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialBondsPenalty: u16 = u16::MAX; pub const InitialBondsResetOn: bool = false; @@ -194,7 +195,6 @@ parameter_types! { pub const InitialRAORecycledForRegistration: u64 = 0; pub const InitialSenateRequiredStakePercentage: u64 = 2; // 2 percent of total stake pub const InitialNetworkImmunityPeriod: u64 = 7200 * 7; - pub const InitialNetworkMinAllowedUids: u16 = 128; pub const InitialNetworkMinLockCost: u64 = 100_000_000_000; pub const InitialSubnetOwnerCut: u16 = 0; // 0%. 100% of rewards go to validators + miners. pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks. @@ -216,6 +216,7 @@ parameter_types! { pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000; pub const HotkeySwapOnSubnetInterval: u64 = 7 * 24 * 60 * 60 / 12; // 7 days pub const LeaseDividendsDistributionInterval: u32 = 100; // 100 blocks + pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); } impl pallet_subtensor::Config for Test { @@ -239,6 +240,7 @@ impl pallet_subtensor::Config for Test { type InitialRho = InitialRho; type InitialAlphaSigmoidSteepness = InitialAlphaSigmoidSteepness; type InitialKappa = InitialKappa; + type InitialMinAllowedUids = InitialMinAllowedUids; type InitialMaxAllowedUids = InitialMaxAllowedUids; type InitialValidatorPruneLen = InitialValidatorPruneLen; type InitialScalingLawPower = InitialScalingLawPower; @@ -270,7 +272,6 @@ impl pallet_subtensor::Config for Test { type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = InitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = InitialNetworkImmunityPeriod; - type InitialNetworkMinAllowedUids = InitialNetworkMinAllowedUids; type InitialNetworkMinLockCost = InitialNetworkMinLockCost; type InitialSubnetOwnerCut = InitialSubnetOwnerCut; type InitialNetworkLockReductionInterval = InitialNetworkLockReductionInterval; @@ -293,6 +294,7 @@ impl pallet_subtensor::Config for Test { type ProxyInterface = (); type LeaseDividendsDistributionInterval = LeaseDividendsDistributionInterval; type GetCommitments = (); + type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; } parameter_types! { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index aa87c52a1..0aaf054a0 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -50,7 +50,7 @@ use sp_core::{ use sp_runtime::Cow; use sp_runtime::generic::Era; use sp_runtime::{ - AccountId32, ApplyExtrinsicResult, ConsensusEngineId, generic, impl_opaque_keys, + AccountId32, ApplyExtrinsicResult, ConsensusEngineId, Percent, generic, impl_opaque_keys, traits::{ AccountIdLookup, BlakeTwo256, Block as BlockT, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf, UniqueSaturatedInto, Verify, @@ -1152,7 +1152,7 @@ parameter_types! { pub const SubtensorInitialRAORecycledForRegistration: u64 = 0; // 0 rao pub const SubtensorInitialSenateRequiredStakePercentage: u64 = 1; // 1 percent of total stake pub const SubtensorInitialNetworkImmunity: u64 = 7 * 7200; - pub const SubtensorInitialMinAllowedUids: u16 = 128; + pub const SubtensorInitialMinAllowedUids: u16 = 64; pub const SubtensorInitialMinLockCost: u64 = 1_000_000_000_000; // 1000 TAO pub const SubtensorInitialSubnetOwnerCut: u16 = 11_796; // 18 percent // pub const SubtensorInitialSubnetLimit: u16 = 12; // (DEPRECATED) @@ -1174,6 +1174,7 @@ parameter_types! { pub const SubtensorInitialKeySwapOnSubnetCost: u64 = 1_000_000; // 0.001 TAO pub const HotkeySwapOnSubnetInterval : BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const LeaseDividendsDistributionInterval: BlockNumber = 100; // 100 blocks + pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); } impl pallet_subtensor::Config for Runtime { @@ -1188,6 +1189,7 @@ impl pallet_subtensor::Config for Runtime { type InitialRho = SubtensorInitialRho; type InitialAlphaSigmoidSteepness = SubtensorInitialAlphaSigmoidSteepness; type InitialKappa = SubtensorInitialKappa; + type InitialMinAllowedUids = SubtensorInitialMinAllowedUids; type InitialMaxAllowedUids = SubtensorInitialMaxAllowedUids; type InitialBondsMovingAverage = SubtensorInitialBondsMovingAverage; type InitialBondsPenalty = SubtensorInitialBondsPenalty; @@ -1228,7 +1230,6 @@ impl pallet_subtensor::Config for Runtime { type InitialRAORecycledForRegistration = SubtensorInitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = SubtensorInitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = SubtensorInitialNetworkImmunity; - type InitialNetworkMinAllowedUids = SubtensorInitialMinAllowedUids; type InitialNetworkMinLockCost = SubtensorInitialMinLockCost; type InitialNetworkLockReductionInterval = SubtensorInitialNetworkLockReductionInterval; type InitialSubnetOwnerCut = SubtensorInitialSubnetOwnerCut; @@ -1251,6 +1252,7 @@ impl pallet_subtensor::Config for Runtime { type ProxyInterface = Proxier; type LeaseDividendsDistributionInterval = LeaseDividendsDistributionInterval; type GetCommitments = GetCommitmentsStruct; + type MaxImmuneUidsPercentage = MaxImmuneUidsPercentage; } parameter_types! {