Skip to content

Commit bda0c5a

Browse files
committed
Fix coldkey_swap and its tests
1 parent 347b8de commit bda0c5a

File tree

5 files changed

+125
-110
lines changed

5 files changed

+125
-110
lines changed

pallets/subtensor/src/lib.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,11 +1209,13 @@ pub mod pallet {
12091209

12101210
// Update Owned map
12111211
let mut hotkeys = Owned::<T>::get(&coldkey);
1212-
hotkeys.push(hotkey.clone());
1213-
Owned::<T>::insert(
1214-
&coldkey,
1215-
hotkeys,
1216-
);
1212+
if !hotkeys.contains(&hotkey) {
1213+
hotkeys.push(hotkey.clone());
1214+
Owned::<T>::insert(
1215+
&coldkey,
1216+
hotkeys,
1217+
);
1218+
}
12171219

12181220
TotalHotkeyStake::<T>::insert(hotkey.clone(), stake);
12191221
TotalColdkeyStake::<T>::insert(

pallets/subtensor/src/migration.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -494,10 +494,12 @@ pub fn migrate_populate_owned<T: Config>() -> Weight {
494494
let mut longest_coldkey: Option<T::AccountId> = None;
495495
Owner::<T>::iter().for_each(|(hotkey, coldkey)| {
496496
let mut hotkeys = Owned::<T>::get(&coldkey);
497-
hotkeys.push(hotkey);
498-
if longest_hotkey_vector < hotkeys.len() {
499-
longest_hotkey_vector = hotkeys.len();
500-
longest_coldkey = Some(coldkey.clone());
497+
if !hotkeys.contains(&hotkey) {
498+
hotkeys.push(hotkey);
499+
if longest_hotkey_vector < hotkeys.len() {
500+
longest_hotkey_vector = hotkeys.len();
501+
longest_coldkey = Some(coldkey.clone());
502+
}
501503
}
502504

503505
Owned::<T>::insert(

pallets/subtensor/src/staking.rs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -563,11 +563,13 @@ impl<T: Config> Pallet<T> {
563563

564564
// Update Owned map
565565
let mut hotkeys = Owned::<T>::get(&coldkey);
566-
hotkeys.push(hotkey.clone());
567-
Owned::<T>::insert(
568-
&coldkey,
569-
hotkeys,
570-
);
566+
if !hotkeys.contains(&hotkey) {
567+
hotkeys.push(hotkey.clone());
568+
Owned::<T>::insert(
569+
&coldkey,
570+
hotkeys,
571+
);
572+
}
571573
}
572574
}
573575

@@ -789,6 +791,31 @@ impl<T: Config> Pallet<T> {
789791
Ok(credit)
790792
}
791793

794+
pub fn kill_coldkey_account(
795+
coldkey: &T::AccountId,
796+
amount: <<T as Config>::Currency as fungible::Inspect<<T as system::Config>::AccountId>>::Balance,
797+
) -> Result<u64, DispatchError> {
798+
if amount == 0 {
799+
return Ok(0);
800+
}
801+
802+
let credit = T::Currency::withdraw(
803+
coldkey,
804+
amount,
805+
Precision::Exact,
806+
Preservation::Expendable,
807+
Fortitude::Force,
808+
)
809+
.map_err(|_| Error::<T>::BalanceWithdrawalError)?
810+
.peek();
811+
812+
if credit == 0 {
813+
return Err(Error::<T>::ZeroBalanceAfterWithdrawn.into());
814+
}
815+
816+
Ok(credit)
817+
}
818+
792819
pub fn unstake_all_coldkeys_from_hotkey_account(hotkey: &T::AccountId) {
793820
// Iterate through all coldkeys that have a stake on this hotkey account.
794821
for (delegate_coldkey_i, stake_i) in

pallets/subtensor/src/swap.rs

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,13 @@ impl<T: Config> Pallet<T> {
167167
new_coldkey,
168168
&mut weight,
169169
);
170-
Self::swap_keys_for_coldkey(old_coldkey, new_coldkey, &mut weight);
171170
Self::swap_subnet_owner_for_coldkey(old_coldkey, new_coldkey, &mut weight);
171+
Self::swap_owned_for_coldkey(old_coldkey, new_coldkey, &mut weight);
172172

173173
// Transfer any remaining balance from old_coldkey to new_coldkey
174174
let remaining_balance = Self::get_coldkey_balance(old_coldkey);
175175
if remaining_balance > 0 {
176-
Self::remove_balance_from_coldkey_account(old_coldkey, remaining_balance)?;
176+
Self::kill_coldkey_account(old_coldkey, remaining_balance)?;
177177
Self::add_balance_to_coldkey_account(new_coldkey, remaining_balance);
178178
}
179179

@@ -226,7 +226,10 @@ impl<T: Config> Pallet<T> {
226226

227227
// Update Owned map
228228
let mut hotkeys = Owned::<T>::get(&coldkey);
229-
hotkeys.push(new_hotkey.clone());
229+
if !hotkeys.contains(&new_hotkey) {
230+
hotkeys.push(new_hotkey.clone());
231+
}
232+
hotkeys.retain(|hk| *hk != *old_hotkey);
230233
Owned::<T>::insert(
231234
&coldkey,
232235
hotkeys,
@@ -578,11 +581,6 @@ impl<T: Config> Pallet<T> {
578581
Owner::<T>::insert(&hotkey, new_coldkey);
579582
weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 1));
580583
}
581-
582-
// Update Owned map with new coldkey
583-
Owned::<T>::remove(old_coldkey);
584-
Owned::<T>::insert(new_coldkey, hotkeys);
585-
weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 2));
586584
}
587585

588586
/// Swaps the total hotkey-coldkey stakes for the current interval from the old coldkey to the new coldkey.
@@ -603,48 +601,17 @@ impl<T: Config> Pallet<T> {
603601
new_coldkey: &T::AccountId,
604602
weight: &mut Weight,
605603
) {
606-
for (hotkey, (stakes, block)) in
607-
TotalHotkeyColdkeyStakesThisInterval::<T>::iter_prefix(old_coldkey)
608-
{
609-
TotalHotkeyColdkeyStakesThisInterval::<T>::remove(old_coldkey, &hotkey);
604+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 0));
605+
for hotkey in Owned::<T>::get(old_coldkey).iter() {
606+
let (stake, block) = TotalHotkeyColdkeyStakesThisInterval::<T>::get(&hotkey, old_coldkey);
607+
TotalHotkeyColdkeyStakesThisInterval::<T>::remove(&hotkey, old_coldkey);
610608
TotalHotkeyColdkeyStakesThisInterval::<T>::insert(
611-
new_coldkey,
612609
&hotkey,
613-
(stakes, block),
610+
new_coldkey,
611+
(stake, block),
614612
);
613+
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
615614
}
616-
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
617-
}
618-
619-
/// Swaps all keys associated with a coldkey from the old coldkey to the new coldkey across all networks.
620-
///
621-
/// # Arguments
622-
///
623-
/// * `old_coldkey` - The AccountId of the old coldkey.
624-
/// * `new_coldkey` - The AccountId of the new coldkey.
625-
/// * `weight` - Mutable reference to the weight of the transaction.
626-
///
627-
/// # Effects
628-
///
629-
/// * Updates all keys associated with the old coldkey to be associated with the new coldkey.
630-
/// * Updates the transaction weight.
631-
pub fn swap_keys_for_coldkey(
632-
old_coldkey: &T::AccountId,
633-
new_coldkey: &T::AccountId,
634-
weight: &mut Weight,
635-
) {
636-
for netuid in 0..TotalNetworks::<T>::get() {
637-
for (uid, hotkey) in Keys::<T>::iter_prefix(netuid) {
638-
if Owner::<T>::get(&hotkey) == *old_coldkey {
639-
Keys::<T>::remove(netuid, uid);
640-
Keys::<T>::insert(netuid, uid, new_coldkey);
641-
}
642-
}
643-
}
644-
weight.saturating_accrue(T::DbWeight::get().reads_writes(
645-
TotalNetworks::<T>::get() as u64,
646-
TotalNetworks::<T>::get() as u64,
647-
));
648615
}
649616

650617
/// Checks if a coldkey has any associated hotkeys.
@@ -677,7 +644,7 @@ impl<T: Config> Pallet<T> {
677644
new_coldkey: &T::AccountId,
678645
weight: &mut Weight,
679646
) {
680-
for netuid in 0..TotalNetworks::<T>::get() {
647+
for netuid in 0..=TotalNetworks::<T>::get() {
681648
let subnet_owner = SubnetOwner::<T>::get(netuid);
682649
if subnet_owner == *old_coldkey {
683650
SubnetOwner::<T>::insert(netuid, new_coldkey.clone());
@@ -687,6 +654,30 @@ impl<T: Config> Pallet<T> {
687654
weight.saturating_accrue(T::DbWeight::get().reads(TotalNetworks::<T>::get() as u64));
688655
}
689656

657+
/// Swaps the owned hotkeys for the coldkey
658+
///
659+
/// # Arguments
660+
///
661+
/// * `old_coldkey` - The AccountId of the old coldkey.
662+
/// * `new_coldkey` - The AccountId of the new coldkey.
663+
/// * `weight` - Mutable reference to the weight of the transaction.
664+
///
665+
/// # Effects
666+
///
667+
/// * Updates the subnet owner to the new coldkey for all networks where the old coldkey was the owner.
668+
/// * Updates the transaction weight.
669+
pub fn swap_owned_for_coldkey(
670+
old_coldkey: &T::AccountId,
671+
new_coldkey: &T::AccountId,
672+
weight: &mut Weight,
673+
) {
674+
// Update Owned map with new coldkey
675+
let hotkeys = Owned::<T>::get(&old_coldkey);
676+
Owned::<T>::remove(old_coldkey);
677+
Owned::<T>::insert(new_coldkey, hotkeys);
678+
weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 2));
679+
}
680+
690681
/// Returns the cost of swapping a coldkey.
691682
///
692683
/// # Returns

pallets/subtensor/tests/swap.rs

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,11 +1058,12 @@ fn test_do_swap_coldkey_success() {
10581058
let netuid = 1u16;
10591059
let stake_amount = 1000u64;
10601060
let swap_cost = SubtensorModule::get_coldkey_swap_cost();
1061+
let free_balance = 12345;
10611062

10621063
// Setup initial state
10631064
add_network(netuid, 13, 0);
10641065
register_ok_neuron(netuid, hotkey, old_coldkey, 0);
1065-
SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount + swap_cost);
1066+
SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount + swap_cost + free_balance);
10661067

10671068
// Add stake to the neuron
10681069
assert_ok!(SubtensorModule::add_stake(
@@ -1072,12 +1073,22 @@ fn test_do_swap_coldkey_success() {
10721073
));
10731074

10741075
log::info!("TotalColdkeyStake::<Test>::get(old_coldkey): {:?}", TotalColdkeyStake::<Test>::get(old_coldkey));
1075-
log::info!("Stake::<Test>::get(old_coldkey, hotkey): {:?}", Stake::<Test>::get(old_coldkey, hotkey));
1076+
log::info!("Stake::<Test>::get(old_coldkey, hotkey): {:?}", Stake::<Test>::get(hotkey, old_coldkey));
10761077

10771078
// Verify initial stake
10781079
assert_eq!(TotalColdkeyStake::<Test>::get(old_coldkey), stake_amount);
1079-
// assert_eq!(Stake::<Test>::get(old_coldkey, hotkey), stake_amount);
1080+
assert_eq!(Stake::<Test>::get(hotkey, old_coldkey), stake_amount);
1081+
1082+
1083+
assert_eq!(Owned::<Test>::get(old_coldkey), vec![hotkey]);
1084+
assert!(!Owned::<Test>::contains_key(new_coldkey));
1085+
1086+
1087+
10801088

1089+
// Get coldkey free balance before swap
1090+
let balance = SubtensorModule::get_coldkey_balance(&old_coldkey);
1091+
assert_eq!(balance, free_balance + swap_cost);
10811092

10821093
// Perform the swap
10831094
assert_ok!(SubtensorModule::do_swap_coldkey(
@@ -1086,19 +1097,19 @@ fn test_do_swap_coldkey_success() {
10861097
&new_coldkey
10871098
));
10881099

1089-
// Verify the swap
1100+
// Verify the swap
10901101
assert_eq!(Owner::<Test>::get(hotkey), new_coldkey);
10911102
assert_eq!(TotalColdkeyStake::<Test>::get(new_coldkey), stake_amount);
10921103
assert!(!TotalColdkeyStake::<Test>::contains_key(old_coldkey));
1093-
assert_eq!(Stake::<Test>::get(new_coldkey, hotkey), stake_amount);
1094-
assert!(!Stake::<Test>::contains_key(old_coldkey, hotkey));
1104+
assert_eq!(Stake::<Test>::get(hotkey, new_coldkey), stake_amount);
1105+
assert!(!Stake::<Test>::contains_key(hotkey, old_coldkey));
1106+
assert_eq!(Owned::<Test>::get(new_coldkey), vec![hotkey]);
1107+
assert!(!Owned::<Test>::contains_key(old_coldkey));
10951108

10961109
// Verify balance transfer
1097-
assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), stake_amount);
1110+
assert_eq!(SubtensorModule::get_coldkey_balance(&new_coldkey), balance - swap_cost);
10981111
assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0);
10991112

1100-
1101-
11021113
// Verify event emission
11031114
System::assert_last_event(Event::ColdkeySwapped {
11041115
old_coldkey: old_coldkey,
@@ -1244,6 +1255,9 @@ fn test_swap_owner_for_coldkey() {
12441255
Owner::<Test>::insert(hotkey1, old_coldkey);
12451256
Owner::<Test>::insert(hotkey2, old_coldkey);
12461257

1258+
// Initialize Owned map
1259+
Owned::<Test>::insert(old_coldkey, vec![hotkey1, hotkey2]);
1260+
12471261
// Perform the swap
12481262
SubtensorModule::swap_owner_for_coldkey(&old_coldkey, &new_coldkey, &mut weight);
12491263

@@ -1269,8 +1283,11 @@ fn test_swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey() {
12691283
let mut weight = Weight::zero();
12701284

12711285
// Initialize TotalHotkeyColdkeyStakesThisInterval for old_coldkey
1272-
TotalHotkeyColdkeyStakesThisInterval::<Test>::insert(old_coldkey, hotkey1, stake1);
1273-
TotalHotkeyColdkeyStakesThisInterval::<Test>::insert(old_coldkey, hotkey2, stake2);
1286+
TotalHotkeyColdkeyStakesThisInterval::<Test>::insert(hotkey1, old_coldkey, stake1);
1287+
TotalHotkeyColdkeyStakesThisInterval::<Test>::insert(hotkey2, old_coldkey, stake2);
1288+
1289+
// Populate Owned map
1290+
Owned::<Test>::insert(old_coldkey, vec![hotkey1, hotkey2]);
12741291

12751292
// Perform the swap
12761293
SubtensorModule::swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey(
@@ -1281,11 +1298,11 @@ fn test_swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey() {
12811298

12821299
// Verify the swap
12831300
assert_eq!(
1284-
TotalHotkeyColdkeyStakesThisInterval::<Test>::get(new_coldkey, hotkey1),
1301+
TotalHotkeyColdkeyStakesThisInterval::<Test>::get(hotkey1, new_coldkey),
12851302
stake1
12861303
);
12871304
assert_eq!(
1288-
TotalHotkeyColdkeyStakesThisInterval::<Test>::get(new_coldkey, hotkey2),
1305+
TotalHotkeyColdkeyStakesThisInterval::<Test>::get(hotkey2, new_coldkey),
12891306
stake2
12901307
);
12911308
assert!(!TotalHotkeyColdkeyStakesThisInterval::<Test>::contains_key(
@@ -1298,42 +1315,7 @@ fn test_swap_total_hotkey_coldkey_stakes_this_interval_for_coldkey() {
12981315
));
12991316

13001317
// Verify weight update
1301-
let expected_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(1, 4);
1302-
assert_eq!(weight, expected_weight);
1303-
});
1304-
}
1305-
1306-
#[test]
1307-
fn test_swap_keys_for_coldkey() {
1308-
new_test_ext(1).execute_with(|| {
1309-
let old_coldkey = U256::from(1);
1310-
let new_coldkey = U256::from(2);
1311-
let hotkey1 = U256::from(3);
1312-
let hotkey2 = U256::from(4);
1313-
let netuid1 = 1u16;
1314-
let netuid2 = 2u16;
1315-
let uid1 = 10u16;
1316-
let uid2 = 20u16;
1317-
let mut weight = Weight::zero();
1318-
1319-
// Initialize Keys and Owner for old_coldkey
1320-
Keys::<Test>::insert(netuid1, uid1, hotkey1);
1321-
Keys::<Test>::insert(netuid2, uid2, hotkey2);
1322-
Owner::<Test>::insert(hotkey1, old_coldkey);
1323-
Owner::<Test>::insert(hotkey2, old_coldkey);
1324-
1325-
// Set up TotalNetworks
1326-
TotalNetworks::<Test>::put(3);
1327-
1328-
// Perform the swap
1329-
SubtensorModule::swap_keys_for_coldkey(&old_coldkey, &new_coldkey, &mut weight);
1330-
1331-
// Verify the swap
1332-
assert_eq!(Keys::<Test>::get(netuid1, uid1), hotkey1);
1333-
assert_eq!(Keys::<Test>::get(netuid2, uid2), hotkey2);
1334-
1335-
// Verify weight update
1336-
let expected_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(3, 2);
1318+
let expected_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(5, 4);
13371319
assert_eq!(weight, expected_weight);
13381320
});
13391321
}
@@ -1380,9 +1362,16 @@ fn test_do_swap_coldkey_with_subnet_ownership() {
13801362
// Setup initial state
13811363
add_network(netuid, 13, 0);
13821364
register_ok_neuron(netuid, hotkey, old_coldkey, 0);
1365+
1366+
// Set TotalNetworks because swap relies on it
1367+
pallet_subtensor::TotalNetworks::<Test>::set(1);
1368+
13831369
SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount + swap_cost);
13841370
SubnetOwner::<Test>::insert(netuid, old_coldkey);
13851371

1372+
// Populate Owned map
1373+
Owned::<Test>::insert(old_coldkey, vec![hotkey]);
1374+
13861375
// Perform the swap
13871376
assert_ok!(SubtensorModule::do_swap_coldkey(
13881377
<<Test as Config>::RuntimeOrigin>::signed(old_coldkey),
@@ -1402,8 +1391,12 @@ fn test_do_swap_coldkey_tx_rate_limit() {
14021391
let new_coldkey = U256::from(2);
14031392
let swap_cost = SubtensorModule::get_coldkey_swap_cost();
14041393

1394+
// Set non-zero tx rate limit
1395+
SubtensorModule::set_tx_rate_limit(1);
1396+
14051397
// Setup initial state
14061398
SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, swap_cost * 2);
1399+
SubtensorModule::add_balance_to_coldkey_account(&new_coldkey, swap_cost * 2);
14071400

14081401
// Perform first swap
14091402
assert_ok!(SubtensorModule::do_swap_coldkey(

0 commit comments

Comments
 (0)