From 3f581e1b43680d62a707acb43ff3bfa9f9df583c Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 15 Feb 2025 19:48:45 -0500 Subject: [PATCH 01/10] only allow replace non-top-stake owner hk --- pallets/subtensor/src/subnets/uids.rs | 44 +++++-- pallets/subtensor/src/tests/uids.rs | 169 ++++++++++++++++++++++++++ 2 files changed, 201 insertions(+), 12 deletions(-) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 8eec0fdab7..628f7edb3d 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -1,6 +1,7 @@ use super::*; use frame_support::storage::IterableStorageDoubleMap; use sp_std::vec; +use substrate_fixed::types::I64F64; impl Pallet { /// Returns the number of filled slots on a network. @@ -42,18 +43,37 @@ impl Pallet { // 1. Get the old hotkey under this position. let old_hotkey: T::AccountId = Keys::::get(netuid, uid_to_replace); - // Do not deregister the owner - let coldkey = Self::get_owning_coldkey_for_hotkey(&old_hotkey); - if Self::get_subnet_owner(netuid) == coldkey { - log::warn!( - "replace_neuron: Skipped replacement because neuron belongs to the subnet owner. \ - netuid: {:?}, uid_to_replace: {:?}, new_hotkey: {:?}, owner_coldkey: {:?}", - netuid, - uid_to_replace, - new_hotkey, - coldkey - ); - return; + // Do not deregister the owner's top-stake hotkey + let mut top_stake_sn_owner_hotkey: Option = None; + let mut max_stake_weight: I64F64 = I64F64::from_num(-1); + for neuron_uid in 0..Self::get_subnetwork_n(netuid) { + if let Ok(hotkey) = Self::get_hotkey_for_net_and_uid(netuid, neuron_uid) { + let coldkey = Self::get_owning_coldkey_for_hotkey(&hotkey); + if Self::get_subnet_owner(netuid) != coldkey { + continue; + } + + let stake_weights = Self::get_stake_weights_for_hotkey_on_subnet(&hotkey, netuid); + if stake_weights.0 > max_stake_weight { + max_stake_weight = stake_weights.0; + top_stake_sn_owner_hotkey = Some(hotkey); + } + } + } + + if let Some(ref sn_owner_hotkey) = top_stake_sn_owner_hotkey { + if sn_owner_hotkey == &old_hotkey { + log::warn!( + "replace_neuron: Skipped replacement because neuron belongs to the subnet owner. \ + And this hotkey has the highest stake weight of all the owner's hotkeys. \ + netuid: {:?}, uid_to_replace: {:?}, new_hotkey: {:?}, owner_hotkey: {:?}", + netuid, + uid_to_replace, + new_hotkey, + sn_owner_hotkey + ); + return; + } } // 2. Remove previous set memberships. diff --git a/pallets/subtensor/src/tests/uids.rs b/pallets/subtensor/src/tests/uids.rs index cc72f73a78..df308f79ed 100644 --- a/pallets/subtensor/src/tests/uids.rs +++ b/pallets/subtensor/src/tests/uids.rs @@ -280,6 +280,175 @@ fn test_replace_neuron_subnet_owner_not_replaced() { }); } +#[test] +fn test_replace_neuron_subnet_owner_not_replaced_if_top_stake_owner_hotkey() { + new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(123); + let owner_coldkey = U256::from(999); + let other_owner_hotkey = U256::from(456); + + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + SubtensorModule::set_max_registrations_per_block(netuid, 100); + SubtensorModule::set_target_registrations_per_interval(netuid, 100); + SubnetOwner::::insert(netuid, owner_coldkey); + + let owner_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey) + .expect("Owner neuron should already be registered by add_dynamic_network"); + + // Register another hotkey for the owner + register_ok_neuron(netuid, other_owner_hotkey, owner_coldkey, 0); + let other_owner_uid = + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) + .expect("Should be registered"); + + let additional_hotkey_1 = U256::from(1000); + let additional_hotkey_2 = U256::from(1001); + + let current_block = SubtensorModule::get_current_block_as_u64(); + SubtensorModule::replace_neuron(netuid, owner_uid, &additional_hotkey_1, current_block); + + let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey); + assert_ok!(still_uid); + assert_eq!( + still_uid.unwrap(), + owner_uid, + "Owner's first hotkey should remain registered" + ); + + let new_key_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_1); + assert_err!(new_key_uid, Error::::HotKeyNotRegisteredInSubNet,); + + // Try to replace the other owner hotkey + SubtensorModule::replace_neuron( + netuid, + other_owner_uid, + &additional_hotkey_1, + current_block, + ); + let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey); + assert_err!(still_uid, Error::::HotKeyNotRegisteredInSubNet,); // Was replaced + + // Re-register this hotkey + register_ok_neuron(netuid, other_owner_hotkey, owner_coldkey, 0); + let _other_owner_uid = + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) + .expect("Should be registered"); + + // Give the owner's other hotkey some stake + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &other_owner_hotkey, + &owner_coldkey, + netuid, + 1000, + ); + + SubtensorModule::replace_neuron(netuid, owner_uid, &additional_hotkey_2, current_block); + + // The owner's first hotkey should be replaceable; it's not the top-stake owner hotkey + let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey); + assert_err!(still_uid, Error::::HotKeyNotRegisteredInSubNet,); + + let new_key_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_2); + assert_ok!(new_key_uid); + }); +} + +#[test] +fn test_replace_neuron_subnet_owner_not_replaced_if_top_stake_owner_hotkey_chk() { + new_test_ext(1).execute_with(|| { + let owner_hotkey = U256::from(123); + let owner_coldkey = U256::from(999); + let other_owner_hotkey = U256::from(456); + let parent_hotkey = U256::from(4567); + let parent_coldkey = U256::from(4568); + + let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); + + SubtensorModule::set_max_registrations_per_block(netuid, 100); + SubtensorModule::set_target_registrations_per_interval(netuid, 100); + SubnetOwner::::insert(netuid, owner_coldkey); + + let owner_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey) + .expect("Owner neuron should already be registered by add_dynamic_network"); + + // Register another hotkey for the owner + register_ok_neuron(netuid, other_owner_hotkey, owner_coldkey, 0); + let other_owner_uid = + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) + .expect("Should be registered"); + + register_ok_neuron(netuid, parent_hotkey, parent_coldkey, 3); + let _uid_4: u16 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &parent_hotkey) + .expect("Should be registered"); + + // Give parent key some stake + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &parent_hotkey, + &parent_coldkey, + netuid, + 10_000_000, + ); + + let additional_hotkey_1 = U256::from(1000); + let additional_hotkey_2 = U256::from(1001); + + let current_block = SubtensorModule::get_current_block_as_u64(); + SubtensorModule::replace_neuron(netuid, owner_uid, &additional_hotkey_1, current_block); + + let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey); + assert_ok!(still_uid); + assert_eq!( + still_uid.unwrap(), + owner_uid, + "Owner's first hotkey should remain registered" + ); + + let new_key_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_1); + assert_err!(new_key_uid, Error::::HotKeyNotRegisteredInSubNet,); + + SubtensorModule::replace_neuron( + netuid, + other_owner_uid, + &additional_hotkey_1, + current_block, + ); + let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey); + assert_err!(still_uid, Error::::HotKeyNotRegisteredInSubNet,); // Was replaced + + // Re-register this hotkey + register_ok_neuron(netuid, other_owner_hotkey, owner_coldkey, 0); + let _other_owner_uid = + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) + .expect("Should be registered"); + + // Give the owner's other hotkey some CHK stake; Doesn't need to be much + mock_set_children_no_epochs( + netuid, + &parent_hotkey, + &[( + I64F64::saturating_from_num(0.1) + .saturating_mul(I64F64::saturating_from_num(u64::MAX)) + .saturating_to_num::(), + other_owner_hotkey, + )], + ); + // Check stake weight of other_owner_hotkey + let stake_weight = + SubtensorModule::get_stake_weights_for_hotkey_on_subnet(&other_owner_hotkey, netuid); + assert!(stake_weight.0 > 0); + + SubtensorModule::replace_neuron(netuid, owner_uid, &additional_hotkey_2, current_block); + + // The owner's first hotkey should be replaceable; it's not the top-stake owner hotkey + let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey); + assert_err!(still_uid, Error::::HotKeyNotRegisteredInSubNet,); + + let new_key_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_2); + assert_ok!(new_key_uid); + }); +} + #[test] fn test_get_neuron_to_prune_owner_not_pruned() { new_test_ext(1).execute_with(|| { From e37591535ebcb0d52f9f3c2b9c4dd5f1127cbc45 Mon Sep 17 00:00:00 2001 From: camfairchild Date: Sat, 15 Feb 2025 19:49:52 -0500 Subject: [PATCH 02/10] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 1b86426bb2..bc8d74dd23 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -229,7 +229,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 237, + spec_version: 238, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 1019ad0e7d8dc26937b550bf3f9770402311e495 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:20:22 -0500 Subject: [PATCH 03/10] add tests using SubnetOwnerHotkey --- pallets/subtensor/src/tests/uids.rs | 216 +--------------------------- 1 file changed, 6 insertions(+), 210 deletions(-) diff --git a/pallets/subtensor/src/tests/uids.rs b/pallets/subtensor/src/tests/uids.rs index df308f79ed..5ff8a3d1a0 100644 --- a/pallets/subtensor/src/tests/uids.rs +++ b/pallets/subtensor/src/tests/uids.rs @@ -281,7 +281,7 @@ fn test_replace_neuron_subnet_owner_not_replaced() { } #[test] -fn test_replace_neuron_subnet_owner_not_replaced_if_top_stake_owner_hotkey() { +fn test_replace_neuron_subnet_owner_not_replaced_if_in_sn_owner_hotkey_map() { new_test_ext(1).execute_with(|| { let owner_hotkey = U256::from(123); let owner_coldkey = U256::from(999); @@ -335,108 +335,8 @@ fn test_replace_neuron_subnet_owner_not_replaced_if_top_stake_owner_hotkey() { SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) .expect("Should be registered"); - // Give the owner's other hotkey some stake - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &other_owner_hotkey, - &owner_coldkey, - netuid, - 1000, - ); - - SubtensorModule::replace_neuron(netuid, owner_uid, &additional_hotkey_2, current_block); - - // The owner's first hotkey should be replaceable; it's not the top-stake owner hotkey - let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey); - assert_err!(still_uid, Error::::HotKeyNotRegisteredInSubNet,); - - let new_key_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_2); - assert_ok!(new_key_uid); - }); -} - -#[test] -fn test_replace_neuron_subnet_owner_not_replaced_if_top_stake_owner_hotkey_chk() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(123); - let owner_coldkey = U256::from(999); - let other_owner_hotkey = U256::from(456); - let parent_hotkey = U256::from(4567); - let parent_coldkey = U256::from(4568); - - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - SubtensorModule::set_max_registrations_per_block(netuid, 100); - SubtensorModule::set_target_registrations_per_interval(netuid, 100); - SubnetOwner::::insert(netuid, owner_coldkey); - - let owner_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey) - .expect("Owner neuron should already be registered by add_dynamic_network"); - - // Register another hotkey for the owner - register_ok_neuron(netuid, other_owner_hotkey, owner_coldkey, 0); - let other_owner_uid = - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) - .expect("Should be registered"); - - register_ok_neuron(netuid, parent_hotkey, parent_coldkey, 3); - let _uid_4: u16 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &parent_hotkey) - .expect("Should be registered"); - - // Give parent key some stake - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &parent_hotkey, - &parent_coldkey, - netuid, - 10_000_000, - ); - - let additional_hotkey_1 = U256::from(1000); - let additional_hotkey_2 = U256::from(1001); - - let current_block = SubtensorModule::get_current_block_as_u64(); - SubtensorModule::replace_neuron(netuid, owner_uid, &additional_hotkey_1, current_block); - - let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey); - assert_ok!(still_uid); - assert_eq!( - still_uid.unwrap(), - owner_uid, - "Owner's first hotkey should remain registered" - ); - - let new_key_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_1); - assert_err!(new_key_uid, Error::::HotKeyNotRegisteredInSubNet,); - - SubtensorModule::replace_neuron( - netuid, - other_owner_uid, - &additional_hotkey_1, - current_block, - ); - let still_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey); - assert_err!(still_uid, Error::::HotKeyNotRegisteredInSubNet,); // Was replaced - - // Re-register this hotkey - register_ok_neuron(netuid, other_owner_hotkey, owner_coldkey, 0); - let _other_owner_uid = - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) - .expect("Should be registered"); - - // Give the owner's other hotkey some CHK stake; Doesn't need to be much - mock_set_children_no_epochs( - netuid, - &parent_hotkey, - &[( - I64F64::saturating_from_num(0.1) - .saturating_mul(I64F64::saturating_from_num(u64::MAX)) - .saturating_to_num::(), - other_owner_hotkey, - )], - ); - // Check stake weight of other_owner_hotkey - let stake_weight = - SubtensorModule::get_stake_weights_for_hotkey_on_subnet(&other_owner_hotkey, netuid); - assert!(stake_weight.0 > 0); + // Set this hotkey as the SubnetOwnerHotkey + SubnetOwnerHotkey::::insert(netuid, other_owner_hotkey); SubtensorModule::replace_neuron(netuid, owner_uid, &additional_hotkey_2, current_block); @@ -507,7 +407,7 @@ fn test_get_neuron_to_prune_owner_not_pruned() { } #[test] -fn test_get_neuron_to_prune_owner_pruned_if_not_top_stake_owner_hotkey() { +fn test_get_neuron_to_prune_owner_pruned_if_not_in_sn_owner_hotkey_map() { new_test_ext(1).execute_with(|| { let owner_hotkey = U256::from(123); let owner_coldkey = U256::from(999); @@ -551,118 +451,14 @@ fn test_get_neuron_to_prune_owner_pruned_if_not_top_stake_owner_hotkey() { let pruned_uid = SubtensorModule::get_neuron_to_prune(netuid); assert_eq!(pruned_uid, other_owner_uid, "Should prune the owner"); - // Give the owner's other hotkey some stake - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &other_owner_hotkey, - &owner_coldkey, - netuid, - 1000, - ); - - // Reset pruning scores - SubtensorModule::set_pruning_score_for_uid(netuid, owner_uid, 0); - SubtensorModule::set_pruning_score_for_uid(netuid, other_owner_uid, 1); - SubtensorModule::set_pruning_score_for_uid(netuid, uid_2, 2); - SubtensorModule::set_pruning_score_for_uid(netuid, uid_3, 3); - - let pruned_uid = SubtensorModule::get_neuron_to_prune(netuid); - - // - The pruned UID must be `uid_1` (score=1). - // - The owner's UID remains unpruned. - assert_eq!( - pruned_uid, owner_uid, - "Should prune the owner, not the top-stake owner hotkey and not the additional hotkeys" - ); - }); -} - -#[test] -fn test_get_neuron_to_prune_owner_pruned_if_not_top_stake_owner_hotkey_chk() { - new_test_ext(1).execute_with(|| { - let owner_hotkey = U256::from(123); - let owner_coldkey = U256::from(999); - let other_owner_hotkey = U256::from(456); - let parent_hotkey = U256::from(4567); - let parent_coldkey = U256::from(4568); - - let netuid = add_dynamic_network(&owner_hotkey, &owner_coldkey); - - SubtensorModule::set_max_registrations_per_block(netuid, 100); - SubtensorModule::set_target_registrations_per_interval(netuid, 100); - SubnetOwner::::insert(netuid, owner_coldkey); - - let owner_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &owner_hotkey) - .expect("Owner neuron should already be registered by add_dynamic_network"); - - // Register another hotkey for the owner - register_ok_neuron(netuid, other_owner_hotkey, owner_coldkey, 0); - let other_owner_uid = - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &other_owner_hotkey) - .expect("Should be registered"); - - let additional_hotkey_1 = U256::from(1000); - let additional_coldkey_1 = U256::from(2000); - - let additional_hotkey_2 = U256::from(1001); - let additional_coldkey_2 = U256::from(2001); - - register_ok_neuron(netuid, additional_hotkey_1, additional_coldkey_1, 1); - let uid_2 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_1) - .expect("Should be registered"); - - register_ok_neuron(netuid, additional_hotkey_2, additional_coldkey_2, 2); - let uid_3 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &additional_hotkey_2) - .expect("Should be registered"); - - register_ok_neuron(netuid, parent_hotkey, parent_coldkey, 3); - let uid_4: u16 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &parent_hotkey) - .expect("Should be registered"); - - // Give parent key some stake - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &parent_hotkey, - &parent_coldkey, - netuid, - 10_000_000, - ); - - SubtensorModule::set_pruning_score_for_uid(netuid, owner_uid, 0); - // Other owner key has pruning score not worse than the owner's first hotkey, but worse than the additional hotkeys - SubtensorModule::set_pruning_score_for_uid(netuid, other_owner_uid, 1); - SubtensorModule::set_pruning_score_for_uid(netuid, uid_2, 2); - SubtensorModule::set_pruning_score_for_uid(netuid, uid_3, 3); - - // Ensure parent key is not pruned - SubtensorModule::set_pruning_score_for_uid(netuid, uid_4, 10_000); - - let pruned_uid = SubtensorModule::get_neuron_to_prune(netuid); - assert_eq!( - pruned_uid, other_owner_uid, - "Should prune the owner's other hotkey" - ); - - // Give the owner's other hotkey some CHK stake; Doesn't need to be much - mock_set_children_no_epochs( - netuid, - &parent_hotkey, - &[( - I64F64::saturating_from_num(0.1) - .saturating_mul(I64F64::saturating_from_num(u64::MAX)) - .saturating_to_num::(), - other_owner_hotkey, - )], - ); - // Check stake weight of other_owner_hotkey - let stake_weight = - SubtensorModule::get_stake_weights_for_hotkey_on_subnet(&other_owner_hotkey, netuid); - assert!(stake_weight.0 > 0); + // Set the owner's other hotkey as the SubnetOwnerHotkey + SubnetOwnerHotkey::::insert(netuid, owner_hotkey); // Reset pruning scores SubtensorModule::set_pruning_score_for_uid(netuid, owner_uid, 0); SubtensorModule::set_pruning_score_for_uid(netuid, other_owner_uid, 1); SubtensorModule::set_pruning_score_for_uid(netuid, uid_2, 2); SubtensorModule::set_pruning_score_for_uid(netuid, uid_3, 3); - SubtensorModule::set_pruning_score_for_uid(netuid, uid_4, 10_000); let pruned_uid = SubtensorModule::get_neuron_to_prune(netuid); From c6d3ae32b18f79a10ecbef2c4ffac858ceb0d587 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:20:27 -0500 Subject: [PATCH 04/10] add impl for replace neuron --- pallets/subtensor/src/subnets/uids.rs | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 628f7edb3d..3e383cb4bc 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -43,29 +43,11 @@ impl Pallet { // 1. Get the old hotkey under this position. let old_hotkey: T::AccountId = Keys::::get(netuid, uid_to_replace); - // Do not deregister the owner's top-stake hotkey - let mut top_stake_sn_owner_hotkey: Option = None; - let mut max_stake_weight: I64F64 = I64F64::from_num(-1); - for neuron_uid in 0..Self::get_subnetwork_n(netuid) { - if let Ok(hotkey) = Self::get_hotkey_for_net_and_uid(netuid, neuron_uid) { - let coldkey = Self::get_owning_coldkey_for_hotkey(&hotkey); - if Self::get_subnet_owner(netuid) != coldkey { - continue; - } - - let stake_weights = Self::get_stake_weights_for_hotkey_on_subnet(&hotkey, netuid); - if stake_weights.0 > max_stake_weight { - max_stake_weight = stake_weights.0; - top_stake_sn_owner_hotkey = Some(hotkey); - } - } - } - - if let Some(ref sn_owner_hotkey) = top_stake_sn_owner_hotkey { - if sn_owner_hotkey == &old_hotkey { + // Do not replace owner hotkey from `SubnetOwnerHotkey` + if let Ok(sn_owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { + if sn_owner_hotkey == old_hotkey.clone() { log::warn!( - "replace_neuron: Skipped replacement because neuron belongs to the subnet owner. \ - And this hotkey has the highest stake weight of all the owner's hotkeys. \ + "replace_neuron: Skipped replacement because neuron is the subnet owner hotkey. \ netuid: {:?}, uid_to_replace: {:?}, new_hotkey: {:?}, owner_hotkey: {:?}", netuid, uid_to_replace, From ccb39d7329cba8872dac4008a90bc7e5cf2541fb Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:25:59 -0500 Subject: [PATCH 05/10] add swap test --- pallets/subtensor/src/tests/swap_hotkey.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index 8cf04d2855..887b5a26f3 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -1359,3 +1359,25 @@ fn test_swap_child_hotkey_childkey_maps() { ); }) } + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_swap_hotkey_is_sn_owner_hotkey --exact --nocapture +#[test] +fn test_swap_hotkey_is_sn_owner_hotkey() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let mut weight = Weight::zero(); + + // Create dynamic network + let netuid = add_dynamic_network(&old_hotkey, &coldkey); + // Check for SubnetOwnerHotkey + assert_eq!(SubnetOwnerHotkey::::get(netuid), old_hotkey); + + // Perform the swap + SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); + + // Check for SubnetOwnerHotkey + assert_eq!(SubnetOwnerHotkey::::get(netuid), new_hotkey); + }); +} From 8f0a7cbb6ec1fc5dc1cbdee6fa80166832f10aaa Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:26:06 -0500 Subject: [PATCH 06/10] add neuron prune impl --- pallets/subtensor/src/subnets/registration.rs | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index f38a4e94a0..c5f7b38792 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -425,29 +425,11 @@ impl Pallet { return 0; // If there are no neurons in this network. } - // Get SN owner top stake hotkey - let mut top_stake_sn_owner_hotkey: Option = None; - let mut max_stake_weight: I64F64 = I64F64::from_num(-1); for neuron_uid in 0..neurons_n { + // Do not deregister the owner's hotkey from the `SubnetOwnerHotkey` map if let Ok(hotkey) = Self::get_hotkey_for_net_and_uid(netuid, neuron_uid) { - let coldkey = Self::get_owning_coldkey_for_hotkey(&hotkey); - if Self::get_subnet_owner(netuid) != coldkey { - continue; - } - - let stake_weights = Self::get_stake_weights_for_hotkey_on_subnet(&hotkey, netuid); - if stake_weights.0 > max_stake_weight { - max_stake_weight = stake_weights.0; - top_stake_sn_owner_hotkey = Some(hotkey); - } - } - } - - for neuron_uid in 0..neurons_n { - // Do not deregister the owner's top-stake hotkey - if let Ok(hotkey) = Self::get_hotkey_for_net_and_uid(netuid, neuron_uid) { - if let Some(ref top_sn_owner_hotkey) = top_stake_sn_owner_hotkey { - if top_sn_owner_hotkey == &hotkey { + if let Ok(top_sn_owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { + if top_sn_owner_hotkey == hotkey { continue; } } From e9b00518b31093f82179f7e36d3890a2f3a3e92c Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:26:10 -0500 Subject: [PATCH 07/10] add swap hotkey impl --- pallets/subtensor/src/swap/swap_hotkey.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index f8bd979df8..ffe9dca8d9 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -437,6 +437,18 @@ impl Pallet { } } + // 15. Swap SubnetOwnerHotkey + // SubnetOwnerHotkey( netuid ) --> hotkey -- the hotkey that is the owner of the subnet. + for netuid in Self::get_all_subnet_netuids() { + if let Ok(old_subnet_owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if old_subnet_owner_hotkey == *old_hotkey { + SubnetOwnerHotkey::::insert(netuid, new_hotkey); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } + } + } + // Return successful after swapping all the relevant terms. Ok(()) } From 40c53a203bb1f291be3f93fd768b1b5f2fd76475 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:26:17 -0500 Subject: [PATCH 08/10] clippy --- pallets/subtensor/src/tests/swap_hotkey.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index 887b5a26f3..4e02258708 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -1370,14 +1370,14 @@ fn test_swap_hotkey_is_sn_owner_hotkey() { let mut weight = Weight::zero(); // Create dynamic network - let netuid = add_dynamic_network(&old_hotkey, &coldkey); - // Check for SubnetOwnerHotkey - assert_eq!(SubnetOwnerHotkey::::get(netuid), old_hotkey); + let netuid = add_dynamic_network(&old_hotkey, &coldkey); + // Check for SubnetOwnerHotkey + assert_eq!(SubnetOwnerHotkey::::get(netuid), old_hotkey); // Perform the swap SubtensorModule::perform_hotkey_swap(&old_hotkey, &new_hotkey, &coldkey, &mut weight); - // Check for SubnetOwnerHotkey - assert_eq!(SubnetOwnerHotkey::::get(netuid), new_hotkey); + // Check for SubnetOwnerHotkey + assert_eq!(SubnetOwnerHotkey::::get(netuid), new_hotkey); }); } From a89b2c264f575111bdcf584b072b7e741fad7ef9 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:27:08 -0500 Subject: [PATCH 09/10] clippy --- pallets/subtensor/src/subnets/registration.rs | 1 - pallets/subtensor/src/subnets/uids.rs | 1 - pallets/subtensor/src/tests/uids.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index c5f7b38792..bcde11994d 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -2,7 +2,6 @@ use super::*; use sp_core::{H256, U256}; use sp_io::hashing::{keccak_256, sha2_256}; use sp_runtime::Saturating; -use substrate_fixed::types::I64F64; use system::pallet_prelude::BlockNumberFor; const LOG_TARGET: &str = "runtime::subtensor::registration"; diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 3e383cb4bc..aaf3b5fe6b 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -1,7 +1,6 @@ use super::*; use frame_support::storage::IterableStorageDoubleMap; use sp_std::vec; -use substrate_fixed::types::I64F64; impl Pallet { /// Returns the number of filled slots on a network. diff --git a/pallets/subtensor/src/tests/uids.rs b/pallets/subtensor/src/tests/uids.rs index 5ff8a3d1a0..8aba557d33 100644 --- a/pallets/subtensor/src/tests/uids.rs +++ b/pallets/subtensor/src/tests/uids.rs @@ -5,7 +5,6 @@ use crate::*; use frame_support::{assert_err, assert_ok}; use frame_system::Config; use sp_core::U256; -use substrate_fixed::types::I64F64; /******************************************** tests for uids.rs file From 17ca77d16fcb0184208a04636638e75d1e5da025 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 15 Feb 2025 20:45:07 -0500 Subject: [PATCH 10/10] mistake in test --- pallets/subtensor/src/tests/uids.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pallets/subtensor/src/tests/uids.rs b/pallets/subtensor/src/tests/uids.rs index 8aba557d33..87f6459689 100644 --- a/pallets/subtensor/src/tests/uids.rs +++ b/pallets/subtensor/src/tests/uids.rs @@ -451,7 +451,7 @@ fn test_get_neuron_to_prune_owner_pruned_if_not_in_sn_owner_hotkey_map() { assert_eq!(pruned_uid, other_owner_uid, "Should prune the owner"); // Set the owner's other hotkey as the SubnetOwnerHotkey - SubnetOwnerHotkey::::insert(netuid, owner_hotkey); + SubnetOwnerHotkey::::insert(netuid, other_owner_hotkey); // Reset pruning scores SubtensorModule::set_pruning_score_for_uid(netuid, owner_uid, 0); @@ -461,8 +461,6 @@ fn test_get_neuron_to_prune_owner_pruned_if_not_in_sn_owner_hotkey_map() { let pruned_uid = SubtensorModule::get_neuron_to_prune(netuid); - // - The pruned UID must be `uid_1` (score=1). - // - The owner's UID remains unpruned. assert_eq!( pruned_uid, owner_uid, "Should prune the owner, not the top-stake owner hotkey and not the additional hotkeys"