From 795d366d1adc7cd46cd8360fe76b02b1d2ec0da4 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 11 Feb 2025 17:39:33 -0800 Subject: [PATCH 01/13] add tao_emission to stakeinfo runtime --- pallets/subtensor/src/rpc_info/stake_info.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 631e3a167..bda619596 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -3,7 +3,7 @@ use frame_support::pallet_prelude::{Decode, Encode}; extern crate alloc; use codec::Compact; -#[freeze_struct("4f16c654467bc8b6")] +#[freeze_struct("5cfb3c84c3af3116")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct StakeInfo { hotkey: AccountId, @@ -12,6 +12,7 @@ pub struct StakeInfo { stake: Compact, locked: Compact, emission: Compact, + tao_emission: Compact, drain: Compact, is_registered: bool, } @@ -38,6 +39,7 @@ impl Pallet { continue; } let emission: u64 = AlphaDividendsPerSubnet::::get(*netuid_i, &hotkey_i); + let tao_emission: u64 = TaoDividendsPerSubnet::::get(*netuid_i, &hotkey_i); let is_registered: bool = Self::is_hotkey_registered_on_network(*netuid_i, hotkey_i); stake_info_for_coldkey.push(StakeInfo { @@ -47,6 +49,7 @@ impl Pallet { stake: alpha.into(), locked: 0.into(), emission: emission.into(), + tao_emission: tao_emission.into(), drain: 0.into(), is_registered, }); @@ -94,6 +97,7 @@ impl Pallet { netuid, ); let emission: u64 = AlphaDividendsPerSubnet::::get(netuid, &hotkey_account); + let tao_emission: u64 = TaoDividendsPerSubnet::::get(netuid, &hotkey_account); let is_registered: bool = Self::is_hotkey_registered_on_network(netuid, &hotkey_account); Some(StakeInfo { @@ -103,6 +107,7 @@ impl Pallet { stake: alpha.into(), locked: 0.into(), emission: emission.into(), + tao_emission: tao_emission.into(), drain: 0.into(), is_registered, }) From ef65dc4527d6ff0c2439a5ffb06e922422b3a12d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 12:59:39 -0500 Subject: [PATCH 02/13] add negation for pow-reg-allowed --- pallets/subtensor/src/coinbase/run_coinbase.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d0a668f92..9930451bc 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -87,7 +87,7 @@ impl Pallet { let alpha_out_i = alpha_emission_i; // Only emit TAO if the subnetwork allows registration. if !Self::get_network_registration_allowed(*netuid_i) - && Self::get_network_pow_registration_allowed(*netuid_i) + && !Self::get_network_pow_registration_allowed(*netuid_i) { tao_in_i = asfloat!(0.0); } From a7aac95951cbdb647b119d439776b242a1e3a557 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 16:28:34 -0500 Subject: [PATCH 03/13] only root can set min pow diff --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index e8f37526e..7bcceb8ed 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -158,7 +158,7 @@ pub mod pallet { netuid: u16, min_difficulty: u64, ) -> DispatchResult { - pallet_subtensor::Pallet::::ensure_subnet_owner_or_root(origin, netuid)?; + pallet_subtensor::Pallet::::ensure_root(origin, netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), From 4fd4757783971137bf1f5093012b617a91560f62 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 16:29:28 -0500 Subject: [PATCH 04/13] oops --- pallets/admin-utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 7bcceb8ed..9cb51e1b2 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -158,7 +158,7 @@ pub mod pallet { netuid: u16, min_difficulty: u64, ) -> DispatchResult { - pallet_subtensor::Pallet::::ensure_root(origin, netuid)?; + ensure_root(origin)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), From e538804c28d1471066b5d89ae7db21fd31c258e8 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 16:46:49 -0500 Subject: [PATCH 05/13] dont let ck-in-swap-sched move any stake/register --- pallets/subtensor/src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 58406e516..4399b25a3 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1885,6 +1885,12 @@ where netuid, amount_staked, }) => { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } // Fully validate the user input Self::result_to_validity(Pallet::::validate_add_stake( who, @@ -1902,6 +1908,13 @@ where limit_price, allow_partial, }) => { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } + // Calcaulate the maximum amount that can be executed with price limit let max_amount = Pallet::::get_max_amount_add(*netuid, *limit_price); @@ -1957,6 +1970,13 @@ where destination_netuid, alpha_amount, }) => { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } + // Fully validate the user input Self::result_to_validity(Pallet::::validate_stake_transition( who, @@ -1978,6 +1998,13 @@ where destination_netuid, alpha_amount, }) => { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } + // Fully validate the user input Self::result_to_validity(Pallet::::validate_stake_transition( who, @@ -1998,6 +2025,13 @@ where destination_netuid, alpha_amount, }) => { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } + // Fully validate the user input Self::result_to_validity(Pallet::::validate_stake_transition( who, @@ -2020,6 +2054,13 @@ where limit_price, allow_partial, }) => { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } + // Get the max amount possible to exchange let max_amount = Pallet::::get_max_amount_move( *origin_netuid, @@ -2042,6 +2083,13 @@ where )) } Some(Call::register { netuid, .. } | Call::burned_register { netuid, .. }) => { + if ColdkeySwapScheduled::::contains_key(who) { + return InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into(), + ) + .into(); + } + let registrations_this_interval = Pallet::::get_registrations_this_interval(*netuid); let max_registrations_per_interval = From 8f197a29cf66a72703e6014bdf50ab810e60b4f9 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 17:32:50 -0500 Subject: [PATCH 06/13] add tests for validation filter --- pallets/subtensor/src/tests/swap_coldkey.rs | 327 +++++++++++++++++++- 1 file changed, 326 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index 776100578..d7f712334 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -1726,7 +1726,7 @@ fn test_schedule_swap_coldkey_duplicate() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --test swap_coldkey -- test_schedule_swap_coldkey_execution --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_schedule_swap_coldkey_execution --exact --show-output --nocapture #[test] fn test_schedule_swap_coldkey_execution() { new_test_ext(1).execute_with(|| { @@ -2022,3 +2022,328 @@ fn test_cant_schedule_swap_without_enough_to_burn() { ); }); } + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_in_swap_schedule_prevents_funds_usage --exact --show-output --nocapture +#[test] +fn test_coldkey_in_swap_schedule_prevents_funds_usage() { + // Testing the signed extension validate function + // correctly filters transactions that attempt to use funds + // while a coldkey swap is scheduled. + + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let version_key: u64 = 0; + let coldkey = U256::from(0); + let new_coldkey = U256::from(1); + let hotkey: U256 = U256::from(2); // Add the hotkey field + assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! + let fee = DefaultStakingFee::::get(); + + let who = coldkey; // The coldkey signs this transaction + + // Disallowed transactions are + // - add_stake + // - add_stake_limit + // - swap_stake + // - swap_stake_limit + // - move_stake + // - transfer_stake + // - balances.transfer_all + // - balances.transfer_allow_death + // - balances.transfer_keep_alive + + // Allowed transactions are: + // - remove_stake + // - remove_stake_limit + // others... + + // Create netuid + add_network(netuid, 1, 0); + // Register the hotkey + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + + SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); + + // Set the minimum stake to 0. + SubtensorModule::set_stake_threshold(0); + // Add stake to the hotkey + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey, + netuid, + 100_000_000_000 + )); + + // Schedule the coldkey for a swap + assert_ok!(SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(who), + new_coldkey + )); + + assert!(ColdkeySwapScheduled::::contains_key(who)); + + // Setup the extension + let info: crate::DispatchInfo = + crate::DispatchInfoOf::<::RuntimeCall>::default(); + let extension = crate::SubtensorSignedExtension::::new(); + + // Try each call + + // Add stake + let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake { + hotkey, + netuid, + amount_staked: 100_000_000_000, + }); + let result: Result = + extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Add stake limit + let call = RuntimeCall::SubtensorModule(SubtensorCall::add_stake_limit { + hotkey, + netuid, + amount_staked: 100_000_000_000, + limit_price: 100_000_000_000, + allow_partial: false, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Swap stake + let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_stake { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: 100_000_000_000, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Swap stake limit + let call = RuntimeCall::SubtensorModule(SubtensorCall::swap_stake_limit { + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: 100_000_000_000, + limit_price: 100_000_000_000, + allow_partial: false, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Move stake + let call = RuntimeCall::SubtensorModule(SubtensorCall::move_stake { + origin_hotkey: hotkey, + destination_hotkey: hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: 100_000_000_000, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Transfer stake + let call = RuntimeCall::SubtensorModule(SubtensorCall::transfer_stake { + destination_coldkey: new_coldkey, + hotkey, + origin_netuid: netuid, + destination_netuid: netuid, + alpha_amount: 100_000_000_000, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Transfer all + let call = RuntimeCall::Balances(BalancesCall::transfer_all { + dest: new_coldkey, + keep_alive: false, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Transfer keep alive + let call = RuntimeCall::Balances(BalancesCall::transfer_keep_alive { + dest: new_coldkey, + value: 100_000_000_000, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Transfer allow death + let call = RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: new_coldkey, + value: 100_000_000_000, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Burned register + let call = RuntimeCall::SubtensorModule(SubtensorCall::burned_register { netuid, hotkey }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + + // Remove stake + let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake { + hotkey, + netuid, + amount_unstaked: 1_000_000, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should pass, not in list. + assert_ok!(result); + + // Remove stake limit + let call = RuntimeCall::SubtensorModule(SubtensorCall::remove_stake_limit { + hotkey, + netuid, + amount_unstaked: 1_000_000, + limit_price: 123456789, // should be low enough + allow_partial: true, + }); + let result = extension.validate(&who, &call.clone(), &info, 10); + // Should pass, not in list. + assert_ok!(result); + }); +} + +// SKIP_WASM_BUILD=1 RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::swap_coldkey::test_coldkey_in_swap_schedule_prevents_critical_calls --exact --show-output --nocapture +#[test] +fn test_coldkey_in_swap_schedule_prevents_critical_calls() { + // Testing the signed extension validate function + // correctly filters transactions that are critical + // while a coldkey swap is scheduled. + + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let version_key: u64 = 0; + let coldkey = U256::from(0); + let new_coldkey = U256::from(1); + let hotkey: U256 = U256::from(2); // Add the hotkey field + assert_ne!(hotkey, coldkey); // Ensure hotkey is NOT the same as coldkey !!! + let fee = DefaultStakingFee::::get(); + + let who = coldkey; // The coldkey signs this transaction + + // Disallowed transactions are + // - dissolve_network + + // Create netuid + add_network(netuid, 1, 0); + // Register the hotkey + SubtensorModule::append_neuron(netuid, &hotkey, 0); + crate::Owner::::insert(hotkey, coldkey); + + SubtensorModule::add_balance_to_coldkey_account(&who, u64::MAX); + + // Set the minimum stake to 0. + SubtensorModule::set_stake_threshold(0); + // Add stake to the hotkey + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(who), + hotkey, + netuid, + 100_000_000_000 + )); + + // Schedule the coldkey for a swap + assert_ok!(SubtensorModule::schedule_swap_coldkey( + <::RuntimeOrigin>::signed(who), + new_coldkey + )); + + assert!(ColdkeySwapScheduled::::contains_key(who)); + + // Setup the extension + let info: crate::DispatchInfo = + crate::DispatchInfoOf::<::RuntimeCall>::default(); + let extension = crate::SubtensorSignedExtension::::new(); + + // Try each call + + // Dissolve network + let call = + RuntimeCall::SubtensorModule(SubtensorCall::dissolve_network { netuid, coldkey }); + let result: Result = + extension.validate(&who, &call.clone(), &info, 10); + // Should fail + assert_err!( + // Should get an invalid transaction error + result, + crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom( + CustomTransactionError::ColdkeyInSwapSchedule.into() + )) + ); + }); +} From 5b3d4e66e37831932450debd47fd1a6276c58763 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 17:33:21 -0500 Subject: [PATCH 07/13] add transfer stake to call nontransfer proxy filter --- runtime/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 26bf16206..f3b7535b5 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -726,7 +726,15 @@ impl InstanceFilter for ProxyType { fn filter(&self, c: &RuntimeCall) -> bool { match self { ProxyType::Any => true, - ProxyType::NonTransfer => !matches!(c, RuntimeCall::Balances(..)), + ProxyType::NonTransfer => !matches!( + c, + RuntimeCall::Balances(..) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { .. }) + | RuntimeCall::SubtensorModule( + pallet_subtensor::Call::schedule_swap_coldkey { .. } + ) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_coldkey { .. }) + ), ProxyType::NonFungibile => !matches!( c, RuntimeCall::Balances(..) From 45a67fcd88c56de47a3ce6539a0654a8e013e01c Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 17:40:06 -0500 Subject: [PATCH 08/13] also add proxy filters for new calls --- runtime/src/lib.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f3b7535b5..b8c48d380 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -739,12 +739,25 @@ impl InstanceFilter for ProxyType { c, RuntimeCall::Balances(..) | RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake_limit { .. }) | RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { .. }) + | RuntimeCall::SubtensorModule( + pallet_subtensor::Call::remove_stake_limit { .. } + ) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::unstake_all { .. }) + | RuntimeCall::SubtensorModule( + pallet_subtensor::Call::unstake_all_alpha { .. } + ) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_stake { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_stake_limit { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::move_stake { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { .. }) | RuntimeCall::SubtensorModule(pallet_subtensor::Call::burned_register { .. }) | RuntimeCall::SubtensorModule(pallet_subtensor::Call::root_register { .. }) | RuntimeCall::SubtensorModule( pallet_subtensor::Call::schedule_swap_coldkey { .. } ) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_coldkey { .. }) | RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_hotkey { .. }) ), ProxyType::Transfer => matches!( @@ -752,6 +765,7 @@ impl InstanceFilter for ProxyType { RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { .. }) | RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) | RuntimeCall::Balances(pallet_balances::Call::transfer_all { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { .. }) ), ProxyType::SmallTransfer => match c { RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { @@ -761,6 +775,10 @@ impl InstanceFilter for ProxyType { value, .. }) => *value < SMALL_TRANSFER_LIMIT, + RuntimeCall::SubtensorModule(pallet_subtensor::Call::transfer_stake { + alpha_amount, + .. + }) => *alpha_amount < SMALL_TRANSFER_LIMIT, _ => false, }, ProxyType::Owner => matches!(c, RuntimeCall::AdminUtils(..)), @@ -788,6 +806,17 @@ impl InstanceFilter for ProxyType { c, RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake { .. }) | RuntimeCall::SubtensorModule(pallet_subtensor::Call::remove_stake { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::unstake_all { .. }) + | RuntimeCall::SubtensorModule( + pallet_subtensor::Call::unstake_all_alpha { .. } + ) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_stake { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::swap_stake_limit { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::move_stake { .. }) + | RuntimeCall::SubtensorModule(pallet_subtensor::Call::add_stake_limit { .. }) + | RuntimeCall::SubtensorModule( + pallet_subtensor::Call::remove_stake_limit { .. } + ) ), ProxyType::Registration => matches!( c, From bf1933c503fec69bd382e8cef9f2e2996b37cd1f Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:40:18 -0800 Subject: [PATCH 09/13] update staking priority --- pallets/subtensor/src/lib.rs | 217 ++++++++++++------- pallets/subtensor/src/staking/stake_utils.rs | 10 + 2 files changed, 145 insertions(+), 82 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 58406e516..b7243ae5e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1550,6 +1550,18 @@ pub mod pallet { pub type RevealPeriodEpochs = StorageMap<_, Twox64Concat, u16, u64, ValueQuery, DefaultRevealPeriodEpochs>; + #[pallet::storage] + /// --- Map (coldkey, hotkey) --> u64 the last block at which stake was added/removed. + pub type LastColdkeyHotkeyStakeBlock = StorageDoubleMap< + _, + Twox64Concat, + T::AccountId, + Twox64Concat, + T::AccountId, + u64, + OptionQuery, + >; + /// ================== /// ==== Genesis ===== /// ================== @@ -1588,6 +1600,19 @@ pub mod pallet { 0 } + /// Returns the transaction priority for stake operations. + pub fn get_priority_staking(coldkey: &T::AccountId, hotkey: &T::AccountId) -> u64 { + match LastColdkeyHotkeyStakeBlock::::get(coldkey, hotkey) { + Some(last_stake_block) => { + let current_block_number = Self::get_current_block_as_u64(); + let default_priority = current_block_number.saturating_sub(last_stake_block); + + default_priority.saturating_add(u32::MAX as u64) + } + None => 0, + } + } + /// Is the caller allowed to set weights pub fn check_weights_min_stake(hotkey: &T::AccountId, netuid: u16) -> bool { // Blacklist weights transactions for low stake peers. @@ -1705,11 +1730,15 @@ where Pallet::::get_priority_set_weights(who, netuid) } + pub fn get_priority_staking(coldkey: &T::AccountId, hotkey: &T::AccountId) -> u64 { + Pallet::::get_priority_staking(coldkey, hotkey) + } + pub fn check_weights_min_stake(who: &T::AccountId, netuid: u16) -> bool { Pallet::::check_weights_min_stake(who, netuid) } - pub fn result_to_validity(result: Result<(), Error>) -> TransactionValidity { + pub fn result_to_validity(result: Result<(), Error>, priority: u64) -> TransactionValidity { if let Err(err) = result { match err { Error::::AmountTooLow => Err(InvalidTransaction::Custom( @@ -1750,7 +1779,7 @@ where } } else { Ok(ValidTransaction { - priority: Self::get_priority_vanilla(), + priority: priority, ..Default::default() }) } @@ -1886,14 +1915,17 @@ where amount_staked, }) => { // Fully validate the user input - Self::result_to_validity(Pallet::::validate_add_stake( - who, - hotkey, - *netuid, - *amount_staked, - *amount_staked, - false, - )) + Self::result_to_validity( + Pallet::::validate_add_stake( + who, + hotkey, + *netuid, + *amount_staked, + *amount_staked, + false, + ), + Self::get_priority_staking(who, hotkey), + ) } Some(Call::add_stake_limit { hotkey, @@ -1906,14 +1938,17 @@ where let max_amount = Pallet::::get_max_amount_add(*netuid, *limit_price); // Fully validate the user input - Self::result_to_validity(Pallet::::validate_add_stake( - who, - hotkey, - *netuid, - *amount_staked, - max_amount, - *allow_partial, - )) + Self::result_to_validity( + Pallet::::validate_add_stake( + who, + hotkey, + *netuid, + *amount_staked, + max_amount, + *allow_partial, + ), + Self::get_priority_vanilla(), + ) } Some(Call::remove_stake { hotkey, @@ -1921,14 +1956,17 @@ where amount_unstaked, }) => { // Fully validate the user input - Self::result_to_validity(Pallet::::validate_remove_stake( - who, - hotkey, - *netuid, - *amount_unstaked, - *amount_unstaked, - false, - )) + Self::result_to_validity( + Pallet::::validate_remove_stake( + who, + hotkey, + *netuid, + *amount_unstaked, + *amount_unstaked, + false, + ), + Self::get_priority_staking(who, hotkey), + ) } Some(Call::remove_stake_limit { hotkey, @@ -1941,14 +1979,17 @@ where let max_amount = Pallet::::get_max_amount_remove(*netuid, *limit_price); // Fully validate the user input - Self::result_to_validity(Pallet::::validate_remove_stake( - who, - hotkey, - *netuid, - *amount_unstaked, - max_amount, - *allow_partial, - )) + Self::result_to_validity( + Pallet::::validate_remove_stake( + who, + hotkey, + *netuid, + *amount_unstaked, + max_amount, + *allow_partial, + ), + Self::get_priority_vanilla(), + ) } Some(Call::move_stake { origin_hotkey, @@ -1958,18 +1999,21 @@ where alpha_amount, }) => { // Fully validate the user input - Self::result_to_validity(Pallet::::validate_stake_transition( - who, - who, - origin_hotkey, - destination_hotkey, - *origin_netuid, - *destination_netuid, - *alpha_amount, - *alpha_amount, - None, - false, - )) + Self::result_to_validity( + Pallet::::validate_stake_transition( + who, + who, + origin_hotkey, + destination_hotkey, + *origin_netuid, + *destination_netuid, + *alpha_amount, + *alpha_amount, + None, + false, + ), + Self::get_priority_vanilla(), + ) } Some(Call::transfer_stake { destination_coldkey, @@ -1979,18 +2023,21 @@ where alpha_amount, }) => { // Fully validate the user input - Self::result_to_validity(Pallet::::validate_stake_transition( - who, - destination_coldkey, - hotkey, - hotkey, - *origin_netuid, - *destination_netuid, - *alpha_amount, - *alpha_amount, - None, - true, - )) + Self::result_to_validity( + Pallet::::validate_stake_transition( + who, + destination_coldkey, + hotkey, + hotkey, + *origin_netuid, + *destination_netuid, + *alpha_amount, + *alpha_amount, + None, + true, + ), + Self::get_priority_vanilla(), + ) } Some(Call::swap_stake { hotkey, @@ -1999,18 +2046,21 @@ where alpha_amount, }) => { // Fully validate the user input - Self::result_to_validity(Pallet::::validate_stake_transition( - who, - who, - hotkey, - hotkey, - *origin_netuid, - *destination_netuid, - *alpha_amount, - *alpha_amount, - None, - false, - )) + Self::result_to_validity( + Pallet::::validate_stake_transition( + who, + who, + hotkey, + hotkey, + *origin_netuid, + *destination_netuid, + *alpha_amount, + *alpha_amount, + None, + false, + ), + Self::get_priority_vanilla(), + ) } Some(Call::swap_stake_limit { hotkey, @@ -2028,18 +2078,21 @@ where ); // Fully validate the user input - Self::result_to_validity(Pallet::::validate_stake_transition( - who, - who, - hotkey, - hotkey, - *origin_netuid, - *destination_netuid, - *alpha_amount, - max_amount, - Some(*allow_partial), - false, - )) + Self::result_to_validity( + Pallet::::validate_stake_transition( + who, + who, + hotkey, + hotkey, + *origin_netuid, + *destination_netuid, + *alpha_amount, + max_amount, + Some(*allow_partial), + false, + ), + Self::get_priority_vanilla(), + ) } Some(Call::register { netuid, .. } | Call::burned_register { netuid, .. }) => { let registrations_this_interval = diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 1be858529..cf55f9f5e 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -748,6 +748,11 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(actual_fee); }); + LastColdkeyHotkeyStakeBlock::::insert( + &coldkey, + &hotkey, + Self::get_current_block_as_u64(), + ); // Step 5. Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( @@ -807,6 +812,11 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(actual_fee); }); + LastColdkeyHotkeyStakeBlock::::insert( + &coldkey, + &hotkey, + Self::get_current_block_as_u64(), + ); // Step 6. Deposit and log the staking event. Self::deposit_event(Event::StakeAdded( From 9dce4e73c26a8bbed86838a44088d105f0d36340 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 12 Feb 2025 17:43:09 -0500 Subject: [PATCH 10/13] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 26bf16206..815beaeb4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -229,7 +229,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: 233, + spec_version: 234, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From b05e21304d8915d16e5f8e41e0a8ed4846c5f3a6 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:58:29 -0800 Subject: [PATCH 11/13] use get_priority_staking for all stake operations --- pallets/subtensor/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b7243ae5e..7aadfd39c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1947,7 +1947,7 @@ where max_amount, *allow_partial, ), - Self::get_priority_vanilla(), + Self::get_priority_staking(who, hotkey), ) } Some(Call::remove_stake { @@ -1988,7 +1988,7 @@ where max_amount, *allow_partial, ), - Self::get_priority_vanilla(), + Self::get_priority_staking(who, hotkey), ) } Some(Call::move_stake { @@ -2012,7 +2012,7 @@ where None, false, ), - Self::get_priority_vanilla(), + Self::get_priority_staking(who, origin_hotkey), ) } Some(Call::transfer_stake { @@ -2036,7 +2036,7 @@ where None, true, ), - Self::get_priority_vanilla(), + Self::get_priority_staking(who, hotkey), ) } Some(Call::swap_stake { @@ -2059,7 +2059,7 @@ where None, false, ), - Self::get_priority_vanilla(), + Self::get_priority_staking(who, hotkey), ) } Some(Call::swap_stake_limit { @@ -2091,7 +2091,7 @@ where Some(*allow_partial), false, ), - Self::get_priority_vanilla(), + Self::get_priority_staking(who, hotkey), ) } Some(Call::register { netuid, .. } | Call::burned_register { netuid, .. }) => { From f5d4708ee217242c3efb11868fe6de09ba9971f2 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:59:23 -0800 Subject: [PATCH 12/13] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 26bf16206..815beaeb4 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -229,7 +229,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: 233, + spec_version: 234, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From 1916e1cb089ea5f2ae18d2e8e7b44f820269ba11 Mon Sep 17 00:00:00 2001 From: John Reed <87283488+JohnReedV@users.noreply.github.com> Date: Wed, 12 Feb 2025 15:02:13 -0800 Subject: [PATCH 13/13] clippy --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/staking/stake_utils.rs | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7aadfd39c..b0a927c91 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1779,7 +1779,7 @@ where } } else { Ok(ValidTransaction { - priority: priority, + priority, ..Default::default() }) } diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index cf55f9f5e..3543c1a23 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -748,11 +748,7 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(actual_fee); }); - LastColdkeyHotkeyStakeBlock::::insert( - &coldkey, - &hotkey, - Self::get_current_block_as_u64(), - ); + LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); // Step 5. Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( @@ -812,11 +808,7 @@ impl Pallet { TotalStake::::mutate(|total| { *total = total.saturating_add(actual_fee); }); - LastColdkeyHotkeyStakeBlock::::insert( - &coldkey, - &hotkey, - Self::get_current_block_as_u64(), - ); + LastColdkeyHotkeyStakeBlock::::insert(coldkey, hotkey, Self::get_current_block_as_u64()); // Step 6. Deposit and log the staking event. Self::deposit_event(Event::StakeAdded(