diff --git a/chain-extensions/src/mock.rs b/chain-extensions/src/mock.rs index 98ea096199..ee1b3d0099 100644 --- a/chain-extensions/src/mock.rs +++ b/chain-extensions/src/mock.rs @@ -325,9 +325,7 @@ parameter_types! { pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On - // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) - pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // Default as 1 day + pub const InitialColdKeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -397,8 +395,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = Preimage; - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/benchmarking.rs b/pallets/admin-utils/src/benchmarking.rs index 08589e530b..28f2b1d75d 100644 --- a/pallets/admin-utils/src/benchmarking.rs +++ b/pallets/admin-utils/src/benchmarking.rs @@ -480,7 +480,7 @@ mod benchmarks { } #[benchmark] - fn sudo_set_coldkey_swap_schedule_duration() { + fn sudo_set_coldkey_swap_announcement_delay() { #[extrinsic_call] _(RawOrigin::Root, 100u32.into()); } diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index de6ac5825b..a2967ac499 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1348,44 +1348,6 @@ pub mod pallet { res } - /// Sets the duration of the coldkey swap schedule. - /// - /// This extrinsic allows the root account to set the duration for the coldkey swap schedule. - /// The coldkey swap schedule determines how long it takes for a coldkey swap operation to complete. - /// - /// # Arguments - /// * `origin` - The origin of the call, which must be the root account. - /// * `duration` - The new duration for the coldkey swap schedule, in number of blocks. - /// - /// # Errors - /// * `BadOrigin` - If the caller is not the root account. - /// - /// # Weight - /// Weight is handled by the `#[pallet::weight]` attribute. - #[pallet::call_index(54)] - #[pallet::weight(( - Weight::from_parts(5_000_000, 0) - .saturating_add(T::DbWeight::get().reads(0_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)), - DispatchClass::Operational, - Pays::Yes - ))] - pub fn sudo_set_coldkey_swap_schedule_duration( - origin: OriginFor, - duration: BlockNumberFor, - ) -> DispatchResult { - // Ensure the call is made by the root account - ensure_root(origin)?; - - // Set the new duration of schedule coldkey swap - pallet_subtensor::Pallet::::set_coldkey_swap_schedule_duration(duration); - - // Log the change - log::trace!("ColdkeySwapScheduleDurationSet( duration: {duration:?} )"); - - Ok(()) - } - /// Sets the duration of the dissolve network schedule. /// /// This extrinsic allows the root account to set the duration for the dissolve network schedule. @@ -2213,6 +2175,25 @@ pub mod pallet { log::debug!("set_tao_flow_smoothing_factor( {smoothing_factor:?} ) "); Ok(()) } + + /// Sets the announcement delay for coldkey swap. + #[pallet::call_index(84)] + #[pallet::weight(( + Weight::from_parts(5_000_000, 0) + .saturating_add(T::DbWeight::get().reads(0_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn sudo_set_coldkey_swap_announcement_delay( + origin: OriginFor, + duration: BlockNumberFor, + ) -> DispatchResult { + ensure_root(origin)?; + pallet_subtensor::Pallet::::set_coldkey_swap_announcement_delay(duration); + log::trace!("ColdkeySwapScheduleDurationSet( duration: {duration:?} )"); + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 0140808baa..e49afe124f 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -131,17 +131,13 @@ parameter_types! { 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. - // pub const InitialSubnetLimit: u16 = 10; // (DEPRECATED) pub const InitialNetworkRateLimit: u64 = 0; 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 pub const InitialYuma3On: bool = false; // Default value for Yuma3On - // pub const InitialHotkeyEmissionTempo: u64 = 1; // (DEPRECATED) - // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) - pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // 1 day + pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -210,8 +206,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = (); - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 1aaefc8f8d..7ca9372385 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1382,7 +1382,7 @@ fn test_sudo_get_set_alpha() { } #[test] -fn test_sudo_set_coldkey_swap_schedule_duration() { +fn test_sudo_set_coldkey_swap_announcement_delay() { new_test_ext().execute_with(|| { // Arrange let root = RuntimeOrigin::root(); @@ -1391,12 +1391,12 @@ fn test_sudo_set_coldkey_swap_schedule_duration() { // Act & Assert: Non-root account should fail assert_noop!( - AdminUtils::sudo_set_coldkey_swap_schedule_duration(non_root, new_duration), + AdminUtils::sudo_set_coldkey_swap_announcement_delay(non_root, new_duration), DispatchError::BadOrigin ); // Act: Root account should succeed - assert_ok!(AdminUtils::sudo_set_coldkey_swap_schedule_duration( + assert_ok!(AdminUtils::sudo_set_coldkey_swap_announcement_delay( root.clone(), new_duration )); @@ -1408,7 +1408,7 @@ fn test_sudo_set_coldkey_swap_schedule_duration() { ); // Act & Assert: Setting the same value again should succeed (idempotent operation) - assert_ok!(AdminUtils::sudo_set_coldkey_swap_schedule_duration( + assert_ok!(AdminUtils::sudo_set_coldkey_swap_announcement_delay( root, new_duration )); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index dd7645e8ff..258f97915e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -943,17 +943,11 @@ pub mod pallet { pub fn DefaultAlphaValues() -> (u16, u16) { (45875, 58982) } - - /// Default value for coldkey swap schedule duration + + /// Default value for coldkey swap announcement delay. #[pallet::type_value] - pub fn DefaultColdkeySwapScheduleDuration() -> BlockNumberFor { - T::InitialColdkeySwapScheduleDuration::get() - } - - /// Default value for coldkey swap reschedule duration - #[pallet::type_value] - pub fn DefaultColdkeySwapRescheduleDuration() -> BlockNumberFor { - T::InitialColdkeySwapRescheduleDuration::get() + pub fn DefaultColdkeySwapAnnouncementDelay() -> BlockNumberFor { + T::InitialColdkeySwapAnnouncementDelay::get() } /// Default value for applying pending items (e.g. childkeys). @@ -1012,15 +1006,6 @@ pub mod pallet { 360 } - /// Default value for coldkey swap scheduled - #[pallet::type_value] - pub fn DefaultColdkeySwapScheduled() -> (BlockNumberFor, T::AccountId) { - #[allow(clippy::expect_used)] - let default_account = T::AccountId::decode(&mut TrailingZeroInput::zeroes()) - .expect("trailing zeroes always produce a valid account ID; qed"); - (BlockNumberFor::::from(0_u32), default_account) - } - /// Default value for setting subnet owner hotkey rate limit #[pallet::type_value] pub fn DefaultSetSNOwnerHotkeyRateLimit() -> u64 { @@ -1072,16 +1057,6 @@ pub mod pallet { pub type OwnerHyperparamRateLimit = StorageValue<_, u16, ValueQuery, DefaultOwnerHyperparamRateLimit>; - /// Duration of coldkey swap schedule before execution - #[pallet::storage] - pub type ColdkeySwapScheduleDuration = - StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapScheduleDuration>; - - /// Duration of coldkey swap reschedule before execution - #[pallet::storage] - pub type ColdkeySwapRescheduleDuration = - StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapRescheduleDuration>; - /// Duration of dissolve network schedule before execution #[pallet::storage] pub type DissolveNetworkScheduleDuration = @@ -1358,16 +1333,16 @@ pub mod pallet { ValueQuery, >; - /// --- DMAP ( cold ) --> (block_expected, new_coldkey), Maps coldkey to the block to swap at and new coldkey. + /// The delay after an announcement before a coldkey swap can be performed. #[pallet::storage] - pub type ColdkeySwapScheduled = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - (BlockNumberFor, T::AccountId), - ValueQuery, - DefaultColdkeySwapScheduled, - >; + pub type ColdkeySwapAnnouncementDelay = + StorageValue<_, BlockNumberFor, ValueQuery, DefaultColdkeySwapAnnouncementDelay>; + + /// A map of the coldkey swap announcements from a coldkey + /// to the block number the announcement was made and the new coldkey. + #[pallet::storage] + pub type ColdkeySwapAnnouncements = + StorageMap<_, Twox64Concat, T::AccountId, (BlockNumberFor, T::Hash), OptionQuery>; /// --- DMAP ( hot, netuid ) --> alpha | Returns the total amount of alpha a hotkey owns. #[pallet::storage] @@ -2427,7 +2402,7 @@ pub mod pallet { #[derive(Debug, PartialEq)] pub enum CustomTransactionError { - ColdkeyInSwapSchedule, + ColdkeySwapAnnounced, StakeAmountTooLow, BalanceTooLow, SubnetNotExists, @@ -2454,7 +2429,7 @@ pub enum CustomTransactionError { impl From for u8 { fn from(variant: CustomTransactionError) -> u8 { match variant { - CustomTransactionError::ColdkeyInSwapSchedule => 0, + CustomTransactionError::ColdkeySwapAnnounced => 0, CustomTransactionError::StakeAmountTooLow => 1, CustomTransactionError::BalanceTooLow => 2, CustomTransactionError::SubnetNotExists => 3, diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index a735bde1e1..6f8b5466ee 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -220,10 +220,7 @@ mod config { // type InitialHotkeyEmissionTempo: Get; /// Coldkey swap schedule duartion. #[pallet::constant] - type InitialColdkeySwapScheduleDuration: Get>; - /// Coldkey swap reschedule duration. - #[pallet::constant] - type InitialColdkeySwapRescheduleDuration: Get>; + type InitialColdkeySwapAnnouncementDelay: Get>; /// Dissolve network schedule duration #[pallet::constant] type InitialDissolveNetworkScheduleDuration: Get>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index edf561810e..178565a8fe 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -6,11 +6,10 @@ use frame_support::pallet_macros::pallet_section; #[pallet_section] mod dispatches { use crate::subnets::leasing::SubnetLeasingWeightInfo; - use frame_support::traits::schedule::DispatchTime; use frame_support::traits::schedule::v3::Anon as ScheduleAnon; use frame_system::pallet_prelude::BlockNumberFor; use sp_core::ecdsa::Signature; - use sp_runtime::{Percent, traits::Saturating}; + use sp_runtime::{Percent, traits::Hash}; use crate::MAX_CRV3_COMMIT_SIZE_BYTES; use crate::MAX_NUM_ROOT_CLAIMS; @@ -1068,34 +1067,17 @@ mod dispatches { /// The extrinsic for user to change the coldkey associated with their account. /// - /// # Arguments - /// - /// * `origin` - The origin of the call, must be signed by the old coldkey. - /// * `old_coldkey` - The current coldkey associated with the account. - /// * `new_coldkey` - The new coldkey to be associated with the account. - /// - /// # Returns - /// - /// Returns a `DispatchResultWithPostInfo` indicating success or failure of the operation. - /// - /// # Weight - /// - /// Weight is calculated based on the number of database reads and writes. + /// WARNING: This is deprecated in favor of `announce_coldkey_swap`/`coldkey_swap` #[pallet::call_index(71)] - #[pallet::weight((Weight::from_parts(161_700_000, 0) - .saturating_add(T::DbWeight::get().reads(16_u64)) - .saturating_add(T::DbWeight::get().writes(9)), DispatchClass::Operational, Pays::Yes))] + #[pallet::weight(Weight::zero())] + #[deprecated(note = "Deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap`")] pub fn swap_coldkey( - origin: OriginFor, - old_coldkey: T::AccountId, - new_coldkey: T::AccountId, - swap_cost: TaoCurrency, - ) -> DispatchResultWithPostInfo { - // Ensure it's called with root privileges (scheduler has root privileges) - ensure_root(origin)?; - log::debug!("swap_coldkey: {:?} -> {:?}", old_coldkey, new_coldkey); - - Self::do_swap_coldkey(&old_coldkey, &new_coldkey, swap_cost) + _origin: OriginFor, + _old_coldkey: T::AccountId, + _new_coldkey: T::AccountId, + _swap_cost: TaoCurrency, + ) -> DispatchResult { + Err(Error::::Deprecated.into()) } /// Sets the childkey take for a given hotkey. @@ -1332,94 +1314,15 @@ mod dispatches { /// Schedules a coldkey swap operation to be executed at a future block. /// - /// This function allows a user to schedule the swapping of their coldkey to a new one - /// at a specified future block. The swap is not executed immediately but is scheduled - /// to occur at the specified block number. - /// - /// # Arguments - /// - /// * `origin` - The origin of the call, which should be signed by the current coldkey owner. - /// * `new_coldkey` - The account ID of the new coldkey that will replace the current one. - /// * `when` - The block number at which the coldkey swap should be executed. - /// - /// # Returns - /// - /// Returns a `DispatchResultWithPostInfo` indicating whether the scheduling was successful. - /// - /// # Errors - /// - /// This function may return an error if: - /// * The origin is not signed. - /// * The scheduling fails due to conflicts or system constraints. - /// - /// # Notes - /// - /// - The actual swap is not performed by this function. It merely schedules the swap operation. - /// - The weight of this call is set to a fixed value and may need adjustment based on benchmarking. - /// - /// # TODO - /// - /// - Implement proper weight calculation based on the complexity of the operation. - /// - Consider adding checks to prevent scheduling too far into the future. - /// TODO: Benchmark this call + /// WARNING: This function is deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap` #[pallet::call_index(73)] - #[pallet::weight((Weight::from_parts(37_830_000, 0) - .saturating_add(T::DbWeight::get().reads(4)) - .saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::Yes))] + #[pallet::weight(Weight::zero())] + #[deprecated(note = "Deprecated, please migrate to `announce_coldkey_swap`/`coldkey_swap`")] pub fn schedule_swap_coldkey( - origin: OriginFor, - new_coldkey: T::AccountId, - ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin)?; - let current_block = >::block_number(); - - // If the coldkey has a scheduled swap, check if we can reschedule it - if ColdkeySwapScheduled::::contains_key(&who) { - let (scheduled_block, _scheduled_coldkey) = ColdkeySwapScheduled::::get(&who); - let reschedule_duration = ColdkeySwapRescheduleDuration::::get(); - let redo_when = scheduled_block.saturating_add(reschedule_duration); - ensure!(redo_when <= current_block, Error::::SwapAlreadyScheduled); - } - - // Calculate the swap cost and ensure sufficient balance - let swap_cost = Self::get_key_swap_cost(); - ensure!( - Self::can_remove_balance_from_coldkey_account(&who, swap_cost.into()), - Error::::NotEnoughBalanceToPaySwapColdKey - ); - - let current_block: BlockNumberFor = >::block_number(); - let duration: BlockNumberFor = ColdkeySwapScheduleDuration::::get(); - let when: BlockNumberFor = current_block.saturating_add(duration); - - let call = Call::::swap_coldkey { - old_coldkey: who.clone(), - new_coldkey: new_coldkey.clone(), - swap_cost, - }; - - let bound_call = ::Preimages::bound(LocalCallOf::::from(call.clone())) - .map_err(|_| Error::::FailedToSchedule)?; - - T::Scheduler::schedule( - DispatchTime::At(when), - None, - 63, - frame_system::RawOrigin::Root.into(), - bound_call, - ) - .map_err(|_| Error::::FailedToSchedule)?; - - ColdkeySwapScheduled::::insert(&who, (when, new_coldkey.clone())); - // Emit the SwapScheduled event - Self::deposit_event(Event::ColdkeySwapScheduled { - old_coldkey: who.clone(), - new_coldkey: new_coldkey.clone(), - execution_block: when, - swap_cost, - }); - - Ok(().into()) + _origin: OriginFor, + _new_coldkey: T::AccountId, + ) -> DispatchResult { + Err(Error::::Deprecated.into()) } /// ---- Set prometheus information for the neuron. @@ -2431,5 +2334,62 @@ mod dispatches { Ok(()) } + + /// Announces a coldkey swap using coldkey hash. + /// This is required before the coldkey swap can be performed after the delay period. + #[pallet::call_index(125)] + #[pallet::weight(Weight::zero())] + pub fn announce_coldkey_swap( + origin: OriginFor, + new_coldkey_hash: T::Hash, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let now = >::block_number(); + + if let Some(existing) = ColdkeySwapAnnouncements::::get(who.clone()) { + let delay = ColdkeySwapAnnouncementDelay::::get(); + let when = existing.0; + ensure!( + now > when + delay, + Error::::ColdKeySwapReannouncedTooEarly + ); + } + + ColdkeySwapAnnouncements::::insert(who.clone(), (now, new_coldkey_hash.clone())); + + Self::deposit_event(Event::ColdkeySwapAnnounced { + who: who.clone(), + new_coldkey_hash: new_coldkey_hash.clone(), + block_number: now, + }); + Ok(()) + } + + /// Performs a coldkey swap iff an announcement has been made. + /// The provided new coldkey must match the announced coldkey hash. + #[pallet::call_index(126)] + #[pallet::weight(Weight::zero())] + pub fn swap_coldkey_announced( + origin: OriginFor, + new_coldkey: T::AccountId, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + let (when, new_coldkey_hash) = ColdkeySwapAnnouncements::::take(who.clone()) + .ok_or(Error::::ColdKeySwapAnnouncementNotFound)?; + + ensure!( + new_coldkey_hash == T::Hashing::hash_of(&new_coldkey), + Error::::AnnouncedColdkeyHashDoesNotMatch + ); + + let now = >::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get(); + ensure!(now > when + delay, Error::::ColdKeySwapTooEarly); + + Self::do_swap_coldkey(&who, &new_coldkey)?; + + Ok(()) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 5a15330075..710d9c4487 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -138,8 +138,6 @@ mod errors { ColdKeyAlreadyAssociated, /// The coldkey balance is not enough to pay for the swap NotEnoughBalanceToPaySwapColdKey, - /// The coldkey is in arbitration - ColdkeyIsInArbitration, /// Attempting to set an invalid child for a hotkey on a network. InvalidChild, /// Duplicate child when setting children. @@ -150,10 +148,14 @@ mod errors { TooManyChildren, /// Default transaction rate limit exceeded. TxRateLimitExceeded, - /// Swap already scheduled. - SwapAlreadyScheduled, - /// failed to swap coldkey - FailedToSchedule, + /// Coldkey swap announcement not found + ColdKeySwapAnnouncementNotFound, + /// Coldkey swap too early. + ColdKeySwapTooEarly, + /// Coldkey swap reannounced too early. + ColdKeySwapReannouncedTooEarly, + /// The announced coldkey hash does not match the new coldkey hash. + AnnouncedColdkeyHashDoesNotMatch, /// New coldkey is hotkey NewColdKeyIsHotkey, /// Childkey take is invalid. @@ -266,5 +268,7 @@ mod errors { InvalidRootClaimThreshold, /// Exceeded subnet limit number or zero. InvalidSubnetNumber, + /// Deprecated call. + Deprecated, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index d015205d4d..4babd7f0a8 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -170,6 +170,20 @@ mod events { MaxDelegateTakeSet(u16), /// minimum delegate take is set by sudo/admin transaction MinDelegateTakeSet(u16), + /// A coldkey swap announcement has been made. + ColdkeySwapAnnounced { + /// The account ID of the coldkey that made the announcement. + who: T::AccountId, + /// The hash of the new coldkey. + new_coldkey_hash: T::Hash, + /// The block number the announcement was made. + block_number: BlockNumberFor, + }, + /// A coldkey swap announcement has been removed. + ColdkeySwapAnnouncementRemoved { + /// The account ID of the coldkey that made the announcement. + who: T::AccountId, + }, /// A coldkey has been swapped ColdkeySwapped { /// the account ID of old coldkey @@ -190,17 +204,6 @@ mod events { ::AccountId, >>::Balance, }, - /// A coldkey swap has been scheduled - ColdkeySwapScheduled { - /// The account ID of the old coldkey - old_coldkey: T::AccountId, - /// The account ID of the new coldkey - new_coldkey: T::AccountId, - /// The arbitration block for the coldkey swap - execution_block: BlockNumberFor, - /// The swap cost - swap_cost: TaoCurrency, - }, /// The arbitration period has been extended ArbitrationPeriodExtended { /// The account ID of the coldkey @@ -222,15 +225,15 @@ mod events { SubnetIdentityRemoved(NetUid), /// A dissolve network extrinsic scheduled. DissolveNetworkScheduled { - /// The account ID schedule the dissolve network extrisnic + /// The account ID schedule the dissolve network extrinsic account: T::AccountId, /// network ID will be dissolved netuid: NetUid, /// extrinsic execution block number execution_block: BlockNumberFor, }, - /// The duration of schedule coldkey swap has been set - ColdkeySwapScheduleDurationSet(BlockNumberFor), + /// The coldkey swap announcement delay has been set. + ColdkeySwapAnnouncementDelaySet(BlockNumberFor), /// The duration of dissolve network has been set DissolveNetworkScheduleDurationSet(BlockNumberFor), /// Commit-reveal v3 weights have been successfully committed. diff --git a/pallets/subtensor/src/macros/genesis.rs b/pallets/subtensor/src/macros/genesis.rs index 7bf8ba2a53..6924e04511 100644 --- a/pallets/subtensor/src/macros/genesis.rs +++ b/pallets/subtensor/src/macros/genesis.rs @@ -101,7 +101,6 @@ mod genesis { netuid, U64F64::saturating_from_num(1_000_000_000), ); - // TotalColdkeyAlpha::::insert(hotkey.clone(), netuid, 1_000_000_000); SubnetAlphaOut::::insert(netuid, AlphaCurrency::from(1_000_000_000)); let mut staking_hotkeys = StakingHotkeys::::get(hotkey.clone()); if !staking_hotkeys.contains(&hotkey) { diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index c81138b58c..491daf9c98 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -1,228 +1,130 @@ use super::*; -use frame_support::weights::Weight; -use sp_core::Get; use substrate_fixed::types::U64F64; impl Pallet { - /// Swaps the coldkey associated with a set of hotkeys from an old coldkey to a new coldkey. - /// - /// # Arguments - /// - /// * `origin` - The origin of the call, which must be signed by the old coldkey. - /// * `new_coldkey` - The account ID of the new coldkey. - /// - /// # Returns - /// - /// Returns a `DispatchResultWithPostInfo` indicating success or failure, along with the weight consumed. - /// - /// # Errors - /// - /// This function will return an error if: - /// - The caller is not a valid signed origin. - /// - The old coldkey (caller) is in arbitration. - /// - The new coldkey is already associated with other hotkeys or is a hotkey itself. - /// - There's not enough balance to pay for the swap. - /// - /// # Events - /// - /// Emits a `ColdkeySwapped` event when successful. - /// - /// # Weight - /// - /// Weight is tracked and updated throughout the function execution. + /// Transfer all assets, stakes, subnet ownerships, and hotkey associations from `old_coldkey` to + /// to `new_coldkey`. pub fn do_swap_coldkey( old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, - swap_cost: TaoCurrency, - ) -> DispatchResultWithPostInfo { - // 2. Initialize the weight for this operation - let mut weight: Weight = T::DbWeight::get().reads(2); - // 3. Ensure the new coldkey is not associated with any hotkeys + ) -> DispatchResult { ensure!( - StakingHotkeys::::get(new_coldkey).is_empty(), + StakingHotkeys::::get(&new_coldkey).is_empty(), Error::::ColdKeyAlreadyAssociated ); - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - - // 4. Ensure the new coldkey is not a hotkey ensure!( - !Self::hotkey_account_exists(new_coldkey), + !Self::hotkey_account_exists(&new_coldkey), Error::::NewColdKeyIsHotkey ); - weight = weight.saturating_add(T::DbWeight::get().reads(1)); - - // 5. Swap the identity if the old coldkey has one - if let Some(identity) = IdentitiesV2::::take(old_coldkey) { - IdentitiesV2::::insert(new_coldkey, identity); - } - // 6. Ensure sufficient balance for the swap cost + // Remove and recycle the swap cost from the old coldkey's account + let swap_cost = Self::get_key_swap_cost(); ensure!( Self::can_remove_balance_from_coldkey_account(old_coldkey, swap_cost.into()), Error::::NotEnoughBalanceToPaySwapColdKey ); + let burn_amount = Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; + Self::recycle_tao(burn_amount); + + // Swap the identity if the old coldkey has one and the new coldkey doesn't + if IdentitiesV2::::get(new_coldkey).is_none() + && let Some(identity) = IdentitiesV2::::take(old_coldkey) + { + IdentitiesV2::::insert(new_coldkey.clone(), identity); + } - // 7. Remove and recycle the swap cost from the old coldkey's account - let actual_burn_amount = - Self::remove_balance_from_coldkey_account(old_coldkey, swap_cost.into())?; - Self::recycle_tao(actual_burn_amount); - - // 8. Update the weight for the balance operations - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - - // 9. Perform the actual coldkey swap - let _ = Self::perform_swap_coldkey(old_coldkey, new_coldkey, &mut weight); + for netuid in Self::get_all_subnet_netuids() { + Self::transfer_subnet_ownership(netuid, old_coldkey, &new_coldkey); + Self::transfer_auto_stake_destination(netuid, old_coldkey, &new_coldkey); + Self::transfer_coldkey_stake(netuid, old_coldkey, &new_coldkey); + } + Self::transfer_staking_hotkeys(old_coldkey, &new_coldkey); + Self::transfer_hotkeys_ownership(old_coldkey, &new_coldkey); - // 10. Update the last transaction block for the new coldkey - Self::set_last_tx_block(new_coldkey, Self::get_current_block_as_u64()); - weight.saturating_accrue(T::DbWeight::get().writes(1)); + // Transfer any remaining balance from old_coldkey to new_coldkey + let remaining_balance = Self::get_coldkey_balance(old_coldkey); + if remaining_balance > 0 { + Self::kill_coldkey_account(old_coldkey, remaining_balance)?; + Self::add_balance_to_coldkey_account(&new_coldkey, remaining_balance); + } - // 11. Remove the coldkey swap scheduled record - ColdkeySwapScheduled::::remove(old_coldkey); + Self::set_last_tx_block(&new_coldkey, Self::get_current_block_as_u64()); - // 12. Emit the ColdkeySwapped event Self::deposit_event(Event::ColdkeySwapped { old_coldkey: old_coldkey.clone(), new_coldkey: new_coldkey.clone(), swap_cost, }); - - // 12. Return the result with the updated weight - Ok(Some(weight).into()) + Ok(()) } - /// Performs the actual coldkey swap operation, transferring all associated data and balances from the old coldkey to the new coldkey. - /// - /// # Arguments - /// - /// * `old_coldkey` - The account ID of the old coldkey. - /// * `new_coldkey` - The account ID of the new coldkey. - /// * `weight` - A mutable reference to the current transaction weight. - /// - /// # Returns - /// - /// Returns a `DispatchResult` indicating success or failure of the operation. - /// - /// # Steps - /// - /// 1. Swap TotalHotkeyColdkeyStakesThisInterval: - /// - For each hotkey owned by the old coldkey, transfer its stake and block data to the new coldkey. - /// - /// 2. Swap subnet ownership: - /// - For each subnet, if the old coldkey is the owner, transfer ownership to the new coldkey. - /// - /// 3. Swap Stakes: - /// - For each hotkey staking for the old coldkey, transfer its stake to the new coldkey. - /// - /// 4. Swap total coldkey stake: - /// - Transfer the total stake from the old coldkey to the new coldkey. - /// - /// 5. Swap StakingHotkeys: - /// - Transfer the list of staking hotkeys from the old coldkey to the new coldkey. - /// - /// 6. Swap hotkey owners: - /// - For each hotkey owned by the old coldkey, transfer ownership to the new coldkey. - /// - Update the list of owned hotkeys for both old and new coldkeys. - /// - /// 7. Transfer remaining balance: - /// - Transfer any remaining balance from the old coldkey to the new coldkey. - /// - /// Throughout the process, the function updates the transaction weight to reflect the operations performed. - /// - /// # Notes - /// - /// This function is a critical part of the coldkey swap process and should be called only after all necessary checks and validations have been performed. - pub fn perform_swap_coldkey( + /// Transfer the ownership of the subnet to the new coldkey if it is owned by the old coldkey. + fn transfer_subnet_ownership( + netuid: NetUid, old_coldkey: &T::AccountId, new_coldkey: &T::AccountId, - weight: &mut Weight, - ) -> DispatchResult { - // 1. Swap TotalHotkeyColdkeyStakesThisInterval - // TotalHotkeyColdkeyStakesThisInterval: MAP ( hotkey, coldkey ) --> ( stake, block ) | Stake of the hotkey for the coldkey. - // for hotkey in OwnedHotkeys::::get(old_coldkey).iter() { - // let (stake, block) = - // TotalHotkeyColdkeyStakesThisInterval::::get(&hotkey, old_coldkey); - // TotalHotkeyColdkeyStakesThisInterval::::remove(&hotkey, old_coldkey); - // TotalHotkeyColdkeyStakesThisInterval::::insert(&hotkey, new_coldkey, (stake, block)); - // weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); - // } (DEPRECATED) - - // 2. Swap subnet owner. - // SubnetOwner: MAP ( netuid ) --> (coldkey) | Owner of the subnet. - for netuid in Self::get_all_subnet_netuids() { - let subnet_owner = SubnetOwner::::get(netuid); - if subnet_owner == *old_coldkey { - SubnetOwner::::insert(netuid, new_coldkey.clone()); - } - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + ) { + let subnet_owner = SubnetOwner::::get(netuid); + if subnet_owner == *old_coldkey { + SubnetOwner::::insert(netuid, new_coldkey.clone()); + } + } - if let Some(old_auto_stake_hotkey) = AutoStakeDestination::::get(old_coldkey, netuid) - { - AutoStakeDestination::::remove(old_coldkey, netuid); - AutoStakeDestination::::insert( - new_coldkey, - netuid, - old_auto_stake_hotkey.clone(), - ); - AutoStakeDestinationColdkeys::::mutate(old_auto_stake_hotkey, netuid, |v| { - // Remove old/new coldkeys (avoid duplicates), then add the new one. - v.retain(|c| *c != *old_coldkey && *c != *new_coldkey); - v.push(new_coldkey.clone()); - }); - } + /// Transfer the auto stake destination from the old coldkey to the new coldkey if it is set. + fn transfer_auto_stake_destination( + netuid: NetUid, + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) { + if let Some(old_auto_stake_hotkey) = AutoStakeDestination::::get(old_coldkey, netuid) { + AutoStakeDestination::::remove(old_coldkey, netuid); + AutoStakeDestination::::insert(new_coldkey, netuid, old_auto_stake_hotkey.clone()); + AutoStakeDestinationColdkeys::::mutate(old_auto_stake_hotkey, netuid, |v| { + // Remove old/new coldkeys (avoid duplicates), then add the new one. + v.retain(|c| *c != *old_coldkey && *c != *new_coldkey); + v.push(new_coldkey.clone()); + }); } + } - // 3. Swap Stake. - // StakingHotkeys: MAP ( coldkey ) --> Vec( hotkey ) + /// Transfer the stake of all staking hotkeys linked to the old coldkey to the new coldkey. + fn transfer_coldkey_stake( + netuid: NetUid, + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) { for hotkey in StakingHotkeys::::get(old_coldkey) { - // 3.1 Swap Alpha - for netuid in Self::get_all_subnet_netuids() { - // Get the stake on the old (hot,coldkey) account. - let old_alpha: U64F64 = Alpha::::get((&hotkey, old_coldkey, netuid)); - // Get the stake on the new (hot,coldkey) account. - let new_alpha: U64F64 = Alpha::::get((&hotkey, new_coldkey, netuid)); - // Add the stake to new account. - Alpha::::insert( - (&hotkey, new_coldkey, netuid), - new_alpha.saturating_add(old_alpha), + // Get the stake on the old (hot,coldkey) account. + let old_alpha: U64F64 = Alpha::::get((&hotkey, old_coldkey, netuid)); + // Get the stake on the new (hot,coldkey) account. + let new_alpha: U64F64 = Alpha::::get((&hotkey, new_coldkey, netuid)); + // Add the stake to new account. + Alpha::::insert( + (&hotkey, new_coldkey, netuid), + new_alpha.saturating_add(old_alpha), + ); + // Remove the value from the old account. + Alpha::::remove((&hotkey, old_coldkey, netuid)); + + if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { + Self::transfer_root_claimed_for_new_keys( + netuid, + &hotkey, + &hotkey, + old_coldkey, + new_coldkey, ); - // Remove the value from the old account. - Alpha::::remove((&hotkey, old_coldkey, netuid)); - - if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { - Self::transfer_root_claimed_for_new_keys( - netuid, - &hotkey, - &hotkey, - old_coldkey, - new_coldkey, - ); - if netuid == NetUid::ROOT { - // Register new coldkey with root stake - Self::maybe_add_coldkey_index(new_coldkey); - } + if netuid == NetUid::ROOT { + // Register new coldkey with root stake + Self::maybe_add_coldkey_index(new_coldkey); } } - // Add the weight for the read and write. - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } + } - // 4. Swap TotalColdkeyAlpha (DEPRECATED) - // for netuid in Self::get_all_subnet_netuids() { - // let old_alpha_stake: u64 = TotalColdkeyAlpha::::get(old_coldkey, netuid); - // let new_alpha_stake: u64 = TotalColdkeyAlpha::::get(new_coldkey, netuid); - // TotalColdkeyAlpha::::insert( - // new_coldkey, - // netuid, - // new_alpha_stake.saturating_add(old_alpha_stake), - // ); - // TotalColdkeyAlpha::::remove(old_coldkey, netuid); - // } - // weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); - - // 5. Swap StakingHotkeys. - // StakingHotkeys: MAP ( coldkey ) --> Vec | Hotkeys staking for the coldkey. + /// Transfer staking hotkeys from the old coldkey to the new coldkey. + fn transfer_staking_hotkeys(old_coldkey: &T::AccountId, new_coldkey: &T::AccountId) { let old_staking_hotkeys: Vec = StakingHotkeys::::get(old_coldkey); let mut new_staking_hotkeys: Vec = StakingHotkeys::::get(new_coldkey); for hotkey in old_staking_hotkeys { @@ -231,13 +133,13 @@ impl Pallet { new_staking_hotkeys.push(hotkey); } } + StakingHotkeys::::remove(old_coldkey); StakingHotkeys::::insert(new_coldkey, new_staking_hotkeys); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + } - // 6. Swap hotkey owners. - // Owner: MAP ( hotkey ) --> coldkey | Owner of the hotkey. - // OwnedHotkeys: MAP ( coldkey ) --> Vec | Hotkeys owned by the coldkey. + /// Transfer the ownership of the hotkeys owned by the old coldkey to the new coldkey. + fn transfer_hotkeys_ownership(old_coldkey: &T::AccountId, new_coldkey: &T::AccountId) { let old_owned_hotkeys: Vec = OwnedHotkeys::::get(old_coldkey); let mut new_owned_hotkeys: Vec = OwnedHotkeys::::get(new_coldkey); for owned_hotkey in old_owned_hotkeys.iter() { @@ -252,19 +154,5 @@ impl Pallet { } OwnedHotkeys::::remove(old_coldkey); OwnedHotkeys::::insert(new_coldkey, new_owned_hotkeys); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); - - // 7. Transfer remaining balance. - // Balance: MAP ( coldkey ) --> u64 | Balance of the coldkey. - // Transfer any remaining balance from old_coldkey to new_coldkey - let remaining_balance = Self::get_coldkey_balance(old_coldkey); - if remaining_balance > 0 { - Self::kill_coldkey_account(old_coldkey, remaining_balance)?; - Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance); - } - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); - - // Return ok. - Ok(()) } } diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs index b910fa1e83..0158579553 100644 --- a/pallets/subtensor/src/tests/claim_root.rs +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -1184,13 +1184,9 @@ fn test_claim_root_with_swap_coldkey() { ); // Swap coldkey - let mut weight = Weight::zero(); - - assert_ok!(SubtensorModule::perform_swap_coldkey( - &coldkey, - &new_coldkey, - &mut weight - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); // Check swapped keys claimed values diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 090fcf8f75..1d17b38cb9 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -212,9 +212,7 @@ parameter_types! { pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On - // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) - pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // Default as 1 day + pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -284,8 +282,7 @@ impl crate::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = Preimage; - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 9d3bdbfc62..367dda4927 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -8,16 +8,16 @@ use approx::assert_abs_diff_eq; use codec::Encode; -use frame_support::dispatch::DispatchInfo; +use frame_support::dispatch::{DispatchInfo, GetDispatchInfo}; use frame_support::error::BadOrigin; use frame_support::traits::OnInitialize; use frame_support::traits::schedule::DispatchTime; use frame_support::traits::schedule::v3::Named as ScheduleNamed; -use frame_support::weights::Weight; use frame_support::{assert_err, assert_noop, assert_ok}; use frame_system::{Config, RawOrigin}; use sp_core::{Get, H256, U256}; -use sp_runtime::traits::{DispatchInfoOf, TransactionExtension}; +use sp_runtime::traits::Hash; +use sp_runtime::traits::{DispatchInfoOf, DispatchTransaction, TransactionExtension}; use sp_runtime::{DispatchError, traits::TxBaseImplication}; use substrate_fixed::types::U96F32; use subtensor_runtime_common::{AlphaCurrency, Currency, SubnetInfo, TaoCurrency}; @@ -27,335 +27,581 @@ use super::mock; use super::mock::*; use crate::transaction_extension::SubtensorTransactionExtension; use crate::*; -use crate::{Call, ColdkeySwapScheduleDuration, Error}; -// // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_total_hotkey_coldkey_stakes_this_interval --exact --nocapture -// #[test] -// fn test_swap_total_hotkey_coldkey_stakes_this_interval() { -// new_test_ext(1).execute_with(|| { -// let old_coldkey = U256::from(1); -// let new_coldkey = U256::from(2); -// let hotkey = U256::from(3); -// let stake = 100; -// let block = 42; - -// OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); -// TotalHotkeyColdkeyStakesThisInterval::::insert(hotkey, old_coldkey, (stake, block)); - -// let mut weight = Weight::zero(); -// assert_ok!(SubtensorModule::perform_swap_coldkey( -// &old_coldkey, -// &new_coldkey, -// &mut weight -// )); - -// assert!(!TotalHotkeyColdkeyStakesThisInterval::::contains_key( -// hotkey, -// old_coldkey -// )); -// assert_eq!( -// TotalHotkeyColdkeyStakesThisInterval::::get(hotkey, new_coldkey), -// (stake, block) -// ); -// }); -// } - -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_subnet_owner --exact --nocapture +use crate::{Call, Error}; + #[test] -fn test_swap_subnet_owner() { +fn test_announce_coldkey_swap_works() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); - let netuid = NetUid::from(1u16); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - add_network(netuid, 1, 0); - SubnetOwner::::insert(netuid, old_coldkey); + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, )); - assert_eq!(SubnetOwner::::get(netuid), new_coldkey); + let now = System::block_number(); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey_hash))] + ); + assert_eq!( + last_event(), + RuntimeEvent::SubtensorModule(Event::ColdkeySwapAnnounced { + who, + new_coldkey_hash, + block_number: now, + }) + ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_total_coldkey_stake --exact --show-output #[test] -fn test_swap_total_coldkey_stake() { +fn test_announce_coldkey_swap_with_existing_announcement_past_delay_works() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); - let other_coldkey = U256::from(3); - let hotkey = U256::from(4); - let other_hotkey = U256::from(5); - let stake = DefaultMinStake::::get().to_u64() * 10; - - let netuid = NetUid::from(1u16); - add_network(netuid, 1, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake * 2 + 1_000); - register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); - register_ok_neuron(netuid, other_hotkey, other_coldkey, 1001000); - - let reserve = stake * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake.into() - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - other_hotkey, - netuid, - stake.into() - )); - let total_stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, )); + let now = System::block_number(); assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey_hash))] ); + + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; + System::run_to_block::(now + delay); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_2_hash, + )); + + let now = System::block_number(); assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - total_stake_before_swap + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey_2_hash))] ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_staking_hotkeys --exact --nocapture #[test] -fn test_swap_staking_hotkeys() { +fn test_announce_coldkey_swap_with_bad_origin_fails() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - - StakingHotkeys::::insert(old_coldkey, vec![hotkey]); + let new_coldkey = U256::from(1); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::none(), new_coldkey_hash), + BadOrigin + ); - assert!(StakingHotkeys::::get(old_coldkey).is_empty()); - assert_eq!(StakingHotkeys::::get(new_coldkey), vec![hotkey]); + assert_noop!( + SubtensorModule::announce_coldkey_swap(RuntimeOrigin::root(), new_coldkey_hash), + BadOrigin + ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_hotkey_owners --exact --nocapture #[test] -fn test_swap_hotkey_owners() { +fn test_announce_coldkey_swap_with_existing_announcement_not_past_delay_fails() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); - let hotkey = U256::from(3); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let new_coldkey_2 = U256::from(3); + let new_coldkey_2_hash = ::Hashing::hash_of(&new_coldkey_2); - Owner::::insert(hotkey, old_coldkey); - OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); + assert_eq!(ColdkeySwapAnnouncements::::iter().count(), 0); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, )); - assert_eq!(Owner::::get(hotkey), new_coldkey); - assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); - assert_eq!(OwnedHotkeys::::get(new_coldkey), vec![hotkey]); + let now = System::block_number(); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey_hash))] + ); + + let unmet_delay = ColdkeySwapAnnouncementDelay::::get(); + System::run_to_block::(now + unmet_delay); + + assert_noop!( + SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_2_hash, + ), + Error::::ColdKeySwapReannouncedTooEarly + ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_transfer_remaining_balance --exact --nocapture + #[test] -fn test_transfer_remaining_balance() { +fn test_swap_coldkey_announced_works() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); - let balance = 100; + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let hotkey1 = U256::from(1001); + let hotkey2 = U256::from(1002); + let hotkey3 = U256::from(1003); + let swap_cost = SubtensorModule::get_key_swap_cost(); + let left_over = 12345; + let min_stake = DefaultMinStake::::get().to_u64(); + let stake1 = min_stake * 10; + let stake2 = min_stake * 20; + let stake3 = min_stake * 30; + let now = System::block_number(); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, balance); + SubtensorModule::add_balance_to_coldkey_account( + &who, + stake1 + stake2 + stake3 + swap_cost.to_u64() + left_over, + ); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight + // Announce the coldkey swap + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, )); + assert_eq!( + ColdkeySwapAnnouncements::::iter().collect::>(), + vec![(who.clone(), (now, new_coldkey_hash))] + ); - assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); - assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), balance); - }); -} + // Run some blocks for the announcement to be past the delay + // WARN: this is required before staking to neurons to avoid + // value mismatch due to coinbase run + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; + System::run_to_block::(now + delay); -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_with_no_stake --exact --show-output -#[test] -fn test_swap_with_no_stake() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); + // Setup networks and subnet ownerships + let netuid1 = NetUid::from(1); + let netuid2 = NetUid::from(2); + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + SubnetOwner::::insert(netuid1, who); + SubnetOwner::::insert(netuid2, who); + + // Setup reserves + let reserve1 = (stake1 + stake3) * 10; + let reserve2 = stake2 * 10; + mock::setup_reserves(netuid1, reserve1.into(), reserve1.into()); + mock::setup_reserves(netuid2, reserve2.into(), reserve2.into()); + + // Setup auto stake destinations + AutoStakeDestination::::insert(who, netuid1, hotkey1); + AutoStakeDestination::::insert(who, netuid2, hotkey2); + AutoStakeDestinationColdkeys::::insert( + hotkey1, + netuid1, + vec![who, U256::from(3), U256::from(4)], + ); + AutoStakeDestinationColdkeys::::insert( + hotkey2, + netuid2, + vec![U256::from(7), U256::from(8), who], + ); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight + // Setup neurons with stake + register_ok_neuron(netuid1, hotkey1, who, 0); + register_ok_neuron(netuid2, hotkey2, who, 0); + register_ok_neuron(netuid1, hotkey3, who, 0); + + let hotkeys = vec![hotkey1, hotkey2, hotkey3]; + assert_eq!(StakingHotkeys::::get(&who), hotkeys); + assert_eq!(OwnedHotkeys::::get(&who), hotkeys); + assert_eq!(Owner::::get(hotkey1), who); + assert_eq!(Owner::::get(hotkey2), who); + assert_eq!(Owner::::get(hotkey3), who); + + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey1, + netuid1, + stake1.into() + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey2, + netuid2, + stake2.into() + )); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey3, + netuid1, + stake3.into() + )); + let hk1_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &who, netuid1); + let hk2_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &who, netuid2); + let hk3_alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey3, &who, netuid1); + let total_ck_stake = SubtensorModule::get_total_stake_for_coldkey(&who); + + // Setup identity + let identity = ChainIdentityV2::default(); + IdentitiesV2::::insert(who, identity.clone()); + assert_eq!(IdentitiesV2::::get(who), Some(identity.clone())); + assert!(IdentitiesV2::::get(new_coldkey).is_none()); + + let balance_before = SubtensorModule::get_coldkey_balance(&who); + let total_stake_before = SubtensorModule::get_total_stake(); + + assert_ok!(SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who), + new_coldkey )); + // Ensure the announcement has been consumed + assert_eq!(ColdkeySwapAnnouncements::::get(who), None); + + // Ensure the cost has been withdrawn from the old coldkey and recycled + let balance_after = SubtensorModule::get_coldkey_balance(&who); assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), + balance_before - swap_cost.to_u64(), + balance_after + left_over + ); + + // Ensure the identity is correctly swapped + assert!(IdentitiesV2::::get(&who).is_none()); + assert_eq!(IdentitiesV2::::get(&new_coldkey), Some(identity)); + + // Ensure the subnet ownerships are correctly swapped + assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); + assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); + + // Ensure the auto stake destinations are correctly swapped + assert!(AutoStakeDestination::::get(who, netuid1).is_none()); + assert!(AutoStakeDestination::::get(who, netuid2).is_none()); + assert_eq!( + AutoStakeDestination::::get(new_coldkey, netuid1), + Some(hotkey1) + ); + assert_eq!( + AutoStakeDestination::::get(new_coldkey, netuid2), + Some(hotkey2) + ); + assert_eq!( + AutoStakeDestinationColdkeys::::get(hotkey1, netuid1), + vec![U256::from(3), U256::from(4), new_coldkey] + ); + assert_eq!( + AutoStakeDestinationColdkeys::::get(hotkey2, netuid2), + vec![U256::from(7), U256::from(8), new_coldkey] + ); + + // Ensure the coldkey stake is correctly swapped + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &who, netuid1), + AlphaCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &who, netuid2), + AlphaCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey3, &who, netuid1), + AlphaCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, + &new_coldkey, + netuid1 + ), + hk1_alpha + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey2, + &new_coldkey, + netuid2 + ), + hk2_alpha + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey3, + &new_coldkey, + netuid1 + ), + hk3_alpha + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&who), TaoCurrency::ZERO ); assert_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - TaoCurrency::ZERO + total_ck_stake, + ); + + // Ensure the staking hotkeys are correctly swapped + assert!(StakingHotkeys::::get(&who).is_empty()); + assert_eq!(StakingHotkeys::::get(&new_coldkey), hotkeys); + + // Ensure the hotkey ownership is correctly swapped + assert!(OwnedHotkeys::::get(&who).is_empty()); + assert_eq!(OwnedHotkeys::::get(&new_coldkey), hotkeys); + assert_eq!(Owner::::get(hotkey1), new_coldkey); + assert_eq!(Owner::::get(hotkey2), new_coldkey); + assert_eq!(Owner::::get(hotkey3), new_coldkey); + + // Ensure the remaining balance is transferred to the new coldkey + assert_eq!(SubtensorModule::get_coldkey_balance(&who), 0); + assert_eq!( + SubtensorModule::get_coldkey_balance(&new_coldkey), + left_over + ); + + // Ensure total stake is unchanged + assert_eq!( + SubtensorModule::get_total_stake(), + total_stake_before, + "Total stake changed unexpectedly" + ); + + // Verify event emission + System::assert_last_event( + Event::ColdkeySwapped { + old_coldkey: who, + new_coldkey, + swap_cost, + } + .into(), ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_multiple_hotkeys --exact --nocapture #[test] -fn test_swap_with_multiple_hotkeys() { +fn test_do_swap_coldkey_preserves_new_coldkey_identity() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - OwnedHotkeys::::insert(old_coldkey, vec![hotkey1, hotkey2]); + let old_identity = ChainIdentityV2 { + name: b"Old identity".to_vec(), + ..Default::default() + }; + IdentitiesV2::::insert(who, old_identity.clone()); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); + let new_identity = ChainIdentityV2 { + name: b"New identity".to_vec(), + ..Default::default() + }; + IdentitiesV2::::insert(new_coldkey, new_identity.clone()); - assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); - assert_eq!( - OwnedHotkeys::::get(new_coldkey), - vec![hotkey1, hotkey2] + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&who, &new_coldkey)); + + // Identity is preserved + assert_eq!(IdentitiesV2::::get(who), Some(old_identity)); + assert_eq!(IdentitiesV2::::get(new_coldkey), Some(new_identity)); + }); +} + +#[test] +fn test_swap_coldkey_announced_with_bad_origin_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + assert_noop!( + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::none(), new_coldkey), + BadOrigin + ); + + assert_noop!( + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::root(), new_coldkey), + BadOrigin ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_multiple_subnets --exact --nocapture #[test] -fn test_swap_with_multiple_subnets() { +fn test_swap_coldkey_announced_without_announcement_fails() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); - let netuid1 = NetUid::from(1); - let netuid2 = NetUid::from(2); - add_network(netuid1, 1, 0); - add_network(netuid2, 1, 0); - SubnetOwner::::insert(netuid1, old_coldkey); - SubnetOwner::::insert(netuid2, old_coldkey); + assert_noop!( + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who), new_coldkey), + Error::::ColdKeySwapAnnouncementNotFound + ); + }) +} - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight +#[test] +fn test_swap_coldkey_announced_with_mismatched_coldkey_hash_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let other_coldkey = U256::from(3); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, )); - assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); - assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); - }); + assert_noop!( + SubtensorModule::swap_coldkey_announced(RuntimeOrigin::signed(who), other_coldkey), + Error::::AnnouncedColdkeyHashDoesNotMatch + ); + }) } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_zero_balance --exact --nocapture #[test] -fn test_swap_with_zero_balance() { +fn test_swap_coldkey_announced_too_early_fails() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, )); - assert_eq!(Balances::free_balance(old_coldkey), 0); - assert_eq!(Balances::free_balance(new_coldkey), 0); - }); + assert_noop!( + SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who), + new_coldkey + ), + Error::::ColdKeySwapTooEarly + ); + }) } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_idempotency --exact --show-output #[test] -fn test_swap_idempotency() { +fn test_swap_coldkey_announced_with_already_associated_coldkey_fails() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let stake = DefaultMinStake::::get().to_u64() * 10; - let reserve = stake * 10; - - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - // Add a network - add_network(netuid, 1, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake); // Give old coldkey some balance - // Stake to a hotkey - register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake.into() + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + new_coldkey_hash, )); - // Get stake before swap - let stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); + let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; + System::run_to_block::(now + delay); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - stake_before_swap + SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); + + assert_noop!( + SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who), + new_coldkey + ), + Error::::ColdKeyAlreadyAssociated ); - }); + }) } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_with_max_values --exact --show-output #[test] -fn test_swap_with_max_values() { +fn test_swap_coldkey_announced_with_hotkey_fails() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); + let who = U256::from(1); let new_coldkey = U256::from(2); - let old_coldkey2 = U256::from(3); - let new_coldkey2 = U256::from(4); - let hotkey = U256::from(5); + let hotkey = U256::from(3); + let hotkey_hash = ::Hashing::hash_of(&hotkey); + + assert_ok!(SubtensorModule::announce_coldkey_swap( + RuntimeOrigin::signed(who.clone()), + hotkey_hash, + )); + + let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; + System::run_to_block::(now + delay); + + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&who, swap_cost.to_u64()); + + SubtensorModule::create_account_if_non_existent(&new_coldkey, &hotkey); + + assert_noop!( + SubtensorModule::swap_coldkey_announced( + ::RuntimeOrigin::signed(who), + hotkey + ), + Error::::NewColdKeyIsHotkey + ); + }) +} + +#[test] +fn test_do_swap_coldkey_with_not_enough_balance_to_pay_swap_cost_fails() { + new_test_ext(1).execute_with(|| { + let who = U256::from(1); + let new_coldkey = U256::from(2); + + let now = System::block_number(); + let delay = ColdkeySwapAnnouncementDelay::::get() + 1; + System::run_to_block::(now + delay); + + assert_noop!( + SubtensorModule::do_swap_coldkey(&who, &new_coldkey), + Error::::NotEnoughBalanceToPaySwapColdKey + ); + }); +} + +#[test] +fn test_do_swap_coldkey_with_no_stake() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); + + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), + TaoCurrency::ZERO + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + TaoCurrency::ZERO + ); + }); +} + +#[test] +fn test_do_swap_coldkey_with_max_values() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + let old_coldkey2 = U256::from(3); + let new_coldkey2 = U256::from(4); + let hotkey = U256::from(5); let hotkey2 = U256::from(6); let other_coldkey = U256::from(7); let netuid = NetUid::from(1); @@ -405,16 +651,14 @@ fn test_swap_with_max_values() { netuid2, ); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); - assert_ok!(SubtensorModule::perform_swap_coldkey( + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey2, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); + assert_ok!(SubtensorModule::do_swap_coldkey( &old_coldkey2, - &new_coldkey2, - &mut weight + &new_coldkey2 )); assert_eq!( @@ -438,88 +682,8 @@ fn test_swap_with_max_values() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_with_non_existent_new_coldkey --exact --show-output -#[test] -fn test_swap_with_non_existent_new_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let stake = DefaultMinStake::::get().to_u64() * 10; - let netuid = NetUid::from(1); - - add_network(netuid, 1, 0); - register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); - // Give old coldkey some balance. - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake + 1_000); - - let reserve = stake * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Stake to hotkey. - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake.into() - )); - let expected_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &old_coldkey, - netuid, - ); - - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); - - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO - ); - - let actual_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid, - ); - assert_abs_diff_eq!( - actual_stake, - expected_stake, - epsilon = expected_stake / 1000.into() - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_max_hotkeys --exact --nocapture -#[test] -fn test_swap_with_max_hotkeys() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let max_hotkeys = 1000; - let hotkeys: Vec = (0..max_hotkeys).map(U256::from).collect(); - - OwnedHotkeys::::insert(old_coldkey, hotkeys.clone()); - - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); - - assert!(OwnedHotkeys::::get(old_coldkey).is_empty()); - assert_eq!(OwnedHotkeys::::get(new_coldkey), hotkeys); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_effect_on_delegated_stake --exact --nocapture #[test] -fn test_swap_effect_on_delegated_stake() { +fn test_do_swap_coldkey_effect_on_delegated_stake() { new_test_ext(1).execute_with(|| { let subnet_owner_coldkey = U256::from(1001); let subnet_owner_hotkey = U256::from(1002); @@ -552,12 +716,10 @@ fn test_swap_effect_on_delegated_stake() { let coldkey_stake_before = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); let delegator_stake_before = SubtensorModule::get_total_stake_for_coldkey(&delegator); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); + + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); assert_abs_diff_eq!( SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), @@ -577,423 +739,136 @@ fn test_swap_effect_on_delegated_stake() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_concurrent_modifications --exact --show-output #[test] -fn test_swap_concurrent_modifications() { +fn test_swap_delegated_stake_for_coldkey() { new_test_ext(1).execute_with(|| { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - let hotkey = U256::from(3); + let other_coldkey = U256::from(3); + let hotkey1 = U256::from(4); + let hotkey2 = U256::from(5); + let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; + let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; let netuid = NetUid::from(1); - let initial_stake = 1_000_000_000_000; - let additional_stake = 500_000_000_000; - - let reserve = (initial_stake + additional_stake) * 1000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); // Setup initial state - add_network(netuid, 1, 1); - SubtensorModule::add_balance_to_coldkey_account( - &new_coldkey, - initial_stake + additional_stake + 1_000_000, - ); - register_ok_neuron(netuid, hotkey, new_coldkey, 1001000); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(new_coldkey), - hotkey, - netuid, - initial_stake.into() - )); - - // Verify initial stake - let stake_before_swap = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid, - ); - - // Wait some blocks - step_block(10); - - // Simulate concurrent stake addition - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(new_coldkey), - hotkey, - netuid, - additional_stake.into() - )); - - let stake_with_additional = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid, - ); - - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); - - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &new_coldkey, - netuid - ), - stake_with_additional - ); - assert!(stake_with_additional > stake_before_swap); - assert!(!Alpha::::contains_key((hotkey, old_coldkey, netuid))); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_swap_with_invalid_subnet_ownership --exact --nocapture -#[test] -fn test_swap_with_invalid_subnet_ownership() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let netuid = NetUid::from(1u16); - - SubnetOwner::::insert(netuid, old_coldkey); - - // Simulate an invalid state where the subnet owner doesn't match the old_coldkey - SubnetOwner::::insert(netuid, U256::from(3)); - - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); - - // The swap should not affect the mismatched subnet ownership - assert_eq!(SubnetOwner::::get(netuid), U256::from(3)); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_do_swap_coldkey_success --exact --show-output -#[test] -fn test_do_swap_coldkey_success() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - let netuid = NetUid::from(1u16); - let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; - let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let swap_cost = SubtensorModule::get_key_swap_cost(); - let free_balance_old = 12345 + swap_cost.to_u64(); + add_network(netuid, 1, 0); + register_ok_neuron(netuid, hotkey1, other_coldkey, 0); + register_ok_neuron(netuid, hotkey2, other_coldkey, 0); let reserve = (stake_amount1 + stake_amount2) * 10; mock::setup_reserves(netuid, reserve.into(), reserve.into()); - // Setup initial state - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey1, old_coldkey, 0); - register_ok_neuron(netuid, hotkey2, old_coldkey, 0); - - // Add balance to old coldkey + // Notice hotkey1 and hotkey2 are Owned by other_coldkey + // old_coldkey and new_coldkey therefore delegates stake to them + // === Give old_coldkey some balance === SubtensorModule::add_balance_to_coldkey_account( &old_coldkey, - stake_amount1 + stake_amount2 + free_balance_old, - ); - - // Log initial state - log::info!( - "Initial total stake: {}", - SubtensorModule::get_total_stake() - ); - log::info!( - "Initial old coldkey stake: {}", - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) - ); - log::info!( - "Initial new coldkey stake: {}", - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) + stake_amount1 + stake_amount2 + 1_000_000, ); - // Add stake to the neurons + // === Stake to hotkeys === assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(old_coldkey), hotkey1, netuid, stake_amount1.into() )); + let expected_stake_alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, + &old_coldkey, + netuid, + ); + + let (expected_stake_alpha2, fee) = mock::swap_tao_to_alpha(netuid, stake_amount2.into()); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(old_coldkey), hotkey2, netuid, stake_amount2.into() )); - - // Insert an Identity - let name: Vec = b"The fourth Coolest Identity".to_vec(); - let identity: ChainIdentityV2 = ChainIdentityV2 { - name: name.clone(), - url: vec![], - github_repo: vec![], - image: vec![], - discord: vec![], - description: vec![], - additional: vec![], - }; - - IdentitiesV2::::insert(old_coldkey, identity.clone()); - - assert!(IdentitiesV2::::get(old_coldkey).is_some()); - assert!(IdentitiesV2::::get(new_coldkey).is_none()); - - // Log state after adding stake - log::info!( - "Total stake after adding: {}", - SubtensorModule::get_total_stake() + let expected_stake_alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey2, + &old_coldkey, + netuid, ); - log::info!( - "Old coldkey stake after adding: {}", - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) + let fee = (expected_stake_alpha2.to_u64() as f64 * 0.003) as u64; + + // Record initial values + let initial_total_issuance = SubtensorModule::get_total_issuance(); + let initial_total_stake = SubtensorModule::get_total_stake(); + let coldkey_stake = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); + let stake_coldkey_hotkey1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, + &old_coldkey, + netuid, ); - log::info!( - "New coldkey stake after adding: {}", - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) + let stake_coldkey_hotkey2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey2, + &old_coldkey, + netuid, ); + let total_hotkey1_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); + let total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); - // Record total stake before swap - let total_stake_before_swap = SubtensorModule::get_total_stake(); - - let hk1_alpha = Alpha::::get((hotkey1, old_coldkey, netuid)); - let hk2_alpha = Alpha::::get((hotkey2, old_coldkey, netuid)); - let total_ck_stake = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64()); // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey( - // <::RuntimeOrigin>::signed(old_coldkey), - &old_coldkey, - &new_coldkey, - swap_cost - )); + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey,)); - // Log state after swap - log::info!( - "Total stake after swap: {}", - SubtensorModule::get_total_stake() - ); - log::info!( - "Old coldkey stake after swap: {}", - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey) - ); - log::info!( - "New coldkey stake after swap: {}", - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey) + // Verify stake transfer + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, + &new_coldkey, + netuid + ), + expected_stake_alpha1 ); - - // Verify the swap - assert_eq!(Owner::::get(hotkey1), new_coldkey); - assert_eq!(Owner::::get(hotkey2), new_coldkey); assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - total_ck_stake + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey2, + &new_coldkey, + netuid + ), + expected_stake_alpha2 ); assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO - ); - assert_eq!( - Alpha::::get((hotkey1, new_coldkey, netuid)), - hk1_alpha - ); - assert_eq!( - Alpha::::get((hotkey2, new_coldkey, netuid)), - hk2_alpha - ); - assert!(!Alpha::::contains_key((hotkey1, old_coldkey, netuid))); - assert!(!Alpha::::contains_key((hotkey2, old_coldkey, netuid))); - - // Verify OwnedHotkeys - let new_owned_hotkeys = OwnedHotkeys::::get(new_coldkey); - assert!(new_owned_hotkeys.contains(&hotkey1)); - assert!(new_owned_hotkeys.contains(&hotkey2)); - assert_eq!(new_owned_hotkeys.len(), 2); - assert!(!OwnedHotkeys::::contains_key(old_coldkey)); - - // Verify balance transfer - assert_eq!( - SubtensorModule::get_coldkey_balance(&new_coldkey), - free_balance_old - swap_cost.to_u64() - ); - assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0); - - // Verify total stake remains unchanged - assert_eq!( - SubtensorModule::get_total_stake(), - total_stake_before_swap, - "Total stake changed unexpectedly" - ); - - // Verify identities were swapped - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - assert_eq!( - IdentitiesV2::::get(new_coldkey).expect("Expected an Identity"), - identity - ); - - // Verify event emission - System::assert_last_event( - Event::ColdkeySwapped { - old_coldkey, - new_coldkey, - swap_cost, - } - .into(), - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_stake_for_coldkey --exact --show-output -#[test] -fn test_swap_stake_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey1 = U256::from(3); - let hotkey2 = U256::from(4); - let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; - let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let stake_amount3 = DefaultMinStake::::get().to_u64() * 30; - let mut weight = Weight::zero(); - - // Setup initial state - // Add a network - let netuid = NetUid::from(1u16); - add_network(netuid, 1, 0); - - // Register hotkeys - register_ok_neuron(netuid, hotkey1, old_coldkey, 0); - register_ok_neuron(netuid, hotkey2, old_coldkey, 0); - // Give some balance to old coldkey - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake_amount1 + stake_amount2 + 1_000_000, - ); - - let reserve = (stake_amount1 + stake_amount2 + stake_amount3) * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Stake to hotkeys - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey1, - netuid, - stake_amount1.into() - )); - let expected_stake_alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid, - ); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey2, - netuid, - stake_amount2.into() - )); - let expected_stake_alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid, - ); - - // Insert existing for same hotkey1 - // give new coldkey some balance - SubtensorModule::add_balance_to_coldkey_account(&new_coldkey, stake_amount3 + 1_000_000); - // Stake to hotkey1 - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(new_coldkey), - hotkey1, - netuid, - stake_amount3.into() - )); - let expected_stake_alpha3 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &new_coldkey, - netuid, - ); - - // Record initial values - let initial_total_issuance = SubtensorModule::get_total_issuance(); - let initial_total_stake = SubtensorModule::get_total_stake(); - let initial_total_stake_for_old_coldkey = - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - let initial_total_stake_for_new_coldkey = - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey); - let initial_total_hotkey1_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); - let initial_total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); - - // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); - - // Verify stake is additive, not replaced - assert_abs_diff_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - initial_total_stake_for_old_coldkey + initial_total_stake_for_new_coldkey, - epsilon = 2.into() - ); - - // Verify ownership transfer - assert_eq!( - SubtensorModule::get_owned_hotkeys(&new_coldkey), - vec![hotkey1, hotkey2] - ); - assert_eq!(SubtensorModule::get_owned_hotkeys(&old_coldkey), vec![]); - - // Verify stake transfer - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &new_coldkey, - netuid - ), - expected_stake_alpha1 + expected_stake_alpha3 + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, + &old_coldkey, + netuid + ), + AlphaCurrency::ZERO ); assert_eq!( SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey2, - &new_coldkey, - netuid - ), - expected_stake_alpha2 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, &old_coldkey, netuid ), AlphaCurrency::ZERO ); + + // Verify TotalColdkeyStake assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid - ), - AlphaCurrency::ZERO + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + coldkey_stake + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), + TaoCurrency::ZERO ); // Verify TotalHotkeyStake remains unchanged assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey1), - initial_total_hotkey1_stake + total_hotkey1_stake ); assert_eq!( SubtensorModule::get_total_stake_for_hotkey(&hotkey2), - initial_total_hotkey2_stake + total_hotkey2_stake ); // Verify total stake and issuance remain unchanged @@ -1010,359 +885,70 @@ fn test_swap_stake_for_coldkey() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_staking_hotkeys_for_coldkey --exact --show-output #[test] -fn test_swap_staking_hotkeys_for_coldkey() { +fn test_coldkey_swap_total() { new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let other_coldkey = U256::from(3); - let hotkey1 = U256::from(4); - let hotkey2 = U256::from(5); - let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; - let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let mut weight = Weight::zero(); + let coldkey = U256::from(1); + let nominator1 = U256::from(2); + let nominator2 = U256::from(3); + let nominator3 = U256::from(4); + let delegate1 = U256::from(5); + let delegate2 = U256::from(6); + let delegate3 = U256::from(7); + let hotkey1 = U256::from(2); + let hotkey2 = U256::from(3); + let hotkey3 = U256::from(4); + let netuid1 = NetUid::from(1); + let netuid2 = NetUid::from(2); + let netuid3 = NetUid::from(3); + let stake = DefaultMinStake::::get().to_u64() * 10; + SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 6); + SubtensorModule::add_balance_to_coldkey_account(&delegate1, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&delegate2, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&delegate3, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&nominator1, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&nominator2, stake * 2); + SubtensorModule::add_balance_to_coldkey_account(&nominator3, stake * 2); - // Setup initial state - // Add a network - let netuid = NetUid::from(1u16); - add_network(netuid, 1, 0); - // Give some balance to old coldkey - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake_amount1 + stake_amount2 + 1_000_000, - ); // Register hotkeys - register_ok_neuron(netuid, hotkey1, old_coldkey, 0); - register_ok_neuron(netuid, hotkey2, other_coldkey, 0); + let reserve = stake * 10; + mock::setup_reserves(netuid1, reserve.into(), reserve.into()); + mock::setup_reserves(netuid2, reserve.into(), reserve.into()); + mock::setup_reserves(netuid3, reserve.into(), reserve.into()); - let reserve = (stake_amount1 + stake_amount2) * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); + // Setup initial state + add_network(netuid1, 13, 0); + add_network(netuid2, 14, 0); + add_network(netuid3, 15, 0); + register_ok_neuron(netuid1, hotkey1, coldkey, 0); + register_ok_neuron(netuid2, hotkey2, coldkey, 0); + register_ok_neuron(netuid3, hotkey3, coldkey, 0); + register_ok_neuron(netuid1, delegate1, delegate1, 0); + register_ok_neuron(netuid2, delegate2, delegate2, 0); + register_ok_neuron(netuid3, delegate3, delegate3, 0); + Delegates::::insert(hotkey1, u16::MAX / 10); + Delegates::::insert(hotkey2, u16::MAX / 10); + Delegates::::insert(hotkey3, u16::MAX / 10); + Delegates::::insert(delegate1, u16::MAX / 10); + Delegates::::insert(delegate2, u16::MAX / 10); + Delegates::::insert(delegate3, u16::MAX / 10); - // Stake to hotkeys assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), + <::RuntimeOrigin>::signed(coldkey), hotkey1, - netuid, - stake_amount1.into() + netuid1, + stake.into() )); - let expected_stake_alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid, - ); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), + <::RuntimeOrigin>::signed(coldkey), hotkey2, - netuid, - stake_amount2.into() + netuid1, + stake.into() )); - let expected_stake_alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid, - ); - - // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); - - // Verify StakingHotkeys transfer - assert_eq!( - StakingHotkeys::::get(new_coldkey), - vec![hotkey1, hotkey2] - ); - assert_eq!(StakingHotkeys::::get(old_coldkey), vec![]); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_swap_delegated_stake_for_coldkey --exact --show-output -#[test] -fn test_swap_delegated_stake_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let other_coldkey = U256::from(3); - let hotkey1 = U256::from(4); - let hotkey2 = U256::from(5); - let stake_amount1 = DefaultMinStake::::get().to_u64() * 10; - let stake_amount2 = DefaultMinStake::::get().to_u64() * 20; - let mut weight = Weight::zero(); - let netuid = NetUid::from(1); - - // Setup initial state - add_network(netuid, 1, 0); - register_ok_neuron(netuid, hotkey1, other_coldkey, 0); - register_ok_neuron(netuid, hotkey2, other_coldkey, 0); - - let reserve = (stake_amount1 + stake_amount2) * 10; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Notice hotkey1 and hotkey2 are Owned by other_coldkey - // old_coldkey and new_coldkey therefore delegates stake to them - // === Give old_coldkey some balance === - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - stake_amount1 + stake_amount2 + 1_000_000, - ); - - // === Stake to hotkeys === assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey1, - netuid, - stake_amount1.into() - )); - let expected_stake_alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid, - ); - - let (expected_stake_alpha2, fee) = mock::swap_tao_to_alpha(netuid, stake_amount2.into()); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey2, - netuid, - stake_amount2.into() - )); - let expected_stake_alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid, - ); - let fee = (expected_stake_alpha2.to_u64() as f64 * 0.003) as u64; - - // Record initial values - let initial_total_issuance = SubtensorModule::get_total_issuance(); - let initial_total_stake = SubtensorModule::get_total_stake(); - let coldkey_stake = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - let stake_coldkey_hotkey1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid, - ); - let stake_coldkey_hotkey2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid, - ); - let total_hotkey1_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey1); - let total_hotkey2_stake = SubtensorModule::get_total_stake_for_hotkey(&hotkey2); - - // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); - - // Verify stake transfer - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &new_coldkey, - netuid - ), - expected_stake_alpha1 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &new_coldkey, - netuid - ), - expected_stake_alpha2 - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey1, - &old_coldkey, - netuid - ), - AlphaCurrency::ZERO - ); - assert_eq!( - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey2, - &old_coldkey, - netuid - ), - AlphaCurrency::ZERO - ); - - // Verify TotalColdkeyStake - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - coldkey_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO - ); - - // Verify TotalHotkeyStake remains unchanged - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey1), - total_hotkey1_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey2), - total_hotkey2_stake - ); - - // Verify total stake and issuance remain unchanged - assert_eq!( - SubtensorModule::get_total_stake(), - initial_total_stake, - "Total stake changed unexpectedly" - ); - assert_eq!( - SubtensorModule::get_total_issuance(), - initial_total_issuance, - "Total issuance changed unexpectedly" - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_swap_subnet_owner_for_coldkey --exact --nocapture -#[test] -fn test_swap_subnet_owner_for_coldkey() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let netuid1 = NetUid::from(1); - let netuid2 = NetUid::from(2); - let mut weight = Weight::zero(); - - // Initialize SubnetOwner for old_coldkey - add_network(netuid1, 13, 0); - add_network(netuid2, 14, 0); - SubnetOwner::::insert(netuid1, old_coldkey); - SubnetOwner::::insert(netuid2, old_coldkey); - - // Set up TotalNetworks - TotalNetworks::::put(3); - - // Perform the swap - SubtensorModule::perform_swap_coldkey(&old_coldkey, &new_coldkey, &mut weight); - - // Verify the swap - assert_eq!(SubnetOwner::::get(netuid1), new_coldkey); - assert_eq!(SubnetOwner::::get(netuid2), new_coldkey); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_do_swap_coldkey_with_subnet_ownership --exact --nocapture -#[test] -fn test_do_swap_coldkey_with_subnet_ownership() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let stake_amount = 1000; - let swap_cost = SubtensorModule::get_key_swap_cost().to_u64(); - - // Setup initial state - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, old_coldkey, 0); - - // Set TotalNetworks because swap relies on it - crate::TotalNetworks::::set(1); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount + swap_cost); - SubnetOwner::::insert(netuid, old_coldkey); - - // Populate OwnedHotkeys map - OwnedHotkeys::::insert(old_coldkey, vec![hotkey]); - - // Perform the swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - swap_cost.into() - )); - - // Verify subnet ownership transfer - assert_eq!(SubnetOwner::::get(netuid), new_coldkey); - }); -} -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_coldkey_has_associated_hotkeys --exact --nocapture -#[test] -fn test_coldkey_has_associated_hotkeys() { - new_test_ext(1).execute_with(|| { - let coldkey = U256::from(1); - let hotkey = U256::from(2); - let netuid = NetUid::from(1u16); - - // Setup initial state - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1000); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_swap_total --exact --show-output -#[test] -fn test_coldkey_swap_total() { - new_test_ext(1).execute_with(|| { - let coldkey = U256::from(1); - let nominator1 = U256::from(2); - let nominator2 = U256::from(3); - let nominator3 = U256::from(4); - let delegate1 = U256::from(5); - let delegate2 = U256::from(6); - let delegate3 = U256::from(7); - let hotkey1 = U256::from(2); - let hotkey2 = U256::from(3); - let hotkey3 = U256::from(4); - let netuid1 = NetUid::from(1); - let netuid2 = NetUid::from(2); - let netuid3 = NetUid::from(3); - let stake = DefaultMinStake::::get().to_u64() * 10; - SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 6); - SubtensorModule::add_balance_to_coldkey_account(&delegate1, stake * 2); - SubtensorModule::add_balance_to_coldkey_account(&delegate2, stake * 2); - SubtensorModule::add_balance_to_coldkey_account(&delegate3, stake * 2); - SubtensorModule::add_balance_to_coldkey_account(&nominator1, stake * 2); - SubtensorModule::add_balance_to_coldkey_account(&nominator2, stake * 2); - SubtensorModule::add_balance_to_coldkey_account(&nominator3, stake * 2); - - let reserve = stake * 10; - mock::setup_reserves(netuid1, reserve.into(), reserve.into()); - mock::setup_reserves(netuid2, reserve.into(), reserve.into()); - mock::setup_reserves(netuid3, reserve.into(), reserve.into()); - - // Setup initial state - add_network(netuid1, 13, 0); - add_network(netuid2, 14, 0); - add_network(netuid3, 15, 0); - register_ok_neuron(netuid1, hotkey1, coldkey, 0); - register_ok_neuron(netuid2, hotkey2, coldkey, 0); - register_ok_neuron(netuid3, hotkey3, coldkey, 0); - register_ok_neuron(netuid1, delegate1, delegate1, 0); - register_ok_neuron(netuid2, delegate2, delegate2, 0); - register_ok_neuron(netuid3, delegate3, delegate3, 0); - Delegates::::insert(hotkey1, u16::MAX / 10); - Delegates::::insert(hotkey2, u16::MAX / 10); - Delegates::::insert(hotkey3, u16::MAX / 10); - Delegates::::insert(delegate1, u16::MAX / 10); - Delegates::::insert(delegate2, u16::MAX / 10); - Delegates::::insert(delegate3, u16::MAX / 10); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey), - hotkey1, - netuid1, - stake.into() - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey), - hotkey2, - netuid1, - stake.into() - )); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey), - hotkey3, - netuid1, - stake.into() + <::RuntimeOrigin>::signed(coldkey), + hotkey3, + netuid1, + stake.into() )); assert_ok!(SubtensorModule::add_stake( <::RuntimeOrigin>::signed(coldkey), @@ -1495,1122 +1081,363 @@ fn test_coldkey_swap_total() { SubtensorModule::get_all_staked_hotkeys(&delegate2), vec![delegate2, hotkey2] ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&delegate3), - vec![delegate3, hotkey3] - ); - - assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator1), vec![]); - assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator2), vec![]); - assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator3), vec![]); - - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&nominator1), - vec![hotkey1, delegate1] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&nominator2), - vec![hotkey2, delegate2] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&nominator3), - vec![hotkey3, delegate3] - ); - - // Perform the swap - let new_coldkey = U256::from(1100); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&coldkey), - ck_stake - ); - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &coldkey, - &new_coldkey, - &mut weight - )); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - ck_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&coldkey), - TaoCurrency::ZERO - ); - - // Check everything is swapped. - assert_eq!( - SubtensorModule::get_owned_hotkeys(&new_coldkey), - vec![hotkey1, hotkey2, hotkey3] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&new_coldkey), - vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] - ); - // Shouldn't change. - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey1), - hk1_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey2), - hk2_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&hotkey3), - hk3_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&delegate1), - d1_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&delegate2), - d2_stake - ); - assert_eq!( - SubtensorModule::get_total_stake_for_hotkey(&delegate3), - d3_stake - ); - - assert_eq!( - SubtensorModule::get_owned_hotkeys(&delegate1), - vec![delegate1] - ); - assert_eq!( - SubtensorModule::get_owned_hotkeys(&delegate2), - vec![delegate2] - ); - assert_eq!( - SubtensorModule::get_owned_hotkeys(&delegate3), - vec![delegate3] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&delegate1), - vec![delegate1, hotkey1] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&delegate2), - vec![delegate2, hotkey2] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&delegate3), - vec![delegate3, hotkey3] - ); - - assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator1), vec![]); - assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator2), vec![]); - assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator3), vec![]); - - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&nominator1), - vec![hotkey1, delegate1] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&nominator2), - vec![hotkey2, delegate2] - ); - assert_eq!( - SubtensorModule::get_all_staked_hotkeys(&nominator3), - vec![hotkey3, delegate3] - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_delegations --exact --show-output -#[test] -fn test_coldkey_delegations() { - new_test_ext(1).execute_with(|| { - let new_coldkey = U256::from(0); - let owner = U256::from(1); - let coldkey = U256::from(4); - let delegate = U256::from(2); - let netuid = NetUid::from(0); // Stake to 0 - let netuid2 = NetUid::from(1); // Stake to 1 - let stake = DefaultMinStake::::get().to_u64() * 10; - let reserve = stake * 1000; - - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - mock::setup_reserves(netuid2, reserve.into(), reserve.into()); - - add_network(netuid, 13, 0); // root - add_network(netuid2, 13, 0); - - assert_ok!(SubtensorModule::root_register( - <::RuntimeOrigin>::signed(owner), - delegate - )); // register on root - register_ok_neuron(netuid2, delegate, owner, 0); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 10); - - // since the reserves are equal and we stake the same amount to both networks, we can reuse - // this values for different networks. but you should take it into account in case of tests - // changes - let (expected_stake, fee) = mock::swap_tao_to_alpha(netuid, stake.into()); - - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey), - delegate, - netuid, - stake.into() - )); - - // Add stake to netuid2 - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(coldkey), - delegate, - netuid2, - stake.into() - )); - - // Perform the swap - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &coldkey, - &new_coldkey, - &mut weight - )); - - // Verify stake was moved for the delegate - let approx_total_stake = TaoCurrency::from(stake * 2 - fee * 2); - assert_abs_diff_eq!( - SubtensorModule::get_total_stake_for_hotkey(&delegate), - approx_total_stake, - epsilon = approx_total_stake / 100.into() - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&coldkey), - TaoCurrency::ZERO - ); - assert_abs_diff_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - approx_total_stake, - epsilon = approx_total_stake / 100.into() - ); - assert_eq!( - expected_stake, - Alpha::::get((delegate, new_coldkey, netuid)) - .to_num::() - .into(), - ); - assert_eq!(Alpha::::get((delegate, coldkey, netuid)), 0); - - assert_eq!( - expected_stake, - Alpha::::get((delegate, new_coldkey, netuid2)) - .to_num::() - .into() - ); - assert_eq!(Alpha::::get((delegate, coldkey, netuid2)), 0); - }); -} - -#[test] -fn test_schedule_swap_coldkey_success() { - new_test_ext(1).execute_with(|| { - // Initialize test accounts - let old_coldkey: U256 = U256::from(1); - let new_coldkey: U256 = U256::from(2); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - // Add balance to the old coldkey account - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + 1_000); - - // Schedule the coldkey swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - )); - - // Get the current block number - let current_block: u64 = System::block_number(); - - // Calculate the expected execution block (5 days from now) - let expected_execution_block: u64 = current_block + 5 * 24 * 60 * 60 / 12; - - // Check for the SwapScheduled event - System::assert_last_event( - Event::ColdkeySwapScheduled { - old_coldkey, - new_coldkey, - execution_block: expected_execution_block, - swap_cost, - } - .into(), - ); - - // TODO: Add additional checks to ensure the swap is correctly scheduled in the system - // For example, verify that the swap is present in the appropriate storage or scheduler - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_duplicate --exact --nocapture -#[test] -fn test_schedule_swap_coldkey_duplicate() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + 2_000); - - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - )); - - // Attempt to schedule again - assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - ), - Error::::SwapAlreadyScheduled - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_schedule_swap_coldkey_execution --exact --show-output --nocapture -#[test] -fn test_schedule_swap_coldkey_execution() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let stake_amount = DefaultMinStake::::get().to_u64() * 10; - let reserve = stake_amount * 10; - - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - add_network(netuid, 13, 0); - register_ok_neuron(netuid, hotkey, old_coldkey, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 1000000000000000); - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(old_coldkey), - hotkey, - netuid, - stake_amount.into() - )); - - // Check initial ownership - assert_eq!( - Owner::::get(hotkey), - old_coldkey, - "Initial ownership check failed" - ); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - // Schedule the swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - )); - - // Get the scheduled execution block - let current_block = System::block_number(); - let execution_block = current_block + ColdkeySwapScheduleDuration::::get(); - - System::assert_last_event( - Event::ColdkeySwapScheduled { - old_coldkey, - new_coldkey, - execution_block, - swap_cost, - } - .into(), - ); - - run_to_block(execution_block - 1); - - let stake_before_swap = SubtensorModule::get_total_stake_for_coldkey(&old_coldkey); - - run_to_block(execution_block); - - // Run on_initialize for the execution block - >::on_initialize(execution_block); - - // Also run Scheduler's on_initialize - as OnInitialize>::on_initialize( - execution_block, - ); - - // Check if the swap has occurred - let new_owner = Owner::::get(hotkey); - assert_eq!( - new_owner, new_coldkey, - "Ownership was not updated as expected" - ); - - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), - stake_before_swap, - "Stake was not transferred to new coldkey" - ); - assert_eq!( - SubtensorModule::get_total_stake_for_coldkey(&old_coldkey), - TaoCurrency::ZERO, - "Old coldkey still has stake" - ); - - // Check for the SwapExecuted event - System::assert_has_event( - Event::ColdkeySwapped { - old_coldkey, - new_coldkey, - swap_cost, - } - .into(), - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_direct_swap_coldkey_call_fails --exact --nocapture -#[test] -fn test_direct_swap_coldkey_call_fails() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - assert_noop!( - SubtensorModule::swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - old_coldkey, - new_coldkey, - TaoCurrency::ZERO - ), - BadOrigin - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_with_pending_swap --exact --nocapture -#[test] -fn test_schedule_swap_coldkey_with_pending_swap() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey1 = U256::from(2); - let new_coldkey2 = U256::from(3); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + 1_000); - - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey1 - )); - - // Attempt to schedule another swap before the first one executes - assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey2 - ), - Error::::SwapAlreadyScheduled - ); - }); -} - -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_failure_and_reschedule --exact --nocapture -#[test] -fn test_schedule_swap_coldkey_failure_and_reschedule() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey1 = U256::from(2); - let new_coldkey2 = U256::from(3); - - let swap_cost = SubtensorModule::get_key_swap_cost(); - - // Two swaps - SubtensorModule::add_balance_to_coldkey_account( - &old_coldkey, - swap_cost.to_u64() + 1_000 * 2, - ); - - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey1 - )); - - let current_block = >::block_number(); - let duration = ColdkeySwapScheduleDuration::::get(); - let when = current_block.saturating_add(duration); - - // Setup first key to fail - // -- will fail if the new coldkey is already a hotkey (has an Owner) - Owner::::insert(new_coldkey1, U256::from(4)); - - // First swap fails - run_to_block(when - 1); - next_block(); - - // Check the failure - next_block(); // Still in the scheduled-swap map - assert!(ColdkeySwapScheduled::::contains_key(old_coldkey)); - - // Try to schedule the second swap - assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey2 - ), - Error::::SwapAlreadyScheduled - ); - - // Wait for correct duration after first swap fails - let fail_duration = ColdkeySwapRescheduleDuration::::get(); - run_to_block(when + fail_duration); - - // Schedule the second swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey2 - )); - - let current_block = >::block_number(); - let duration = ColdkeySwapScheduleDuration::::get(); - let when = current_block.saturating_add(duration); - run_to_block(when - 1); - next_block(); - - // Check the success - next_block(); // Now in the scheduled-swap map - assert!(!ColdkeySwapScheduled::::contains_key(old_coldkey)); - }); -} - -#[test] -fn test_coldkey_swap_delegate_identity_updated() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - let netuid = NetUid::from(1); - let burn_cost = TaoCurrency::from(10); - let tempo = 1; - - SubtensorModule::set_burn(netuid, burn_cost); - add_network(netuid, tempo, 0); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); - mock::setup_reserves(netuid, 1_000_000_000_000.into(), 1_000_000_000_000.into()); - - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(old_coldkey), - netuid, - old_coldkey - )); - - let name: Vec = b"The Third Coolest Identity".to_vec(); - let identity: ChainIdentityV2 = ChainIdentityV2 { - name: name.clone(), - url: vec![], - image: vec![], - github_repo: vec![], - discord: vec![], - description: vec![], - additional: vec![], - }; - - IdentitiesV2::::insert(old_coldkey, identity.clone()); - - assert!(IdentitiesV2::::get(old_coldkey).is_some()); - assert!(IdentitiesV2::::get(new_coldkey).is_none()); - - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - burn_cost - )); - - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - assert_eq!( - IdentitiesV2::::get(new_coldkey).expect("Expected an Identity"), - identity - ); - }); -} - -#[test] -fn test_coldkey_swap_no_identity_no_changes() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(1); - let new_coldkey = U256::from(2); - - let netuid = NetUid::from(1); - let burn_cost = TaoCurrency::from(10); - let tempo = 1; - - SubtensorModule::set_burn(netuid, burn_cost); - add_network(netuid, tempo, 0); - - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); - mock::setup_reserves(netuid, 1_000_000_000_000.into(), 1_000_000_000_000.into()); - - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(old_coldkey), - netuid, - old_coldkey - )); - - // Ensure the old coldkey does not have an identity before the swap - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - - // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - burn_cost - )); - - // Ensure no identities have been changed - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_none()); - }); -} - -#[test] -fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(3); - let new_coldkey = U256::from(4); - - let netuid = NetUid::from(1); - let burn_cost = TaoCurrency::from(10); - let tempo = 1; + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate3), + vec![delegate3, hotkey3] + ); - SubtensorModule::set_burn(netuid, burn_cost); - add_network(netuid, tempo, 0); - SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, 100_000_000_000); - mock::setup_reserves(netuid, 1_000_000_000_000.into(), 1_000_000_000_000.into()); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator1), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator2), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator3), vec![]); - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(old_coldkey), - netuid, - old_coldkey - )); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator1), + vec![hotkey1, delegate1] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator2), + vec![hotkey2, delegate2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator3), + vec![hotkey3, delegate3] + ); - let name: Vec = b"The Coolest Identity".to_vec(); - let identity: ChainIdentityV2 = ChainIdentityV2 { - name: name.clone(), - url: vec![], - github_repo: vec![], - image: vec![], - discord: vec![], - description: vec![], - additional: vec![], - }; + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); - IdentitiesV2::::insert(new_coldkey, identity.clone()); - // Ensure the new coldkey does have an identity before the swap - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - assert!(IdentitiesV2::::get(old_coldkey).is_none()); + // Perform the swap + let new_coldkey = U256::from(1100); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&coldkey), + ck_stake + ); + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + ck_stake + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&coldkey), + TaoCurrency::ZERO + ); - // Perform the coldkey swap - assert_ok!(SubtensorModule::do_swap_coldkey( - &old_coldkey, - &new_coldkey, - burn_cost - )); + // Check everything is swapped. + assert_eq!( + SubtensorModule::get_owned_hotkeys(&new_coldkey), + vec![hotkey1, hotkey2, hotkey3] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&new_coldkey), + vec![hotkey1, hotkey2, hotkey3, delegate1, delegate2, delegate3] + ); + // Shouldn't change. + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey1), + hk1_stake + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey2), + hk2_stake + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&hotkey3), + hk3_stake + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&delegate1), + d1_stake + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&delegate2), + d2_stake + ); + assert_eq!( + SubtensorModule::get_total_stake_for_hotkey(&delegate3), + d3_stake + ); - // Ensure no identities have been changed - assert!(IdentitiesV2::::get(old_coldkey).is_none()); - assert!(IdentitiesV2::::get(new_coldkey).is_some()); - }); -} + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate1), + vec![delegate1] + ); + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate2), + vec![delegate2] + ); + assert_eq!( + SubtensorModule::get_owned_hotkeys(&delegate3), + vec![delegate3] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate1), + vec![delegate1, hotkey1] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate2), + vec![delegate2, hotkey2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&delegate3), + vec![delegate3, hotkey3] + ); -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_cant_schedule_swap_without_enough_to_burn --exact --nocapture -#[test] -fn test_cant_schedule_swap_without_enough_to_burn() { - new_test_ext(1).execute_with(|| { - let old_coldkey = U256::from(3); - let new_coldkey = U256::from(4); - let hotkey = U256::from(5); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator1), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator2), vec![]); + assert_eq!(SubtensorModule::get_owned_hotkeys(&nominator3), vec![]); - let burn_cost = SubtensorModule::get_key_swap_cost(); - assert_noop!( - SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(old_coldkey), - new_coldkey - ), - Error::::NotEnoughBalanceToPaySwapColdKey + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator1), + vec![hotkey1, delegate1] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator2), + vec![hotkey2, delegate2] + ); + assert_eq!( + SubtensorModule::get_all_staked_hotkeys(&nominator3), + vec![hotkey3, delegate3] ); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_in_swap_schedule_prevents_funds_usage --exact --show-output --nocapture #[test] -fn test_coldkey_in_swap_schedule_prevents_funds_usage() { - // Testing the signed extension validate function - // correctly filters transactions that attempt to use funds - // while a coldkey swap is scheduled. - - new_test_ext(0).execute_with(|| { - let netuid = NetUid::from(1); - let version_key: u64 = 0; - let coldkey = U256::from(0); - let new_coldkey = U256::from(1); - let hotkey: U256 = U256::from(2); // Add the hotkey field - assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - - let stake = 100_000_000_000; - let reserve = stake * 100; +fn test_do_swap_coldkey_effect_on_delegations() { + new_test_ext(1).execute_with(|| { + let new_coldkey = U256::from(0); + let owner = U256::from(1); + let coldkey = U256::from(4); + let delegate = U256::from(2); + let netuid = NetUid::from(0); // Stake to 0 + let netuid2 = NetUid::from(1); // Stake to 1 + let stake = DefaultMinStake::::get().to_u64() * 10; + let reserve = stake * 1000; mock::setup_reserves(netuid, reserve.into(), reserve.into()); + mock::setup_reserves(netuid2, reserve.into(), reserve.into()); - let who = coldkey; // The coldkey signs this transaction - - // Disallowed transactions are - // - add_stake - // - add_stake_limit - // - swap_stake - // - swap_stake_limit - // - move_stake - // - transfer_stake - // - balances.transfer_all - // - balances.transfer_allow_death - // - balances.transfer_keep_alive - - // Allowed transactions are: - // - remove_stake - // - remove_stake_limit - // others... - - // Create netuid - add_network(netuid, 1, 0); - // Register the hotkey - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); + add_network(netuid, 13, 0); // root + add_network(netuid2, 13, 0); - SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); + assert_ok!(SubtensorModule::root_register( + <::RuntimeOrigin>::signed(owner), + delegate + )); // register on root + register_ok_neuron(netuid2, delegate, owner, 0); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, stake * 10); + + // since the reserves are equal and we stake the same amount to both networks, we can reuse + // this values for different networks. but you should take it into account in case of tests + // changes + let (expected_stake, fee) = mock::swap_tao_to_alpha(netuid, stake.into()); - // Set the minimum stake to 0. - SubtensorModule::set_stake_threshold(0); - // Add stake to the hotkey assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(who), - hotkey, + <::RuntimeOrigin>::signed(coldkey), + delegate, netuid, stake.into() )); - // Schedule the coldkey for a swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(who), - new_coldkey + // Add stake to netuid2 + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey), + delegate, + netuid2, + stake.into() )); - assert!(ColdkeySwapScheduled::::contains_key(who)); + let swap_cost = SubtensorModule::get_key_swap_cost(); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.to_u64()); - // Setup the extension - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); + // Perform the swap + assert_ok!(SubtensorModule::do_swap_coldkey(&coldkey, &new_coldkey,)); - // Try each call + // Verify stake was moved for the delegate + let approx_total_stake = TaoCurrency::from(stake * 2 - fee * 2); + assert_abs_diff_eq!( + SubtensorModule::get_total_stake_for_hotkey(&delegate), + approx_total_stake, + epsilon = approx_total_stake / 100.into() + ); + assert_eq!( + SubtensorModule::get_total_stake_for_coldkey(&coldkey), + TaoCurrency::ZERO + ); + assert_abs_diff_eq!( + SubtensorModule::get_total_stake_for_coldkey(&new_coldkey), + approx_total_stake, + epsilon = approx_total_stake / 100.into() + ); + assert_eq!( + expected_stake, + Alpha::::get((delegate, new_coldkey, netuid)) + .to_num::() + .into(), + ); + assert_eq!(Alpha::::get((delegate, coldkey, netuid)), 0); - // Add stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { - hotkey, - netuid, - amount_staked: stake.into(), - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Add stake limit - let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { - hotkey, - netuid, - amount_staked: stake.into(), - limit_price: stake.into(), - allow_partial: false, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Swap stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_stake { - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Swap stake limit - let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_stake_limit { - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - limit_price: stake.into(), - allow_partial: false, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Move stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::move_stake { - origin_hotkey: hotkey, - destination_hotkey: hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Transfer stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::transfer_stake { - destination_coldkey: new_coldkey, - hotkey, - origin_netuid: netuid, - destination_netuid: netuid, - alpha_amount: stake.into(), - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Transfer all - let call = RuntimeCall::Balances(BalancesCall::transfer_all { - dest: new_coldkey, - keep_alive: false, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Transfer keep alive - let call = RuntimeCall::Balances(BalancesCall::transfer_keep_alive { - dest: new_coldkey, - value: 100_000_000_000, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Transfer allow death - let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { - dest: new_coldkey, - value: 100_000_000_000, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Burned register - let call = RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - remove_stake_rate_limit_for_tests(&hotkey, &coldkey, netuid); - - // Remove stake - let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { - hotkey, - netuid, - amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Remove stake limit - let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { - hotkey, - netuid, - amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), - limit_price: 123456789.into(), // should be low enough - allow_partial: true, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); - - // Schedule swap should succeed - let call = RuntimeCall::SubtensorModule(SubtensorCall::schedule_swap_coldkey { - new_coldkey: hotkey, - }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should be ok - assert_ok!(result); + assert_eq!( + expected_stake, + Alpha::::get((delegate, new_coldkey, netuid2)) + .to_num::() + .into() + ); + assert_eq!(Alpha::::get((delegate, coldkey, netuid2)), 0); }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_in_swap_schedule_prevents_critical_calls --exact --show-output --nocapture #[test] -fn test_coldkey_in_swap_schedule_prevents_critical_calls() { - // Testing the signed extension validate function - // correctly filters transactions that are critical - // while a coldkey swap is scheduled. - +fn test_subtensor_extension_rejects_any_call_that_is_not_swap_coldkey_announced() { new_test_ext(0).execute_with(|| { let netuid = NetUid::from(1); - let version_key: u64 = 0; - let coldkey = U256::from(0); + let who = U256::from(0); let new_coldkey = U256::from(1); - let hotkey: U256 = U256::from(2); // Add the hotkey field - assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! - let stake = 100_000_000_000; - let reserve = stake * 10; + let new_coldkey_hash = ::Hashing::hash_of(&new_coldkey); + let hotkey = U256::from(2); + let stake = DefaultMinStake::::get().to_u64(); + assert_ne!(hotkey, who); + // Setup reserves + let reserve = stake * 10; mock::setup_reserves(netuid, reserve.into(), reserve.into()); - let who = coldkey; // The coldkey signs this transaction - - // Disallowed transactions are - // - dissolve_network - - // Create netuid + // Setup network and neuron add_network(netuid, 1, 0); - // Register the hotkey - SubtensorModule::append_neuron(netuid, &hotkey, 0); - crate::Owner::::insert(hotkey, coldkey); + register_ok_neuron(netuid, hotkey, who, 0); SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); - // Set the minimum stake to 0. - SubtensorModule::set_stake_threshold(0); - // Add stake to the hotkey - assert_ok!(SubtensorModule::add_stake( - <::RuntimeOrigin>::signed(who), - hotkey, - netuid, - stake.into() - )); - // Schedule the coldkey for a swap - assert_ok!(SubtensorModule::schedule_swap_coldkey( - <::RuntimeOrigin>::signed(who), - new_coldkey - )); - - assert!(ColdkeySwapScheduled::::contains_key(who)); - - // Setup the extension - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - - // Try each call - - // Dissolve network + assert_ok!(SubtensorModule::announce_coldkey_swap( + ::RuntimeOrigin::signed(who), + new_coldkey_hash, + )); + assert!(ColdkeySwapAnnouncements::::contains_key(who)); + + let forbidden_calls: Vec = vec![ + RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { + netuid, + coldkey: who, + }), + RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey, + netuid, + amount_staked: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { + hotkey, + netuid, + amount_staked: stake.into(), + limit_price: stake.into(), + allow_partial: false, + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_stake { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::swap_stake_limit { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + limit_price: stake.into(), + allow_partial: false, + }), + RuntimeCall::SubtensorModule(SubtensorCall::move_stake { + origin_hotkey: hotkey, + destination_hotkey: hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::transfer_stake { + destination_coldkey: new_coldkey, + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: stake.into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { + hotkey, + netuid, + amount_unstaked: (DefaultMinStake::::get().to_u64() * 2).into(), + }), + RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { + hotkey, + netuid, + amount_unstaked: (stake * 2).into(), + limit_price: 123456789.into(), + allow_partial: true, + }), + RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }), + RuntimeCall::Balances(BalancesCall::transfer_all { + dest: new_coldkey, + keep_alive: false, + }), + RuntimeCall::Balances(BalancesCall::transfer_keep_alive { + dest: new_coldkey, + value: 100_000_000_000, + }), + RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: new_coldkey, + value: 100_000_000_000, + }), + ]; + + for call in forbidden_calls { + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_noop!( + ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0), + CustomTransactionError::ColdkeySwapAnnounced + ); + } + + // Swap coldkey announced should succeed let call = - RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { netuid, coldkey }); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.clone(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); - // Should fail - assert_eq!( - // Should get an invalid transaction error - result.unwrap_err(), - CustomTransactionError::ColdkeyInSwapSchedule.into() - ); + RuntimeCall::SubtensorModule(SubtensorCall::swap_coldkey_announced { new_coldkey }); + let info = call.get_dispatch_info(); + let ext = SubtensorTransactionExtension::::new(); + assert_ok!(ext.dispatch_transaction(RuntimeOrigin::signed(who).into(), call, &info, 0, 0)); }); } #[test] -fn test_swap_auto_stake_destination_coldkeys() { +#[allow(deprecated)] +fn test_swap_coldkey_deprecated() { new_test_ext(1).execute_with(|| { let old_coldkey = U256::from(1); let new_coldkey = U256::from(2); - let hotkey = U256::from(3); - let netuid = NetUid::from(1u16); - let coldkeys = vec![U256::from(4), U256::from(5), old_coldkey]; - add_network(netuid, 1, 0); - AutoStakeDestinationColdkeys::::insert(hotkey, netuid, coldkeys.clone()); - AutoStakeDestination::::insert(old_coldkey, netuid, hotkey); + assert_noop!( + SubtensorModule::swap_coldkey( + <::RuntimeOrigin>::root(), + old_coldkey, + new_coldkey, + TaoCurrency::MAX + ), + Error::::Deprecated + ); + }); +} - let mut weight = Weight::zero(); - assert_ok!(SubtensorModule::perform_swap_coldkey( - &old_coldkey, - &new_coldkey, - &mut weight - )); +#[test] +#[allow(deprecated)] +fn test_schedule_swap_coldkey_deprecated() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); - let new_coldkeys = AutoStakeDestinationColdkeys::::get(hotkey, netuid); - assert!(new_coldkeys.contains(&new_coldkey)); - assert!(!new_coldkeys.contains(&old_coldkey)); - assert_eq!( - AutoStakeDestination::::try_get(old_coldkey, netuid), - Err(()) - ); - assert_eq!( - AutoStakeDestination::::try_get(new_coldkey, netuid), - Ok(hotkey) + assert_noop!( + SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::root(), + new_coldkey, + ), + Error::::Deprecated ); }); } + +// TEST TRANSFER ROOT CLAIM WITH NEW KEYS + ROOT CASE diff --git a/pallets/subtensor/src/transaction_extension.rs b/pallets/subtensor/src/transaction_extension.rs index cf1d410ea9..32eb057f98 100644 --- a/pallets/subtensor/src/transaction_extension.rs +++ b/pallets/subtensor/src/transaction_extension.rs @@ -1,24 +1,27 @@ use crate::{ - BalancesCall, Call, ColdkeySwapScheduled, Config, CustomTransactionError, Error, Pallet, + BalancesCall, Call, ColdkeySwapAnnouncements, Config, CustomTransactionError, Error, Pallet, TransactionType, }; use codec::{Decode, DecodeWithMemTracking, Encode}; use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; -use frame_support::pallet_prelude::Weight; use frame_support::traits::IsSubType; use scale_info::TypeInfo; use sp_runtime::traits::{ AsSystemOriginSigner, DispatchInfoOf, Dispatchable, Implication, TransactionExtension, ValidateResult, }; -use sp_runtime::transaction_validity::{ - TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, +use sp_runtime::{ + impl_tx_ext_default, + transaction_validity::{TransactionSource, TransactionValidity, ValidTransaction}, }; use sp_std::marker::PhantomData; use sp_std::vec::Vec; use subtensor_macros::freeze_struct; use subtensor_runtime_common::{NetUid, NetUidStorageIndex}; +type CallOf = ::RuntimeCall; +type OriginOf = ::RuntimeOrigin; + #[freeze_struct("2e02eb32e5cb25d3")] #[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)] pub struct SubtensorTransactionExtension(pub PhantomData); @@ -31,9 +34,7 @@ impl sp_std::fmt::Debug for SubtensorTransac impl SubtensorTransactionExtension where - ::RuntimeCall: - Dispatchable, - ::RuntimeCall: IsSubType>, + CallOf: Dispatchable + IsSubType>, { pub fn new() -> Self { Self(Default::default()) @@ -52,30 +53,29 @@ where pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { if let Err(err) = result { Err(match err { - Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow.into(), - Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists.into(), - Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow.into(), + Error::::AmountTooLow => CustomTransactionError::StakeAmountTooLow, + Error::::SubnetNotExists => CustomTransactionError::SubnetNotExists, + Error::::NotEnoughBalanceToStake => CustomTransactionError::BalanceTooLow, Error::::HotKeyAccountNotExists => { - CustomTransactionError::HotkeyAccountDoesntExist.into() + CustomTransactionError::HotkeyAccountDoesntExist } Error::::NotEnoughStakeToWithdraw => { - CustomTransactionError::NotEnoughStakeToWithdraw.into() - } - Error::::InsufficientLiquidity => { - CustomTransactionError::InsufficientLiquidity.into() + CustomTransactionError::NotEnoughStakeToWithdraw } - Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh.into(), - Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed.into(), + Error::::InsufficientLiquidity => CustomTransactionError::InsufficientLiquidity, + Error::::SlippageTooHigh => CustomTransactionError::SlippageTooHigh, + Error::::TransferDisallowed => CustomTransactionError::TransferDisallowed, Error::::HotKeyNotRegisteredInNetwork => { - CustomTransactionError::HotKeyNotRegisteredInNetwork.into() + CustomTransactionError::HotKeyNotRegisteredInNetwork } - Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress.into(), + Error::::InvalidIpAddress => CustomTransactionError::InvalidIpAddress, Error::::ServingRateLimitExceeded => { - CustomTransactionError::ServingRateLimitExceeded.into() + CustomTransactionError::ServingRateLimitExceeded } - Error::::InvalidPort => CustomTransactionError::InvalidPort.into(), - _ => CustomTransactionError::BadRequest.into(), - }) + Error::::InvalidPort => CustomTransactionError::InvalidPort, + _ => CustomTransactionError::BadRequest, + } + .into()) } else { Ok(ValidTransaction { priority, @@ -89,52 +89,46 @@ impl TransactionExtension<::RuntimeCall> for SubtensorTransactionExtension where - ::RuntimeCall: - Dispatchable, - ::RuntimeOrigin: AsSystemOriginSigner + Clone, - ::RuntimeCall: IsSubType>, - ::RuntimeCall: IsSubType>, + CallOf: Dispatchable + + IsSubType> + + IsSubType>, + OriginOf: AsSystemOriginSigner + Clone, { const IDENTIFIER: &'static str = "SubtensorTransactionExtension"; type Implicit = (); - type Val = Option; + type Val = (); type Pre = (); - fn weight(&self, _call: &::RuntimeCall) -> Weight { - // TODO: benchmark transaction extension - Weight::zero() - } - fn validate( &self, - origin: ::RuntimeOrigin, - call: &::RuntimeCall, - _info: &DispatchInfoOf<::RuntimeCall>, + origin: OriginOf, + call: &CallOf, + _info: &DispatchInfoOf>, _len: usize, _self_implicit: Self::Implicit, _inherited_implication: &impl Implication, _source: TransactionSource, - ) -> ValidateResult::RuntimeCall> { + ) -> ValidateResult> { // Ensure the transaction is signed, else we just skip the extension. let Some(who) = origin.as_system_origin_signer() else { - return Ok((Default::default(), None, origin)); + return Ok((Default::default(), (), origin)); }; - // Verify ColdkeySwapScheduled map for coldkey - match call.is_sub_type() { - // Whitelist - Some(Call::schedule_swap_coldkey { .. }) => {} - _ => { - if ColdkeySwapScheduled::::contains_key(who) { - return Err(CustomTransactionError::ColdkeyInSwapSchedule.into()); - } - } + // Ensure the origin coldkey is not announced for a swap. + if ColdkeySwapAnnouncements::::contains_key(who) + && !matches!( + call.is_sub_type(), + Some(Call::swap_coldkey_announced { .. }) + ) + { + return Err(CustomTransactionError::ColdkeySwapAnnounced.into()); } + match call.is_sub_type() { Some(Call::commit_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::StakeAmountTooLow.into()) } @@ -158,7 +152,7 @@ where match Pallet::::find_commit_block_via_hash(provided_hash) { Some(commit_block) => { if Pallet::::is_reveal_block_range(*netuid, commit_block) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) } @@ -203,7 +197,7 @@ where if provided_hashes.len() == batch_reveal_block.len() { if Pallet::::is_batch_reveal_block_range(*netuid, batch_reveal_block) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::CommitBlockNotInRevealRange.into()) } @@ -219,7 +213,7 @@ where } Some(Call::set_weights { netuid, .. }) => { if Self::check_weights_min_stake(who, *netuid) { - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::StakeAmountTooLow.into()) } @@ -233,7 +227,7 @@ where if *reveal_round < pallet_drand::LastStoredRound::::get() { return Err(CustomTransactionError::InvalidRevealRound.into()); } - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } else { Err(CustomTransactionError::StakeAmountTooLow.into()) } @@ -249,7 +243,7 @@ where return Err(CustomTransactionError::RateLimitExceeded.into()); } - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } Some(Call::serve_axon { netuid, @@ -276,41 +270,25 @@ where ), 0u64, ) - .map(|validity| (validity, Some(who.clone()), origin.clone())) + .map(|validity| (validity, (), origin.clone())) } Some(Call::register_network { .. }) => { if !TransactionType::RegisterNetwork.passes_rate_limit::(who) { return Err(CustomTransactionError::RateLimitExceeded.into()); } - Ok((Default::default(), Some(who.clone()), origin)) + Ok((Default::default(), (), origin)) } Some(Call::associate_evm_key { netuid, .. }) => { - match Pallet::::get_uid_for_net_and_hotkey(*netuid, who) { - Ok(uid) => { - match Pallet::::ensure_evm_key_associate_rate_limit(*netuid, uid) { - Ok(_) => Ok((Default::default(), Some(who.clone()), origin)), - Err(_) => { - Err(CustomTransactionError::EvmKeyAssociateRateLimitExceeded.into()) - } - } - } - Err(_) => Err(CustomTransactionError::UidNotFound.into()), - } + let uid = Pallet::::get_uid_for_net_and_hotkey(*netuid, who) + .map_err(|_| CustomTransactionError::UidNotFound)?; + Pallet::::ensure_evm_key_associate_rate_limit(*netuid, uid) + .map_err(|_| CustomTransactionError::EvmKeyAssociateRateLimitExceeded)?; + Ok((Default::default(), (), origin)) } - _ => Ok((Default::default(), Some(who.clone()), origin)), + _ => Ok((Default::default(), (), origin)), } } - // NOTE: Add later when we put in a pre and post dispatch step. - fn prepare( - self, - _val: Self::Val, - _origin: &::RuntimeOrigin, - _call: &::RuntimeCall, - _info: &DispatchInfoOf<::RuntimeCall>, - _len: usize, - ) -> Result { - Ok(()) - } + impl_tx_ext_default!(::RuntimeCall; weight prepare); } diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index cf2d55d168..cbf1e3fa8d 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -830,19 +830,9 @@ impl Pallet { TransferToggle::::get(netuid) } - /// Set the duration for coldkey swap - /// - /// # Arguments - /// - /// * `duration` - The blocks for coldkey swap execution. - /// - /// # Effects - /// - /// * Update the ColdkeySwapScheduleDuration storage. - /// * Emits a ColdkeySwapScheduleDurationSet evnet. - pub fn set_coldkey_swap_schedule_duration(duration: BlockNumberFor) { - ColdkeySwapScheduleDuration::::set(duration); - Self::deposit_event(Event::ColdkeySwapScheduleDurationSet(duration)); + pub fn set_coldkey_swap_announcement_delay(duration: BlockNumberFor) { + ColdkeySwapAnnouncementDelay::::set(duration); + Self::deposit_event(Event::ColdkeySwapAnnouncementDelaySet(duration)); } /// Set the duration for dissolve network diff --git a/pallets/transaction-fee/src/tests/mock.rs b/pallets/transaction-fee/src/tests/mock.rs index ee5b1693ba..4ebef59c38 100644 --- a/pallets/transaction-fee/src/tests/mock.rs +++ b/pallets/transaction-fee/src/tests/mock.rs @@ -196,17 +196,13 @@ parameter_types! { 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. - // pub const InitialSubnetLimit: u16 = 10; // (DEPRECATED) pub const InitialNetworkRateLimit: u64 = 0; 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 pub const InitialYuma3On: bool = false; // Default value for Yuma3On - // pub const InitialHotkeyEmissionTempo: u64 = 1; // (DEPRECATED) - // pub const InitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) - pub const InitialColdkeySwapScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapRescheduleDuration: u64 = 24 * 60 * 60 / 12; // 1 day + pub const InitialColdkeySwapAnnouncementDelay: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks @@ -275,8 +271,7 @@ impl pallet_subtensor::Config for Test { type LiquidAlphaOn = InitialLiquidAlphaOn; type Yuma3On = InitialYuma3On; type Preimages = (); - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 391036e380..637112de29 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1039,7 +1039,6 @@ parameter_types! { 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) pub const SubtensorInitialNetworkLockReductionInterval: u64 = 14 * 7200; pub const SubtensorInitialNetworkRateLimit: u64 = 7200; pub const SubtensorInitialKeySwapCost: u64 = 100_000_000; // 0.1 TAO @@ -1047,14 +1046,11 @@ parameter_types! { pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn pub const InitialYuma3On: bool = false; // Default value for Yuma3On - // pub const SubtensorInitialNetworkMaxStake: u64 = u64::MAX; // (DEPRECATED) - pub const InitialColdkeySwapScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days - pub const InitialColdkeySwapRescheduleDuration: BlockNumber = 24 * 60 * 60 / 12; // 1 day + pub const InitialColdkeySwapAnnouncementDelay: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks - // 7 * 24 * 60 * 60 / 12 = 7 days - pub const DurationOfStartCall: u64 = prod_or_fast!(7 * 24 * 60 * 60 / 12, 10); + pub const DurationOfStartCall: u64 = prod_or_fast!(7 * 24 * 60 * 60 / 12, 10); // 7 days 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 @@ -1120,8 +1116,7 @@ impl pallet_subtensor::Config for Runtime { type Yuma3On = InitialYuma3On; type InitialTaoWeight = SubtensorInitialTaoWeight; type Preimages = Preimage; - type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; - type InitialColdkeySwapRescheduleDuration = InitialColdkeySwapRescheduleDuration; + type InitialColdkeySwapAnnouncementDelay = InitialColdkeySwapAnnouncementDelay; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; type DurationOfStartCall = DurationOfStartCall;