From 00edfefacdc8b4532fe3defe70f1d18f4b66bda5 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 29 May 2024 09:32:52 -0500 Subject: [PATCH 01/12] add test --- pallets/subtensor/tests/senate.rs | 78 +++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/pallets/subtensor/tests/senate.rs b/pallets/subtensor/tests/senate.rs index 7d742832d..297ea013f 100644 --- a/pallets/subtensor/tests/senate.rs +++ b/pallets/subtensor/tests/senate.rs @@ -571,3 +571,81 @@ fn test_senate_not_leave_when_stake_removed() { assert!(Senate::is_member(&hotkey_account_id)); }); } + +#[test] +fn test_senate_join_current_delegate() { + // Test that a current delegate can join the senate + new_test_ext().execute_with(|| { + migration::migrate_create_root_network::(); + + let netuid: u16 = 1; + let tempo: u16 = 13; + let hotkey_account_id = U256::from(6); + let burn_cost = 1000; + let coldkey_account_id = U256::from(667); + + //add network + SubtensorModule::set_burn(netuid, burn_cost); + add_network(netuid, tempo, 0); + // Give some coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10000); + + // Subscribe and check extrinsic output + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + hotkey_account_id + )); + // Check if balance has decreased to pay for the burn. + assert_eq!( + SubtensorModule::get_coldkey_balance(&coldkey_account_id), + (10000 - burn_cost) + ); // funds drained on reg. + // Check if neuron has added to the specified network(netuid) + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 1); + // Check if hotkey is added to the Hotkeys + assert_eq!( + SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey_account_id), + coldkey_account_id + ); + + // Register in the root network *before* having enough stake to join the senate + assert_ok!(SubtensorModule::root_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id + )); + + // Should *NOT* be a member of the senate + assert!(!Senate::is_member(&hotkey_account_id)); + + // Add/delegate enough stake to join the senate + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id, + 100_000 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey( + &coldkey_account_id, + &hotkey_account_id + ), + 99_999 + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), + 99_999 + ); + + // We expect to still NOT be a member of the senate + assert!(!Senate::is_member(&hotkey_account_id)); + + // We can call now to adjust the senate + assert_ok!(SubtensorModule::adjust_senate( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id + )); + + // This should make the hotkey a member of the senate + assert!(Senate::is_member(&hotkey_account_id)); + }); +} From 1025aee6cb1042dc0230ed080a043c26123e1241 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 29 May 2024 10:06:50 -0500 Subject: [PATCH 02/12] add test tracking events --- pallets/subtensor/tests/senate.rs | 188 ++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/pallets/subtensor/tests/senate.rs b/pallets/subtensor/tests/senate.rs index 297ea013f..2c691f132 100644 --- a/pallets/subtensor/tests/senate.rs +++ b/pallets/subtensor/tests/senate.rs @@ -639,6 +639,8 @@ fn test_senate_join_current_delegate() { // We expect to still NOT be a member of the senate assert!(!Senate::is_member(&hotkey_account_id)); + System::reset_events(); + // We can call now to adjust the senate assert_ok!(SubtensorModule::adjust_senate( <::RuntimeOrigin>::signed(coldkey_account_id), @@ -647,5 +649,191 @@ fn test_senate_join_current_delegate() { // This should make the hotkey a member of the senate assert!(Senate::is_member(&hotkey_account_id)); + + // Check the events + assert_eq!( + System::events(), + vec![record(RuntimeEvent::SubtensorModule( + SubtensorEvent::SenateAdjusted { + old_member: None, + new_member: hotkey_account_id + } + )),], + ); + }); +} + +#[test] +fn test_adjust_senate_events() { + // Test the events emitted after adjusting the senate successfully + new_test_ext().execute_with(|| { + migration::migrate_create_root_network::(); + + let netuid: u16 = 1; + let tempo: u16 = 13; + let hotkey_account_id = U256::from(6); + let burn_cost = 1000; + let coldkey_account_id = U256::from(667); + let root_netuid = SubtensorModule::get_root_netuid(); + + let max_senate_size: u16 = SenateMaxMembers::get() as u16; + let stake_threshold: u64 = 100_000; // If everyone has this much stake plus or minus 12, they allow have sufficient threshold + + // We will be registering MaxMembers hotkeys and one more to replace hotkey_account_id + let balance_to_add = 50_000 + (stake_threshold + burn_cost) * (max_senate_size + 1) as u64; + + let replacement_hotkey_account_id = U256::from(7); // Will be added to the senate to replace hotkey_account_id + + //add network + SubtensorModule::set_burn(netuid, burn_cost); + add_network(netuid, tempo, 0); + // Give some coldkey balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, balance_to_add); + + // Allow all registrations in netuid in same block. Same for root network. + SubtensorModule::set_max_registrations_per_block(netuid, max_senate_size + 1); + SubtensorModule::set_target_registrations_per_interval(netuid, max_senate_size + 1); + SubtensorModule::set_max_registrations_per_block(root_netuid, max_senate_size + 1); + SubtensorModule::set_target_registrations_per_interval(root_netuid, max_senate_size + 1); + + // Subscribe and check extrinsic output + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + hotkey_account_id + )); + // Check if balance has decreased to pay for the burn. + assert_eq!( + SubtensorModule::get_coldkey_balance(&coldkey_account_id), + (balance_to_add - burn_cost) + ); // funds drained on reg. + // Check if neuron has added to the specified network(netuid) + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 1); + // Check if hotkey is added to the Hotkeys + assert_eq!( + SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey_account_id), + coldkey_account_id + ); + + // Register in the root network *before* having enough stake to join the senate + assert_ok!(SubtensorModule::root_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id + )); + + // Should *NOT* be a member of the senate + assert!(!Senate::is_member(&hotkey_account_id)); + + // Add/delegate enough stake to join the senate + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id, + stake_threshold + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey( + &coldkey_account_id, + &hotkey_account_id + ), + stake_threshold + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), + stake_threshold + ); + + // We expect to still NOT be a member of the senate + assert!(!Senate::is_member(&hotkey_account_id)); + + System::reset_events(); + + // We can call now to adjust the senate + assert_ok!(SubtensorModule::adjust_senate( + <::RuntimeOrigin>::signed(coldkey_account_id), + hotkey_account_id + )); + + // This should make the hotkey a member of the senate + assert!(Senate::is_member(&hotkey_account_id)); + + // Check the events + assert_eq!( + System::events(), + vec![record(RuntimeEvent::SubtensorModule( + SubtensorEvent::SenateAdjusted { + old_member: None, // Replaced nothing + new_member: hotkey_account_id + } + )),], + ); + + // Register MaxMembers - 2 more hotkeys + // Give them enough Stake to join the senate, and leave hotkey_account_id last + for i in 0..(max_senate_size - 2) { + let new_hotkey_account_id = U256::from(8 + i); + + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + new_hotkey_account_id + )); + // Check if this hotkey is added to the Hotkeys + assert_eq!( + SubtensorModule::get_owning_coldkey_for_hotkey(&new_hotkey_account_id), + coldkey_account_id + ); + // Add/delegate enough stake to join the senate + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey_account_id), + new_hotkey_account_id, + stake_threshold + 1 + i as u64 // Increasing with i to make them ordered + )); // +1 to be above hotkey_account_id + // Join senate + assert_ok!(SubtensorModule::root_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + new_hotkey_account_id + )); + // Check if they are a member of the senate + assert!(Senate::is_member(&new_hotkey_account_id)); + } + + // Verify we are at max senate size + assert_eq!(Senate::members().len(), max_senate_size as usize); + + // Register the replacement hotkey + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid, + replacement_hotkey_account_id + )); + + // Add enough stake to join the senate + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey_account_id), + replacement_hotkey_account_id, + stake_threshold + 1 + max_senate_size as u64 + )); // Will be more than hotkey_account_id, the last one in the senate by stake + + System::reset_events(); + + // We can call now to adjust the senate + assert_ok!(SubtensorModule::adjust_senate( + <::RuntimeOrigin>::signed(coldkey_account_id), + replacement_hotkey_account_id + )); + + // This should make the replacement hotkey a member of the senate + assert!(Senate::is_member(&replacement_hotkey_account_id)); + + // Check the events + assert_eq!( + System::events(), + vec![record(RuntimeEvent::SubtensorModule( + SubtensorEvent::SenateAdjusted { + old_member: Some(hotkey_account_id), // Replaced hotkey_account_id + new_member: replacement_hotkey_account_id + } + )),], + ); }); } From ab2f91815502c093438b0df674d61856e998bc7e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 4 Jun 2024 21:27:12 -0400 Subject: [PATCH 03/12] only test for contains event --- pallets/subtensor/tests/senate.rs | 139 +++++++++++------------------- 1 file changed, 52 insertions(+), 87 deletions(-) diff --git a/pallets/subtensor/tests/senate.rs b/pallets/subtensor/tests/senate.rs index 2c691f132..f75994eb5 100644 --- a/pallets/subtensor/tests/senate.rs +++ b/pallets/subtensor/tests/senate.rs @@ -596,7 +596,7 @@ fn test_senate_join_current_delegate() { netuid, hotkey_account_id )); - // Check if balance has decreased to pay for the burn. + // Check if balance has decreased to pay for the burn. assert_eq!( SubtensorModule::get_coldkey_balance(&coldkey_account_id), (10000 - burn_cost) @@ -609,34 +609,18 @@ fn test_senate_join_current_delegate() { coldkey_account_id ); - // Register in the root network *before* having enough stake to join the senate + // Register in the root network assert_ok!(SubtensorModule::root_register( <::RuntimeOrigin>::signed(coldkey_account_id), hotkey_account_id )); - - // Should *NOT* be a member of the senate - assert!(!Senate::is_member(&hotkey_account_id)); - - // Add/delegate enough stake to join the senate - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey_account_id), - hotkey_account_id, - 100_000 + // But, remove from the senate + assert_ok!(SenateMembers::remove_member( + <::RuntimeOrigin>::root(), + hotkey_account_id )); - assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey( - &coldkey_account_id, - &hotkey_account_id - ), - 99_999 - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), - 99_999 - ); - // We expect to still NOT be a member of the senate + // Should *NOT* be a member of the senate now assert!(!Senate::is_member(&hotkey_account_id)); System::reset_events(); @@ -651,14 +635,13 @@ fn test_senate_join_current_delegate() { assert!(Senate::is_member(&hotkey_account_id)); // Check the events - assert_eq!( - System::events(), - vec![record(RuntimeEvent::SubtensorModule( + assert!( + System::events().contains(&record(RuntimeEvent::SubtensorModule( SubtensorEvent::SenateAdjusted { old_member: None, new_member: hotkey_account_id } - )),], + ))) ); }); } @@ -677,10 +660,10 @@ fn test_adjust_senate_events() { let root_netuid = SubtensorModule::get_root_netuid(); let max_senate_size: u16 = SenateMaxMembers::get() as u16; - let stake_threshold: u64 = 100_000; // If everyone has this much stake plus or minus 12, they allow have sufficient threshold + let stake_threshold: u64 = 100_000; // Give this much to every senator - // We will be registering MaxMembers hotkeys and one more to replace hotkey_account_id - let balance_to_add = 50_000 + (stake_threshold + burn_cost) * (max_senate_size + 1) as u64; + // We will be registering MaxMembers hotkeys and two more to try a replace + let balance_to_add = 50_000 + (stake_threshold + burn_cost) * (max_senate_size + 2) as u64; let replacement_hotkey_account_id = U256::from(7); // Will be added to the senate to replace hotkey_account_id @@ -715,61 +698,20 @@ fn test_adjust_senate_events() { coldkey_account_id ); - // Register in the root network *before* having enough stake to join the senate - assert_ok!(SubtensorModule::root_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - hotkey_account_id - )); - // Should *NOT* be a member of the senate assert!(!Senate::is_member(&hotkey_account_id)); - // Add/delegate enough stake to join the senate - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey_account_id), - hotkey_account_id, - stake_threshold - )); - assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey( - &coldkey_account_id, - &hotkey_account_id - ), - stake_threshold - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id), - stake_threshold - ); - - // We expect to still NOT be a member of the senate - assert!(!Senate::is_member(&hotkey_account_id)); - - System::reset_events(); - - // We can call now to adjust the senate - assert_ok!(SubtensorModule::adjust_senate( + // root register + assert_ok!(SubtensorModule::root_register( <::RuntimeOrigin>::signed(coldkey_account_id), hotkey_account_id - )); + )); // Has no stake, but is now a senate member - // This should make the hotkey a member of the senate + // Check if they are a member of the senate assert!(Senate::is_member(&hotkey_account_id)); - // Check the events - assert_eq!( - System::events(), - vec![record(RuntimeEvent::SubtensorModule( - SubtensorEvent::SenateAdjusted { - old_member: None, // Replaced nothing - new_member: hotkey_account_id - } - )),], - ); - - // Register MaxMembers - 2 more hotkeys - // Give them enough Stake to join the senate, and leave hotkey_account_id last - for i in 0..(max_senate_size - 2) { + // Register MaxMembers - 1 more hotkeys, add stake and join the senate + for i in 0..(max_senate_size - 1) { let new_hotkey_account_id = U256::from(8 + i); assert_ok!(SubtensorModule::burned_register( @@ -800,19 +742,43 @@ fn test_adjust_senate_events() { // Verify we are at max senate size assert_eq!(Senate::members().len(), max_senate_size as usize); - // Register the replacement hotkey + // Verify the replacement hotkey is not a member of the senate + assert!(!Senate::is_member(&replacement_hotkey_account_id)); + + // Register assert_ok!(SubtensorModule::burned_register( <::RuntimeOrigin>::signed(coldkey_account_id), netuid, replacement_hotkey_account_id )); - // Add enough stake to join the senate + // Register in root network + assert_ok!(SubtensorModule::root_register( + <::RuntimeOrigin>::signed(coldkey_account_id), + replacement_hotkey_account_id + )); + + // Check if they are a member of the senate, should not be, + // as they have no stake + assert!(!Senate::is_member(&replacement_hotkey_account_id)); + + // Add/delegate enough stake to join the senate assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey_account_id), replacement_hotkey_account_id, - stake_threshold + 1 + max_senate_size as u64 - )); // Will be more than hotkey_account_id, the last one in the senate by stake + 1 // Will be more than the last one in the senate by stake (has 0 stake) + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey( + &coldkey_account_id, + &replacement_hotkey_account_id + ), + 1 + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&replacement_hotkey_account_id), + 1 + ); System::reset_events(); @@ -822,18 +788,17 @@ fn test_adjust_senate_events() { replacement_hotkey_account_id )); - // This should make the replacement hotkey a member of the senate + // This should make the hotkey a member of the senate assert!(Senate::is_member(&replacement_hotkey_account_id)); // Check the events - assert_eq!( - System::events(), - vec![record(RuntimeEvent::SubtensorModule( + assert!( + System::events().contains(&record(RuntimeEvent::SubtensorModule( SubtensorEvent::SenateAdjusted { - old_member: Some(hotkey_account_id), // Replaced hotkey_account_id + old_member: None, new_member: replacement_hotkey_account_id } - )),], + ))) ); }); } From a7da684a4b981459c7e252aff960e595cd1c8564 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 4 Jun 2024 21:28:37 -0400 Subject: [PATCH 04/12] add events and errors --- pallets/subtensor/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ae403b30f..4a7eba6ff 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -947,6 +947,10 @@ pub mod pallet { }, // Event created when a hotkey is swapped MaxDelegateTakeSet(u16), // Event emitted when maximum delegate take is set by sudo/admin transaction MinDelegateTakeSet(u16), // Event emitted when minimum delegate take is set by sudo/admin transaction + SenateAdjusted { + old_member: Option, + new_member: T::AccountId, + }, // Event emitted when a member of the senate is adjusted } // Errors inform users that something went wrong. @@ -1014,7 +1018,9 @@ pub mod pallet { NoNeuronIdAvailable, // -- Thrown when no neuron id is available /// Thrown a stake would be below the minimum threshold for nominator validations NomStakeBelowMinimumThreshold, - InvalidTake, // --- Thrown when delegate take is being set out of bounds + InvalidTake, // --- Thrown when delegate take is being set out of bounds + StakeTooLowForSenate, // --- Thrown when a hotkey attempts to join the senate with less stake than the lowest incumbent. + CouldNotJoinSenate, // --- Thrown when we cannot join the Senate for whatever reason. } // ================== From 03ec10d151164332bf1e5864df3cd55a65422fbc Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 4 Jun 2024 21:28:54 -0400 Subject: [PATCH 05/12] fix comment numbering --- pallets/subtensor/src/root.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index 196311ce3..5811a3d42 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -502,14 +502,14 @@ impl Pallet { // --- 8. Check if the root net is below its allowed size. // max allowed is senate size. if current_num_root_validators < Self::get_max_root_validators() { - // --- 12.1.1 We can append to the subnetwork as it's not full. + // --- 8.1.1 We can append to the subnetwork as it's not full. subnetwork_uid = current_num_root_validators; - // --- 12.1.2 Add the new account and make them a member of the Senate. + // --- 8.1.2 Add the new account and make them a member of the Senate. Self::append_neuron(root_netuid, &hotkey, current_block_number); log::info!("add new neuron: {:?} on uid {:?}", hotkey, subnetwork_uid); } else { - // --- 13.1.1 The network is full. Perform replacement. + // --- 8.2.1 The network is full. Perform replacement. // Find the neuron with the lowest stake value to replace. let mut lowest_stake: u64 = u64::MAX; let mut lowest_uid: u16 = 0; @@ -530,13 +530,13 @@ impl Pallet { let replaced_hotkey: T::AccountId = Self::get_hotkey_for_net_and_uid(root_netuid, subnetwork_uid)?; - // --- 13.1.2 The new account has a higher stake than the one being replaced. + // --- 8.2.2 The new account has a higher stake than the one being replaced. ensure!( lowest_stake < Self::get_total_stake_for_hotkey(&hotkey), Error::::StakeTooLowForRoot ); - // --- 13.1.3 The new account has a higher stake than the one being replaced. + // --- 8.2.3 The new account has a higher stake than the one being replaced. // Replace the neuron account with new information. Self::replace_neuron(root_netuid, lowest_uid, &hotkey, current_block_number); From 357672b9b99a1a447b6368c5faa3ce2f316bef8e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 4 Jun 2024 21:29:34 -0400 Subject: [PATCH 06/12] extract join senate to helper --- pallets/subtensor/src/root.rs | 95 +++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index 5811a3d42..8227d20ed 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -548,40 +548,20 @@ impl Pallet { ); } - let current_stake = Self::get_total_stake_for_hotkey(&hotkey); - // If we're full, we'll swap out the lowest stake member. - let members = T::SenateMembers::members(); - if (members.len() as u32) == T::SenateMembers::max_members() { - let mut sorted_members = members.clone(); - sorted_members.sort_by(|a, b| { - let a_stake = Self::get_total_stake_for_hotkey(a); - let b_stake = Self::get_total_stake_for_hotkey(b); - - b_stake.cmp(&a_stake) - }); - - if let Some(last) = sorted_members.last() { - let last_stake = Self::get_total_stake_for_hotkey(last); - - if last_stake < current_stake { - T::SenateMembers::swap_member(last, &hotkey)?; - T::TriumvirateInterface::remove_votes(last)?; - } - } - } else { - T::SenateMembers::add_member(&hotkey)?; - } + // --- 9. Join the Senate if eligible. + // Returns the replaced member, if any. + let _ = Self::join_senate_if_eligible(&hotkey)?; - // --- 13. Force all members on root to become a delegate. + // --- 10. Force all members on root to become a delegate. if !Self::hotkey_is_delegate(&hotkey) { Self::delegate_hotkey(&hotkey, 11_796); // 18% cut defaulted. } - // --- 14. Update the registration counters for both the block and interval. + // --- 11. Update the registration counters for both the block and interval. RegistrationsThisInterval::::mutate(root_netuid, |val| *val += 1); RegistrationsThisBlock::::mutate(root_netuid, |val| *val += 1); - // --- 15. Log and announce the successful registration. + // --- 12. Log and announce the successful registration. log::info!( "RootRegistered(netuid:{:?} uid:{:?} hotkey:{:?})", root_netuid, @@ -590,7 +570,7 @@ impl Pallet { ); Self::deposit_event(Event::NeuronRegistered(root_netuid, subnetwork_uid, hotkey)); - // --- 16. Finish and return success. + // --- 13. Finish and return success. Ok(()) } @@ -640,6 +620,67 @@ impl Pallet { .into()) } + // Checks if a hotkey should be a member of the Senate, and if so, adds them. + // + // # Arguments: + // * 'hotkey': The hotkey that the user wants to register to the root network. + // + // # Returns: + // * 'Result, Error>': A result containing the replaced member, if any. + // + fn join_senate_if_eligible(hotkey: &T::AccountId) -> Result, Error> { + // Get the root network UID. + let root_netuid: u16 = Self::get_root_netuid(); + + // --- 1. Check the hotkey is registered in the root network. + ensure!( + Uids::::contains_key(root_netuid, hotkey), + Error::::NotRegistered + ); + + // --- 2. Verify the hotkey is NOT already a member of the Senate. + ensure!( + !T::SenateMembers::is_member(hotkey), + Error::::AlreadySenateMember + ); + + // --- 3. Grab the hotkey's stake. + let current_stake = Self::get_total_stake_for_hotkey(hotkey); + + // Add the hotkey to the Senate. + // If we're full, we'll swap out the lowest stake member. + let members = T::SenateMembers::members(); + let last: Option<&T::AccountId> = None; + if (members.len() as u32) == T::SenateMembers::max_members() { + let mut sorted_members = members.clone(); + sorted_members.sort_by(|a, b| { + let a_stake = Self::get_total_stake_for_hotkey(a); + let b_stake = Self::get_total_stake_for_hotkey(b); + + b_stake.cmp(&a_stake) + }); + + if let Some(last) = sorted_members.last() { + let last_stake = Self::get_total_stake_for_hotkey(last); + + if last_stake < current_stake { + T::SenateMembers::swap_member(last, hotkey) + .map_err(|_| Error::::CouldNotJoinSenate)?; + T::TriumvirateInterface::remove_votes(last) + .map_err(|_| Error::::CouldNotJoinSenate)?; + } else { + // Not eligible to join the Senate. + return Ok(None); // Return early. Not an error, as we only join if eligible. + } + } + } else { + T::SenateMembers::add_member(hotkey).map_err(|_| Error::::CouldNotJoinSenate)?; + } + + // Return the swapped out member, if any. + Ok(last) + } + // Facilitates user registration of a new subnetwork. // // # Args: From a7ea8517c82d0084760ea59f678fc8f7573df97a Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 4 Jun 2024 21:29:40 -0400 Subject: [PATCH 07/12] add adjust senate call --- pallets/subtensor/src/lib.rs | 10 +++++- pallets/subtensor/src/root.rs | 66 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 4a7eba6ff..9c78fe461 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1006,7 +1006,7 @@ pub mod pallet { SenateMember, // --- Thrown when attempting to do something to a senate member that is limited NotSenateMember, // --- Thrown when a hotkey attempts to do something only senate members can do AlreadySenateMember, // --- Thrown when a hotkey attempts to join the senate while already being a member - BelowStakeThreshold, // --- Thrown when a hotkey attempts to join the senate without enough stake + BelowStakeThreshold, // --- Thrown when a hotkey attempts to join the senate with a stake below the threshold NotDelegate, // --- Thrown when a hotkey attempts to join the senate without being a delegate first IncorrectNetuidsLength, // --- Thrown when an incorrect amount of Netuids are passed as input FaucetDisabled, // --- Thrown when the faucet is disabled @@ -1706,6 +1706,14 @@ pub mod pallet { Self::do_root_register(origin, hotkey) } + #[pallet::call_index(63)] + #[pallet::weight((Weight::from_parts(0, 0) + .saturating_add(T::DbWeight::get().reads(0)) + .saturating_add(T::DbWeight::get().writes(0)), DispatchClass::Normal, Pays::No))] + pub fn adjust_senate(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { + Self::do_adjust_senate(origin, hotkey) + } + #[pallet::call_index(7)] #[pallet::weight((Weight::from_parts(89_000_000, 0) .saturating_add(T::DbWeight::get().reads(27)) diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index 8227d20ed..d3930b4be 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -620,6 +620,72 @@ impl Pallet { .into()) } + // Checks if a hotkey should be a member of the Senate, and if so, adds them. + // + // This function is responsible for adding a hotkey to the Senate if they meet the requirements. + // The root key with the least stake is pruned in the event of a filled membership. + // + // # Arguments: + // * 'origin': Represents the origin of the call. + // * 'hotkey': The hotkey that the user wants to register to the root network. + // + // # Returns: + // * 'DispatchResult': A result type indicating success or failure of the registration. + // + pub fn do_adjust_senate(origin: T::RuntimeOrigin, hotkey: T::AccountId) -> DispatchResult { + // --- 0. Get the unique identifier (UID) for the root network. + let root_netuid: u16 = Self::get_root_netuid(); + ensure!( + Self::if_subnet_exist(root_netuid), + Error::::NetworkDoesNotExist + ); + + // --- 1. Ensure that the call originates from a signed source and retrieve the caller's account ID (coldkey). + let coldkey = ensure_signed(origin)?; + log::info!( + "do_root_register( coldkey: {:?}, hotkey: {:?} )", + coldkey, + hotkey + ); + + // --- 2. Check if the hotkey is already registered to the root network. If not, error out. + ensure!( + Uids::::contains_key(root_netuid, &hotkey), + Error::::NotRegistered + ); + + // --- 3. Create a network account for the user if it doesn't exist. + Self::create_account_if_non_existent(&coldkey, &hotkey); + + // --- 4. Join the Senate if eligible. + // Returns the replaced member, if any. + let replaced = Self::join_senate_if_eligible(&hotkey)?; + + if replaced.is_none() { + // Not eligible to join the Senate, or no replacement needed. + // Check if the hotkey is *now* a member of the Senate. + // Otherwise, error out. + ensure!( + T::SenateMembers::is_member(&hotkey), + Error::::StakeTooLowForSenate, // Had less stake than the lowest stake incumbent. + ); + } + + // --- 5. Log and announce the successful Senate adjustment. + log::info!( + "SenateAdjusted(old_hotkey:{:?} hotkey:{:?})", + replaced, + hotkey + ); + Self::deposit_event(Event::SenateAdjusted { + old_member: replaced.cloned(), + new_member: hotkey, + }); + + // --- 6. Finish and return success. + Ok(()) + } + // Checks if a hotkey should be a member of the Senate, and if so, adds them. // // # Arguments: From 5ad6570283d115682198469efdd7aae426ddafa5 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 13 Jun 2024 21:47:55 -0400 Subject: [PATCH 08/12] chore: cargo fmt --- pallets/subtensor/src/errors.rs | 2 +- pallets/subtensor/src/events.rs | 8 ++++---- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/root.rs | 12 +++++------- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/errors.rs b/pallets/subtensor/src/errors.rs index 0aac35c3e..5ea6ccf2f 100644 --- a/pallets/subtensor/src/errors.rs +++ b/pallets/subtensor/src/errors.rs @@ -126,7 +126,7 @@ mod errors { CommitRevealEnabled, /// Attemtping to commit/reveal weights when disabled. CommitRevealDisabled, - /// Not able to join the senate. + /// Not able to join the senate. CouldNotJoinSenate, } } diff --git a/pallets/subtensor/src/events.rs b/pallets/subtensor/src/events.rs index 9b155ba93..86c7acd1f 100644 --- a/pallets/subtensor/src/events.rs +++ b/pallets/subtensor/src/events.rs @@ -132,11 +132,11 @@ mod events { MinDelegateTakeSet(u16), /// the target stakes per interval is set by sudo/admin transaction TargetStakesPerIntervalSet(u64), - /// a member of the senate is adjusted - SenateAdjusted { - /// the account ID of the old senate member, if any + /// a member of the senate is adjusted + SenateAdjusted { + /// the account ID of the old senate member, if any old_member: Option, - /// the account ID of the new senate member + /// the account ID of the new senate member new_member: T::AccountId, }, } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 608141e06..b4d21eae4 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1903,7 +1903,7 @@ pub mod pallet { Self::do_root_register(origin, hotkey) } - /// Attempt to adjust the senate membership to include a hotkey + /// Attempt to adjust the senate membership to include a hotkey #[pallet::call_index(63)] #[pallet::weight((Weight::from_parts(0, 0) .saturating_add(T::DbWeight::get().reads(0)) diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index e7be32aa5..6418323ee 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -554,7 +554,7 @@ impl Pallet { ); } - // --- 13. Join the Senate if eligible. + // --- 13. Join the Senate if eligible. // Returns the replaced member, if any. let _ = Self::join_senate_if_eligible(&hotkey)?; @@ -580,7 +580,6 @@ impl Pallet { Ok(()) } - // Checks if a hotkey should be a member of the Senate, and if so, adds them. // // This function is responsible for adding a hotkey to the Senate if they meet the requirements. @@ -677,7 +676,7 @@ impl Pallet { // Add the hotkey to the Senate. // If we're full, we'll swap out the lowest stake member. let members = T::SenateMembers::members(); - let last: Option<&T::AccountId> = None; + let last: Option<&T::AccountId> = None; if (members.len() as u32) == T::SenateMembers::max_members() { let mut sorted_members = members.clone(); sorted_members.sort_by(|a, b| { @@ -691,14 +690,13 @@ impl Pallet { let last_stake = Self::get_total_stake_for_hotkey(last); if last_stake < current_stake { - // Swap the member with the lowest stake. + // Swap the member with the lowest stake. T::SenateMembers::swap_member(last, hotkey) - .map_err(|_| Error::::CouldNotJoinSenate)?; + .map_err(|_| Error::::CouldNotJoinSenate)?; } } } else { - T::SenateMembers::add_member(hotkey) - .map_err(|_| Error::::CouldNotJoinSenate)?; + T::SenateMembers::add_member(hotkey).map_err(|_| Error::::CouldNotJoinSenate)?; } // Return the swapped out member, if any. From 51160f3a335ffc92efbba02d2fa7c6fdab2ebb5e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 13 Jun 2024 21:51:57 -0400 Subject: [PATCH 09/12] mock should match lib --- pallets/subtensor/tests/mock.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 9995acf84..b95497553 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -192,23 +192,29 @@ use pallet_subtensor::{CollectiveInterface, MemberManagement}; pub struct ManageSenateMembers; impl MemberManagement for ManageSenateMembers { fn add_member(account: &AccountId) -> DispatchResultWithPostInfo { - SenateMembers::add_member(RawOrigin::Root.into(), *account) + let who = *account; + SenateMembers::add_member(RawOrigin::Root.into(), who) } fn remove_member(account: &AccountId) -> DispatchResultWithPostInfo { - SenateMembers::remove_member(RawOrigin::Root.into(), *account) + let who = *account; + SenateMembers::remove_member(RawOrigin::Root.into(), who) } - fn swap_member(remove: &AccountId, add: &AccountId) -> DispatchResultWithPostInfo { - SenateMembers::swap_member(RawOrigin::Root.into(), *remove, *add) + fn swap_member(rm: &AccountId, add: &AccountId) -> DispatchResultWithPostInfo { + let remove = *rm; + let add = *add; + + Triumvirate::remove_votes(rm)?; + SenateMembers::swap_member(RawOrigin::Root.into(), remove, add) } fn is_member(account: &AccountId) -> bool { - Senate::is_member(account) + SenateMembers::members().contains(account) } fn members() -> Vec { - Senate::members() + SenateMembers::members().into() } fn max_members() -> u32 { From d9676447b92de4d70cde5ddac031fafbcc59b0ba Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Thu, 13 Jun 2024 21:52:05 -0400 Subject: [PATCH 10/12] add extra assert to test --- pallets/subtensor/tests/senate.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pallets/subtensor/tests/senate.rs b/pallets/subtensor/tests/senate.rs index 05fdfef45..e30b78086 100644 --- a/pallets/subtensor/tests/senate.rs +++ b/pallets/subtensor/tests/senate.rs @@ -486,7 +486,10 @@ fn test_senate_leave_vote_removal() { assert!( SubtensorModule::get_uid_for_net_and_hotkey(root_netuid, &hotkey_account_id).is_err() ); + // No longer a member of the senate + assert!(!Senate::is_member(&hotkey_account_id)); assert_eq!( + // Vote is removed Triumvirate::has_voted(hash, 0, &hotkey_account_id), Ok(false) ); From a570df6e13b7ba261194f07191554405db5b6cbd Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 19 Jun 2024 15:07:21 -0400 Subject: [PATCH 11/12] Pays Yes --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b4d21eae4..5201408b7 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1907,7 +1907,7 @@ pub mod pallet { #[pallet::call_index(63)] #[pallet::weight((Weight::from_parts(0, 0) .saturating_add(T::DbWeight::get().reads(0)) - .saturating_add(T::DbWeight::get().writes(0)), DispatchClass::Normal, Pays::No))] + .saturating_add(T::DbWeight::get().writes(0)), DispatchClass::Normal, Pays::Yes))] pub fn adjust_senate(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_adjust_senate(origin, hotkey) } From dc96aad8bc7d21638ef957cf0e4cc4732cff49b1 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Wed, 10 Jul 2024 13:46:36 -0400 Subject: [PATCH 12/12] fix clippy issue --- pallets/subtensor/src/root.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index ad764dc6a..e1bab02e0 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -577,7 +577,11 @@ impl Pallet { } // --- 15. Update the registration counters for both the block and interval. + #[allow(clippy::arithmetic_side_effects)] + // note this RA + clippy false positive is a known substrate issue RegistrationsThisInterval::::mutate(root_netuid, |val| *val += 1); + #[allow(clippy::arithmetic_side_effects)] + // note this RA + clippy false positive is a known substrate issue RegistrationsThisBlock::::mutate(root_netuid, |val| *val += 1); // --- 16. Log and announce the successful registration.