Skip to content

Commit 5b5ac3a

Browse files
Merge pull request #615 from opentensor/feat/cold-hot-staking-map
Add migration to populate StakingHotkeys, unstake delegations
2 parents a62975c + f037dbc commit 5b5ac3a

File tree

6 files changed

+137
-5
lines changed

6 files changed

+137
-5
lines changed

pallets/subtensor/src/lib.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,9 @@ pub mod pallet {
380380
ValueQuery,
381381
DefaultAccountTake<T>,
382382
>;
383+
#[pallet::storage] // --- DMAP ( cold ) --> Vec<hot> | Maps coldkey to hotkeys that stake to it
384+
pub type StakingHotkeys<T: Config> =
385+
StorageMap<_, Blake2_128Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;
383386
/// -- ITEM (switches liquid alpha on)
384387
#[pallet::type_value]
385388
pub fn DefaultLiquidAlpha<T: Config>() -> bool {
@@ -1225,6 +1228,13 @@ pub mod pallet {
12251228

12261229
Stake::<T>::insert(hotkey.clone(), coldkey.clone(), stake);
12271230

1231+
// Update StakingHotkeys map
1232+
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
1233+
if !staking_hotkeys.contains(hotkey) {
1234+
staking_hotkeys.push(hotkey.clone());
1235+
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys);
1236+
}
1237+
12281238
next_uid = next_uid.checked_add(1).expect(
12291239
"should not have total number of hotkey accounts larger than u16::MAX",
12301240
);
@@ -1337,7 +1347,9 @@ pub mod pallet {
13371347
// Doesn't check storage version. TODO: Remove after upgrade
13381348
.saturating_add(migration::migration5_total_issuance::<T>(false))
13391349
// Populate OwnedHotkeys map for coldkey swap. Doesn't update storage vesion.
1340-
.saturating_add(migration::migrate_populate_owned::<T>());
1350+
.saturating_add(migration::migrate_populate_owned::<T>())
1351+
// Populate StakingHotkeys map for coldkey swap. Doesn't update storage vesion.
1352+
.saturating_add(migration::migrate_populate_staking_hotkeys::<T>());
13411353

13421354
weight
13431355
}

pallets/subtensor/src/migration.rs

+65
Original file line numberDiff line numberDiff line change
@@ -539,3 +539,68 @@ pub fn migrate_populate_owned<T: Config>() -> Weight {
539539
Weight::zero()
540540
}
541541
}
542+
543+
/// Populate the StakingHotkeys map from Stake map
544+
pub fn migrate_populate_staking_hotkeys<T: Config>() -> Weight {
545+
// Setup migration weight
546+
let mut weight = T::DbWeight::get().reads(1);
547+
let migration_name = "Populate StakingHotkeys map";
548+
549+
// Check if this migration is needed (if StakingHotkeys map is empty)
550+
let migrate = StakingHotkeys::<T>::iter().next().is_none();
551+
552+
// Only runs if the migration is needed
553+
if migrate {
554+
info!(target: LOG_TARGET_1, ">>> Starting Migration: {}", migration_name);
555+
556+
let mut longest_hotkey_vector: usize = 0;
557+
let mut longest_coldkey: Option<T::AccountId> = None;
558+
let mut keys_touched: u64 = 0;
559+
let mut storage_reads: u64 = 0;
560+
let mut storage_writes: u64 = 0;
561+
562+
// Iterate through all Owner entries
563+
Stake::<T>::iter().for_each(|(hotkey, coldkey, stake)| {
564+
storage_reads = storage_reads.saturating_add(1); // Read from Owner storage
565+
if stake > 0 {
566+
let mut hotkeys = StakingHotkeys::<T>::get(&coldkey);
567+
storage_reads = storage_reads.saturating_add(1); // Read from StakingHotkeys storage
568+
569+
// Add the hotkey if it's not already in the vector
570+
if !hotkeys.contains(&hotkey) {
571+
hotkeys.push(hotkey);
572+
keys_touched = keys_touched.saturating_add(1);
573+
574+
// Update longest hotkey vector info
575+
if longest_hotkey_vector < hotkeys.len() {
576+
longest_hotkey_vector = hotkeys.len();
577+
longest_coldkey = Some(coldkey.clone());
578+
}
579+
580+
// Update the StakingHotkeys storage
581+
StakingHotkeys::<T>::insert(&coldkey, hotkeys);
582+
storage_writes = storage_writes.saturating_add(1); // Write to StakingHotkeys storage
583+
}
584+
585+
// Accrue weight for reads and writes
586+
weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 1));
587+
}
588+
});
589+
590+
// Log migration results
591+
info!(
592+
target: LOG_TARGET_1,
593+
"Migration {} finished. Keys touched: {}, Longest hotkey vector: {}, Storage reads: {}, Storage writes: {}",
594+
migration_name, keys_touched, longest_hotkey_vector, storage_reads, storage_writes
595+
);
596+
if let Some(c) = longest_coldkey {
597+
info!(target: LOG_TARGET_1, "Longest hotkey vector is controlled by: {:?}", c);
598+
}
599+
600+
weight
601+
} else {
602+
info!(target: LOG_TARGET_1, "Migration {} already done!", migration_name);
603+
Weight::zero()
604+
}
605+
}
606+

pallets/subtensor/src/staking.rs

+40
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,13 @@ impl<T: Config> Pallet<T> {
569569
hotkeys.push(hotkey.clone());
570570
OwnedHotkeys::<T>::insert(coldkey, hotkeys);
571571
}
572+
573+
// Update StakingHotkeys map
574+
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
575+
if !staking_hotkeys.contains(hotkey) {
576+
staking_hotkeys.push(hotkey.clone());
577+
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys);
578+
}
572579
}
573580
}
574581

