diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ec9d9c7ed9..8be8c58c95 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1714,7 +1714,7 @@ pub mod pallet { } /// Ensure subtoken enalbed - pub fn ensure_subtoken_enabled(subnet: u16) -> DispatchResult { + pub fn ensure_subtoken_enabled(subnet: u16) -> Result<(), Error> { ensure!( SubtokenEnabled::::get(subnet), Error::::SubtokenDisabled diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 1b79721e80..d1a6f36884 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -551,11 +551,11 @@ mod dispatches { Self::do_increase_take(origin, hotkey, take) } - /// --- Adds stake to a hotkey. The call is made from the - /// coldkey account linked in the hotkey. - /// Only the associated coldkey is allowed to make staking and - /// unstaking requests. This protects the neuron against - /// attacks on its hotkey running in production code. + /// --- Adds stake to a hotkey. The call is made from a coldkey account. + /// This delegates stake to the hotkey. + /// + /// Note: the coldkey account may own the hotkey, in which case they are + /// delegating to themselves. /// /// # Args: /// * 'origin': (Origin): @@ -1786,7 +1786,7 @@ mod dispatches { /// #[pallet::call_index(88)] #[pallet::weight((Weight::from_parts(159_200_000, 0) - .saturating_add(T::DbWeight::get().reads(13)) + .saturating_add(T::DbWeight::get().reads(14)) .saturating_add(T::DbWeight::get().writes(10)), DispatchClass::Normal, Pays::No))] pub fn add_stake_limit( origin: OriginFor, diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 4b4aacb4b3..135e531653 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -909,6 +909,9 @@ impl Pallet { // Ensure that the subnet exists. ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + // Ensure that the subnet is enabled. + Self::ensure_subtoken_enabled(netuid)?; + // Get the minimum balance (and amount) that satisfies the transaction let min_amount = DefaultMinStake::::get().saturating_add(DefaultStakingFee::::get()); @@ -962,6 +965,9 @@ impl Pallet { // Ensure that the subnet exists. ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); + // Ensure that the subnet is enabled. + // Self::ensure_subtoken_enabled(netuid)?; + // Ensure that the stake amount to be removed is above the minimum in tao equivalent. if let Some(tao_equivalent) = Self::sim_swap_alpha_for_tao(netuid, alpha_unstaked) { ensure!( diff --git a/pallets/subtensor/src/tests/subnet.rs b/pallets/subtensor/src/tests/subnet.rs index ec737f8601..c95a841736 100644 --- a/pallets/subtensor/src/tests/subnet.rs +++ b/pallets/subtensor/src/tests/subnet.rs @@ -239,7 +239,9 @@ fn test_subtoken_enable() { }); } -// cargo test --package pallet-subtensor --lib -- tests::subnet::test_subtoken_enable_reject_trading_before_enable --exact --show-output +// cargo test --package pallet-subtensor --lib -- +// tests::subnet::test_subtoken_enable_reject_trading_before_enable --exact --show-output +#[allow(clippy::unwrap_used)] #[test] fn test_subtoken_enable_reject_trading_before_enable() { // ensure_subtoken_enabled @@ -251,9 +253,20 @@ fn test_subtoken_enable_reject_trading_before_enable() { let hotkey_account_2_id: U256 = U256::from(3); let amount = DefaultMinStake::::get() * 10; + let stake_bal = 10_000_000_000; // 10 Alpha + + let limit_price = 1_000_000_000; // not important + add_network_disable_subtoken(netuid, 10, 0); add_network_disable_subtoken(netuid2, 10, 0); + assert!(!SubtokenEnabled::::get(netuid)); + assert!(!SubtokenEnabled::::get(netuid2)); + + // Set liq high enough to not trigger other errors + SubnetTAO::::set(netuid, 20_000_000_000); + SubnetAlphaIn::::set(netuid, 20_000_000_000); + // Register so staking *could* work register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 0); register_ok_neuron(netuid2, hotkey_account_id, coldkey_account_id, 100); @@ -262,6 +275,14 @@ fn test_subtoken_enable_reject_trading_before_enable() { SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10_000); + // Give some stake + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid, + stake_bal, + ); + // all trading extrinsic should be rejected. assert_noop!( SubtensorModule::add_stake( @@ -273,6 +294,64 @@ fn test_subtoken_enable_reject_trading_before_enable() { Error::::SubtokenDisabled ); + assert_noop!( + SubtensorModule::add_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount, + limit_price, + false + ), + Error::::SubtokenDisabled + ); + + // For unstake_all and unstake_all_alpha, the result is Ok, but the + // operation is not performed. + assert_ok!( + SubtensorModule::unstake_all( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id + ), + () + ); + // Check that the stake is still the same + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid + ), + stake_bal + ); + + assert_ok!( + SubtensorModule::unstake_all_alpha( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id + ), + () + ); + // Check that the stake is still the same + assert_eq!( + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account_id, + &coldkey_account_id, + netuid + ), + stake_bal + ); + + SubtensorModule::remove_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount, + limit_price, + false, + ) + .unwrap(); + assert_noop!( SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id),