diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index 1652610afb..c2d7c54567 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -57,11 +57,21 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads(2)); - // 7. Ensure the new hotkey is not already registered on any network - ensure!( - !Self::is_hotkey_registered_on_any_network(new_hotkey), - Error::::HotKeyAlreadyRegisteredInSubNet - ); + // 7. Ensure the new hotkey is not already registered on any network, only if netuid is none + if netuid.is_none() { + ensure!( + !Self::is_hotkey_registered_on_any_network(new_hotkey), + Error::::HotKeyAlreadyRegisteredInSubNet + ); + } + + // 7.1. Ensure the hotkey is not registered on the network before, if netuid is provided + if let Some(netuid) = netuid { + ensure!( + !Self::is_hotkey_registered_on_specific_network(new_hotkey, netuid), + Error::::HotKeyAlreadyRegisteredInSubNet + ); + } // 8. Swap LastTxBlock let last_tx_block: u64 = Self::get_last_tx_block(old_hotkey); diff --git a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs index f71133859c..be2895941c 100644 --- a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs +++ b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs @@ -11,7 +11,9 @@ use super::mock::*; use crate::*; use sp_core::{Get, H160, H256, U256}; use sp_runtime::SaturatedConversion; +use std::collections::BTreeSet; use substrate_fixed::types::U64F64; + // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey_with_subnet -- test_swap_owner --exact --nocapture #[test] fn test_swap_owner() { @@ -1555,35 +1557,700 @@ fn test_swap_owner_check_swap_record_clean_up() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test swap_hotkey_with_subnet -- test_swap_hotkey_error_cases --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_hotkey_swap_stake_is_not_lost --exact --nocapture #[test] -fn test_swap_hotkey_registered_on_other_subnet() { +fn test_revert_hotkey_swap_stake_is_not_lost() { new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + let netuid2 = NetUid::from(2); + let tempo: u16 = 13; + let hk1 = U256::from(1); + let hk2 = U256::from(2); + let coldkey = U256::from(3); + let swap_cost = 1_000_000_000u64 * 2; + let stake2 = 1_000_000_000u64; + + // Setup + add_network(netuid, tempo, 0); + add_network(netuid2, tempo, 0); + register_ok_neuron(netuid, hk1, coldkey, 0); + register_ok_neuron(netuid2, hk1, coldkey, 0); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.into()); + + let hk1_stake_before_increase = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk1, &coldkey, netuid); + assert!( + hk1_stake_before_increase == 0.into(), + "hk1 should have empty stake" + ); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hk1, + &coldkey, + netuid, + 1_000_000_000u64.into(), + ); + + let hk1_stake_before_swap = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk1, &coldkey, netuid); + assert!( + hk1_stake_before_swap == 1_000_000_000.into(), + "hk1 should have stake before swap" + ); + + step_block(20); + + assert_ok!(SubtensorModule::do_swap_hotkey( + <::RuntimeOrigin>::signed(coldkey), + &hk1, + &hk2, + Some(netuid) + )); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hk1, + &coldkey, + netuid, + stake2.into(), + ); + + step_block(20); + + let hk2_stake_before_revert = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk2, &coldkey, netuid); + let hk1_stake_before_revert = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk1, &coldkey, netuid); + + assert_eq!(hk1_stake_before_revert, stake2.into()); + + // Revert: hk2 -> hk1 + assert_ok!(SubtensorModule::do_swap_hotkey( + <::RuntimeOrigin>::signed(coldkey), + &hk2, + &hk1, + Some(netuid) + )); + + let hk1_stake_after_revert = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk1, &coldkey, netuid); + let hk2_stake_after_revert = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk2, &coldkey, netuid); + + assert_eq!( + hk1_stake_after_revert, + hk2_stake_before_revert + stake2.into(), + ); + + // hk2 should be empty + assert_eq!( + hk2_stake_after_revert, + 0.into(), + "hk2 should have no stake after revert" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_hotkey_swap --exact --nocapture +// This test confirms, that the old hotkey can be reverted after the hotkey swap +#[test] +fn test_revert_hotkey_swap() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(1); + let netuid2 = NetUid::from(2); + let tempo: u16 = 13; let old_hotkey = U256::from(1); let new_hotkey = U256::from(2); let coldkey = U256::from(3); - let wrong_coldkey = U256::from(4); - let netuid = add_dynamic_network(&old_hotkey, &coldkey); - let other_netuid = add_dynamic_network(&old_hotkey, &coldkey); + let swap_cost = 1_000_000_000u64 * 2; - // Set up initial state - Owner::::insert(old_hotkey, coldkey); - TotalNetworks::::put(1); + // Setup initial state + add_network(netuid, tempo, 0); + add_network(netuid2, tempo, 0); + register_ok_neuron(netuid, old_hotkey, coldkey, 0); + register_ok_neuron(netuid2, old_hotkey, coldkey, 0); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, swap_cost.into()); + step_block(20); - let initial_balance = SubtensorModule::get_key_swap_cost() + 1000.into(); - SubtensorModule::add_balance_to_coldkey_account(&coldkey, initial_balance); + // Perform the first swap (only on netuid) + assert_ok!(SubtensorModule::do_swap_hotkey( + <::RuntimeOrigin>::signed(coldkey), + &old_hotkey, + &new_hotkey, + Some(netuid) + )); + + assert!(SubtensorModule::is_hotkey_registered_on_any_network( + &old_hotkey + )); + + step_block(20); + + assert_ok!(SubtensorModule::do_swap_hotkey( + <::RuntimeOrigin>::signed(coldkey), + &new_hotkey, + &old_hotkey, + Some(netuid) + )); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_hotkey_swap_parent_hotkey_childkey_maps --exact --nocapture +#[test] +fn test_revert_hotkey_swap_parent_hotkey_childkey_maps() { + new_test_ext(1).execute_with(|| { + let hk1 = U256::from(1); + let coldkey = U256::from(2); + let child = U256::from(3); + let child_other = U256::from(4); + let hk2 = U256::from(5); + + let netuid = add_dynamic_network(&hk1, &coldkey); + let netuid2 = add_dynamic_network(&hk1, &coldkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + SubtensorModule::create_account_if_non_existent(&coldkey, &hk1); + + mock_set_children(&coldkey, &hk1, netuid, &[(u64::MAX, child)]); + step_rate_limit(&TransactionType::SetChildren, netuid); + mock_schedule_children(&coldkey, &hk1, netuid, &[(u64::MAX, child_other)]); + + assert_eq!( + ParentKeys::::get(child, netuid), + vec![(u64::MAX, hk1)] + ); + assert_eq!(ChildKeys::::get(hk1, netuid), vec![(u64::MAX, child)]); + let existing_pending_child_keys = PendingChildKeys::::get(netuid, hk1); + assert_eq!(existing_pending_child_keys.0, vec![(u64::MAX, child_other)]); - // Test new hotkey already registered on other subnet - IsNetworkMember::::insert(new_hotkey, other_netuid, true); System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); - assert_noop!( - SubtensorModule::do_swap_hotkey( - RuntimeOrigin::signed(coldkey), - &old_hotkey, - &new_hotkey, - Some(netuid) - ), - Error::::HotKeyAlreadyRegisteredInSubNet + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk1, + &hk2, + Some(netuid) + )); + + assert_eq!( + ParentKeys::::get(child, netuid), + vec![(u64::MAX, hk2)] + ); + assert_eq!(ChildKeys::::get(hk2, netuid), vec![(u64::MAX, child)]); + assert_eq!( + PendingChildKeys::::get(netuid, hk2), + existing_pending_child_keys + ); + assert!(ChildKeys::::get(hk1, netuid).is_empty()); + assert!(PendingChildKeys::::get(netuid, hk1).0.is_empty()); + + // Revert: hk2 -> hk1 + step_block(20); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk2, + &hk1, + Some(netuid) + )); + + assert_eq!( + ParentKeys::::get(child, netuid), + vec![(u64::MAX, hk1)], + "ParentKeys must point back to hk1 after revert" + ); + assert_eq!( + ChildKeys::::get(hk1, netuid), + vec![(u64::MAX, child)], + "ChildKeys must be restored to hk1 after revert" + ); + assert_eq!( + PendingChildKeys::::get(netuid, hk1), + existing_pending_child_keys, + "PendingChildKeys must be restored to hk1 after revert" + ); + + assert!( + ChildKeys::::get(hk2, netuid).is_empty(), + "hk2 must have no ChildKeys after revert" + ); + assert!( + PendingChildKeys::::get(netuid, hk2).0.is_empty(), + "hk2 must have no PendingChildKeys after revert" + ); + }) +} +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_hotkey_swap_uids_and_keys --exact --nocapture +#[test] +fn test_revert_hotkey_swap_uids_and_keys() { + new_test_ext(1).execute_with(|| { + let uid = 5u16; + let hk1 = U256::from(1); + let hk2 = U256::from(2); + let coldkey = U256::from(3); + + let netuid = add_dynamic_network(&hk1, &coldkey); + let netuid2 = add_dynamic_network(&hk1, &coldkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + + IsNetworkMember::::insert(hk1, netuid, true); + Uids::::insert(netuid, hk1, uid); + Keys::::insert(netuid, uid, hk1); + + System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk1, + &hk2, + Some(netuid) + )); + + assert_eq!(Uids::::get(netuid, hk1), None); + assert_eq!(Uids::::get(netuid, hk2), Some(uid)); + assert_eq!(Keys::::get(netuid, uid), hk2); + + // Revert: hk2 -> hk1 + step_block(20); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk2, + &hk1, + Some(netuid) + )); + + assert_eq!( + Uids::::get(netuid, hk2), + None, + "hk2 must have no uid after revert" + ); + assert_eq!( + Uids::::get(netuid, hk1), + Some(uid), + "hk1 must have its uid restored after revert" + ); + assert_eq!( + Keys::::get(netuid, uid), + hk1, + "Keys must point back to hk1 after revert" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_hotkey_swap_auto_stake_destination --exact --nocapture +#[test] +fn test_revert_hotkey_swap_auto_stake_destination() { + new_test_ext(1).execute_with(|| { + let hk1 = U256::from(1); + let hk2 = U256::from(2); + let coldkey = U256::from(3); + let netuid = NetUid::from(2u16); + let netuid2 = NetUid::from(3u16); + let staker1 = U256::from(4); + let staker2 = U256::from(5); + let coldkeys = vec![staker1, staker2, coldkey]; + + add_network(netuid, 1, 0); + add_network(netuid2, 1, 0); + register_ok_neuron(netuid, hk1, coldkey, 0); + register_ok_neuron(netuid2, hk1, coldkey, 0); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + + AutoStakeDestinationColdkeys::::insert(hk1, netuid, coldkeys.clone()); + AutoStakeDestination::::insert(coldkey, netuid, hk1); + AutoStakeDestination::::insert(staker1, netuid, hk1); + AutoStakeDestination::::insert(staker2, netuid, hk1); + + System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk1, + &hk2, + Some(netuid) + )); + + assert_eq!( + AutoStakeDestinationColdkeys::::get(hk2, netuid), + coldkeys + ); + assert!(AutoStakeDestinationColdkeys::::get(hk1, netuid).is_empty()); + assert_eq!( + AutoStakeDestination::::get(coldkey, netuid), + Some(hk2) + ); + assert_eq!( + AutoStakeDestination::::get(staker1, netuid), + Some(hk2) + ); + assert_eq!( + AutoStakeDestination::::get(staker2, netuid), + Some(hk2) + ); + + // Revert: hk2 -> hk1 + step_block(20); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk2, + &hk1, + Some(netuid) + )); + + assert_eq!( + AutoStakeDestinationColdkeys::::get(hk1, netuid), + coldkeys, + "AutoStakeDestinationColdkeys must be restored to hk1 after revert" + ); + assert!( + AutoStakeDestinationColdkeys::::get(hk2, netuid).is_empty(), + "hk2 must have no AutoStakeDestinationColdkeys after revert" + ); + assert_eq!( + AutoStakeDestination::::get(coldkey, netuid), + Some(hk1), + "coldkey AutoStakeDestination must point back to hk1 after revert" + ); + assert_eq!( + AutoStakeDestination::::get(staker1, netuid), + Some(hk1), + "staker1 AutoStakeDestination must point back to hk1 after revert" + ); + assert_eq!( + AutoStakeDestination::::get(staker2, netuid), + Some(hk1), + "staker2 AutoStakeDestination must point back to hk1 after revert" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_hotkey_swap_subnet_owner --exact --nocapture +#[test] +fn test_revert_hotkey_swap_subnet_owner() { + new_test_ext(1).execute_with(|| { + let hk1 = U256::from(1); + let hk2 = U256::from(2); + let coldkey = U256::from(3); + + let netuid = add_dynamic_network(&hk1, &coldkey); + let netuid2 = add_dynamic_network(&hk1, &coldkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + + assert_eq!(SubnetOwnerHotkey::::get(netuid), hk1); + + System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk1, + &hk2, + Some(netuid) + )); + + assert_eq!( + SubnetOwnerHotkey::::get(netuid), + hk2, + "hk2 must be subnet owner after swap" + ); + + // Revert: hk2 -> hk1 + step_block(20); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk2, + &hk1, + Some(netuid) + )); + + assert_eq!( + SubnetOwnerHotkey::::get(netuid), + hk1, + "hk1 must be restored as subnet owner after revert" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_hotkey_swap_dividends --exact --nocapture +#[test] +fn test_revert_hotkey_swap_dividends() { + new_test_ext(1).execute_with(|| { + let hk1 = U256::from(1); + let hk2 = U256::from(2); + let coldkey = U256::from(3); + + let netuid = add_dynamic_network(&hk1, &coldkey); + let netuid2 = add_dynamic_network(&hk1, &coldkey); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, u64::MAX.into()); + + let amount = 10_000; + let shares = U64F64::from_num(123456); + + TotalHotkeyAlpha::::insert(hk1, netuid, AlphaBalance::from(amount)); + TotalHotkeyAlphaLastEpoch::::insert(hk1, netuid, AlphaBalance::from(amount * 2)); + TotalHotkeyShares::::insert(hk1, netuid, U64F64::from_num(shares)); + Alpha::::insert((hk1, coldkey, netuid), U64F64::from_num(amount)); + AlphaDividendsPerSubnet::::insert(netuid, hk1, AlphaBalance::from(amount)); + + System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk1, + &hk2, + Some(netuid) + )); + + assert_eq!( + TotalHotkeyAlpha::::get(hk1, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlpha::::get(hk2, netuid), + AlphaBalance::from(amount) + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hk1, netuid), + AlphaBalance::ZERO + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hk2, netuid), + AlphaBalance::from(amount * 2) + ); + assert_eq!( + TotalHotkeyShares::::get(hk1, netuid), + U64F64::from_num(0) + ); + assert_eq!( + TotalHotkeyShares::::get(hk2, netuid), + U64F64::from_num(shares) + ); + assert_eq!( + Alpha::::get((hk1, coldkey, netuid)), + U64F64::from_num(0) + ); + assert_eq!( + Alpha::::get((hk2, coldkey, netuid)), + U64F64::from_num(amount) + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, hk1), + AlphaBalance::ZERO + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, hk2), + AlphaBalance::from(amount) + ); + + // Revert: hk2 -> hk1 + step_block(20); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(coldkey), + &hk2, + &hk1, + Some(netuid) + )); + + assert_eq!( + TotalHotkeyAlpha::::get(hk2, netuid), + AlphaBalance::ZERO, + "hk2 TotalHotkeyAlpha must be zero after revert" + ); + assert_eq!( + TotalHotkeyAlpha::::get(hk1, netuid), + AlphaBalance::from(amount), + "hk1 TotalHotkeyAlpha must be restored after revert" + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hk2, netuid), + AlphaBalance::ZERO, + "hk2 TotalHotkeyAlphaLastEpoch must be zero after revert" + ); + assert_eq!( + TotalHotkeyAlphaLastEpoch::::get(hk1, netuid), + AlphaBalance::from(amount * 2), + "hk1 TotalHotkeyAlphaLastEpoch must be restored after revert" + ); + assert_eq!( + TotalHotkeyShares::::get(hk2, netuid), + U64F64::from_num(0), + "hk2 TotalHotkeyShares must be zero after revert" + ); + assert_eq!( + TotalHotkeyShares::::get(hk1, netuid), + U64F64::from_num(shares), + "hk1 TotalHotkeyShares must be restored after revert" + ); + assert_eq!( + Alpha::::get((hk2, coldkey, netuid)), + U64F64::from_num(0), + "hk2 Alpha must be zero after revert" + ); + assert_eq!( + Alpha::::get((hk1, coldkey, netuid)), + U64F64::from_num(amount), + "hk1 Alpha must be restored after revert" + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, hk2), + AlphaBalance::ZERO, + "hk2 AlphaDividendsPerSubnet must be zero after revert" + ); + assert_eq!( + AlphaDividendsPerSubnet::::get(netuid, hk1), + AlphaBalance::from(amount), + "hk1 AlphaDividendsPerSubnet must be restored after revert" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_voting_power_transfers_on_hotkey_swap --exact --nocapture +#[test] +fn test_revert_voting_power_transfers_on_hotkey_swap() { + new_test_ext(1).execute_with(|| { + let hk1 = U256::from(1); + let hk2 = U256::from(99); + let coldkey = U256::from(2); + let netuid = add_dynamic_network(&hk1, &coldkey); + let voting_power_value = 5_000_000_000_000_u64; + + VotingPower::::insert(netuid, hk1, voting_power_value); + assert_eq!( + SubtensorModule::get_voting_power(netuid, &hk1), + voting_power_value + ); + assert_eq!(SubtensorModule::get_voting_power(netuid, &hk2), 0); + + SubtensorModule::swap_voting_power_for_hotkey(&hk1, &hk2, netuid); + + assert_eq!(SubtensorModule::get_voting_power(netuid, &hk1), 0); + assert_eq!( + SubtensorModule::get_voting_power(netuid, &hk2), + voting_power_value + ); + + // Revert: hk2 -> hk1 + SubtensorModule::swap_voting_power_for_hotkey(&hk2, &hk1, netuid); + + assert_eq!( + SubtensorModule::get_voting_power(netuid, &hk1), + voting_power_value, + "hk1 voting power must be fully restored after revert" + ); + assert_eq!( + SubtensorModule::get_voting_power(netuid, &hk2), + 0, + "hk2 must have no voting power after revert" + ); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::swap_hotkey_with_subnet::test_revert_claim_root_with_swap_hotkey --exact --nocapture +#[test] +fn test_revert_claim_root_with_swap_hotkey() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hk1 = U256::from(1002); + let hk2 = U256::from(1003); + let coldkey = U256::from(1004); + + let netuid = add_dynamic_network(&hk1, &owner_coldkey); + let netuid2 = add_dynamic_network(&hk1, &owner_coldkey); + + SubtensorModule::add_balance_to_coldkey_account(&owner_coldkey, u64::MAX.into()); + SubtensorModule::set_tao_weight(u64::MAX); + + let root_stake = 2_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hk1, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hk1, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + let pending_root_alpha = 1_000_000u64; + SubtensorModule::distribute_emission( + netuid, + AlphaBalance::ZERO, + AlphaBalance::ZERO, + pending_root_alpha.into(), + AlphaBalance::ZERO, + ); + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + )); + assert_ok!(SubtensorModule::claim_root( + RuntimeOrigin::signed(coldkey), + BTreeSet::from([netuid]) + )); + + let stake_after_claim: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hk1, &coldkey, netuid) + .into(); + + let hk1_root_claimed = RootClaimed::::get((netuid, &hk1, &coldkey)); + let hk1_claimable = *RootClaimable::::get(hk1) + .get(&netuid) + .unwrap(); + + assert_eq!(u128::from(stake_after_claim), hk1_root_claimed); + assert!(!RootClaimable::::get(hk2).contains_key(&netuid)); + + System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(owner_coldkey), + &hk1, + &hk2, + Some(netuid) + )); + + assert_eq!( + RootClaimed::::get((netuid, &hk1, &coldkey)), + 0u128, + "hk1 RootClaimed must be zero after swap" + ); + assert_eq!( + RootClaimed::::get((netuid, &hk2, &coldkey)), + hk1_root_claimed, + "hk2 must have hk1's RootClaimed after swap" + ); + assert!(!RootClaimable::::get(hk1).contains_key(&netuid)); + assert_eq!( + *RootClaimable::::get(hk2) + .get(&netuid) + .unwrap(), + hk1_claimable, + "hk2 must have hk1's RootClaimable after swap" + ); + + // Revert: hk2 -> hk1 + step_block(20); + assert_ok!(SubtensorModule::do_swap_hotkey( + RuntimeOrigin::signed(owner_coldkey), + &hk2, + &hk1, + Some(netuid) + )); + + assert_eq!( + RootClaimed::::get((netuid, &hk2, &coldkey)), + 0u128, + "hk2 RootClaimed must be zero after revert" + ); + assert_eq!( + RootClaimed::::get((netuid, &hk1, &coldkey)), + hk1_root_claimed, + "hk1 RootClaimed must be restored after revert" + ); + + assert!(!RootClaimable::::get(hk2).contains_key(&netuid)); + assert_eq!( + *RootClaimable::::get(hk1) + .get(&netuid) + .unwrap(), + hk1_claimable, + "hk1 RootClaimable must be restored after revert" ); }); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index e554ce0b25..b21af85162 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1116,7 +1116,7 @@ parameter_types! { // 0 days pub const InitialStartCallDelay: u64 = 0; pub const SubtensorInitialKeySwapOnSubnetCost: TaoBalance = TaoBalance::new(1_000_000); // 0.001 TAO - pub const HotkeySwapOnSubnetInterval : BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days + pub const HotkeySwapOnSubnetInterval : BlockNumber = 24 * 60 * 60 / 12; // 1 day pub const LeaseDividendsDistributionInterval: BlockNumber = 100; // 100 blocks pub const MaxImmuneUidsPercentage: Percent = Percent::from_percent(80); pub const EvmKeyAssociateRateLimit: u64 = EVM_KEY_ASSOCIATE_RATELIMIT;