diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index f4f511e74a..5a6decd597 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1785,7 +1785,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 650fb50451..5c71c64179 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): diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 058622177e..2f9704a6b9 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -911,6 +911,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()); @@ -964,6 +967,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..fdcaa00222 100644 --- a/pallets/subtensor/src/tests/subnet.rs +++ b/pallets/subtensor/src/tests/subnet.rs @@ -251,9 +251,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 +273,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 +292,66 @@ 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 + ); + + assert_noop!( + SubtensorModule::remove_stake_limit( + RuntimeOrigin::signed(coldkey_account_id), + hotkey_account_id, + netuid, + amount, + limit_price, + false + ), + Error::::SubtokenDisabled + ); + assert_noop!( SubtensorModule::remove_stake( RuntimeOrigin::signed(coldkey_account_id), diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 0b64ee3cde..9bf932b298 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -209,7 +209,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: 273, + spec_version: 274, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,