|
| 1 | +use super::*; |
| 2 | +use frame_support::pallet_prelude::Weight; |
| 3 | +use frame_system::pallet_prelude::BlockNumberFor; |
| 4 | +use scale_info::prelude::string::String; |
| 5 | +use sp_core::crypto::Ss58Codec; |
| 6 | +use sp_runtime::AccountId32; |
| 7 | +use substrate_fixed::types::U64F64; |
| 8 | + |
| 9 | +pub fn decode_account_id32<T: Config>(ss58_string: &str) -> Option<T::AccountId> { |
| 10 | + let account_id32: AccountId32 = AccountId32::from_ss58check(ss58_string).ok()?; |
| 11 | + let mut account_id32_slice: &[u8] = account_id32.as_ref(); |
| 12 | + T::AccountId::decode(&mut account_id32_slice).ok() |
| 13 | +} |
| 14 | + |
| 15 | +/// Fixes the consequences of a bug in `perform_hotkey_swap_on_one_subnet` where |
| 16 | +/// `transfer_root_claimable_for_new_hotkey` unconditionally transferred the **entire** |
| 17 | +/// `RootClaimable` BTreeMap (all subnets) from the old hotkey to the new hotkey, even |
| 18 | +/// during a single-subnet swap. |
| 19 | +/// |
| 20 | +/// This left the old hotkey with: |
| 21 | +/// - `RootClaimable[old_hotkey]` = empty (wiped for ALL subnets) |
| 22 | +/// - `RootClaimed[(subnet, old_hotkey, coldkey)]` = old watermarks (for non-swapped subnets) |
| 23 | +/// |
| 24 | +/// Resulting in `owed = claimable_rate * root_stake - root_claimed = 0 - positive = negative → 0`, |
| 25 | +/// effectively freezing root dividends for the old hotkey. |
| 26 | +/// |
| 27 | +/// Remediation: restore the pre-swap `RootClaimable` and `RootClaimed` storage maps |
| 28 | +pub fn migrate_fix_root_claimed_overclaim<T: Config>() -> Weight { |
| 29 | + let migration_name = b"migrate_fix_root_claimed_overclaim".to_vec(); |
| 30 | + let mut weight = T::DbWeight::get().reads(1); |
| 31 | + |
| 32 | + if HasMigrationRun::<T>::get(&migration_name) { |
| 33 | + log::info!( |
| 34 | + "Migration '{:?}' has already run. Skipping.", |
| 35 | + String::from_utf8_lossy(&migration_name) |
| 36 | + ); |
| 37 | + return weight; |
| 38 | + } |
| 39 | + |
| 40 | + log::info!( |
| 41 | + "Running migration '{}'", |
| 42 | + String::from_utf8_lossy(&migration_name) |
| 43 | + ); |
| 44 | + |
| 45 | + // Only run on mainnet. |
| 46 | + // Mainnet genesis: 0x2f0555cc76fc2840a25a6ea3b9637146806f1f44b090c175ffde2a7e5ab36c03 |
| 47 | + let genesis_hash = frame_system::Pallet::<T>::block_hash(BlockNumberFor::<T>::zero()); |
| 48 | + let genesis_bytes = genesis_hash.as_ref(); |
| 49 | + let mainnet_genesis = |
| 50 | + hex_literal::hex!("2f0555cc76fc2840a25a6ea3b9637146806f1f44b090c175ffde2a7e5ab36c03"); |
| 51 | + if genesis_bytes == mainnet_genesis { |
| 52 | + let old_hotkey_ss58 = "5GmvyePN9aYErXBBhBnxZKGoGk4LKZApE4NkaSzW62CYCYNA"; |
| 53 | + let new_hotkey_ss58 = "5H6BqkzjYvViiqp7rQLXjpnaEmW7U9CoKxXhQ4efMqtX1mQw"; |
| 54 | + let netuid = NetUid::from(27); |
| 55 | + |
| 56 | + if let (Some(old_hotkey), Some(new_hotkey)) = ( |
| 57 | + decode_account_id32::<T>(old_hotkey_ss58), |
| 58 | + decode_account_id32::<T>(new_hotkey_ss58), |
| 59 | + ) { |
| 60 | + // Reverting the Root Claimable because it only should happen for root subnet |
| 61 | + Pallet::<T>::transfer_root_claimable_for_new_hotkey(&new_hotkey, &old_hotkey); |
| 62 | + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); |
| 63 | + |
| 64 | + let alpha_values: Vec<((T::AccountId, NetUid), U64F64)> = |
| 65 | + Alpha::<T>::iter_prefix((&new_hotkey,)).collect(); |
| 66 | + weight.saturating_accrue(T::DbWeight::get().reads(alpha_values.len() as u64)); |
| 67 | + |
| 68 | + // Reverting back root claimed |
| 69 | + for ((coldkey, _), _) in alpha_values |
| 70 | + .into_iter() |
| 71 | + .filter(|((_, netuid_alpha), alpha)| *netuid_alpha == netuid && *alpha != 0) |
| 72 | + { |
| 73 | + Pallet::<T>::transfer_root_claimed_for_new_keys( |
| 74 | + netuid, |
| 75 | + &new_hotkey, |
| 76 | + &old_hotkey, |
| 77 | + &coldkey, |
| 78 | + &coldkey, |
| 79 | + ); |
| 80 | + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); |
| 81 | + } |
| 82 | + } else { |
| 83 | + log::error!("Failed to decode hotkeys, skipping"); |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + // Mark migration as completed |
| 88 | + HasMigrationRun::<T>::insert(&migration_name, true); |
| 89 | + weight.saturating_accrue(T::DbWeight::get().writes(1)); |
| 90 | + |
| 91 | + log::info!("Migration 'migrate_fix_root_claimed_overclaim' completed."); |
| 92 | + |
| 93 | + weight |
| 94 | +} |
0 commit comments