Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -273,30 +273,51 @@ pub fn migrate_fix_subnet_hotkey_lock_swaps<T: Config>() -> Weight {
};
let netuid = NetUid::from(fix.netuid);

let index_reads: u64;
let index_writes: u64;
let lock_take_reads: u64;
let lock_take_writes: u64;
let locks_to_fix: Vec<(T::AccountId, LockState)> = if let Some(coldkey) = fix.coldkey {
let Some(coldkey) = decode_account_id32::<T>(coldkey) else {
log::error!("Failed to decode coldkey: {}", coldkey);
continue;
};
Lock::<T>::take((coldkey.clone(), netuid, old_hotkey.clone()))
.map(|lock| vec![(coldkey, lock)])
.unwrap_or_default()
index_reads = 0;
index_writes = 1;
lock_take_reads = 1;
LockingColdkeys::<T>::remove((netuid, old_hotkey.clone(), coldkey.clone()));
if let Some(lock) = Lock::<T>::take((coldkey.clone(), netuid, old_hotkey.clone())) {
lock_take_writes = 1;
vec![(coldkey, lock)]
} else {
lock_take_writes = 0;
Vec::new()
}
} else {
let locks: Vec<(T::AccountId, LockState)> = Lock::<T>::iter()
.filter_map(|((coldkey, lock_netuid, hotkey), lock)| {
(lock_netuid == netuid && hotkey == old_hotkey).then_some((coldkey, lock))
let coldkeys: Vec<T::AccountId> =
LockingColdkeys::<T>::iter_prefix((netuid, old_hotkey.clone()))
.map(|(coldkey, ())| coldkey)
.collect();
let indexed_coldkeys = coldkeys.len() as u64;
index_reads = indexed_coldkeys;
index_writes = indexed_coldkeys;
lock_take_reads = indexed_coldkeys;

let locks: Vec<(T::AccountId, LockState)> = coldkeys
.into_iter()
.filter_map(|coldkey| {
LockingColdkeys::<T>::remove((netuid, old_hotkey.clone(), coldkey.clone()));
Lock::<T>::take((coldkey.clone(), netuid, old_hotkey.clone()))
.map(|lock| (coldkey, lock))
})
.collect();
for (coldkey, _) in &locks {
Lock::<T>::remove((coldkey.clone(), netuid, old_hotkey.clone()));
}
lock_take_writes = locks.len() as u64;
locks
};
let locks_to_fix_count = locks_to_fix.len() as u64;
weight = weight.saturating_add(
T::DbWeight::get()
.reads_writes(locks_to_fix_count.saturating_add(1), locks_to_fix_count),
);
weight = weight.saturating_add(T::DbWeight::get().reads_writes(
index_reads.saturating_add(lock_take_reads),
index_writes.saturating_add(lock_take_writes),
));

if locks_to_fix.is_empty() {
missing_locks = missing_locks.saturating_add(1);
Expand All @@ -323,7 +344,8 @@ pub fn migrate_fix_subnet_hotkey_lock_swaps<T: Config>() -> Weight {
}

Lock::<T>::insert((coldkey.clone(), netuid, new_hotkey.clone()), lock.clone());
weight = weight.saturating_add(T::DbWeight::get().writes(1));
LockingColdkeys::<T>::insert((netuid, new_hotkey.clone(), coldkey.clone()), ());
weight = weight.saturating_add(T::DbWeight::get().writes(2));

if !new_hotkey_is_owner {
add_to_aggregate::<T>(&coldkey, netuid, &new_hotkey, &lock);
Expand Down
42 changes: 42 additions & 0 deletions pallets/subtensor/src/tests/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,17 @@ fn test_migrate_fix_subnet_hotkey_lock_swaps_moves_or_discards_conflicts() {
(coldkey_to_move, netuid, old_hotkey),
moved_lock.clone(),
);
LockingColdkeys::<Test>::insert((netuid, old_hotkey, coldkey_to_move), ());
Lock::<Test>::insert(
(coldkey_with_conflict, netuid, old_hotkey),
discarded_lock.clone(),
);
LockingColdkeys::<Test>::insert((netuid, old_hotkey, coldkey_with_conflict), ());
Lock::<Test>::insert(
(coldkey_with_conflict, netuid, new_hotkey),
existing_destination_lock.clone(),
);
LockingColdkeys::<Test>::insert((netuid, new_hotkey, coldkey_with_conflict), ());
DecayingLock::<Test>::insert(coldkey_to_move, netuid, false);
DecayingLock::<Test>::insert(coldkey_with_conflict, netuid, false);
DecayingLock::<Test>::insert(chained_coldkey, chained_netuid, false);
Expand All @@ -148,6 +151,10 @@ fn test_migrate_fix_subnet_hotkey_lock_swaps_moves_or_discards_conflicts() {
(chained_coldkey, chained_netuid, chained_first_hotkey),
chained_lock.clone(),
);
LockingColdkeys::<Test>::insert(
(chained_netuid, chained_first_hotkey, chained_coldkey),
(),
);
HotkeyLock::<Test>::insert(chained_netuid, chained_first_hotkey, chained_lock.clone());

let weight =
Expand All @@ -157,14 +164,34 @@ fn test_migrate_fix_subnet_hotkey_lock_swaps_moves_or_discards_conflicts() {
assert!(HasMigrationRun::<Test>::get(&migration_name));
assert!(Lock::<Test>::get((coldkey_to_move, netuid, old_hotkey)).is_none());
assert!(Lock::<Test>::get((coldkey_with_conflict, netuid, old_hotkey)).is_none());
assert!(!LockingColdkeys::<Test>::contains_key((
netuid,
old_hotkey,
coldkey_to_move
)));
assert!(!LockingColdkeys::<Test>::contains_key((
netuid,
old_hotkey,
coldkey_with_conflict
)));
assert_eq!(
Lock::<Test>::get((coldkey_to_move, netuid, new_hotkey)),
Some(moved_lock.clone())
);
assert!(LockingColdkeys::<Test>::contains_key((
netuid,
new_hotkey,
coldkey_to_move
)));
assert_eq!(
Lock::<Test>::get((coldkey_with_conflict, netuid, new_hotkey)),
Some(existing_destination_lock.clone())
);
assert!(LockingColdkeys::<Test>::contains_key((
netuid,
new_hotkey,
coldkey_with_conflict
)));
assert!(HotkeyLock::<Test>::get(netuid, old_hotkey).is_none());

let new_aggregate = HotkeyLock::<Test>::get(netuid, new_hotkey)
Expand Down Expand Up @@ -193,10 +220,25 @@ fn test_migrate_fix_subnet_hotkey_lock_swaps_moves_or_discards_conflicts() {
chained_middle_hotkey
))
.is_none());
assert!(!LockingColdkeys::<Test>::contains_key((
chained_netuid,
chained_first_hotkey,
chained_coldkey
)));
assert!(!LockingColdkeys::<Test>::contains_key((
chained_netuid,
chained_middle_hotkey,
chained_coldkey
)));
assert_eq!(
Lock::<Test>::get((chained_coldkey, chained_netuid, chained_final_hotkey)),
Some(chained_lock.clone())
);
assert!(LockingColdkeys::<Test>::contains_key((
chained_netuid,
chained_final_hotkey,
chained_coldkey
)));
assert!(HotkeyLock::<Test>::get(chained_netuid, chained_first_hotkey).is_none());
assert!(HotkeyLock::<Test>::get(chained_netuid, chained_middle_hotkey).is_none());
assert_eq!(
Expand Down
9 changes: 5 additions & 4 deletions pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,22 +837,23 @@ fn test_swap_owner_old_hotkey_not_exist() {
});
}

// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey_with_subnet -- test_swap_owner_new_hotkey_already_exists --exact --nocapture
// SKIP_WASM_BUILD=1 cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_swap_owner_new_hotkey_already_exists --exact --nocapture
#[test]
fn test_swap_owner_new_hotkey_already_exists() {
new_test_ext(1).execute_with(|| {
let old_hotkey = U256::from(1);
let new_hotkey = U256::from(2);
let coldkey = U256::from(3);
let another_coldkey = U256::from(4);

let netuid = add_dynamic_network(&new_hotkey, &coldkey);
let netuid = add_dynamic_network(&old_hotkey, &coldkey);
add_balance_to_coldkey_account(&coldkey, 1_000_000_000_000_u64.into());

// old_hotkey is owned by coldkey; new_hotkey was already registered on `netuid`
// by add_dynamic_network (the condition under test). Do NOT reassign new_hotkey to
// a foreign coldkey — the new_hotkey-ownership check (NonAssociatedColdKey) would
// then fire before the already-registered-in-subnet check this test targets.
Owner::<Test>::insert(old_hotkey, coldkey);
Owner::<Test>::insert(new_hotkey, another_coldkey);

// Perform the swap
System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get());
Expand All @@ -864,7 +865,7 @@ fn test_swap_owner_new_hotkey_already_exists() {
Some(netuid),
false
),
Error::<Test>::HotKeyAlreadyRegisteredInSubNet
Error::<Test>::NonAssociatedColdKey
);

// Verify the swap
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// `spec_version`, and `authoring_version` are the same between Wasm and native.
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
// the compatible custom types.
spec_version: 424,
spec_version: 426,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down
Loading