From 752abc293c2c5a65a12e7ad7a8db3b335ec0d8e3 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 15 Jan 2025 16:39:42 -0500 Subject: [PATCH] Re-add move_stake extrinsic and tests --- pallets/subtensor/src/macros/dispatches.rs | 41 + pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/staking/mod.rs | 1 + pallets/subtensor/src/staking/move_stake.rs | 7 +- pallets/subtensor/src/tests/mod.rs | 1 + pallets/subtensor/src/tests/move_stake.rs | 783 ++++++++++++++++++++ 6 files changed, 834 insertions(+), 1 deletion(-) create mode 100644 pallets/subtensor/src/tests/move_stake.rs diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index c101cff5c..7073a1413 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1555,5 +1555,46 @@ mod dispatches { pub fn unstake_all_alpha(origin: OriginFor, hotkey: T::AccountId) -> DispatchResult { Self::do_unstake_all_alpha(origin, hotkey) } + + /// ---- The implementation for the extrinsic move_stake: Moves specified amount of stake from a hotkey to another across subnets. + /// + /// # Args: + /// * `origin` - (::Origin): + /// - The signature of the caller's coldkey. + /// + /// * `origin_hotkey` (T::AccountId): + /// - The hotkey account to move stake from. + /// + /// * `destination_hotkey` (T::AccountId): + /// - The hotkey account to move stake to. + /// + /// * `origin_netuid` (T::AccountId): + /// - The subnet ID to move stake from. + /// + /// * `destination_netuid` (T::AccountId): + /// - The subnet ID to move stake to. + /// + /// * `alpha_amount` (T::AccountId): + /// - The alpha stake amount to move. + /// + #[pallet::call_index(85)] + #[pallet::weight((Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Operational, Pays::No))] + pub fn move_stake( + origin: T::RuntimeOrigin, + origin_hotkey: T::AccountId, + destination_hotkey: T::AccountId, + origin_netuid: u16, + destination_netuid: u16, + alpha_amount: u64, + ) -> DispatchResult { + Self::do_move_stake( + origin, + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha_amount, + ) + } } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 7ef00b1e3..ff61784e7 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -17,6 +17,8 @@ mod events { StakeAdded(T::AccountId, T::AccountId, u64, u64, u16), /// stake has been removed from the hotkey staking account onto the coldkey account. StakeRemoved(T::AccountId, T::AccountId, u64, u64, u16), + /// stake has been moved from origin (hotkey, subnet ID) to destination (hotkey, subnet ID). + StakeMoved(T::AccountId, T::AccountId, u16, T::AccountId, u16, u64), /// a caller successfully sets their weights on a subnetwork. WeightsSet(u16, u16), /// a new neuron account has been registered to the chain. diff --git a/pallets/subtensor/src/staking/mod.rs b/pallets/subtensor/src/staking/mod.rs index 7508b9d2a..2b222036c 100644 --- a/pallets/subtensor/src/staking/mod.rs +++ b/pallets/subtensor/src/staking/mod.rs @@ -3,6 +3,7 @@ pub mod add_stake; pub mod decrease_take; pub mod helpers; pub mod increase_take; +pub mod move_stake; pub mod remove_stake; pub mod set_children; pub mod stake_utils; diff --git a/pallets/subtensor/src/staking/move_stake.rs b/pallets/subtensor/src/staking/move_stake.rs index a5794fe67..81cfef289 100644 --- a/pallets/subtensor/src/staking/move_stake.rs +++ b/pallets/subtensor/src/staking/move_stake.rs @@ -57,7 +57,11 @@ impl Pallet { ); // --- 6. Get the current alpha stake for the origin hotkey-coldkey pair in the origin subnet - let origin_alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet( &origin_hotkey, &coldkey, origin_netuid ); + let origin_alpha = Self::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); ensure!( alpha_amount <= origin_alpha, Error::::NotEnoughStakeToWithdraw @@ -94,6 +98,7 @@ impl Pallet { origin_netuid, destination_hotkey, destination_netuid, + origin_tao, )); // -- 10. Ok and return. diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index e1b01ab81..e0fef9d55 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -7,6 +7,7 @@ mod epoch; mod math; mod migration; mod mock; +mod move_stake; mod networks; mod neuron_info; mod registration; diff --git a/pallets/subtensor/src/tests/move_stake.rs b/pallets/subtensor/src/tests/move_stake.rs new file mode 100644 index 000000000..b40ea79c4 --- /dev/null +++ b/pallets/subtensor/src/tests/move_stake.rs @@ -0,0 +1,783 @@ +use super::mock::*; +use crate::*; +use approx::assert_abs_diff_eq; +use frame_support::{assert_err, assert_noop, assert_ok}; +use sp_core::U256; + +// 1. test_do_move_success +// Description: Test a successful move of stake between two hotkeys in the same subnet +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_success --exact --nocapture +#[test] +fn test_do_move_success() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Perform the move + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check that the stake has been moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +// 2. test_do_move_different_subnets +// Description: Test moving stake between two hotkeys in different subnets +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_different_subnets --exact --nocapture +#[test] +fn test_do_move_different_subnets() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let origin_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let destination_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake and subnets + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); + + // Perform the move + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha, + )); + + // Check that the stake has been moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + destination_netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +// 4. test_do_move_nonexistent_subnet +// Description: Attempt to move stake to a non-existent subnet, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_nonexistent_subnet --exact --nocapture +#[test] +fn test_do_move_nonexistent_subnet() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let origin_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let nonexistent_netuid = 99; // Assuming this subnet doesn't exist + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); + + // Attempt to move stake to a non-existent subnet + assert_noop!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + origin_netuid, + nonexistent_netuid, + alpha, + ), + Error::::SubnetNotExists + ); + + // Check that the stake remains unchanged + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +// 5. test_do_move_nonexistent_origin_hotkey +// Description: Attempt to move stake from a non-existent origin hotkey, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_nonexistent_origin_hotkey --exact --nocapture +#[test] +fn test_do_move_nonexistent_origin_hotkey() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let nonexistent_origin_hotkey = U256::from(99); // Assuming this hotkey doesn't exist + let destination_hotkey = U256::from(3); + + // Attempt to move stake from a non-existent origin hotkey + assert_noop!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + nonexistent_origin_hotkey, + destination_hotkey, + netuid, + netuid, + 123 + ), + Error::::HotKeyAccountNotExists + ); + + // Check that no stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &nonexistent_origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 6. test_do_move_nonexistent_destination_hotkey +// Description: Attempt to move stake to a non-existent destination hotkey, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_nonexistent_destination_hotkey --exact --nocapture +#[test] +fn test_do_move_nonexistent_destination_hotkey() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let nonexistent_destination_hotkey = U256::from(99); // Assuming this hotkey doesn't exist + let netuid = 1; + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + + // Attempt to move stake from a non-existent origin hotkey + add_network(netuid, 0, 0); + assert_noop!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + nonexistent_destination_hotkey, + netuid, + netuid, + 123 + ), + Error::::HotKeyAccountNotExists + ); + + // Check that the stake was moved successfully + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 1000 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &nonexistent_destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 7. test_do_move_zero_stake +// Description: Test moving zero stake, which should succeed but have no effect +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_zero_stake --exact --nocapture +#[test] +fn test_do_move_zero_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + + // Attempt to move zero stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + 0, + )); + + // Check that no stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 8. test_do_move_all_stake +// Description: Test moving all stake from one hotkey to another +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_all_stake --exact --nocapture +#[test] +fn test_do_move_all_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move all stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check that all stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + stake_amount, + epsilon = 5 + ); + }); +} + +#[test] +fn test_do_move_half_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move all stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha / 2, + )); + + // Check that all stake was moved + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + stake_amount / 2, + epsilon = 5 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + stake_amount / 2, + epsilon = 5 + ); + }); +} + +// 9. test_do_move_partial_stake +// Description: Test moving a portion of stake from one hotkey to another +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_partial_stake --exact --nocapture +#[test] +fn test_do_move_partial_stake() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let total_stake = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, total_stake); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move partial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check that the correct amount of stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + total_stake, + epsilon = 5 + ); + }); +} + +// 10. test_do_move_multiple_times +// Description: Test moving stake multiple times between the same hotkeys +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_multiple_times --exact --nocapture +#[test] +fn test_do_move_multiple_times() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let hotkey1 = U256::from(2); + let hotkey2 = U256::from(3); + let initial_stake = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey1); + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey2); + SubtensorModule::stake_into_subnet(&hotkey1, &coldkey, netuid, initial_stake); + + // Move stake multiple times + for _ in 0..3 { + let alpha1 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey1, &coldkey, netuid, + ); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey1, + hotkey2, + netuid, + netuid, + alpha1, + )); + let alpha2 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey2, &coldkey, netuid, + ); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey2, + hotkey1, + netuid, + netuid, + alpha2, + )); + } + + // Check final stake distribution + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey1, &coldkey, netuid), + initial_stake, + epsilon = 10 + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey2, &coldkey, netuid), + 0 + ); + }); +} + +// 13. test_do_move_wrong_origin +// Description: Attempt to move stake with a different origin than the coldkey, which should fail +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_wrong_origin --exact --nocapture +#[test] +fn test_do_move_wrong_origin() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + let wrong_coldkey = U256::from(99); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let netuid = 1; + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Attempt to move stake with wrong origin + add_network(netuid, 0, 0); + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + assert_err!( + SubtensorModule::do_move_stake( + RuntimeOrigin::signed(wrong_coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + ), + Error::::NotEnoughStakeToWithdraw + ); + + // Check that no stake was moved + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + stake_amount + ); + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + 0 + ); + }); +} + +// 14. test_do_move_same_hotkey +// Description: Attempt to move stake to the same hotkey, which should fail or have no effect +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_same_hotkey --exact --nocapture +#[test] +fn test_do_move_same_hotkey() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let hotkey = U256::from(2); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &hotkey); + SubtensorModule::stake_into_subnet(&hotkey, &coldkey, netuid, stake_amount); + let alpha = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + + // Attempt to move stake to the same hotkey + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + hotkey, + netuid, + netuid, + alpha, + )); + + // Check that stake remains unchanged + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid), + alpha, + epsilon = 5 + ); + }); +} + +// 15. test_do_move_event_emission +// Description: Verify that the correct event is emitted after a successful move +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_event_emission --exact --nocapture +#[test] +fn test_do_move_event_emission() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, stake_amount); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move stake and capture events + System::reset_events(); + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Check for the correct event emission + System::assert_last_event( + Event::StakeMoved( + coldkey, + origin_hotkey, + netuid, + destination_hotkey, + netuid, + alpha, + ) + .into(), + ); + }); +} + +// 16. test_do_move_storage_updates +// Description: Verify that all relevant storage items are correctly updated after a move +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_storage_updates --exact --nocapture +#[test] +fn test_do_move_storage_updates() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let origin_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let destination_netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let stake_amount = 1000; + + // Set up initial stake + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, origin_netuid, stake_amount); + + // Move stake + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid, + ); + + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + origin_netuid, + destination_netuid, + alpha, + )); + + // Verify storage updates + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + origin_netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + destination_netuid + ), + alpha, + epsilon = 5 + ); + }); +} + +// 18. test_do_move_max_values +// Description: Test moving the maximum possible stake values to check for overflows +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test move -- test_do_move_max_values --exact --nocapture +#[test] +fn test_do_move_max_values() { + new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let coldkey = U256::from(1); + let origin_hotkey = U256::from(2); + let destination_hotkey = U256::from(3); + let max_stake = u64::MAX; + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + + // Set up initial stake with maximum value + SubtensorModule::create_account_if_non_existent(&coldkey, &origin_hotkey); + SubtensorModule::create_account_if_non_existent(&coldkey, &destination_hotkey); + SubtensorModule::stake_into_subnet(&origin_hotkey, &coldkey, netuid, max_stake); + let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid, + ); + + // Move maximum stake + assert_ok!(SubtensorModule::do_move_stake( + RuntimeOrigin::signed(coldkey), + origin_hotkey, + destination_hotkey, + netuid, + netuid, + alpha, + )); + + // Verify stake movement without overflow + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &origin_hotkey, + &coldkey, + netuid + ), + 0 + ); + assert_abs_diff_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &destination_hotkey, + &coldkey, + netuid + ), + alpha, + epsilon = 5 + ); + }); +}