From 48f94c6764f68b2a93f319ec4c66834017ace146 Mon Sep 17 00:00:00 2001 From: bittoby <218712309+bittoby@users.noreply.github.com> Date: Thu, 19 Mar 2026 04:38:16 +0000 Subject: [PATCH 1/3] fix: prevent zero-valued entries in Alpha storage maps and add migration to purge existing ones --- .github/workflows/check-node-compat.yml | 4 +- .../subtensor/src/coinbase/run_coinbase.rs | 13 ++- pallets/subtensor/src/macros/hooks.rs | 4 +- .../migrations/migrate_remove_zero_alpha.rs | 98 +++++++++++++++++++ pallets/subtensor/src/migrations/mod.rs | 1 + pallets/subtensor/src/swap/swap_coldkey.rs | 8 +- pallets/subtensor/src/swap/swap_hotkey.rs | 62 +++++++----- pallets/subtensor/src/tests/migration.rs | 97 ++++++++++++++++++ pallets/subtensor/src/tests/swap_coldkey.rs | 35 +++++++ pallets/subtensor/src/tests/swap_hotkey.rs | 63 ++++++++++++ 10 files changed, 351 insertions(+), 34 deletions(-) create mode 100644 pallets/subtensor/src/migrations/migrate_remove_zero_alpha.rs diff --git a/.github/workflows/check-node-compat.yml b/.github/workflows/check-node-compat.yml index b52a7a88ba..2943feb089 100644 --- a/.github/workflows/check-node-compat.yml +++ b/.github/workflows/check-node-compat.yml @@ -22,8 +22,8 @@ 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/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/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 8cb12a974a..2e9640239e 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -364,10 +364,11 @@ 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) - }); + 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. @@ -387,7 +388,9 @@ 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); + 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(3)); } @@ -397,7 +400,9 @@ 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); + if is_network_member { + IsNetworkMember::::insert(new_hotkey, netuid, is_network_member); + } weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); // 3.2 Swap Uids + Keys. @@ -523,22 +528,25 @@ 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), - ); + let combined_alpha_last_epoch = old_alpha.saturating_add(new_total_hotkey_alpha); + if !combined_alpha_last_epoch.is_zero() { + TotalHotkeyAlphaLastEpoch::::insert( + new_hotkey, + netuid, + combined_alpha_last_epoch, + ); + } weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); // 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), - ); + let combined_dividends = + old_hotkey_alpha_dividends.saturating_add(new_hotkey_alpha_dividends); + if !combined_dividends.is_zero() { + AlphaDividendsPerSubnet::::insert(netuid, new_hotkey, combined_dividends); + } weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); // 8.3 Swap TaoDividendsPerSubnet @@ -574,11 +582,14 @@ 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)), - ); + // Only insert combined alpha if non-zero to avoid storage bloat. + let combined_alpha = alpha.saturating_add(new_alpha); + if combined_alpha != 0 { + AlphaV2::::insert( + (new_hotkey, &coldkey, netuid), + SafeFloat::from(combined_alpha), + ); + } weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); // Swap StakingHotkeys. @@ -602,10 +613,11 @@ 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(), - ); + // Only insert combined alpha if non-zero to avoid storage bloat. + let combined_alpha_v2 = alpha.add(&new_alpha_v2).unwrap_or_default(); + if !combined_alpha_v2.is_zero() { + AlphaV2::::insert((new_hotkey, &coldkey, netuid), combined_alpha_v2); + } weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); // Swap StakingHotkeys. diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index f4d0347686..3394b5ab03 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -3039,3 +3039,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 67362ab614..b0211102c0 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -1505,6 +1505,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 + )); + }); +} From b46e3512c49e1b056989c59ffab99aef1161ac24 Mon Sep 17 00:00:00 2001 From: bittoby <218712309+bittoby@users.noreply.github.com> Date: Thu, 19 Mar 2026 04:42:04 +0000 Subject: [PATCH 2/3] fix: update YAML style --- .github/workflows/check-node-compat.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-node-compat.yml b/.github/workflows/check-node-compat.yml index 2943feb089..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 From f2ce53f9022bc79681ceb7901ad47b498eaac97b Mon Sep 17 00:00:00 2001 From: bittoby <218712309+bittoby@users.noreply.github.com> Date: Thu, 19 Mar 2026 13:16:53 +0000 Subject: [PATCH 3/3] fix: add zero-value guards for additional storage maps and fix weight accountin --- pallets/subtensor/src/coinbase/root.rs | 6 +++++- pallets/subtensor/src/staking/claim_root.rs | 14 +++++++++---- pallets/subtensor/src/staking/set_children.rs | 8 ++----- pallets/subtensor/src/swap/swap_hotkey.rs | 21 ++++++++++++------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index e2714fba1b..7872a205da 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/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 c7ebd62c04..2a97bc6e4c 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_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 2e9640239e..7231b36d2c 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -364,12 +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); + 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)); } - 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. @@ -388,10 +389,11 @@ impl Pallet { let total_old_plus_new_shares = total_new_shares.add(&total_old_shares).unwrap_or_default(); + 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)); } - weight.saturating_accrue(T::DbWeight::get().writes(3)); } // 3. Swap all subnet specific info. @@ -400,10 +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); + 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)); } - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); // 3.2 Swap Uids + Keys. // Keys( netuid, hotkey ) -> uid -- the uid the hotkey has in the network if it is a member. @@ -529,14 +532,15 @@ impl Pallet { let old_alpha = TotalHotkeyAlphaLastEpoch::::take(old_hotkey, netuid); let new_total_hotkey_alpha = TotalHotkeyAlphaLastEpoch::::get(new_hotkey, netuid); 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)); } - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); // 8.2 Swap AlphaDividendsPerSubnet let old_hotkey_alpha_dividends = AlphaDividendsPerSubnet::::get(netuid, old_hotkey); @@ -544,10 +548,11 @@ impl Pallet { AlphaDividendsPerSubnet::::remove(netuid, old_hotkey); 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)); } - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); // 8.3 Swap TaoDividendsPerSubnet // Tao dividends were removed @@ -584,13 +589,14 @@ impl Pallet { // 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)); } - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); // Swap StakingHotkeys. // StakingHotkeys( coldkey ) --> Vec -- the hotkeys that the coldkey stakes. @@ -615,10 +621,11 @@ impl Pallet { AlphaV2::::remove((old_hotkey, &coldkey, netuid)); // 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)); } - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2)); // Swap StakingHotkeys. // StakingHotkeys( coldkey ) --> Vec -- the hotkeys that the coldkey stakes.