diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index 5f71626ad..dbf88bdfa 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -110,7 +110,7 @@ parameter_types! { pub const InitialSubnetLimit: u16 = 10; // Max 10 subnets. pub const InitialNetworkRateLimit: u64 = 0; pub const InitialTargetStakesPerInterval: u16 = 1; - pub const InitialHotkeySwapCost: u64 = 1_000_000_000; + pub const InitialKeySwapCost: u64 = 1_000_000_000; pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn @@ -166,7 +166,7 @@ impl pallet_subtensor::Config for Test { type InitialSubnetLimit = InitialSubnetLimit; type InitialNetworkRateLimit = InitialNetworkRateLimit; type InitialTargetStakesPerInterval = InitialTargetStakesPerInterval; - type HotkeySwapCost = InitialHotkeySwapCost; + type KeySwapCost = InitialKeySwapCost; type AlphaHigh = InitialAlphaHigh; type AlphaLow = InitialAlphaLow; type LiquidAlphaOn = InitialLiquidAlphaOn; diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 6da83c5af..80a375d10 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -429,30 +429,4 @@ reveal_weights { }: reveal_weights(RawOrigin::Signed(hotkey.clone()), netuid, uids, weight_values, salt, version_key) - schedule_coldkey_swap { - let seed: u32 = 1; - let old_coldkey: T::AccountId = account("OldColdkey", 0, seed); - let new_coldkey: T::AccountId = account("NewColdkey", 0, seed + 1); - let hotkey: T::AccountId = account("Hotkey", 0, seed); - - let netuid = 1u16; - let tempo = 1u16; - let block_number: u64 = Subtensor::::get_current_block_as_u64(); - let nonce = 0; - - // Initialize the network - Subtensor::::init_new_network(netuid, tempo); - Subtensor::::set_network_registration_allowed(netuid, true); - - // Add balance to the old coldkey account - let amount_to_be_staked: u64 = 1000000u32.into(); - Subtensor::::add_balance_to_coldkey_account(&old_coldkey.clone(), amount_to_be_staked+1000000000); - // Burned register the hotkey with the old coldkey - assert_ok!(Subtensor::::burned_register( - RawOrigin::Signed(old_coldkey.clone()).into(), - netuid, - hotkey.clone() - )); - - }: schedule_coldkey_swap(RawOrigin::Signed(old_coldkey.clone()), new_coldkey.clone(), vec![], block_number, nonce) } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 636bfd36c..aa7cafde0 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -247,7 +247,7 @@ pub mod pallet { type InitialTargetStakesPerInterval: Get; /// Cost of swapping a hotkey. #[pallet::constant] - type HotkeySwapCost: Get; + type KeySwapCost: Get; /// The upper bound for the alpha parameter. Used for Liquid Alpha. #[pallet::constant] type AlphaHigh: Get; @@ -2098,7 +2098,6 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { Self::do_swap_coldkey(origin, &new_coldkey) } - /// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey. /// /// # Arguments @@ -2114,6 +2113,7 @@ pub mod pallet { /// # Weight /// /// Weight is calculated based on the number of database reads and writes. + #[cfg(test)] #[pallet::call_index(72)] #[pallet::weight((Weight::from_parts(21_000_000, 0) .saturating_add(T::DbWeight::get().reads(3)) diff --git a/pallets/subtensor/src/swap.rs b/pallets/subtensor/src/swap.rs index 24d67aeb4..307aeb641 100644 --- a/pallets/subtensor/src/swap.rs +++ b/pallets/subtensor/src/swap.rs @@ -31,6 +31,7 @@ impl Pallet { new_hotkey: &T::AccountId, ) -> DispatchResultWithPostInfo { let coldkey = ensure_signed(origin)?; + ensure!( !Self::coldkey_in_arbitration(&coldkey), Error::::ColdkeyIsInArbitration @@ -60,6 +61,16 @@ impl Pallet { T::DbWeight::get().reads((TotalNetworks::::get().saturating_add(1u16)) as u64), ); + let swap_cost = Self::get_key_swap_cost(); + log::debug!("Swap cost: {:?}", swap_cost); + + ensure!( + Self::can_remove_balance_from_coldkey_account(&coldkey, swap_cost), + Error::::NotEnoughBalanceToPaySwapHotKey + ); + let actual_burn_amount = Self::remove_balance_from_coldkey_account(&coldkey, swap_cost)?; + Self::burn_tokens(actual_burn_amount); + Self::swap_owner(old_hotkey, new_hotkey, &coldkey, &mut weight); Self::swap_total_hotkey_stake(old_hotkey, new_hotkey, &mut weight); Self::swap_delegates(old_hotkey, new_hotkey, &mut weight); @@ -74,6 +85,7 @@ impl Pallet { Self::swap_loaded_emission(old_hotkey, new_hotkey, &netuid_is_member, &mut weight); Self::swap_uids(old_hotkey, new_hotkey, &netuid_is_member, &mut weight); Self::swap_prometheus(old_hotkey, new_hotkey, &netuid_is_member, &mut weight); + Self::swap_senate_member(old_hotkey, new_hotkey, &mut weight)?; Self::swap_total_hotkey_coldkey_stakes_this_interval(old_hotkey, new_hotkey, &mut weight); @@ -140,6 +152,20 @@ impl Pallet { Error::::ColdKeyAlreadyAssociated ); + // Calculate and charge the swap fee + let swap_cost = Self::get_key_swap_cost(); + log::debug!("Coldkey swap cost: {:?}", swap_cost); + + ensure!( + Self::can_remove_balance_from_coldkey_account(&old_coldkey, swap_cost), + Error::::NotEnoughBalanceToPaySwapColdKey + ); + let actual_burn_amount = + Self::remove_balance_from_coldkey_account(&old_coldkey, swap_cost)?; + Self::burn_tokens(actual_burn_amount); + + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + // Actually do the swap. weight = weight.saturating_add( Self::perform_swap_coldkey(&old_coldkey, new_coldkey) @@ -189,15 +215,15 @@ impl Pallet { /// /// This function calculates the remaining arbitration period by subtracting the current block number /// from the arbitration block number of the coldkey. - // pub fn get_remaining_arbitration_period(coldkey: &T::AccountId) -> u64 { - // let current_block: u64 = Self::get_current_block_as_u64(); - // let arbitration_block: u64 = ColdkeyArbitrationBlock::::get(coldkey); - // if arbitration_block > current_block { - // arbitration_block.saturating_sub(current_block) - // } else { - // 0 - // } - // } + pub fn get_remaining_arbitration_period(coldkey: &T::AccountId) -> u64 { + let current_block: u64 = Self::get_current_block_as_u64(); + let arbitration_block: u64 = ColdkeyArbitrationBlock::::get(coldkey); + if arbitration_block > current_block { + arbitration_block.saturating_sub(current_block) + } else { + 0 + } + } pub fn meets_min_allowed_coldkey_balance(coldkey: &T::AccountId) -> bool { let all_staked_keys: Vec = StakingHotkeys::::get(coldkey); @@ -948,4 +974,17 @@ impl Pallet { } weight.saturating_accrue(T::DbWeight::get().reads(TotalNetworks::::get() as u64)); } + + pub fn swap_senate_member( + old_hotkey: &T::AccountId, + new_hotkey: &T::AccountId, + weight: &mut Weight, + ) -> DispatchResult { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if T::SenateMembers::is_member(old_hotkey) { + T::SenateMembers::swap_member(old_hotkey, new_hotkey).map_err(|e| e.error)?; + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + } + Ok(()) + } } diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index 94f18dfbf..c61133e94 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -671,8 +671,8 @@ impl Pallet { NominatorMinRequiredStake::::put(min_stake); } - pub fn get_hotkey_swap_cost() -> u64 { - T::HotkeySwapCost::get() + pub fn get_key_swap_cost() -> u64 { + T::KeySwapCost::get() } pub fn get_alpha_values(netuid: u16) -> (u16, u16) { diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index f7ac00db6..fc784f46f 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -164,7 +164,7 @@ parameter_types! { pub const InitialSubnetLimit: u16 = 10; // Max 10 subnets. pub const InitialNetworkRateLimit: u64 = 0; pub const InitialTargetStakesPerInterval: u16 = 2; - pub const InitialHotkeySwapCost: u64 = 1_000_000_000; + pub const InitialKeySwapCost: u64 = 1_000_000_000; pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn @@ -375,7 +375,7 @@ impl pallet_subtensor::Config for Test { type InitialSubnetLimit = InitialSubnetLimit; type InitialNetworkRateLimit = InitialNetworkRateLimit; type InitialTargetStakesPerInterval = InitialTargetStakesPerInterval; - type HotkeySwapCost = InitialHotkeySwapCost; + type KeySwapCost = InitialKeySwapCost; type AlphaHigh = InitialAlphaHigh; type AlphaLow = InitialAlphaLow; type LiquidAlphaOn = InitialLiquidAlphaOn; diff --git a/pallets/subtensor/tests/swap.rs b/pallets/subtensor/tests/swap.rs index 90ebdfdcb..2355dc960 100644 --- a/pallets/subtensor/tests/swap.rs +++ b/pallets/subtensor/tests/swap.rs @@ -2,8 +2,8 @@ use codec::Encode; use frame_support::weights::Weight; -use frame_support::{assert_err, assert_ok}; -use frame_system::Config; +use frame_support::{assert_err, assert_noop, assert_ok}; +use frame_system::{Config, RawOrigin}; mod mock; use mock::*; use pallet_subtensor::*; @@ -167,6 +167,22 @@ fn test_do_swap_hotkey_ok_robust() { SubtensorModule::add_balance_to_coldkey_account(coldkey, swap_cost); } + // Add old_hotkeys[0] and old_hotkeys[1] to Senate + assert_ok!(SenateMembers::add_member( + RawOrigin::Root.into(), + old_hotkeys[0] + )); + assert_ok!(SenateMembers::add_member( + RawOrigin::Root.into(), + old_hotkeys[1] + )); + + // Verify initial Senate membership + assert!(Senate::is_member(&old_hotkeys[0])); + assert!(Senate::is_member(&old_hotkeys[1])); + assert!(!Senate::is_member(&new_hotkeys[0])); + assert!(!Senate::is_member(&new_hotkeys[1])); + // Perform the swaps for only two hotkeys assert_ok!(SubtensorModule::do_swap_hotkey( <::RuntimeOrigin>::signed(coldkeys[0]), @@ -268,6 +284,10 @@ fn test_do_swap_hotkey_ok_robust() { assert_eq!(Keys::::get(netuid, uid), new_hotkeys[i]); } } + + // Verify Senate membership swap + assert!(!Senate::is_member(&old_hotkeys[i])); + assert!(Senate::is_member(&new_hotkeys[i])); } else { // Ensure other hotkeys remain unchanged assert_eq!( @@ -278,6 +298,10 @@ fn test_do_swap_hotkey_ok_robust() { SubtensorModule::get_owning_coldkey_for_hotkey(&new_hotkeys[i]), coldkeys[i] ); + + // Verify Senate membership remains unchanged for other hotkeys + assert!(!Senate::is_member(&old_hotkeys[i])); + assert!(!Senate::is_member(&new_hotkeys[i])); } } } @@ -1059,7 +1083,8 @@ fn test_do_swap_coldkey_success() { let netuid = 1u16; let stake_amount1 = 1000u64; let stake_amount2 = 2000u64; - let free_balance_old = 12345u64 + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP; + let swap_cost = SubtensorModule::get_key_swap_cost(); + let free_balance_old = 12345u64 + swap_cost; // Setup initial state add_network(netuid, 13, 0); @@ -1158,7 +1183,7 @@ fn test_do_swap_coldkey_success() { // Verify balance transfer assert_eq!( SubtensorModule::get_coldkey_balance(&new_coldkey), - free_balance_old + free_balance_old - swap_cost ); assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); @@ -1332,6 +1357,7 @@ fn test_do_swap_coldkey_with_subnet_ownership() { let hotkey = U256::from(3); let netuid = 1u16; let stake_amount: u64 = 1000u64; + let swap_cost = SubtensorModule::get_key_swap_cost(); // Setup initial state add_network(netuid, 13, 0); @@ -1340,7 +1366,7 @@ fn test_do_swap_coldkey_with_subnet_ownership() { // Set TotalNetworks because swap relies on it pallet_subtensor::TotalNetworks::::set(1); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount + swap_cost); SubnetOwner::::insert(netuid, old_coldkey); // Populate OwnedHotkeys map @@ -1666,24 +1692,47 @@ fn test_coldkey_swap_total() { }); } -// #[test] -// fn test_coldkey_arbitrated_sw() { -// new_test_ext(1).execute_with(|| { -// let coldkey = U256::from(1); -// let hotkey = U256::from(2); -// let netuid = 1u16; - -// // Setup initial state -// add_network(netuid, 13, 0); -// register_ok_neuron(netuid, hotkey, coldkey, 0); - -// // Check if coldkey has associated hotkeys -// assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey)); - -// // Check for a coldkey without associated hotkeys -// let unassociated_coldkey = U256::from(3); -// assert!(!SubtensorModule::coldkey_has_associated_hotkeys( -// &unassociated_coldkey -// )); -// }); -// } +#[test] +fn test_swap_senate_member() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let non_member_hotkey = U256::from(3); + let mut weight = Weight::zero(); + + // Setup: Add old_hotkey as a Senate member + assert_ok!(SenateMembers::add_member( + RawOrigin::Root.into(), + old_hotkey + )); + + // Test 1: Successful swap + assert_ok!(SubtensorModule::swap_senate_member( + &old_hotkey, + &new_hotkey, + &mut weight + )); + assert!(Senate::is_member(&new_hotkey)); + assert!(!Senate::is_member(&old_hotkey)); + + // Verify weight update + let expected_weight = ::DbWeight::get().reads_writes(2, 2); + assert_eq!(weight, expected_weight); + + // Reset weight for next test + weight = Weight::zero(); + + // Test 2: Swap with non-member (should not change anything) + assert_ok!(SubtensorModule::swap_senate_member( + &non_member_hotkey, + &new_hotkey, + &mut weight + )); + assert!(Senate::is_member(&new_hotkey)); + assert!(!Senate::is_member(&non_member_hotkey)); + + // Verify weight update (should only have read operations) + let expected_weight = ::DbWeight::get().reads(1); + assert_eq!(weight, expected_weight); + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index de8be6e61..b343ad0f0 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -139,7 +139,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: 194, + spec_version: 162, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -312,8 +312,7 @@ impl Contains for SafeModeWhitelistedCalls { | RuntimeCall::SafeMode(_) | RuntimeCall::Timestamp(_) | RuntimeCall::SubtensorModule( - pallet_subtensor::Call::schedule_coldkey_swap { .. } - | pallet_subtensor::Call::set_weights { .. } + pallet_subtensor::Call::set_weights { .. } | pallet_subtensor::Call::set_root_weights { .. } | pallet_subtensor::Call::serve_axon { .. } ) @@ -877,7 +876,7 @@ parameter_types! { pub const SubtensorInitialNetworkLockReductionInterval: u64 = 14 * 7200; pub const SubtensorInitialNetworkRateLimit: u64 = 7200; pub const SubtensorInitialTargetStakesPerInterval: u16 = 1; - pub const SubtensorInitialHotkeySwapCost: u64 = 1_000_000_000; + pub const SubtensorInitialKeySwapCost: u64 = 1_000_000_000; pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn @@ -933,7 +932,7 @@ impl pallet_subtensor::Config for Runtime { type InitialSubnetLimit = SubtensorInitialSubnetLimit; type InitialNetworkRateLimit = SubtensorInitialNetworkRateLimit; type InitialTargetStakesPerInterval = SubtensorInitialTargetStakesPerInterval; - type HotkeySwapCost = SubtensorInitialHotkeySwapCost; + type KeySwapCost = SubtensorInitialKeySwapCost; type AlphaHigh = InitialAlphaHigh; type AlphaLow = InitialAlphaLow; type LiquidAlphaOn = InitialLiquidAlphaOn;