diff --git a/.github/workflows/check-node-compat.yml b/.github/workflows/check-node-compat.yml index b52a7a88ba..1c81a4d2a7 100644 --- a/.github/workflows/check-node-compat.yml +++ b/.github/workflows/check-node-compat.yml @@ -22,8 +22,10 @@ jobs: strategy: matrix: version: - - { name: old, ref: devnet-ready } - - { name: new, ref: ${{ github.head_ref }} } + - name: old + ref: devnet-ready + - name: new + ref: ${{ github.head_ref }} steps: - name: Install dependencies diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index db5b4fe2cc..e4f9597183 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -587,7 +587,11 @@ impl Pallet { LastRateLimitedBlock::::get(rate_limit_key) } pub fn set_rate_limited_last_block(rate_limit_key: &RateLimitKey, block: u64) { - LastRateLimitedBlock::::insert(rate_limit_key, block); + if block == 0 { + LastRateLimitedBlock::::remove(rate_limit_key); + } else { + LastRateLimitedBlock::::insert(rate_limit_key, block); + } } pub fn remove_rate_limited_last_block(rate_limit_key: &RateLimitKey) { LastRateLimitedBlock::::remove(rate_limit_key); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 460a754d45..ea5a30c000 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -598,13 +598,18 @@ impl Pallet { log::debug!("hotkey: {hotkey:?} alpha_divs: {alpha_divs:?}"); Self::increase_stake_for_hotkey_on_subnet(&hotkey, netuid, tou64!(alpha_divs).into()); // Record dividends for this hotkey. - AlphaDividendsPerSubnet::::mutate(netuid, &hotkey, |divs| { - *divs = divs.saturating_add(tou64!(alpha_divs).into()); - }); + let alpha_divs_u64: u64 = tou64!(alpha_divs); + if alpha_divs_u64 != 0 { + AlphaDividendsPerSubnet::::mutate(netuid, &hotkey, |divs| { + *divs = divs.saturating_add(alpha_divs_u64.into()); + }); + } // Record total hotkey alpha based on which this value of AlphaDividendsPerSubnet // was calculated let total_hotkey_alpha = TotalHotkeyAlpha::::get(&hotkey, netuid); - TotalHotkeyAlphaLastEpoch::::insert(hotkey, netuid, total_hotkey_alpha); + if !total_hotkey_alpha.is_zero() { + TotalHotkeyAlphaLastEpoch::::insert(hotkey, netuid, total_hotkey_alpha); + } } // Distribute root alpha divs. diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 899e8d32f2..2f0d62d1f9 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -166,7 +166,9 @@ mod hooks { // Fix staking hot keys .saturating_add(migrations::migrate_fix_staking_hot_keys::migrate_fix_staking_hot_keys::()) // Migrate coldkey swap scheduled to announcements - .saturating_add(migrations::migrate_coldkey_swap_scheduled_to_announcements::migrate_coldkey_swap_scheduled_to_announcements::()); + .saturating_add(migrations::migrate_coldkey_swap_scheduled_to_announcements::migrate_coldkey_swap_scheduled_to_announcements::()) + // Remove zero-valued entries from Alpha and related storage maps + .saturating_add(migrations::migrate_remove_zero_alpha::migrate_remove_zero_alpha::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_remove_zero_alpha.rs b/pallets/subtensor/src/migrations/migrate_remove_zero_alpha.rs new file mode 100644 index 0000000000..befc876ebc --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_remove_zero_alpha.rs @@ -0,0 +1,98 @@ +use super::*; +use frame_support::{traits::Get, weights::Weight}; +use log; +use scale_info::prelude::string::String; + +pub fn migrate_remove_zero_alpha() -> Weight { + let migration_name = b"migrate_remove_zero_alpha".to_vec(); + let mut weight = T::DbWeight::get().reads(1); + + // ------------------------------ + // Step 0: Check if already run + // ------------------------------ + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // ------------------------------ + // Step 1: Remove zero entries in Alpha (StorageNMap, value type: U64F64) + // ------------------------------ + let mut removed_alpha = 0u64; + + for ((hotkey, coldkey, netuid), value) in Alpha::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if value == 0 { + Alpha::::remove((&hotkey, &coldkey, netuid)); + removed_alpha = removed_alpha.saturating_add(1); + } + } + weight = weight.saturating_add(T::DbWeight::get().writes(removed_alpha)); + log::info!("Removed {removed_alpha} zero entries from Alpha."); + + // ------------------------------ + // Step 2: Remove zero entries in TotalHotkeyShares (value type: U64F64) + // ------------------------------ + let mut removed_shares = 0u64; + + for (hotkey, netuid, value) in TotalHotkeyShares::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if value == 0 { + TotalHotkeyShares::::remove(&hotkey, netuid); + removed_shares = removed_shares.saturating_add(1); + } + } + weight = weight.saturating_add(T::DbWeight::get().writes(removed_shares)); + log::info!("Removed {removed_shares} zero entries from TotalHotkeyShares."); + + // ------------------------------ + // Step 3: Remove zero entries in TotalHotkeyAlphaLastEpoch (value type: AlphaBalance) + // ------------------------------ + let mut removed_last_epoch = 0u64; + + for (hotkey, netuid, value) in TotalHotkeyAlphaLastEpoch::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if value.is_zero() { + TotalHotkeyAlphaLastEpoch::::remove(&hotkey, netuid); + removed_last_epoch = removed_last_epoch.saturating_add(1); + } + } + weight = weight.saturating_add(T::DbWeight::get().writes(removed_last_epoch)); + log::info!("Removed {removed_last_epoch} zero entries from TotalHotkeyAlphaLastEpoch."); + + // ------------------------------ + // Step 4: Remove zero entries in AlphaDividendsPerSubnet (value type: AlphaBalance) + // ------------------------------ + let mut removed_dividends = 0u64; + + for (netuid, hotkey, value) in AlphaDividendsPerSubnet::::iter() { + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + if value.is_zero() { + AlphaDividendsPerSubnet::::remove(netuid, &hotkey); + removed_dividends = removed_dividends.saturating_add(1); + } + } + weight = weight.saturating_add(T::DbWeight::get().writes(removed_dividends)); + log::info!("Removed {removed_dividends} zero entries from AlphaDividendsPerSubnet."); + + // ------------------------------ + // Step 5: Mark Migration as Completed + // ------------------------------ + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{}' completed. Removed: Alpha={removed_alpha}, TotalHotkeyShares={removed_shares}, TotalHotkeyAlphaLastEpoch={removed_last_epoch}, AlphaDividendsPerSubnet={removed_dividends}", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 23a2899b94..93c664d3df 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -41,6 +41,7 @@ pub mod migrate_remove_tao_dividends; pub mod migrate_remove_total_hotkey_coldkey_stakes_this_interval; pub mod migrate_remove_unknown_neuron_axon_cert_prom; pub mod migrate_remove_unused_maps_and_values; +pub mod migrate_remove_zero_alpha; pub mod migrate_remove_zero_total_hotkey_alpha; pub mod migrate_reset_bonds_moving_average; pub mod migrate_reset_max_burn; diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 58babb79a6..f07c9953ce 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -263,8 +263,10 @@ impl Pallet { .saturating_to_num(), ); - // Set the new root claimed value. - RootClaimed::::insert((netuid, hotkey, coldkey), new_root_claimed); + // Set the new root claimed value, removing if zero to avoid storage bloat. + if new_root_claimed != 0 { + RootClaimed::::insert((netuid, hotkey, coldkey), new_root_claimed); + } } } @@ -290,8 +292,12 @@ impl Pallet { .saturating_to_num(), ); - // Set the new root_claimed value. - RootClaimed::::insert((netuid, hotkey, coldkey), new_root_claimed); + // Set the new root_claimed value, removing if zero to avoid storage bloat. + if new_root_claimed != 0 { + RootClaimed::::insert((netuid, hotkey, coldkey), new_root_claimed); + } else { + RootClaimed::::remove((netuid, hotkey, coldkey)); + } } } diff --git a/pallets/subtensor/src/staking/set_children.rs b/pallets/subtensor/src/staking/set_children.rs index 8082f22c32..4560d23e1b 100644 --- a/pallets/subtensor/src/staking/set_children.rs +++ b/pallets/subtensor/src/staking/set_children.rs @@ -412,15 +412,11 @@ impl Pallet { for (parent, _) in relations.parents().iter() { let mut ck = ChildKeys::::get(parent.clone(), netuid); PCRelations::::remove_edge(&mut ck, old_hotkey); - ChildKeys::::insert(parent.clone(), netuid, ck); + Self::set_childkeys(parent.clone(), netuid, ck); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); } // 2c) Clear direct maps of old_hotkey - ChildKeys::::insert( - old_hotkey.clone(), - netuid, - Vec::<(u64, T::AccountId)>::new(), - ); + ChildKeys::::remove(old_hotkey.clone(), netuid); Self::set_parentkeys( old_hotkey.clone(), netuid, diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 27fef995b2..94b4989a76 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -160,7 +160,9 @@ impl Pallet { } StakingHotkeys::::remove(old_coldkey); - StakingHotkeys::::insert(new_coldkey, new_staking_hotkeys); + if !new_staking_hotkeys.is_empty() { + StakingHotkeys::::insert(new_coldkey, new_staking_hotkeys); + } } /// Transfer the ownership of the hotkeys owned by the old coldkey to the new coldkey. @@ -178,6 +180,8 @@ impl Pallet { } } OwnedHotkeys::::remove(old_coldkey); - OwnedHotkeys::::insert(new_coldkey, new_owned_hotkeys); + if !new_owned_hotkeys.is_empty() { + OwnedHotkeys::::insert(new_coldkey, new_owned_hotkeys); + } } } diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 1138ed1cde..ce85e3c282 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -364,11 +364,13 @@ impl Pallet { // 1. Swap total hotkey alpha for all subnets it exists on. // TotalHotkeyAlpha( hotkey, netuid ) -> alpha -- the total alpha that the hotkey has on a specific subnet. let alpha = TotalHotkeyAlpha::::take(old_hotkey, netuid); - - TotalHotkeyAlpha::::mutate(new_hotkey, netuid, |value| { - *value = value.saturating_add(alpha) - }); weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + if !alpha.is_zero() { + TotalHotkeyAlpha::::mutate(new_hotkey, netuid, |value| { + *value = value.saturating_add(alpha) + }); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + } // 2. Swap total hotkey shares on all subnets it exists on. // TotalHotkeyShares( hotkey, netuid ) -> share pool denominator for this hotkey on this subnet. @@ -387,8 +389,11 @@ impl Pallet { let total_old_plus_new_shares = total_new_shares.add(&total_old_shares).unwrap_or_default(); - TotalHotkeySharesV2::::insert(new_hotkey, netuid, total_old_plus_new_shares); - weight.saturating_accrue(T::DbWeight::get().writes(3)); + weight.saturating_accrue(T::DbWeight::get().writes(2)); + if !total_old_plus_new_shares.is_zero() { + TotalHotkeySharesV2::::insert(new_hotkey, netuid, total_old_plus_new_shares); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } } // 3. Swap all subnet specific info. @@ -397,8 +402,11 @@ impl Pallet { // IsNetworkMember( hotkey, netuid ) -> bool -- is the hotkey a subnet member. let is_network_member: bool = IsNetworkMember::::get(old_hotkey, netuid); IsNetworkMember::::remove(old_hotkey, netuid); - IsNetworkMember::::insert(new_hotkey, netuid, is_network_member); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + if is_network_member { + IsNetworkMember::::insert(new_hotkey, netuid, is_network_member); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } // 3.2 Swap Uids + Keys. // Keys( netuid, hotkey ) -> uid -- the uid the hotkey has in the network if it is a member. @@ -523,23 +531,28 @@ impl Pallet { // 8.1 Swap TotalHotkeyAlphaLastEpoch let old_alpha = TotalHotkeyAlphaLastEpoch::::take(old_hotkey, netuid); let new_total_hotkey_alpha = TotalHotkeyAlphaLastEpoch::::get(new_hotkey, netuid); - TotalHotkeyAlphaLastEpoch::::insert( - new_hotkey, - netuid, - old_alpha.saturating_add(new_total_hotkey_alpha), - ); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + let combined_alpha_last_epoch = old_alpha.saturating_add(new_total_hotkey_alpha); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 1)); + if !combined_alpha_last_epoch.is_zero() { + TotalHotkeyAlphaLastEpoch::::insert( + new_hotkey, + netuid, + combined_alpha_last_epoch, + ); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } // 8.2 Swap AlphaDividendsPerSubnet let old_hotkey_alpha_dividends = AlphaDividendsPerSubnet::::get(netuid, old_hotkey); let new_hotkey_alpha_dividends = AlphaDividendsPerSubnet::::get(netuid, new_hotkey); AlphaDividendsPerSubnet::::remove(netuid, old_hotkey); - AlphaDividendsPerSubnet::::insert( - netuid, - new_hotkey, - old_hotkey_alpha_dividends.saturating_add(new_hotkey_alpha_dividends), - ); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + let combined_dividends = + old_hotkey_alpha_dividends.saturating_add(new_hotkey_alpha_dividends); + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 1)); + if !combined_dividends.is_zero() { + AlphaDividendsPerSubnet::::insert(netuid, new_hotkey, combined_dividends); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } // 8.3 Swap TaoDividendsPerSubnet // Tao dividends were removed @@ -574,12 +587,16 @@ impl Pallet { let new_alpha = Alpha::::take((new_hotkey, &coldkey, netuid)); Alpha::::remove((old_hotkey, &coldkey, netuid)); - // Insert into AlphaV2 because Alpha is deprecated - AlphaV2::::insert( - (new_hotkey, &coldkey, netuid), - SafeFloat::from(alpha.saturating_add(new_alpha)), - ); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + // Only insert combined alpha if non-zero to avoid storage bloat. + let combined_alpha = alpha.saturating_add(new_alpha); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + if combined_alpha != 0 { + AlphaV2::::insert( + (new_hotkey, &coldkey, netuid), + SafeFloat::from(combined_alpha), + ); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } // Swap StakingHotkeys. // StakingHotkeys( coldkey ) --> Vec -- the hotkeys that the coldkey stakes. @@ -602,11 +619,13 @@ impl Pallet { let new_alpha_v2 = AlphaV2::::take((new_hotkey, &coldkey, netuid)); AlphaV2::::remove((old_hotkey, &coldkey, netuid)); - AlphaV2::::insert( - (new_hotkey, &coldkey, netuid), - alpha.add(&new_alpha_v2).unwrap_or_default(), - ); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); + // Only insert combined alpha if non-zero to avoid storage bloat. + let combined_alpha_v2 = alpha.add(&new_alpha_v2).unwrap_or_default(); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + if !combined_alpha_v2.is_zero() { + AlphaV2::::insert((new_hotkey, &coldkey, netuid), combined_alpha_v2); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + } // Swap StakingHotkeys. // StakingHotkeys( coldkey ) --> Vec -- the hotkeys that the coldkey stakes. diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 7f19c96b3d..98fe49bfd3 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -3122,3 +3122,100 @@ fn test_migrate_coldkey_swap_scheduled_to_announcements() { ); }); } + +#[test] +fn test_migrate_remove_zero_alpha() { + new_test_ext(1).execute_with(|| { + const MIGRATION_NAME: &str = "migrate_remove_zero_alpha"; + let netuid = NetUid::from(1u16); + + let hotkey_zero = U256::from(100u64); + let hotkey_nonzero = U256::from(101u64); + let coldkey_zero = U256::from(200u64); + let coldkey_nonzero = U256::from(201u64); + + let zero = U64F64::from_num(0); + let nonzero = U64F64::from_num(5000); + + // --- Setup: insert zero and non-zero entries across all four maps --- + + // Alpha (StorageNMap) + Alpha::::insert((&hotkey_zero, &coldkey_zero, netuid), zero); + Alpha::::insert((&hotkey_nonzero, &coldkey_nonzero, netuid), nonzero); + + // TotalHotkeyShares + TotalHotkeyShares::::insert(hotkey_zero, netuid, zero); + TotalHotkeyShares::::insert(hotkey_nonzero, netuid, nonzero); + + // TotalHotkeyAlphaLastEpoch + TotalHotkeyAlphaLastEpoch::::insert(hotkey_zero, netuid, AlphaBalance::ZERO); + TotalHotkeyAlphaLastEpoch::::insert(hotkey_nonzero, netuid, AlphaBalance::from(5000)); + + // AlphaDividendsPerSubnet + AlphaDividendsPerSubnet::::insert(netuid, hotkey_zero, AlphaBalance::ZERO); + AlphaDividendsPerSubnet::::insert(netuid, hotkey_nonzero, AlphaBalance::from(5000)); + + // Verify migration has not run yet + assert!( + !HasMigrationRun::::get(MIGRATION_NAME.as_bytes().to_vec()), + "Migration should not have run yet." + ); + + // Run migration + let weight = + crate::migrations::migrate_remove_zero_alpha::migrate_remove_zero_alpha::(); + + // Verify migration is marked as completed + assert!( + HasMigrationRun::::get(MIGRATION_NAME.as_bytes().to_vec()), + "Migration should be marked as run." + ); + assert!(!weight.is_zero(), "Migration weight should be non-zero."); + + // Verify zero entries were removed + assert!( + !Alpha::::contains_key((&hotkey_zero, &coldkey_zero, netuid)), + "Zero Alpha entry should have been removed." + ); + assert!( + !TotalHotkeyShares::::contains_key(hotkey_zero, netuid), + "Zero TotalHotkeyShares entry should have been removed." + ); + assert!( + !TotalHotkeyAlphaLastEpoch::::contains_key(hotkey_zero, netuid), + "Zero TotalHotkeyAlphaLastEpoch entry should have been removed." + ); + assert!( + !AlphaDividendsPerSubnet::::contains_key(netuid, hotkey_zero), + "Zero AlphaDividendsPerSubnet entry should have been removed." + ); + + // Verify non-zero entries were preserved + assert_eq!( + Alpha::::get((&hotkey_nonzero, &coldkey_nonzero, netuid)), + nonzero + ); + assert_eq!( + TotalHotkeyShares::::get(hotkey_nonzero, netuid), + nonzero + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hotkey_nonzero, netuid), + AlphaBalance::from(5000) + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, hotkey_nonzero), + AlphaBalance::from(5000) + ); + + // Verify idempotency: running again should be a no-op + let weight2 = + crate::migrations::migrate_remove_zero_alpha::migrate_remove_zero_alpha::(); + assert_eq!( + weight2, + Weight::from_parts(0, 0) + .saturating_add(::DbWeight::get().reads(1)), + "Second run should only cost a single read (HasMigrationRun check)." + ); + }); +} diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 512f83dd1d..21437533ad 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -1558,6 +1558,41 @@ fn test_schedule_swap_coldkey_deprecated() { }); } +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_coldkey -- test_coldkey_swap_does_not_insert_zero_alpha --exact --nocapture +#[test] +fn test_coldkey_swap_does_not_insert_zero_alpha() { + new_test_ext(1).execute_with(|| { + use substrate_fixed::types::U64F64; + + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(2); + let hotkey = U256::from(3); + let netuid = NetUid::from(1); + + // Add network and register hotkey + add_network(netuid, 1, 0); + register_ok_neuron(netuid, hotkey, old_coldkey, 1001000); + + // Insert zero Alpha entry for the old coldkey + Alpha::::insert((&hotkey, &old_coldkey, netuid), U64F64::from_num(0)); + StakingHotkeys::::insert(old_coldkey, vec![hotkey]); + + // Perform the swap + assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); + + // Verify no zero entry was inserted for the new coldkey + assert!( + !Alpha::::contains_key((&hotkey, &new_coldkey, netuid)), + "Alpha should not contain a zero entry for new_coldkey" + ); + // Verify old entry was removed + assert!( + !Alpha::::contains_key((&hotkey, &old_coldkey, netuid)), + "Alpha should not contain entry for old_coldkey" + ); + }); +} + #[macro_export] macro_rules! comprehensive_setup { ( diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index d0a1de3526..225311bf9f 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -1635,3 +1635,66 @@ fn test_swap_auto_stake_destination_coldkeys() { ); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey -- test_hotkey_swap_does_not_insert_zero_alpha --exact --nocapture +#[test] +fn test_hotkey_swap_does_not_insert_zero_alpha() { + new_test_ext(1).execute_with(|| { + let old_hotkey = U256::from(1); + let new_hotkey = U256::from(2); + let coldkey = U256::from(3); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let mut weight = Weight::zero(); + + // Insert zero values for all alpha-related maps + TotalHotkeyAlpha::::insert(old_hotkey, netuid, AlphaBalance::ZERO); + TotalHotkeyAlphaLastEpoch::::insert(old_hotkey, netuid, AlphaBalance::ZERO); + TotalHotkeyShares::::insert(old_hotkey, netuid, U64F64::from_num(0)); + Alpha::::insert((old_hotkey, coldkey, netuid), U64F64::from_num(0)); + AlphaDividendsPerSubnet::::insert(netuid, old_hotkey, AlphaBalance::ZERO); + + // Perform the swap + SubtensorModule::perform_hotkey_swap_on_all_subnets( + &old_hotkey, + &new_hotkey, + &coldkey, + &mut weight, + false, + ); + + // Verify no zero entries were inserted for the new hotkey + assert!( + !TotalHotkeyAlpha::::contains_key(new_hotkey, netuid), + "TotalHotkeyAlpha should not contain a zero entry for new_hotkey" + ); + assert!( + !TotalHotkeyAlphaLastEpoch::::contains_key(new_hotkey, netuid), + "TotalHotkeyAlphaLastEpoch should not contain a zero entry for new_hotkey" + ); + assert!( + !TotalHotkeyShares::::contains_key(new_hotkey, netuid), + "TotalHotkeyShares should not contain a zero entry for new_hotkey" + ); + assert!( + !Alpha::::contains_key((new_hotkey, coldkey, netuid)), + "Alpha should not contain a zero entry for new_hotkey" + ); + assert!( + !AlphaDividendsPerSubnet::::contains_key(netuid, new_hotkey), + "AlphaDividendsPerSubnet should not contain a zero entry for new_hotkey" + ); + + // Verify old entries were removed + assert!(!TotalHotkeyAlpha::::contains_key(old_hotkey, netuid)); + assert!(!TotalHotkeyAlphaLastEpoch::::contains_key( + old_hotkey, netuid + )); + assert!(!TotalHotkeyShares::::contains_key(old_hotkey, netuid)); + assert!(!Alpha::::contains_key((old_hotkey, coldkey, netuid))); + assert!(!AlphaDividendsPerSubnet::::contains_key( + netuid, old_hotkey + )); + }); +}