@@ -648,6 +655,13 @@ impl<T: Config> Pallet<T> {
648655
Stake::<T>::get(hotkey, coldkey).saturating_add(increment),
649656
);
650657
TotalStake::<T>::put(TotalStake::<T>::get().saturating_add(increment));
658+
659+
// Update StakingHotkeys map
660+
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
661+
if !staking_hotkeys.contains(hotkey) {
662+
staking_hotkeys.push(hotkey.clone());
663+
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys);
664+
}
651665
}
652666

653667
// Decreases the stake on the cold - hot pairing by the decrement while decreasing other counters.
@@ -668,6 +682,8 @@ impl<T: Config> Pallet<T> {
668682
Stake::<T>::get(hotkey, coldkey).saturating_sub(decrement),
669683
);
670684
TotalStake::<T>::put(TotalStake::<T>::get().saturating_sub(decrement));
685+
686+
// TODO: Tech debt: Remove StakingHotkeys entry if stake goes to 0
671687
}
672688

673689
/// Empties the stake associated with a given coldkey-hotkey account pairing.
@@ -693,6 +709,11 @@ impl<T: Config> Pallet<T> {
693709
TotalStake::<T>::mutate(|stake| *stake = stake.saturating_sub(current_stake));
694710
TotalIssuance::<T>::mutate(|issuance| *issuance = issuance.saturating_sub(current_stake));
695711

712+
// Update StakingHotkeys map
713+
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
714+
staking_hotkeys.retain(|h| h != hotkey);
715+
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys);
716+
696717
current_stake
697718
}
698719

@@ -897,6 +918,25 @@ impl<T: Config> Pallet<T> {
897918
}
898919
}
899920

921+
// Unstake all delegate stake make by this coldkey to non-owned hotkeys
922+
let staking_hotkeys = StakingHotkeys::<T>::get(&current_coldkey);
923+
924+
// iterate over all staking hotkeys.
925+
for hotkey in staking_hotkeys {
926+
// Get the current stake
927+
let current_stake: u64 =
928+
Self::get_stake_for_coldkey_and_hotkey(&current_coldkey, &hotkey);
929+
930+
// Unstake all balance if there's any stake
931+
if current_stake > 0 {
932+
Self::do_remove_stake(
933+
RawOrigin::Signed(current_coldkey.clone()).into(),
934+
hotkey.clone(),
935+
current_stake,
936+
)?;
937+
}
938+
}
939+
900940
let total_balance = Self::get_coldkey_balance(&current_coldkey);
901941
log::info!("Total Bank Balance: {:?}", total_balance);
902942

pallets/subtensor/src/swap.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,22 @@ impl<T: Config> Pallet<T> {
272272
for (coldkey, stake_amount) in stakes {
273273
Stake::<T>::insert(new_hotkey, &coldkey, stake_amount);
274274
writes = writes.saturating_add(1u64); // One write for insert
275+
276+
// Update StakingHotkeys map
277+
let mut staking_hotkeys = StakingHotkeys::<T>::get(&coldkey);
278+
if !staking_hotkeys.contains(new_hotkey) {
279+
staking_hotkeys.push(new_hotkey.clone());
280+
StakingHotkeys::<T>::insert(coldkey.clone(), staking_hotkeys);
281+
writes = writes.saturating_add(1u64); // One write for insert
282+
}
275283
}
276284

277285
// Clear the prefix for the old hotkey after transferring all stakes
278286
let _ = Stake::<T>::clear_prefix(old_hotkey, stake_count, None);
279287
writes = writes.saturating_add(1); // One write for insert; // One write for clear_prefix
280288

289+
// TODO: Remove all entries for old hotkey from StakingHotkeys map
290+
281291
weight.saturating_accrue(T::DbWeight::get().writes(writes));
282292
}
283293

@@ -521,7 +531,12 @@ impl<T: Config> Pallet<T> {
521531
let stake = Stake::<T>::get(&hotkey, old_coldkey);
522532
Stake::<T>::remove(&hotkey, old_coldkey);
523533
Stake::<T>::insert(&hotkey, new_coldkey, stake);
524-
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
534+
535+
// Update StakingHotkeys map
536+
let staking_hotkeys = StakingHotkeys::<T>::get(old_coldkey);
537+
StakingHotkeys::<T>::insert(new_coldkey.clone(), staking_hotkeys);
538+
539+
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 3));
525540
}
526541
}
527542

pallets/subtensor/tests/swap.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ fn test_swap_stake_weight_update() {
625625
SubtensorModule::swap_stake(&old_hotkey, &new_hotkey, &mut weight);
626626

627627
// Verify the weight update
628-
let expected_weight = <Test as frame_system::Config>::DbWeight::get().writes(2);
628+
let expected_weight = <Test as frame_system::Config>::DbWeight::get().writes(3);
629629
assert_eq!(weight, expected_weight);
630630
});
631631
}
@@ -1213,7 +1213,7 @@ fn test_swap_stake_for_coldkey() {
12131213
assert_eq!(TotalIssuance::<Test>::get(), stake_amount1 + stake_amount2);
12141214

12151215
// Verify weight update
1216-
let expected_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(3, 4);
1216+
let expected_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(5, 6);
12171217
assert_eq!(weight, expected_weight);
12181218
});
12191219
}

runtime/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
139139
// `spec_version`, and `authoring_version` are the same between Wasm and native.
140140
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
141141
// the compatible custom types.
142-
spec_version: 158,
142+
spec_version: 159,
143143
impl_version: 1,
144144
apis: RUNTIME_API_VERSIONS,
145145
transaction_version: 1,

0 commit comments

Comments
 (0)