From cfbee665bc1e61f9bce9e564460545cbbf0fad44 Mon Sep 17 00:00:00 2001 From: Unconst <32490803+unconst@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:04:06 -0500 Subject: [PATCH 01/24] Update pallets/subtensor/src/staking.rs Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index af062a202b..04be7c52c2 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -86,7 +86,7 @@ impl Pallet { Ok(()) } - // ---- The implementation for the extrinsic become_delegate: signals that this hotkey allows delegated stake. + // ---- The implementation for the extrinsic decrease_take // // # Args: // * 'origin': (RuntimeOrigin): From 28583483b29f472c4876d7e427f67d689092abdb Mon Sep 17 00:00:00 2001 From: Unconst <32490803+unconst@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:04:12 -0500 Subject: [PATCH 02/24] Update pallets/subtensor/src/lib.rs Co-authored-by: cuteolaf --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 518d2348ce..287940d6b3 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1284,7 +1284,7 @@ pub mod pallet { // --- Allows delegates to decrease its take value. // // # Args: - // * 'origin': (Origin): + // * 'origin': (::Origin): // - The signature of the caller's coldkey. // // * 'hotkey' (T::AccountId): From bfd5d8b84e960d0b7ee8e84aa27341bb6d0a6e79 Mon Sep 17 00:00:00 2001 From: Unconst <32490803+unconst@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:04:17 -0500 Subject: [PATCH 03/24] Update pallets/subtensor/src/staking.rs Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 04be7c52c2..ba90740895 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -89,7 +89,7 @@ impl Pallet { // ---- The implementation for the extrinsic decrease_take // // # Args: - // * 'origin': (RuntimeOrigin): + // * 'origin': (::RuntimeOrigin): // - The signature of the caller's coldkey. // // * 'hotkey' (T::AccountId): From 56492c1dfff5f4321d2033d196778a7dd1867c02 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:21:53 -0400 Subject: [PATCH 04/24] comment nit Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index ba90740895..20caa6c55f 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -126,7 +126,7 @@ impl Pallet { take ); - // --- 2. Ensure we are delegating an known key. + // --- 2. Ensure we are delegating a known key. ensure!( Self::hotkey_account_exists(&hotkey), Error::::NotRegistered From 60ba03172e760f74e92983bf3c5afa2e9b54ed4d Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:22:50 -0400 Subject: [PATCH 05/24] fix comment Co-authored-by: cuteolaf --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 287940d6b3..264160a421 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1290,7 +1290,7 @@ pub mod pallet { // * 'hotkey' (T::AccountId): // - The hotkey we are delegating (must be owned by the coldkey.) // - // * 'take' (u64): + // * 'take' (u16): // - The new stake proportion that this hotkey takes from delegations. // // # Event: From 3b2fb136df7351677c98f532144996e318b0e3c9 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:23:09 -0400 Subject: [PATCH 06/24] fix comment Co-authored-by: cuteolaf --- pallets/subtensor/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 264160a421..d6bf65348b 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1294,8 +1294,8 @@ pub mod pallet { // - The new stake proportion that this hotkey takes from delegations. // // # Event: - // * DelegateAdded; - // - On successfully setting a hotkey as a delegate. + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. // // # Raises: // * 'NotRegistered': From c1743742a82ce6643639f01527e886912789338e Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:23:24 -0400 Subject: [PATCH 07/24] fix comment Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 20caa6c55f..d8aa9573b5 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -99,8 +99,8 @@ impl Pallet { // - The stake proportion that this hotkey takes from delegations. // // # Event: - // * DelegateAdded; - // - On successfully setting a hotkey as a delegate. + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. // // # Raises: // * 'NotRegistered': From 44351fc8ff59050f8ae87a15b29f4a15fca146d7 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 10 Apr 2024 18:21:04 -0400 Subject: [PATCH 08/24] Allow to also increase take, add tests --- pallets/admin-utils/src/lib.rs | 11 +- pallets/admin-utils/tests/mock.rs | 6 + pallets/admin-utils/tests/tests.rs | 44 ++- pallets/subtensor/src/lib.rs | 56 +++- pallets/subtensor/src/migration.rs | 4 +- pallets/subtensor/src/registration.rs | 1 - pallets/subtensor/src/root.rs | 2 +- pallets/subtensor/src/staking.rs | 119 ++++++-- pallets/subtensor/src/utils.rs | 35 +++ pallets/subtensor/tests/mock.rs | 6 +- pallets/subtensor/tests/staking.rs | 375 ++++++++++++++++++++++++++ runtime/src/lib.rs | 8 +- 12 files changed, 630 insertions(+), 37 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 3a84e7fd03..3876384967 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -53,7 +53,6 @@ pub mod pallet { } #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event {} // Errors inform users that something went wrong. @@ -103,6 +102,15 @@ pub mod pallet { Ok(()) } + #[pallet::call_index(43)] + #[pallet::weight((0, DispatchClass::Operational, Pays::No))] + pub fn sudo_set_tx_rate_limit_delegate_take(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { + ensure_root(origin)?; + T::Subtensor::set_tx_rate_limit_delegate_take(tx_rate_limit); + log::info!("TxRateLimitDelegateTakeSet( tx_rate_limit_delegate_take: {:?} ) ", tx_rate_limit); + Ok(()) + } + #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::sudo_set_serving_rate_limit())] pub fn sudo_set_serving_rate_limit( @@ -788,6 +796,7 @@ impl AuraInterface for () { pub trait SubtensorInterface { fn set_default_take(default_take: u16); fn set_tx_rate_limit(rate_limit: u64); + fn set_tx_rate_limit_delegate_take(rate_limit: u64); fn set_serving_rate_limit(netuid: u16, rate_limit: u64); diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index f0e613fe89..a81cd9efbb 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -84,6 +84,7 @@ parameter_types! { pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing + pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -146,6 +147,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; + type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; @@ -215,6 +217,10 @@ impl pallet_admin_utils::SubtensorInterface f SubtensorModule::set_tx_rate_limit(rate_limit); } + fn set_tx_rate_limit_delegate_take(rate_limit: u64) { + SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + } + fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { SubtensorModule::set_serving_rate_limit(netuid, rate_limit); } diff --git a/pallets/admin-utils/tests/tests.rs b/pallets/admin-utils/tests/tests.rs index 86d62b1482..d7ae01aed1 100644 --- a/pallets/admin-utils/tests/tests.rs +++ b/pallets/admin-utils/tests/tests.rs @@ -9,7 +9,7 @@ mod mock; use mock::*; #[allow(dead_code)] -pub fn add_network(netuid: u16, tempo: u16, modality: u16) { +pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); SubtensorModule::set_network_pow_registration_allowed(netuid, true); @@ -884,3 +884,45 @@ fn test_sudo_set_network_pow_registration_allowed() { ); }); } + +#[test] +fn test_sudo_set_tx_rate_limit() { + new_test_ext().execute_with(|| { + let to_be_set: u64 = 10; + let init_value: u64 = SubtensorModule::get_tx_rate_limit(); + assert_eq!( + AdminUtils::sudo_set_tx_rate_limit( + <::RuntimeOrigin>::signed(U256::from(1)), + to_be_set + ), + Err(DispatchError::BadOrigin.into()) + ); + assert_eq!(SubtensorModule::get_tx_rate_limit(), init_value); + assert_ok!(AdminUtils::sudo_set_tx_rate_limit( + <::RuntimeOrigin>::root(), + to_be_set + )); + assert_eq!(SubtensorModule::get_tx_rate_limit(), to_be_set); + }); +} + +#[test] +fn test_sudo_set_tx_rate_limit_delegate_take() { + new_test_ext().execute_with(|| { + let to_be_set: u64 = 10; + let init_value: u64 = SubtensorModule::get_tx_rate_limit_delegate_take(); + assert_eq!( + AdminUtils::sudo_set_tx_rate_limit_delegate_take( + <::RuntimeOrigin>::signed(U256::from(1)), + to_be_set + ), + Err(DispatchError::BadOrigin.into()) + ); + assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), init_value); + assert_ok!(AdminUtils::sudo_set_tx_rate_limit_delegate_take( + <::RuntimeOrigin>::root(), + to_be_set + )); + assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), to_be_set); + }); +} diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d6bf65348b..21fbf12da9 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -164,6 +164,8 @@ pub mod pallet { type InitialServingRateLimit: Get; #[pallet::constant] // Initial transaction rate limit. type InitialTxRateLimit: Get; + #[pallet::constant] // Initial delegate take transaction rate limit. + type InitialTxRateLimitDelegateTake: Get; #[pallet::constant] // Initial percentage of total stake required to join senate. type InitialSenateRequiredStakePercentage: Get; #[pallet::constant] // Initial adjustment alpha on burn and pow. @@ -539,15 +541,24 @@ pub mod pallet { T::InitialTxRateLimit::get() } #[pallet::type_value] + pub fn DefaultTxRateLimitDelegateTake() -> u64 { + T::InitialTxRateLimitDelegateTake::get() + } + #[pallet::type_value] pub fn DefaultLastTxBlock() -> u64 { 0 } #[pallet::storage] // --- ITEM ( tx_rate_limit ) pub(super) type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; + #[pallet::storage] // --- ITEM ( tx_rate_limit ) + pub(super) type TxRateLimitDelegateTake = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] // --- MAP ( key ) --> last_block pub(super) type LastTxBlock = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; + #[pallet::storage] // --- MAP ( key ) --> last_block + pub(super) type LastTxBlockDelegateTake = + StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; #[pallet::type_value] pub fn DefaultServingRateLimit() -> u64 { @@ -856,6 +867,7 @@ pub mod pallet { MaxBurnSet(u16, u64), // --- Event created when setting max burn on a network. MinBurnSet(u16, u64), // --- Event created when setting min burn on a network. TxRateLimitSet(u64), // --- Event created when setting the transaction rate limit. + TxRateLimitDelegateTakeSet(u64), // --- Event created when setting the transaction rate limit. Sudid(DispatchResult), // --- Event created when a sudo call is done. RegistrationAllowed(u16, bool), // --- Event created when registration is allowed/disallowed for a subnet. PowRegistrationAllowed(u16, bool), // --- Event created when POW registration is allowed/disallowed for a subnet. @@ -872,6 +884,7 @@ pub mod pallet { SubnetLimitSet(u16), // Event created when the maximum number of subnets is set NetworkLockCostReductionIntervalSet(u64), // Event created when the lock cost reduction is set TakeDecreased( T::AccountId, T::AccountId, u16 ), // Event created when the take for a delegate is decreased. + TakeIncreased( T::AccountId, T::AccountId, u16 ), // Event created when the take for a delegate is increased. HotkeySwapped { coldkey: T::AccountId, old_hotkey: T::AccountId, @@ -965,8 +978,6 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - use crate::MemberManagement; - // Set initial total issuance from balances TotalIssuance::::put(self.balances_issuance); @@ -1292,6 +1303,10 @@ pub mod pallet { // // * 'take' (u16): // - The new stake proportion that this hotkey takes from delegations. + // The new value can be between 0 and 11_796 and should be strictly + // lower than the previous value. It T is the new value (rational number), + // the the parameter is calculated as [65535 * T]. For example, 1% would be + // [0.01 * 65535] = [655.35] = 655 // // # Event: // * TakeDecreased; @@ -1313,6 +1328,42 @@ pub mod pallet { Self::do_decrease_take(origin, hotkey, take) } + // --- Allows delegates to increase its take value. This call is rate-limited. + // + // # Args: + // * 'origin': (::Origin): + // - The signature of the caller's coldkey. + // + // * 'hotkey' (T::AccountId): + // - The hotkey we are delegating (must be owned by the coldkey.) + // + // * 'take' (u16): + // - The new stake proportion that this hotkey takes from delegations. + // The new value can be between 0 and 11_796 and should be strictly + // greater than the previous value. It T is the new value (rational number), + // the the parameter is calculated as [65535 * T]. For example, 1% would be + // [0.01 * 65535] = [655.35] = 655 + // + // # Event: + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. + // + // # Raises: + // * 'NotRegistered': + // - The hotkey we are delegating is not registered on the network. + // + // * 'NonAssociatedColdKey': + // - The hotkey we are delegating is not owned by the calling coldkey. + // + // * 'InvalidTransaction': + // - The delegate is setting a take which is not lower than the previous. + // + #[pallet::call_index(64)] + #[pallet::weight((0, DispatchClass::Normal, Pays::No))] + pub fn increase_take(origin: OriginFor, hotkey: T::AccountId, take: u16) -> DispatchResult { + Self::do_decrease_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 @@ -1711,7 +1762,6 @@ pub mod pallet { pub fn get_priority_set_weights(hotkey: &T::AccountId, netuid: u16) -> u64 { if Uids::::contains_key(netuid, &hotkey) { let uid = Self::get_uid_for_net_and_hotkey(netuid, &hotkey.clone()).unwrap(); - let stake = Self::get_total_stake_for_hotkey(&hotkey); let current_block_number: u64 = Self::get_current_block_as_u64(); let default_priority: u64 = current_block_number - Self::get_last_update_for_uid(netuid, uid as u16); diff --git a/pallets/subtensor/src/migration.rs b/pallets/subtensor/src/migration.rs index 452c46ec2b..e013b7cb02 100644 --- a/pallets/subtensor/src/migration.rs +++ b/pallets/subtensor/src/migration.rs @@ -113,8 +113,8 @@ pub fn migrate_create_root_network() -> Weight { // Empty senate members entirely, they will be filled by by registrations // on the subnet. for hotkey_i in T::SenateMembers::members().iter() { - T::TriumvirateInterface::remove_votes(&hotkey_i); - T::SenateMembers::remove_member(&hotkey_i); + let _ = T::TriumvirateInterface::remove_votes(&hotkey_i); + let _ = T::SenateMembers::remove_member(&hotkey_i); weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } diff --git a/pallets/subtensor/src/registration.rs b/pallets/subtensor/src/registration.rs index 3b85292ecd..6dcd91bcff 100644 --- a/pallets/subtensor/src/registration.rs +++ b/pallets/subtensor/src/registration.rs @@ -1,5 +1,4 @@ use super::*; -use crate::system::ensure_root; use frame_support::pallet_prelude::{DispatchResult, DispatchResultWithPostInfo}; use frame_support::storage::IterableStorageDoubleMap; use frame_system::ensure_signed; diff --git a/pallets/subtensor/src/root.rs b/pallets/subtensor/src/root.rs index dd5d7f7841..86111bd03a 100644 --- a/pallets/subtensor/src/root.rs +++ b/pallets/subtensor/src/root.rs @@ -23,7 +23,7 @@ use frame_support::sp_std::vec; use frame_support::storage::{IterableStorageDoubleMap, IterableStorageMap}; use frame_support::traits::Get; use frame_support::weights::Weight; -use substrate_fixed::types::{I32F32, I64F64}; +use substrate_fixed::types::I64F64; impl Pallet { // Retrieves the unique identifier (UID) for the root network. diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index d8aa9573b5..e40d7210c3 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -1,5 +1,6 @@ use super::*; use frame_support::storage::IterableStorageDoubleMap; +use sp_core::Get; impl Pallet { // ---- The implementation for the extrinsic become_delegate: signals that this hotkey allows delegated stake. @@ -42,19 +43,11 @@ impl Pallet { take ); - // --- 2. Ensure we are delegating an known key. - ensure!( - Self::hotkey_account_exists(&hotkey), - Error::::NotRegistered - ); - + // --- 2. Ensure we are delegating a known key. // --- 3. Ensure that the coldkey is the owner. - ensure!( - Self::coldkey_owns_hotkey(&coldkey, &hotkey), - Error::::NonAssociatedColdKey - ); + Self::do_take_checks(&coldkey, &hotkey)?; - // --- 4. Ensure we are not already a delegate (dont allow changing delegate take.) + // --- 4. Ensure we are not already a delegate (dont allow changing delegate take here.) ensure!( !Self::hotkey_is_delegate(&hotkey), Error::::AlreadyDelegate @@ -127,41 +120,111 @@ impl Pallet { ); // --- 2. Ensure we are delegating a known key. + // Ensure that the coldkey is the owner. + Self::do_take_checks(&coldkey, &hotkey)?; + + // --- 3. Ensure we are always strictly decreasing, never increasing take + let current_take: u16 = Delegates::::get(&hotkey); ensure!( - Self::hotkey_account_exists(&hotkey), - Error::::NotRegistered + take < current_take, + Error::::InvalidTake ); - // --- 3. Ensure that the coldkey is the owner. - ensure!( - Self::coldkey_owns_hotkey(&coldkey, &hotkey), - Error::::NonAssociatedColdKey + // --- 4. Set the new take value. + Delegates::::insert(hotkey.clone(), take); + + // --- 5. Emit the take value. + log::info!( + "TakeDecreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", + coldkey, + hotkey, + take ); + Self::deposit_event(Event::TakeDecreased(coldkey, hotkey, take)); - // --- 4. Ensure we are not already a delegate (dont allow changing delegate take.) + // --- 6. Ok and return. + Ok(()) + } + + // ---- The implementation for the extrinsic increase_take + // + // # Args: + // * 'origin': (::RuntimeOrigin): + // - The signature of the caller's coldkey. + // + // * 'hotkey' (T::AccountId): + // - The hotkey we are delegating (must be owned by the coldkey.) + // + // * 'take' (u16): + // - The stake proportion that this hotkey takes from delegations. + // + // # Event: + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. + // + // # Raises: + // * 'NotRegistered': + // - The hotkey we are delegating is not registered on the network. + // + // * 'NonAssociatedColdKey': + // - The hotkey we are delegating is not owned by the calling coldket. + // + // * 'TxRateLimitExceeded': + // - Thrown if key has hit transaction rate limit + // + pub fn do_increase_take( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + take: u16, + ) -> dispatch::DispatchResult { + // --- 1. We check the coldkey signature. + let coldkey = ensure_signed(origin)?; + log::info!( + "do_increase_take( origin:{:?} hotkey:{:?}, take:{:?} )", + coldkey, + hotkey, + take + ); + + // --- 2. Ensure we are delegating a known key. + // Ensure that the coldkey is the owner. + Self::do_take_checks(&coldkey, &hotkey)?; + + // --- 3. Ensure we are strinctly increasing take + let current_take: u16 = Delegates::::get(&hotkey); ensure!( - !Self::hotkey_is_delegate(&hotkey), - Error::::AlreadyDelegate + take > current_take, + Error::::InvalidTake ); - // --- 5. Ensure we are always decreasing take never increasing. - let current_take: u16 = Delegates::::get(hotkey.clone()); + // --- 4. Ensure take is within the 0 ..= InitialDefaultTake (18%) range + let max_take = T::InitialDefaultTake::get(); ensure!( - take < current_take, + take <= max_take, Error::::InvalidTake ); + // --- 5. Enforce the rate limit (independently on do_add_stake rate limits) + let block: u64 = Self::get_current_block_as_u64(); + ensure!( + !Self::exceeds_tx_rate_limit(Self::get_last_tx_block_delegate_take(&coldkey), block), + Error::::TxRateLimitExceeded + ); + + // Set last block for rate limiting + Self::set_last_tx_block_delegate_take(&coldkey, block); + // --- 6. Set the new take value. Delegates::::insert(hotkey.clone(), take); // --- 7. Emit the take value. log::info!( - "TakeDecreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", + "TakeIncreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", coldkey, hotkey, take ); - Self::deposit_event(Event::TakeDecreased(coldkey, hotkey, take)); + Self::deposit_event(Event::TakeIncreased(coldkey, hotkey, take)); // --- 8. Ok and return. Ok(()) @@ -438,6 +501,12 @@ impl Pallet { return Owner::::get(hotkey); } + // Returns the hotkey take + // + pub fn get_hotkey_take(hotkey: &T::AccountId) -> u16 { + Delegates::::get(hotkey) + } + // Returns true if the hotkey account has been created. // pub fn hotkey_account_exists(hotkey: &T::AccountId) -> bool { diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index 66744ba3ad..b512c36e75 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -273,6 +273,28 @@ impl Pallet { BlockAtRegistration::::get(netuid, neuron_uid) } + // ======================== + // ===== Take checks ====== + // ======================== + pub fn do_take_checks( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + ) -> Result<(), Error> { + // Ensure we are delegating a known key. + ensure!( + Self::hotkey_account_exists(hotkey), + Error::::NotRegistered + ); + + // Ensure that the coldkey is the owner. + ensure!( + Self::coldkey_owns_hotkey(coldkey, hotkey), + Error::::NonAssociatedColdKey + ); + + Ok(()) + } + // ======================== // ==== Rate Limiting ===== // ======================== @@ -282,6 +304,12 @@ impl Pallet { pub fn get_last_tx_block(key: &T::AccountId) -> u64 { LastTxBlock::::get(key) } + pub fn set_last_tx_block_delegate_take(key: &T::AccountId, block: u64) { + LastTxBlockDelegateTake::::insert(key, block) + } + pub fn get_last_tx_block_delegate_take(key: &T::AccountId) -> u64 { + LastTxBlockDelegateTake::::get(key) + } pub fn exceeds_tx_rate_limit(prev_tx_block: u64, current_block: u64) -> bool { let rate_limit: u64 = Self::get_tx_rate_limit(); if rate_limit == 0 || prev_tx_block == 0 { @@ -325,6 +353,13 @@ impl Pallet { TxRateLimit::::put(tx_rate_limit); Self::deposit_event(Event::TxRateLimitSet(tx_rate_limit)); } + pub fn get_tx_rate_limit_delegate_take() -> u64 { + TxRateLimitDelegateTake::::get() + } + pub fn set_tx_rate_limit_delegate_take(tx_rate_limit: u64) { + TxRateLimitDelegateTake::::put(tx_rate_limit); + Self::deposit_event(Event::TxRateLimitDelegateTakeSet(tx_rate_limit)); + } pub fn get_serving_rate_limit(netuid: u16) -> u64 { ServingRateLimit::::get(netuid) diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index db50f03d09..d946791566 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -1,3 +1,4 @@ +#![allow(non_snake_case, non_camel_case_types)] use frame_support::traits::{Hash, StorageMapShim}; use frame_support::{ assert_ok, parameter_types, @@ -5,7 +6,6 @@ use frame_support::{ weights, }; use frame_system as system; -use frame_system::Config; use frame_system::{limits, EnsureNever, EnsureRoot, RawOrigin}; use sp_core::{Get, H256, U256}; use sp_runtime::{ @@ -134,6 +134,7 @@ parameter_types! { pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing + pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable delegate take rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -345,6 +346,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; + type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; @@ -455,7 +457,7 @@ pub fn register_ok_neuron( } #[allow(dead_code)] -pub fn add_network(netuid: u16, tempo: u16, modality: u16) { +pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); SubtensorModule::set_network_pow_registration_allowed(netuid, true); diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 5d907c20bf..e4157f321b 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2245,3 +2245,378 @@ fn test_faucet_ok() { )); }); } + +// Verify delegate take can be decreased +#[test] +fn test_delegate_take_can_be_decreased() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 2 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 2); + + // Coldkey / hotkey 0 decreases take to 10% + assert_ok!(SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take can not be increased with do_decrease_take +#[test] +fn test_delegate_take_can_not_be_increased_with_decrease_take() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 5% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + + // Coldkey / hotkey 0 tries to increase take to 10% + assert_eq!( + SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + ), + Err(Error::::InvalidTake.into()) + ); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + }); +} + +// Verify delegate take can be increased +#[test] +fn test_delegate_take_can_be_increased() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + + // Coldkey / hotkey 0 decreases take to 10% + assert_ok!(SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take can not be decreased with increase_take +#[test] +fn test_delegate_take_can_not_be_decreased_with_increase_take() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 10% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + + // Coldkey / hotkey 0 tries to decrease take to 5% + assert_eq!( + SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + ), + Err(Error::::InvalidTake.into()) + ); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take can be increased up to InitialDefaultTake (18%) +#[test] +fn test_delegate_take_can_be_increased_to_limit() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 10% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + + // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 + assert_ok!(SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + InitialDefaultTake::get() + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), InitialDefaultTake::get()); + }); +} + +// Verify delegate take can not be increased above InitialDefaultTake (18%) +#[test] +fn test_delegate_take_can_not_be_increased_beyond_limit() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 10% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + + // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 + // (Disable this check if InitialDefaultTake is u16::MAX) + if InitialDefaultTake::get() != u16::MAX { + assert_eq!( + SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + InitialDefaultTake::get()+1 + ), + Err(Error::::InvalidTake.into()) + ); + } + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take affects emission distribution +#[test] +fn test_delegate_take_affects_distribution() { + new_test_ext().execute_with(|| { + let netuid = 1; + // Make two accounts. + let hotkey0 = U256::from(1); + let hotkey1 = U256::from(2); + + let coldkey0 = U256::from(3); + let coldkey1 = U256::from(4); + SubtensorModule::set_max_registrations_per_block(netuid, 4); + SubtensorModule::set_max_allowed_uids(netuid, 10); // Allow at least 10 to be registered at once, so no unstaking occurs + + // Add balances. + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey1, 100000); + + // Register the 2 neurons to a new network. + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + register_ok_neuron(netuid, hotkey1, coldkey1, 987907); + + // Stake 100 from coldkey/hotkey 0 + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 100 + ); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 2 + )); + + // Hotkey 1 adds 100 delegated stake to coldkey/hotkey 0 + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 0 + ); + assert_eq!(SubtensorModule::get_total_stake(), 100); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey1), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 100 + ); + assert_eq!(SubtensorModule::get_total_stake(), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 0); + + // Lets emit inflation through this new key with distributed ownership. + // We will emit 0 server emission (which should go in-full to the owner of the hotkey). + // We will emit 400 validator emission, which should be distributed in-part to the nominators. + // + // Total initial stake is 200 + // Delegate's initial stake is 100, which is 50% of total stake + // => Delegate will receive 50% of emission (200) + 50% take (100) of nominator reward (200) + SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, 0, 400); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 400 + ); // 100 + 50% * 400 + 50% * 200 = 400 + }); +} + +// Verify changing delegate take also changes emission distribution +#[test] +fn test_changing_delegate_take_changes_distribution() { + new_test_ext().execute_with(|| { + let netuid = 1; + // Make two accounts. + let hotkey0 = U256::from(1); + let hotkey1 = U256::from(2); + + let coldkey0 = U256::from(3); + let coldkey1 = U256::from(4); + SubtensorModule::set_max_registrations_per_block(netuid, 4); + SubtensorModule::set_max_allowed_uids(netuid, 10); // Allow at least 10 to be registered at once, so no unstaking occurs + + // Add balances. + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey1, 100000); + + // Register the 2 neurons to a new network. + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + register_ok_neuron(netuid, hotkey1, coldkey1, 987907); + + // Stake 100 from coldkey/hotkey 0 + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 100 + ); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 2 + )); + + // Hotkey 1 adds 100 delegated stake to coldkey/hotkey 0 + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 0 + ); + assert_eq!(SubtensorModule::get_total_stake(), 100); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey1), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 100 + ); + assert_eq!(SubtensorModule::get_total_stake(), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 0); + + // Coldkey / hotkey 0 decrease take to 10% + assert_ok!(SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + + // Lets emit inflation through this new key with distributed ownership. + // We will emit 0 server emission (which should go in-full to the owner of the hotkey). + // We will emit 400 validator emission, which should be distributed in-part to the nominators. + // + // Total initial stake is 200 + // Delegate's initial stake is 100, which is 50% of total stake + // => Delegate will receive 50% of emission (200) + 10% take (20) of nominator reward (200) + SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, 0, 400); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 320 + ); // 100 + 50% * 400 + 10% * 200 = 320 + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 26d71cd0c1..01f05f236e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -647,7 +647,7 @@ parameter_types! { pub const SubtensorInitialMaxRegistrationsPerBlock: u16 = 1; pub const SubtensorInitialPruningScore : u16 = u16::MAX; pub const SubtensorInitialBondsMovingAverage: u64 = 900_000; - pub const SubtensorInitialDefaultTake: u16 = 11_796; // 18% honest number. + pub const SubtensorInitialDefaultTake: u16 = 11_796; // 18% honest number (65535 * 0.18 = 11_796) pub const SubtensorInitialWeightsVersionKey: u64 = 0; pub const SubtensorInitialMinDifficulty: u64 = 10_000_000; pub const SubtensorInitialMaxDifficulty: u64 = u64::MAX / 4; @@ -656,6 +656,7 @@ parameter_types! { pub const SubtensorInitialMinBurn: u64 = 1_000_000_000; // 1 tao pub const SubtensorInitialMaxBurn: u64 = 100_000_000_000; // 100 tao pub const SubtensorInitialTxRateLimit: u64 = 1000; + pub const SubtensorInitialTxRateLimitDelegateTake: u64 = 216000; // 30 days at 12 seconds per block pub const SubtensorInitialRAORecycledForRegistration: u64 = 0; // 0 rao pub const SubtensorInitialSenateRequiredStakePercentage: u64 = 1; // 1 percent of total stake pub const SubtensorInitialNetworkImmunity: u64 = 7 * 7200; @@ -704,6 +705,7 @@ impl pallet_subtensor::Config for Runtime { type InitialMaxBurn = SubtensorInitialMaxBurn; type InitialMinBurn = SubtensorInitialMinBurn; type InitialTxRateLimit = SubtensorInitialTxRateLimit; + type InitialTxRateLimitDelegateTake = SubtensorInitialTxRateLimitDelegateTake; type InitialRAORecycledForRegistration = SubtensorInitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = SubtensorInitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = SubtensorInitialNetworkImmunity; @@ -741,6 +743,10 @@ impl SubtensorModule::set_tx_rate_limit(rate_limit); } + fn set_tx_rate_limit_delegate_take(rate_limit: u64) { + SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + } + fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { SubtensorModule::set_serving_rate_limit(netuid, rate_limit); } From 9080a94546c40664d24877de23bbf6d1a5680770 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 11 Apr 2024 11:35:06 -0400 Subject: [PATCH 09/24] Rename rate limiting identifiers to match pattern, enforce take bounds on delegate creation - in progress --- pallets/admin-utils/src/lib.rs | 8 +++--- pallets/admin-utils/tests/mock.rs | 8 +++--- pallets/admin-utils/tests/tests.rs | 12 ++++---- pallets/subtensor/src/lib.rs | 10 +++---- pallets/subtensor/src/staking.rs | 25 +++++++++++------ pallets/subtensor/src/utils.rs | 10 +++---- pallets/subtensor/tests/mock.rs | 6 ++-- pallets/subtensor/tests/staking.rs | 44 +++++++++++++++++++++++++++++- runtime/src/lib.rs | 8 +++--- 9 files changed, 90 insertions(+), 41 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 3876384967..d97cd6f6ca 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -104,10 +104,10 @@ pub mod pallet { #[pallet::call_index(43)] #[pallet::weight((0, DispatchClass::Operational, Pays::No))] - pub fn sudo_set_tx_rate_limit_delegate_take(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { + pub fn sudo_set_tx_delegate_take_rate_limit(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_tx_rate_limit_delegate_take(tx_rate_limit); - log::info!("TxRateLimitDelegateTakeSet( tx_rate_limit_delegate_take: {:?} ) ", tx_rate_limit); + T::Subtensor::set_tx_delegate_take_rate_limit(tx_rate_limit); + log::info!("TxRateLimitDelegateTakeSet( tx_delegate_take_rate_limit: {:?} ) ", tx_rate_limit); Ok(()) } @@ -796,7 +796,7 @@ impl AuraInterface for () { pub trait SubtensorInterface { fn set_default_take(default_take: u16); fn set_tx_rate_limit(rate_limit: u64); - fn set_tx_rate_limit_delegate_take(rate_limit: u64); + fn set_tx_delegate_take_rate_limit(rate_limit: u64); fn set_serving_rate_limit(netuid: u16, rate_limit: u64); diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index a81cd9efbb..f8e10fff27 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -84,7 +84,7 @@ parameter_types! { pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing - pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable rate limit for testing + pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -147,7 +147,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; - type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; + type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; @@ -217,8 +217,8 @@ impl pallet_admin_utils::SubtensorInterface f SubtensorModule::set_tx_rate_limit(rate_limit); } - fn set_tx_rate_limit_delegate_take(rate_limit: u64) { - SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + fn set_tx_delegate_take_rate_limit(rate_limit: u64) { + SubtensorModule::set_tx_delegate_take_rate_limit(rate_limit); } fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { diff --git a/pallets/admin-utils/tests/tests.rs b/pallets/admin-utils/tests/tests.rs index d7ae01aed1..20152b6586 100644 --- a/pallets/admin-utils/tests/tests.rs +++ b/pallets/admin-utils/tests/tests.rs @@ -907,22 +907,22 @@ fn test_sudo_set_tx_rate_limit() { } #[test] -fn test_sudo_set_tx_rate_limit_delegate_take() { +fn test_sudo_set_tx_delegate_take_rate_limit() { new_test_ext().execute_with(|| { let to_be_set: u64 = 10; - let init_value: u64 = SubtensorModule::get_tx_rate_limit_delegate_take(); + let init_value: u64 = SubtensorModule::get_tx_delegate_take_rate_limit(); assert_eq!( - AdminUtils::sudo_set_tx_rate_limit_delegate_take( + AdminUtils::sudo_set_tx_delegate_take_rate_limit( <::RuntimeOrigin>::signed(U256::from(1)), to_be_set ), Err(DispatchError::BadOrigin.into()) ); - assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), init_value); - assert_ok!(AdminUtils::sudo_set_tx_rate_limit_delegate_take( + assert_eq!(SubtensorModule::get_tx_delegate_take_rate_limit(), init_value); + assert_ok!(AdminUtils::sudo_set_tx_delegate_take_rate_limit( <::RuntimeOrigin>::root(), to_be_set )); - assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), to_be_set); + assert_eq!(SubtensorModule::get_tx_delegate_take_rate_limit(), to_be_set); }); } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 21fbf12da9..88d53b8488 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -165,7 +165,7 @@ pub mod pallet { #[pallet::constant] // Initial transaction rate limit. type InitialTxRateLimit: Get; #[pallet::constant] // Initial delegate take transaction rate limit. - type InitialTxRateLimitDelegateTake: Get; + type InitialTxDelegateTakeRateLimit: Get; #[pallet::constant] // Initial percentage of total stake required to join senate. type InitialSenateRequiredStakePercentage: Get; #[pallet::constant] // Initial adjustment alpha on burn and pow. @@ -541,8 +541,8 @@ pub mod pallet { T::InitialTxRateLimit::get() } #[pallet::type_value] - pub fn DefaultTxRateLimitDelegateTake() -> u64 { - T::InitialTxRateLimitDelegateTake::get() + pub fn DefaultTxDelegateTakeRateLimit() -> u64 { + T::InitialTxDelegateTakeRateLimit::get() } #[pallet::type_value] pub fn DefaultLastTxBlock() -> u64 { @@ -552,7 +552,7 @@ pub mod pallet { #[pallet::storage] // --- ITEM ( tx_rate_limit ) pub(super) type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] // --- ITEM ( tx_rate_limit ) - pub(super) type TxRateLimitDelegateTake = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; + pub(super) type TxDelegateTakeRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] // --- MAP ( key ) --> last_block pub(super) type LastTxBlock = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; @@ -867,7 +867,7 @@ pub mod pallet { MaxBurnSet(u16, u64), // --- Event created when setting max burn on a network. MinBurnSet(u16, u64), // --- Event created when setting min burn on a network. TxRateLimitSet(u64), // --- Event created when setting the transaction rate limit. - TxRateLimitDelegateTakeSet(u64), // --- Event created when setting the transaction rate limit. + TxDelegateTakeRateLimitSet(u64), // --- Event created when setting the delegate take transaction rate limit. Sudid(DispatchResult), // --- Event created when a sudo call is done. RegistrationAllowed(u16, bool), // --- Event created when registration is allowed/disallowed for a subnet. PowRegistrationAllowed(u16, bool), // --- Event created when POW registration is allowed/disallowed for a subnet. diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index e40d7210c3..46bfb43c21 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -34,7 +34,7 @@ impl Pallet { hotkey: T::AccountId, take: u16, ) -> dispatch::DispatchResult { - // --- 1. We check the coldkey signuture. + // --- 1. We check the coldkey signature. let coldkey = ensure_signed(origin)?; log::info!( "do_become_delegate( origin:{:?} hotkey:{:?}, take:{:?} )", @@ -47,26 +47,36 @@ impl Pallet { // --- 3. Ensure that the coldkey is the owner. Self::do_take_checks(&coldkey, &hotkey)?; - // --- 4. Ensure we are not already a delegate (dont allow changing delegate take here.) + // --- 4. Ensure take is within the 0 ..= InitialDefaultTake (18%) range + let max_take = T::InitialDefaultTake::get(); + ensure!( + take <= max_take, + Error::::InvalidTake + ); + + // --- 5. Ensure we are not already a delegate (dont allow changing delegate take here.) ensure!( !Self::hotkey_is_delegate(&hotkey), Error::::AlreadyDelegate ); - // --- 5. Ensure we don't exceed tx rate limit + // --- 6. Ensure we don't exceed tx rate limit let block: u64 = Self::get_current_block_as_u64(); ensure!( !Self::exceeds_tx_rate_limit(Self::get_last_tx_block(&coldkey), block), Error::::TxRateLimitExceeded ); - // --- 6. Delegate the key. + // --- 7. Delegate the key. Self::delegate_hotkey(&hotkey, take); // Set last block for rate limiting Self::set_last_tx_block(&coldkey, block); - // --- 7. Emit the staking event. + // Also, set last block for take increase rate limiting + Self::set_last_tx_block_delegate_take(&coldkey, block); + + // --- 8. Emit the staking event. log::info!( "DelegateAdded( coldkey:{:?}, hotkey:{:?}, take:{:?} )", coldkey, @@ -75,7 +85,7 @@ impl Pallet { ); Self::deposit_event(Event::DelegateAdded(coldkey, hotkey, take)); - // --- 8. Ok and return. + // --- 9. Ok and return. Ok(()) } @@ -102,9 +112,6 @@ impl Pallet { // * 'NonAssociatedColdKey': // - The hotkey we are delegating is not owned by the calling coldket. // - // * 'TxRateLimitExceeded': - // - Thrown if key has hit transaction rate limit - // pub fn do_decrease_take( origin: T::RuntimeOrigin, hotkey: T::AccountId, diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index b512c36e75..eb343270f4 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -353,12 +353,12 @@ impl Pallet { TxRateLimit::::put(tx_rate_limit); Self::deposit_event(Event::TxRateLimitSet(tx_rate_limit)); } - pub fn get_tx_rate_limit_delegate_take() -> u64 { - TxRateLimitDelegateTake::::get() + pub fn get_tx_delegate_take_rate_limit() -> u64 { + TxDelegateTakeRateLimit::::get() } - pub fn set_tx_rate_limit_delegate_take(tx_rate_limit: u64) { - TxRateLimitDelegateTake::::put(tx_rate_limit); - Self::deposit_event(Event::TxRateLimitDelegateTakeSet(tx_rate_limit)); + pub fn set_tx_delegate_take_rate_limit(tx_rate_limit: u64) { + TxDelegateTakeRateLimit::::put(tx_rate_limit); + Self::deposit_event(Event::TxDelegateTakeRateLimitSet(tx_rate_limit)); } pub fn get_serving_rate_limit(netuid: u16) -> u64 { diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index d946791566..87fc46d0e0 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -130,11 +130,11 @@ parameter_types! { pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; - pub const InitialDefaultTake: u16 = 11_796; // 18% honest number. + pub const InitialDefaultTake: u16 = 32_767; // 50% for tests (18% honest number is used in production (see runtime)) pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing - pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable delegate take rate limit for testing + pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable delegate take rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -346,7 +346,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; - type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; + type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index e4157f321b..e3b333b403 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2246,6 +2246,15 @@ fn test_faucet_ok() { }); } +// Verify that InitialDefaultTake is between 50% and u16::MAX-1, this is important for other tests +#[test] +fn test_delegate_take_limit() { + new_test_ext().execute_with(|| { + assert_eq!(InitialDefaultTake::get() >= u16::MAX/2, true); + assert_eq!(InitialDefaultTake::get() <= u16::MAX-1, true); + }); +} + // Verify delegate take can be decreased #[test] fn test_delegate_take_can_be_decreased() { @@ -2333,7 +2342,7 @@ fn test_delegate_take_can_be_increased() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 50% take + // Coldkey / hotkey 0 become delegates with 5% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, @@ -2422,6 +2431,39 @@ fn test_delegate_take_can_be_increased_to_limit() { }); } +// Verify delegate take can not be set above InitialDefaultTake +#[test] +fn test_delegate_take_can_not_be_set_beyond_limit() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + let before = SubtensorModule::get_hotkey_take(&hotkey0); + + // Coldkey / hotkey 0 attempt to become delegates with take above maximum + // (Disable this check if InitialDefaultTake is u16::MAX) + if InitialDefaultTake::get() != u16::MAX { + assert_eq!( + SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + InitialDefaultTake::get()+1 + ), + Err(Error::::InvalidTake.into()) + ); + } + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), before); + }); +} + // Verify delegate take can not be increased above InitialDefaultTake (18%) #[test] fn test_delegate_take_can_not_be_increased_beyond_limit() { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 01f05f236e..666747d74e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -656,7 +656,7 @@ parameter_types! { pub const SubtensorInitialMinBurn: u64 = 1_000_000_000; // 1 tao pub const SubtensorInitialMaxBurn: u64 = 100_000_000_000; // 100 tao pub const SubtensorInitialTxRateLimit: u64 = 1000; - pub const SubtensorInitialTxRateLimitDelegateTake: u64 = 216000; // 30 days at 12 seconds per block + pub const SubtensorInitialTxDelegateTakeRateLimit: u64 = 216000; // 30 days at 12 seconds per block pub const SubtensorInitialRAORecycledForRegistration: u64 = 0; // 0 rao pub const SubtensorInitialSenateRequiredStakePercentage: u64 = 1; // 1 percent of total stake pub const SubtensorInitialNetworkImmunity: u64 = 7 * 7200; @@ -705,7 +705,7 @@ impl pallet_subtensor::Config for Runtime { type InitialMaxBurn = SubtensorInitialMaxBurn; type InitialMinBurn = SubtensorInitialMinBurn; type InitialTxRateLimit = SubtensorInitialTxRateLimit; - type InitialTxRateLimitDelegateTake = SubtensorInitialTxRateLimitDelegateTake; + type InitialTxDelegateTakeRateLimit = SubtensorInitialTxDelegateTakeRateLimit; type InitialRAORecycledForRegistration = SubtensorInitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = SubtensorInitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = SubtensorInitialNetworkImmunity; @@ -743,8 +743,8 @@ impl SubtensorModule::set_tx_rate_limit(rate_limit); } - fn set_tx_rate_limit_delegate_take(rate_limit: u64) { - SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + fn set_tx_delegate_take_rate_limit(rate_limit: u64) { + SubtensorModule::set_tx_delegate_take_rate_limit(rate_limit); } fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { From ddb34058fb769bedbaf9e9632f009a9207588e0e Mon Sep 17 00:00:00 2001 From: Unconst <32490803+unconst@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:04:06 -0500 Subject: [PATCH 10/24] Update pallets/subtensor/src/staking.rs Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 81 ++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 20d28aba04..4a907cee88 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -95,6 +95,87 @@ impl Pallet { Ok(()) } + // ---- The implementation for the extrinsic decrease_take + // + // # Args: + // * 'origin': (RuntimeOrigin): + // - The signature of the caller's coldkey. + // + // * 'hotkey' (T::AccountId): + // - The hotkey we are delegating (must be owned by the coldkey.) + // + // * 'take' (u16): + // - The stake proportion that this hotkey takes from delegations. + // + // # Event: + // * DelegateAdded; + // - On successfully setting a hotkey as a delegate. + // + // # Raises: + // * 'NotRegistered': + // - The hotkey we are delegating is not registered on the network. + // + // * 'NonAssociatedColdKey': + // - The hotkey we are delegating is not owned by the calling coldket. + // + // * 'TxRateLimitExceeded': + // - Thrown if key has hit transaction rate limit + // + pub fn do_decrease_take( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + take: u16, + ) -> dispatch::DispatchResult { + // --- 1. We check the coldkey signature. + let coldkey = ensure_signed(origin)?; + log::info!( + "do_decrease_take( origin:{:?} hotkey:{:?}, take:{:?} )", + coldkey, + hotkey, + take + ); + + // --- 2. Ensure we are delegating an known key. + ensure!( + Self::hotkey_account_exists(&hotkey), + Error::::NotRegistered + ); + + // --- 3. Ensure that the coldkey is the owner. + ensure!( + Self::coldkey_owns_hotkey(&coldkey, &hotkey), + Error::::NonAssociatedColdKey + ); + + // --- 4. Ensure we are not already a delegate (dont allow changing delegate take.) + ensure!( + !Self::hotkey_is_delegate(&hotkey), + Error::::AlreadyDelegate + ); + + // --- 5. Ensure we are always decreasing take never increasing. + let current_take: u16 = Delegates::::get(hotkey.clone()); + ensure!( + take < current_take, + Error::::InvalidTake + ); + + // --- 6. Set the new take value. + Delegates::::insert(hotkey.clone(), take); + + // --- 7. Emit the take value. + log::info!( + "TakeDecreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", + coldkey, + hotkey, + take + ); + Self::deposit_event(Event::TakeDecreased(coldkey, hotkey, take)); + + // --- 8. Ok and return. + Ok(()) + } + // ---- The implementation for the extrinsic add_stake: Adds stake to a hotkey account. // // # Args: From 58d4fad370086fa07627880ccbbd25b21819298e Mon Sep 17 00:00:00 2001 From: Unconst <32490803+unconst@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:04:12 -0500 Subject: [PATCH 11/24] Update pallets/subtensor/src/lib.rs Co-authored-by: cuteolaf --- pallets/subtensor/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d3d87ae2d0..2e000c16f3 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1351,6 +1351,38 @@ pub mod pallet { Self::do_become_delegate(origin, hotkey, Self::get_default_take()) } + // --- Allows delegates to decrease its take value. + // + // # Args: + // * 'origin': (::Origin): + // - The signature of the caller's coldkey. + // + // * 'hotkey' (T::AccountId): + // - The hotkey we are delegating (must be owned by the coldkey.) + // + // * 'take' (u64): + // - The new stake proportion that this hotkey takes from delegations. + // + // # Event: + // * DelegateAdded; + // - On successfully setting a hotkey as a delegate. + // + // # Raises: + // * 'NotRegistered': + // - The hotkey we are delegating is not registered on the network. + // + // * 'NonAssociatedColdKey': + // - The hotkey we are delegating is not owned by the calling coldkey. + // + // * 'InvalidTransaction': + // - The delegate is setting a take which is not lower than the previous. + // + #[pallet::call_index(63)] + #[pallet::weight((0, DispatchClass::Normal, Pays::No))] + pub fn decrease_take(origin: OriginFor, hotkey: T::AccountId, take: u16) -> DispatchResult { + Self::do_decrease_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 From b725dc3a9e74936941eec04802da8cc92e1336f5 Mon Sep 17 00:00:00 2001 From: Unconst <32490803+unconst@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:04:17 -0500 Subject: [PATCH 12/24] Update pallets/subtensor/src/staking.rs Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 4a907cee88..2b8387414a 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -98,7 +98,7 @@ impl Pallet { // ---- The implementation for the extrinsic decrease_take // // # Args: - // * 'origin': (RuntimeOrigin): + // * 'origin': (::RuntimeOrigin): // - The signature of the caller's coldkey. // // * 'hotkey' (T::AccountId): From 44f409a910efdba8591e8c89995637bc11bc829c Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:21:53 -0400 Subject: [PATCH 13/24] comment nit Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 2b8387414a..fda7c9ccb1 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -135,7 +135,7 @@ impl Pallet { take ); - // --- 2. Ensure we are delegating an known key. + // --- 2. Ensure we are delegating a known key. ensure!( Self::hotkey_account_exists(&hotkey), Error::::NotRegistered From d2eafc74397b83d34b0fb9b0159f90959f474849 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:22:50 -0400 Subject: [PATCH 14/24] fix comment Co-authored-by: cuteolaf --- pallets/subtensor/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 2e000c16f3..4d7f1a3f18 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1360,7 +1360,7 @@ pub mod pallet { // * 'hotkey' (T::AccountId): // - The hotkey we are delegating (must be owned by the coldkey.) // - // * 'take' (u64): + // * 'take' (u16): // - The new stake proportion that this hotkey takes from delegations. // // # Event: From 0cf36ac536e3bd7bb43a4291d3b76f64fbf944b7 Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:23:09 -0400 Subject: [PATCH 15/24] fix comment Co-authored-by: cuteolaf --- pallets/subtensor/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 4d7f1a3f18..b8d9f2ed2d 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1364,8 +1364,8 @@ pub mod pallet { // - The new stake proportion that this hotkey takes from delegations. // // # Event: - // * DelegateAdded; - // - On successfully setting a hotkey as a delegate. + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. // // # Raises: // * 'NotRegistered': From 1e669405bf8a0b11619063e19b32de196cf9cf0f Mon Sep 17 00:00:00 2001 From: Sam Johnson Date: Mon, 8 Apr 2024 12:23:24 -0400 Subject: [PATCH 16/24] fix comment Co-authored-by: cuteolaf --- pallets/subtensor/src/staking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index fda7c9ccb1..73db3c5914 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -108,8 +108,8 @@ impl Pallet { // - The stake proportion that this hotkey takes from delegations. // // # Event: - // * DelegateAdded; - // - On successfully setting a hotkey as a delegate. + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. // // # Raises: // * 'NotRegistered': From b99d4786149bfdc6ceb7246d86c1edb7e546d617 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 10 Apr 2024 18:21:04 -0400 Subject: [PATCH 17/24] Allow to also increase take, add tests --- pallets/admin-utils/src/lib.rs | 10 + pallets/admin-utils/tests/mock.rs | 6 + pallets/admin-utils/tests/tests.rs | 44 ++- pallets/subtensor/src/lib.rs | 55 +++- pallets/subtensor/src/migration.rs | 4 +- pallets/subtensor/src/registration.rs | 3 +- pallets/subtensor/src/staking.rs | 119 ++++++-- pallets/subtensor/src/utils.rs | 35 +++ pallets/subtensor/tests/mock.rs | 4 + pallets/subtensor/tests/staking.rs | 375 ++++++++++++++++++++++++++ runtime/src/lib.rs | 8 +- 11 files changed, 631 insertions(+), 32 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index aee2059f66..a43d9a55e4 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -102,6 +102,15 @@ pub mod pallet { Ok(()) } + #[pallet::call_index(43)] + #[pallet::weight((0, DispatchClass::Operational, Pays::No))] + pub fn sudo_set_tx_rate_limit_delegate_take(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { + ensure_root(origin)?; + T::Subtensor::set_tx_rate_limit_delegate_take(tx_rate_limit); + log::info!("TxRateLimitDelegateTakeSet( tx_rate_limit_delegate_take: {:?} ) ", tx_rate_limit); + Ok(()) + } + #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::sudo_set_serving_rate_limit())] pub fn sudo_set_serving_rate_limit( @@ -809,6 +818,7 @@ impl AuraInterface for () { pub trait SubtensorInterface { fn set_default_take(default_take: u16); fn set_tx_rate_limit(rate_limit: u64); + fn set_tx_rate_limit_delegate_take(rate_limit: u64); fn set_serving_rate_limit(netuid: u16, rate_limit: u64); diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index 441534489d..df85f10883 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -79,6 +79,7 @@ parameter_types! { pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing + pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -143,6 +144,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; + type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; @@ -211,6 +213,10 @@ impl pallet_admin_utils::SubtensorInterface f SubtensorModule::set_tx_rate_limit(rate_limit); } + fn set_tx_rate_limit_delegate_take(rate_limit: u64) { + SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + } + fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { SubtensorModule::set_serving_rate_limit(netuid, rate_limit); } diff --git a/pallets/admin-utils/tests/tests.rs b/pallets/admin-utils/tests/tests.rs index 0647988157..ecfe03a01a 100644 --- a/pallets/admin-utils/tests/tests.rs +++ b/pallets/admin-utils/tests/tests.rs @@ -9,7 +9,7 @@ mod mock; use mock::*; #[allow(dead_code)] -pub fn add_network(netuid: u16, tempo: u16) { +pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); SubtensorModule::set_network_pow_registration_allowed(netuid, true); @@ -926,3 +926,45 @@ fn test_sudo_set_network_pow_registration_allowed() { ); }); } + +#[test] +fn test_sudo_set_tx_rate_limit() { + new_test_ext().execute_with(|| { + let to_be_set: u64 = 10; + let init_value: u64 = SubtensorModule::get_tx_rate_limit(); + assert_eq!( + AdminUtils::sudo_set_tx_rate_limit( + <::RuntimeOrigin>::signed(U256::from(1)), + to_be_set + ), + Err(DispatchError::BadOrigin.into()) + ); + assert_eq!(SubtensorModule::get_tx_rate_limit(), init_value); + assert_ok!(AdminUtils::sudo_set_tx_rate_limit( + <::RuntimeOrigin>::root(), + to_be_set + )); + assert_eq!(SubtensorModule::get_tx_rate_limit(), to_be_set); + }); +} + +#[test] +fn test_sudo_set_tx_rate_limit_delegate_take() { + new_test_ext().execute_with(|| { + let to_be_set: u64 = 10; + let init_value: u64 = SubtensorModule::get_tx_rate_limit_delegate_take(); + assert_eq!( + AdminUtils::sudo_set_tx_rate_limit_delegate_take( + <::RuntimeOrigin>::signed(U256::from(1)), + to_be_set + ), + Err(DispatchError::BadOrigin.into()) + ); + assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), init_value); + assert_ok!(AdminUtils::sudo_set_tx_rate_limit_delegate_take( + <::RuntimeOrigin>::root(), + to_be_set + )); + assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), to_be_set); + }); +} diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index b8d9f2ed2d..ff77efd81a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -164,6 +164,8 @@ pub mod pallet { type InitialServingRateLimit: Get; #[pallet::constant] // Initial transaction rate limit. type InitialTxRateLimit: Get; + #[pallet::constant] // Initial delegate take transaction rate limit. + type InitialTxRateLimitDelegateTake: Get; #[pallet::constant] // Initial percentage of total stake required to join senate. type InitialSenateRequiredStakePercentage: Get; #[pallet::constant] // Initial adjustment alpha on burn and pow. @@ -611,15 +613,24 @@ pub mod pallet { T::InitialTxRateLimit::get() } #[pallet::type_value] + pub fn DefaultTxRateLimitDelegateTake() -> u64 { + T::InitialTxRateLimitDelegateTake::get() + } + #[pallet::type_value] pub fn DefaultLastTxBlock() -> u64 { 0 } #[pallet::storage] // --- ITEM ( tx_rate_limit ) pub(super) type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; + #[pallet::storage] // --- ITEM ( tx_rate_limit ) + pub(super) type TxRateLimitDelegateTake = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] // --- MAP ( key ) --> last_block pub(super) type LastTxBlock = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; + #[pallet::storage] // --- MAP ( key ) --> last_block + pub(super) type LastTxBlockDelegateTake = + StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; #[pallet::type_value] pub fn DefaultServingRateLimit() -> u64 { @@ -928,6 +939,7 @@ pub mod pallet { MaxBurnSet(u16, u64), // --- Event created when setting max burn on a network. MinBurnSet(u16, u64), // --- Event created when setting min burn on a network. TxRateLimitSet(u64), // --- Event created when setting the transaction rate limit. + TxRateLimitDelegateTakeSet(u64), // --- Event created when setting the transaction rate limit. Sudid(DispatchResult), // --- Event created when a sudo call is done. RegistrationAllowed(u16, bool), // --- Event created when registration is allowed/disallowed for a subnet. PowRegistrationAllowed(u16, bool), // --- Event created when POW registration is allowed/disallowed for a subnet. @@ -943,6 +955,8 @@ pub mod pallet { NetworkMinLockCostSet(u64), // Event created when the network minimum locking cost is set. SubnetLimitSet(u16), // Event created when the maximum number of subnets is set NetworkLockCostReductionIntervalSet(u64), // Event created when the lock cost reduction is set + TakeDecreased( T::AccountId, T::AccountId, u16 ), // Event created when the take for a delegate is decreased. + TakeIncreased( T::AccountId, T::AccountId, u16 ), // Event created when the take for a delegate is increased. HotkeySwapped { coldkey: T::AccountId, old_hotkey: T::AccountId, @@ -1362,6 +1376,10 @@ pub mod pallet { // // * 'take' (u16): // - The new stake proportion that this hotkey takes from delegations. + // The new value can be between 0 and 11_796 and should be strictly + // lower than the previous value. It T is the new value (rational number), + // the the parameter is calculated as [65535 * T]. For example, 1% would be + // [0.01 * 65535] = [655.35] = 655 // // # Event: // * TakeDecreased; @@ -1383,6 +1401,42 @@ pub mod pallet { Self::do_decrease_take(origin, hotkey, take) } + // --- Allows delegates to increase its take value. This call is rate-limited. + // + // # Args: + // * 'origin': (::Origin): + // - The signature of the caller's coldkey. + // + // * 'hotkey' (T::AccountId): + // - The hotkey we are delegating (must be owned by the coldkey.) + // + // * 'take' (u16): + // - The new stake proportion that this hotkey takes from delegations. + // The new value can be between 0 and 11_796 and should be strictly + // greater than the previous value. It T is the new value (rational number), + // the the parameter is calculated as [65535 * T]. For example, 1% would be + // [0.01 * 65535] = [655.35] = 655 + // + // # Event: + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. + // + // # Raises: + // * 'NotRegistered': + // - The hotkey we are delegating is not registered on the network. + // + // * 'NonAssociatedColdKey': + // - The hotkey we are delegating is not owned by the calling coldkey. + // + // * 'InvalidTransaction': + // - The delegate is setting a take which is not lower than the previous. + // + #[pallet::call_index(64)] + #[pallet::weight((0, DispatchClass::Normal, Pays::No))] + pub fn increase_take(origin: OriginFor, hotkey: T::AccountId, take: u16) -> DispatchResult { + Self::do_decrease_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 @@ -1806,7 +1860,6 @@ pub mod pallet { pub fn get_priority_set_weights(hotkey: &T::AccountId, netuid: u16) -> u64 { if Uids::::contains_key(netuid, &hotkey) { let uid = Self::get_uid_for_net_and_hotkey(netuid, &hotkey.clone()).unwrap(); - let _stake = Self::get_total_stake_for_hotkey(&hotkey); let current_block_number: u64 = Self::get_current_block_as_u64(); let default_priority: u64 = current_block_number - Self::get_last_update_for_uid(netuid, uid as u16); diff --git a/pallets/subtensor/src/migration.rs b/pallets/subtensor/src/migration.rs index ed25f3542a..243f09f6df 100644 --- a/pallets/subtensor/src/migration.rs +++ b/pallets/subtensor/src/migration.rs @@ -156,8 +156,8 @@ pub fn migrate_create_root_network() -> Weight { // Empty senate members entirely, they will be filled by by registrations // on the subnet. for hotkey_i in T::SenateMembers::members().iter() { - T::TriumvirateInterface::remove_votes(&hotkey_i).unwrap(); - T::SenateMembers::remove_member(&hotkey_i).unwrap(); + let _ = T::TriumvirateInterface::remove_votes(&hotkey_i); + let _ = T::SenateMembers::remove_member(&hotkey_i); weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); } diff --git a/pallets/subtensor/src/registration.rs b/pallets/subtensor/src/registration.rs index b6950de3d3..79cd3c374a 100644 --- a/pallets/subtensor/src/registration.rs +++ b/pallets/subtensor/src/registration.rs @@ -1,6 +1,5 @@ use super::*; - -use frame_support::pallet_prelude::DispatchResultWithPostInfo; +use frame_support::pallet_prelude::{DispatchResult, DispatchResultWithPostInfo}; use frame_support::storage::IterableStorageDoubleMap; use sp_core::{Get, H256, U256}; use sp_io::hashing::{keccak_256, sha2_256}; diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 73db3c5914..700f882c01 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -9,6 +9,7 @@ use frame_support::{ Imbalance, }, }; +use sp_core::Get; impl Pallet { // ---- The implementation for the extrinsic become_delegate: signals that this hotkey allows delegated stake. @@ -51,19 +52,11 @@ impl Pallet { take ); - // --- 2. Ensure we are delegating an known key. - ensure!( - Self::hotkey_account_exists(&hotkey), - Error::::NotRegistered - ); - + // --- 2. Ensure we are delegating a known key. // --- 3. Ensure that the coldkey is the owner. - ensure!( - Self::coldkey_owns_hotkey(&coldkey, &hotkey), - Error::::NonAssociatedColdKey - ); + Self::do_take_checks(&coldkey, &hotkey)?; - // --- 4. Ensure we are not already a delegate (dont allow changing delegate take.) + // --- 4. Ensure we are not already a delegate (dont allow changing delegate take here.) ensure!( !Self::hotkey_is_delegate(&hotkey), Error::::AlreadyDelegate @@ -136,41 +129,111 @@ impl Pallet { ); // --- 2. Ensure we are delegating a known key. + // Ensure that the coldkey is the owner. + Self::do_take_checks(&coldkey, &hotkey)?; + + // --- 3. Ensure we are always strictly decreasing, never increasing take + let current_take: u16 = Delegates::::get(&hotkey); ensure!( - Self::hotkey_account_exists(&hotkey), - Error::::NotRegistered + take < current_take, + Error::::InvalidTake ); - // --- 3. Ensure that the coldkey is the owner. - ensure!( - Self::coldkey_owns_hotkey(&coldkey, &hotkey), - Error::::NonAssociatedColdKey + // --- 4. Set the new take value. + Delegates::::insert(hotkey.clone(), take); + + // --- 5. Emit the take value. + log::info!( + "TakeDecreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", + coldkey, + hotkey, + take ); + Self::deposit_event(Event::TakeDecreased(coldkey, hotkey, take)); - // --- 4. Ensure we are not already a delegate (dont allow changing delegate take.) + // --- 6. Ok and return. + Ok(()) + } + + // ---- The implementation for the extrinsic increase_take + // + // # Args: + // * 'origin': (::RuntimeOrigin): + // - The signature of the caller's coldkey. + // + // * 'hotkey' (T::AccountId): + // - The hotkey we are delegating (must be owned by the coldkey.) + // + // * 'take' (u16): + // - The stake proportion that this hotkey takes from delegations. + // + // # Event: + // * TakeDecreased; + // - On successfully setting a decreased take for this hotkey. + // + // # Raises: + // * 'NotRegistered': + // - The hotkey we are delegating is not registered on the network. + // + // * 'NonAssociatedColdKey': + // - The hotkey we are delegating is not owned by the calling coldket. + // + // * 'TxRateLimitExceeded': + // - Thrown if key has hit transaction rate limit + // + pub fn do_increase_take( + origin: T::RuntimeOrigin, + hotkey: T::AccountId, + take: u16, + ) -> dispatch::DispatchResult { + // --- 1. We check the coldkey signature. + let coldkey = ensure_signed(origin)?; + log::info!( + "do_increase_take( origin:{:?} hotkey:{:?}, take:{:?} )", + coldkey, + hotkey, + take + ); + + // --- 2. Ensure we are delegating a known key. + // Ensure that the coldkey is the owner. + Self::do_take_checks(&coldkey, &hotkey)?; + + // --- 3. Ensure we are strinctly increasing take + let current_take: u16 = Delegates::::get(&hotkey); ensure!( - !Self::hotkey_is_delegate(&hotkey), - Error::::AlreadyDelegate + take > current_take, + Error::::InvalidTake ); - // --- 5. Ensure we are always decreasing take never increasing. - let current_take: u16 = Delegates::::get(hotkey.clone()); + // --- 4. Ensure take is within the 0 ..= InitialDefaultTake (18%) range + let max_take = T::InitialDefaultTake::get(); ensure!( - take < current_take, + take <= max_take, Error::::InvalidTake ); + // --- 5. Enforce the rate limit (independently on do_add_stake rate limits) + let block: u64 = Self::get_current_block_as_u64(); + ensure!( + !Self::exceeds_tx_rate_limit(Self::get_last_tx_block_delegate_take(&coldkey), block), + Error::::TxRateLimitExceeded + ); + + // Set last block for rate limiting + Self::set_last_tx_block_delegate_take(&coldkey, block); + // --- 6. Set the new take value. Delegates::::insert(hotkey.clone(), take); // --- 7. Emit the take value. log::info!( - "TakeDecreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", + "TakeIncreased( coldkey:{:?}, hotkey:{:?}, take:{:?} )", coldkey, hotkey, take ); - Self::deposit_event(Event::TakeDecreased(coldkey, hotkey, take)); + Self::deposit_event(Event::TakeIncreased(coldkey, hotkey, take)); // --- 8. Ok and return. Ok(()) @@ -541,6 +604,12 @@ impl Pallet { return Owner::::get(hotkey); } + // Returns the hotkey take + // + pub fn get_hotkey_take(hotkey: &T::AccountId) -> u16 { + Delegates::::get(hotkey) + } + // Returns true if the hotkey account has been created. // pub fn hotkey_account_exists(hotkey: &T::AccountId) -> bool { diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index e7d972f3b7..622c68ef5f 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -297,6 +297,28 @@ impl Pallet { GlobalStakeWeight::::put(global_stake_weight); } + // ======================== + // ===== Take checks ====== + // ======================== + pub fn do_take_checks( + coldkey: &T::AccountId, + hotkey: &T::AccountId, + ) -> Result<(), Error> { + // Ensure we are delegating a known key. + ensure!( + Self::hotkey_account_exists(hotkey), + Error::::NotRegistered + ); + + // Ensure that the coldkey is the owner. + ensure!( + Self::coldkey_owns_hotkey(coldkey, hotkey), + Error::::NonAssociatedColdKey + ); + + Ok(()) + } + // ======================== // ==== Rate Limiting ===== // ======================== @@ -306,6 +328,12 @@ impl Pallet { pub fn get_last_tx_block(key: &T::AccountId) -> u64 { LastTxBlock::::get(key) } + pub fn set_last_tx_block_delegate_take(key: &T::AccountId, block: u64) { + LastTxBlockDelegateTake::::insert(key, block) + } + pub fn get_last_tx_block_delegate_take(key: &T::AccountId) -> u64 { + LastTxBlockDelegateTake::::get(key) + } pub fn exceeds_tx_rate_limit(prev_tx_block: u64, current_block: u64) -> bool { let rate_limit: u64 = Self::get_tx_rate_limit(); if rate_limit == 0 || prev_tx_block == 0 { @@ -352,6 +380,13 @@ impl Pallet { TxRateLimit::::put(tx_rate_limit); Self::deposit_event(Event::TxRateLimitSet(tx_rate_limit)); } + pub fn get_tx_rate_limit_delegate_take() -> u64 { + TxRateLimitDelegateTake::::get() + } + pub fn set_tx_rate_limit_delegate_take(tx_rate_limit: u64) { + TxRateLimitDelegateTake::::put(tx_rate_limit); + Self::deposit_event(Event::TxRateLimitDelegateTakeSet(tx_rate_limit)); + } pub fn get_serving_rate_limit(netuid: u16) -> u64 { ServingRateLimit::::get(netuid) diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 5bcd988c31..fc18a7c5e3 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -1,3 +1,4 @@ +#![allow(non_snake_case, non_camel_case_types)] use frame_support::traits::Hash; use frame_support::{ assert_ok, parameter_types, @@ -127,6 +128,7 @@ parameter_types! { pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing + pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable delegate take rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -339,6 +341,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; + type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; @@ -461,6 +464,7 @@ pub fn register_ok_neuron( } #[allow(dead_code)] +pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 59859c9d4f..4d12c7295b 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2732,6 +2732,381 @@ fn test_faucet_ok() { }); } +// Verify delegate take can be decreased +#[test] +fn test_delegate_take_can_be_decreased() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 2 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 2); + + // Coldkey / hotkey 0 decreases take to 10% + assert_ok!(SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take can not be increased with do_decrease_take +#[test] +fn test_delegate_take_can_not_be_increased_with_decrease_take() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 5% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + + // Coldkey / hotkey 0 tries to increase take to 10% + assert_eq!( + SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + ), + Err(Error::::InvalidTake.into()) + ); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + }); +} + +// Verify delegate take can be increased +#[test] +fn test_delegate_take_can_be_increased() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + + // Coldkey / hotkey 0 decreases take to 10% + assert_ok!(SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take can not be decreased with increase_take +#[test] +fn test_delegate_take_can_not_be_decreased_with_increase_take() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 10% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + + // Coldkey / hotkey 0 tries to decrease take to 5% + assert_eq!( + SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + ), + Err(Error::::InvalidTake.into()) + ); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take can be increased up to InitialDefaultTake (18%) +#[test] +fn test_delegate_take_can_be_increased_to_limit() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 10% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + + // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 + assert_ok!(SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + InitialDefaultTake::get() + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), InitialDefaultTake::get()); + }); +} + +// Verify delegate take can not be increased above InitialDefaultTake (18%) +#[test] +fn test_delegate_take_can_not_be_increased_beyond_limit() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 10% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + + // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 + // (Disable this check if InitialDefaultTake is u16::MAX) + if InitialDefaultTake::get() != u16::MAX { + assert_eq!( + SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + InitialDefaultTake::get()+1 + ), + Err(Error::::InvalidTake.into()) + ); + } + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); +} + +// Verify delegate take affects emission distribution +#[test] +fn test_delegate_take_affects_distribution() { + new_test_ext().execute_with(|| { + let netuid = 1; + // Make two accounts. + let hotkey0 = U256::from(1); + let hotkey1 = U256::from(2); + + let coldkey0 = U256::from(3); + let coldkey1 = U256::from(4); + SubtensorModule::set_max_registrations_per_block(netuid, 4); + SubtensorModule::set_max_allowed_uids(netuid, 10); // Allow at least 10 to be registered at once, so no unstaking occurs + + // Add balances. + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey1, 100000); + + // Register the 2 neurons to a new network. + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + register_ok_neuron(netuid, hotkey1, coldkey1, 987907); + + // Stake 100 from coldkey/hotkey 0 + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 100 + ); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 2 + )); + + // Hotkey 1 adds 100 delegated stake to coldkey/hotkey 0 + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 0 + ); + assert_eq!(SubtensorModule::get_total_stake(), 100); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey1), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 100 + ); + assert_eq!(SubtensorModule::get_total_stake(), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 0); + + // Lets emit inflation through this new key with distributed ownership. + // We will emit 0 server emission (which should go in-full to the owner of the hotkey). + // We will emit 400 validator emission, which should be distributed in-part to the nominators. + // + // Total initial stake is 200 + // Delegate's initial stake is 100, which is 50% of total stake + // => Delegate will receive 50% of emission (200) + 50% take (100) of nominator reward (200) + SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, 0, 400); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 400 + ); // 100 + 50% * 400 + 50% * 200 = 400 + }); +} + +// Verify changing delegate take also changes emission distribution +#[test] +fn test_changing_delegate_take_changes_distribution() { + new_test_ext().execute_with(|| { + let netuid = 1; + // Make two accounts. + let hotkey0 = U256::from(1); + let hotkey1 = U256::from(2); + + let coldkey0 = U256::from(3); + let coldkey1 = U256::from(4); + SubtensorModule::set_max_registrations_per_block(netuid, 4); + SubtensorModule::set_max_allowed_uids(netuid, 10); // Allow at least 10 to be registered at once, so no unstaking occurs + + // Add balances. + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + SubtensorModule::add_balance_to_coldkey_account(&coldkey1, 100000); + + // Register the 2 neurons to a new network. + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + register_ok_neuron(netuid, hotkey1, coldkey1, 987907); + + // Stake 100 from coldkey/hotkey 0 + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 100 + ); + + // Coldkey / hotkey 0 become delegates with 50% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 2 + )); + + // Hotkey 1 adds 100 delegated stake to coldkey/hotkey 0 + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 0 + ); + assert_eq!(SubtensorModule::get_total_stake(), 100); + assert_ok!(SubtensorModule::add_stake( + <::RuntimeOrigin>::signed(coldkey1), + hotkey0, + 100 + )); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + 100 + ); + assert_eq!(SubtensorModule::get_total_stake(), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey0), 200); + assert_eq!(SubtensorModule::get_total_stake_for_hotkey(&hotkey1), 0); + + // Coldkey / hotkey 0 decrease take to 10% + assert_ok!(SubtensorModule::do_decrease_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + + // Lets emit inflation through this new key with distributed ownership. + // We will emit 0 server emission (which should go in-full to the owner of the hotkey). + // We will emit 400 validator emission, which should be distributed in-part to the nominators. + // + // Total initial stake is 200 + // Delegate's initial stake is 100, which is 50% of total stake + // => Delegate will receive 50% of emission (200) + 10% take (20) of nominator reward (200) + SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, 0, 400); + assert_eq!( + SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + 320 + ); // 100 + 50% * 400 + 10% * 200 = 320 + }); +} + #[test] // Set up 32 subnets with a total of 1024 nodes each, and a root network with 1024 nodes. // Each subnet has a total of 1024 nodes, and a root network has 1024 nodes. diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2ac33945c8..bc496518dd 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -647,7 +647,7 @@ parameter_types! { pub const SubtensorInitialMaxRegistrationsPerBlock: u16 = 1; pub const SubtensorInitialPruningScore : u16 = u16::MAX; pub const SubtensorInitialBondsMovingAverage: u64 = 900_000; - pub const SubtensorInitialDefaultTake: u16 = 11_796; // 18% honest number. + pub const SubtensorInitialDefaultTake: u16 = 11_796; // 18% honest number (65535 * 0.18 = 11_796) pub const SubtensorInitialWeightsVersionKey: u64 = 0; pub const SubtensorInitialMinDifficulty: u64 = 10_000_000; pub const SubtensorInitialMaxDifficulty: u64 = u64::MAX / 4; @@ -656,6 +656,7 @@ parameter_types! { pub const SubtensorInitialMinBurn: u64 = 1_000_000_000; // 1 tao pub const SubtensorInitialMaxBurn: u64 = 100_000_000_000; // 100 tao pub const SubtensorInitialTxRateLimit: u64 = 1000; + pub const SubtensorInitialTxRateLimitDelegateTake: u64 = 216000; // 30 days at 12 seconds per block pub const SubtensorInitialRAORecycledForRegistration: u64 = 0; // 0 rao pub const SubtensorInitialSenateRequiredStakePercentage: u64 = 1; // 1 percent of total stake pub const SubtensorInitialNetworkImmunity: u64 = 7 * 7200; @@ -705,6 +706,7 @@ impl pallet_subtensor::Config for Runtime { type InitialMaxBurn = SubtensorInitialMaxBurn; type InitialMinBurn = SubtensorInitialMinBurn; type InitialTxRateLimit = SubtensorInitialTxRateLimit; + type InitialTxRateLimitDelegateTake = SubtensorInitialTxRateLimitDelegateTake; type InitialRAORecycledForRegistration = SubtensorInitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = SubtensorInitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = SubtensorInitialNetworkImmunity; @@ -743,6 +745,10 @@ impl SubtensorModule::set_tx_rate_limit(rate_limit); } + fn set_tx_rate_limit_delegate_take(rate_limit: u64) { + SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + } + fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { SubtensorModule::set_serving_rate_limit(netuid, rate_limit); } From 6d098dc0ab6035440022eb5d16e0678382b9becb Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 11 Apr 2024 11:35:06 -0400 Subject: [PATCH 18/24] Rename rate limiting identifiers to match pattern, enforce take bounds on delegate creation - in progress --- pallets/admin-utils/src/lib.rs | 8 +++--- pallets/admin-utils/tests/mock.rs | 8 +++--- pallets/admin-utils/tests/tests.rs | 12 ++++---- pallets/subtensor/src/lib.rs | 10 +++---- pallets/subtensor/src/staking.rs | 25 +++++++++++------ pallets/subtensor/src/utils.rs | 10 +++---- pallets/subtensor/tests/mock.rs | 6 ++-- pallets/subtensor/tests/staking.rs | 44 +++++++++++++++++++++++++++++- runtime/src/lib.rs | 8 +++--- 9 files changed, 90 insertions(+), 41 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index a43d9a55e4..cac155ed49 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -104,10 +104,10 @@ pub mod pallet { #[pallet::call_index(43)] #[pallet::weight((0, DispatchClass::Operational, Pays::No))] - pub fn sudo_set_tx_rate_limit_delegate_take(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { + pub fn sudo_set_tx_delegate_take_rate_limit(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { ensure_root(origin)?; - T::Subtensor::set_tx_rate_limit_delegate_take(tx_rate_limit); - log::info!("TxRateLimitDelegateTakeSet( tx_rate_limit_delegate_take: {:?} ) ", tx_rate_limit); + T::Subtensor::set_tx_delegate_take_rate_limit(tx_rate_limit); + log::info!("TxRateLimitDelegateTakeSet( tx_delegate_take_rate_limit: {:?} ) ", tx_rate_limit); Ok(()) } @@ -818,7 +818,7 @@ impl AuraInterface for () { pub trait SubtensorInterface { fn set_default_take(default_take: u16); fn set_tx_rate_limit(rate_limit: u64); - fn set_tx_rate_limit_delegate_take(rate_limit: u64); + fn set_tx_delegate_take_rate_limit(rate_limit: u64); fn set_serving_rate_limit(netuid: u16, rate_limit: u64); diff --git a/pallets/admin-utils/tests/mock.rs b/pallets/admin-utils/tests/mock.rs index df85f10883..84d517f1b5 100644 --- a/pallets/admin-utils/tests/mock.rs +++ b/pallets/admin-utils/tests/mock.rs @@ -79,7 +79,7 @@ parameter_types! { pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing - pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable rate limit for testing + pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -144,7 +144,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; - type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; + type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; @@ -213,8 +213,8 @@ impl pallet_admin_utils::SubtensorInterface f SubtensorModule::set_tx_rate_limit(rate_limit); } - fn set_tx_rate_limit_delegate_take(rate_limit: u64) { - SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + fn set_tx_delegate_take_rate_limit(rate_limit: u64) { + SubtensorModule::set_tx_delegate_take_rate_limit(rate_limit); } fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { diff --git a/pallets/admin-utils/tests/tests.rs b/pallets/admin-utils/tests/tests.rs index ecfe03a01a..1976942eb8 100644 --- a/pallets/admin-utils/tests/tests.rs +++ b/pallets/admin-utils/tests/tests.rs @@ -949,22 +949,22 @@ fn test_sudo_set_tx_rate_limit() { } #[test] -fn test_sudo_set_tx_rate_limit_delegate_take() { +fn test_sudo_set_tx_delegate_take_rate_limit() { new_test_ext().execute_with(|| { let to_be_set: u64 = 10; - let init_value: u64 = SubtensorModule::get_tx_rate_limit_delegate_take(); + let init_value: u64 = SubtensorModule::get_tx_delegate_take_rate_limit(); assert_eq!( - AdminUtils::sudo_set_tx_rate_limit_delegate_take( + AdminUtils::sudo_set_tx_delegate_take_rate_limit( <::RuntimeOrigin>::signed(U256::from(1)), to_be_set ), Err(DispatchError::BadOrigin.into()) ); - assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), init_value); - assert_ok!(AdminUtils::sudo_set_tx_rate_limit_delegate_take( + assert_eq!(SubtensorModule::get_tx_delegate_take_rate_limit(), init_value); + assert_ok!(AdminUtils::sudo_set_tx_delegate_take_rate_limit( <::RuntimeOrigin>::root(), to_be_set )); - assert_eq!(SubtensorModule::get_tx_rate_limit_delegate_take(), to_be_set); + assert_eq!(SubtensorModule::get_tx_delegate_take_rate_limit(), to_be_set); }); } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index ff77efd81a..6769848bcb 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -165,7 +165,7 @@ pub mod pallet { #[pallet::constant] // Initial transaction rate limit. type InitialTxRateLimit: Get; #[pallet::constant] // Initial delegate take transaction rate limit. - type InitialTxRateLimitDelegateTake: Get; + type InitialTxDelegateTakeRateLimit: Get; #[pallet::constant] // Initial percentage of total stake required to join senate. type InitialSenateRequiredStakePercentage: Get; #[pallet::constant] // Initial adjustment alpha on burn and pow. @@ -613,8 +613,8 @@ pub mod pallet { T::InitialTxRateLimit::get() } #[pallet::type_value] - pub fn DefaultTxRateLimitDelegateTake() -> u64 { - T::InitialTxRateLimitDelegateTake::get() + pub fn DefaultTxDelegateTakeRateLimit() -> u64 { + T::InitialTxDelegateTakeRateLimit::get() } #[pallet::type_value] pub fn DefaultLastTxBlock() -> u64 { @@ -624,7 +624,7 @@ pub mod pallet { #[pallet::storage] // --- ITEM ( tx_rate_limit ) pub(super) type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] // --- ITEM ( tx_rate_limit ) - pub(super) type TxRateLimitDelegateTake = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; + pub(super) type TxDelegateTakeRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] // --- MAP ( key ) --> last_block pub(super) type LastTxBlock = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; @@ -939,7 +939,7 @@ pub mod pallet { MaxBurnSet(u16, u64), // --- Event created when setting max burn on a network. MinBurnSet(u16, u64), // --- Event created when setting min burn on a network. TxRateLimitSet(u64), // --- Event created when setting the transaction rate limit. - TxRateLimitDelegateTakeSet(u64), // --- Event created when setting the transaction rate limit. + TxDelegateTakeRateLimitSet(u64), // --- Event created when setting the delegate take transaction rate limit. Sudid(DispatchResult), // --- Event created when a sudo call is done. RegistrationAllowed(u16, bool), // --- Event created when registration is allowed/disallowed for a subnet. PowRegistrationAllowed(u16, bool), // --- Event created when POW registration is allowed/disallowed for a subnet. diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index 700f882c01..a820061251 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -43,7 +43,7 @@ impl Pallet { hotkey: T::AccountId, take: u16, ) -> dispatch::DispatchResult { - // --- 1. We check the coldkey signuture. + // --- 1. We check the coldkey signature. let coldkey = ensure_signed(origin)?; log::info!( "do_become_delegate( origin:{:?} hotkey:{:?}, take:{:?} )", @@ -56,26 +56,36 @@ impl Pallet { // --- 3. Ensure that the coldkey is the owner. Self::do_take_checks(&coldkey, &hotkey)?; - // --- 4. Ensure we are not already a delegate (dont allow changing delegate take here.) + // --- 4. Ensure take is within the 0 ..= InitialDefaultTake (18%) range + let max_take = T::InitialDefaultTake::get(); + ensure!( + take <= max_take, + Error::::InvalidTake + ); + + // --- 5. Ensure we are not already a delegate (dont allow changing delegate take here.) ensure!( !Self::hotkey_is_delegate(&hotkey), Error::::AlreadyDelegate ); - // --- 5. Ensure we don't exceed tx rate limit + // --- 6. Ensure we don't exceed tx rate limit let block: u64 = Self::get_current_block_as_u64(); ensure!( !Self::exceeds_tx_rate_limit(Self::get_last_tx_block(&coldkey), block), Error::::TxRateLimitExceeded ); - // --- 6. Delegate the key. + // --- 7. Delegate the key. Self::delegate_hotkey(&hotkey, take); // Set last block for rate limiting Self::set_last_tx_block(&coldkey, block); - // --- 7. Emit the staking event. + // Also, set last block for take increase rate limiting + Self::set_last_tx_block_delegate_take(&coldkey, block); + + // --- 8. Emit the staking event. log::info!( "DelegateAdded( coldkey:{:?}, hotkey:{:?}, take:{:?} )", coldkey, @@ -84,7 +94,7 @@ impl Pallet { ); Self::deposit_event(Event::DelegateAdded(coldkey, hotkey, take)); - // --- 8. Ok and return. + // --- 9. Ok and return. Ok(()) } @@ -111,9 +121,6 @@ impl Pallet { // * 'NonAssociatedColdKey': // - The hotkey we are delegating is not owned by the calling coldket. // - // * 'TxRateLimitExceeded': - // - Thrown if key has hit transaction rate limit - // pub fn do_decrease_take( origin: T::RuntimeOrigin, hotkey: T::AccountId, diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index 622c68ef5f..1bfc95c35f 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -380,12 +380,12 @@ impl Pallet { TxRateLimit::::put(tx_rate_limit); Self::deposit_event(Event::TxRateLimitSet(tx_rate_limit)); } - pub fn get_tx_rate_limit_delegate_take() -> u64 { - TxRateLimitDelegateTake::::get() + pub fn get_tx_delegate_take_rate_limit() -> u64 { + TxDelegateTakeRateLimit::::get() } - pub fn set_tx_rate_limit_delegate_take(tx_rate_limit: u64) { - TxRateLimitDelegateTake::::put(tx_rate_limit); - Self::deposit_event(Event::TxRateLimitDelegateTakeSet(tx_rate_limit)); + pub fn set_tx_delegate_take_rate_limit(tx_rate_limit: u64) { + TxDelegateTakeRateLimit::::put(tx_rate_limit); + Self::deposit_event(Event::TxDelegateTakeRateLimitSet(tx_rate_limit)); } pub fn get_serving_rate_limit(netuid: u16) -> u64 { diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index fc18a7c5e3..2a9639ddf3 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -124,11 +124,11 @@ parameter_types! { pub const InitialBondsMovingAverage: u64 = 900_000; pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; - pub const InitialDefaultTake: u16 = 11_796; // 18% honest number. + pub const InitialDefaultTake: u16 = 32_767; // 50% for tests (18% honest number is used in production (see runtime)) pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing - pub const InitialTxRateLimitDelegateTake: u64 = 0; // Disable delegate take rate limit for testing + pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable delegate take rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -341,7 +341,7 @@ impl pallet_subtensor::Config for Test { type InitialMinDifficulty = InitialMinDifficulty; type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; - type InitialTxRateLimitDelegateTake = InitialTxRateLimitDelegateTake; + type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 4d12c7295b..af86ecc278 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2732,6 +2732,15 @@ fn test_faucet_ok() { }); } +// Verify that InitialDefaultTake is between 50% and u16::MAX-1, this is important for other tests +#[test] +fn test_delegate_take_limit() { + new_test_ext().execute_with(|| { + assert_eq!(InitialDefaultTake::get() >= u16::MAX/2, true); + assert_eq!(InitialDefaultTake::get() <= u16::MAX-1, true); + }); +} + // Verify delegate take can be decreased #[test] fn test_delegate_take_can_be_decreased() { @@ -2748,7 +2757,7 @@ fn test_delegate_take_can_be_decreased() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 50% take + // Coldkey / hotkey 0 become delegates with 5% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, @@ -2908,6 +2917,39 @@ fn test_delegate_take_can_be_increased_to_limit() { }); } +// Verify delegate take can not be set above InitialDefaultTake +#[test] +fn test_delegate_take_can_not_be_set_beyond_limit() { + new_test_ext().execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + let before = SubtensorModule::get_hotkey_take(&hotkey0); + + // Coldkey / hotkey 0 attempt to become delegates with take above maximum + // (Disable this check if InitialDefaultTake is u16::MAX) + if InitialDefaultTake::get() != u16::MAX { + assert_eq!( + SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + InitialDefaultTake::get()+1 + ), + Err(Error::::InvalidTake.into()) + ); + } + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), before); + }); +} + // Verify delegate take can not be increased above InitialDefaultTake (18%) #[test] fn test_delegate_take_can_not_be_increased_beyond_limit() { diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index bc496518dd..00134cedf1 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -656,7 +656,7 @@ parameter_types! { pub const SubtensorInitialMinBurn: u64 = 1_000_000_000; // 1 tao pub const SubtensorInitialMaxBurn: u64 = 100_000_000_000; // 100 tao pub const SubtensorInitialTxRateLimit: u64 = 1000; - pub const SubtensorInitialTxRateLimitDelegateTake: u64 = 216000; // 30 days at 12 seconds per block + pub const SubtensorInitialTxDelegateTakeRateLimit: u64 = 216000; // 30 days at 12 seconds per block pub const SubtensorInitialRAORecycledForRegistration: u64 = 0; // 0 rao pub const SubtensorInitialSenateRequiredStakePercentage: u64 = 1; // 1 percent of total stake pub const SubtensorInitialNetworkImmunity: u64 = 7 * 7200; @@ -706,7 +706,7 @@ impl pallet_subtensor::Config for Runtime { type InitialMaxBurn = SubtensorInitialMaxBurn; type InitialMinBurn = SubtensorInitialMinBurn; type InitialTxRateLimit = SubtensorInitialTxRateLimit; - type InitialTxRateLimitDelegateTake = SubtensorInitialTxRateLimitDelegateTake; + type InitialTxDelegateTakeRateLimit = SubtensorInitialTxDelegateTakeRateLimit; type InitialRAORecycledForRegistration = SubtensorInitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = SubtensorInitialSenateRequiredStakePercentage; type InitialNetworkImmunityPeriod = SubtensorInitialNetworkImmunity; @@ -745,8 +745,8 @@ impl SubtensorModule::set_tx_rate_limit(rate_limit); } - fn set_tx_rate_limit_delegate_take(rate_limit: u64) { - SubtensorModule::set_tx_rate_limit_delegate_take(rate_limit); + fn set_tx_delegate_take_rate_limit(rate_limit: u64) { + SubtensorModule::set_tx_delegate_take_rate_limit(rate_limit); } fn set_serving_rate_limit(netuid: u16, rate_limit: u64) { From 919066d90ed57466ebb82b2c263d130e1c3176c3 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 11 Apr 2024 15:18:50 -0400 Subject: [PATCH 19/24] Cleanup after rebase to stao branch --- pallets/admin-utils/src/lib.rs | 2 +- pallets/subtensor/src/lib.rs | 6 ++++-- pallets/subtensor/src/utils.rs | 22 ---------------------- pallets/subtensor/tests/mock.rs | 3 --- pallets/subtensor/tests/staking.rs | 20 ++++++++++---------- pallets/subtensor/tests/weights.rs | 1 - 6 files changed, 15 insertions(+), 39 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index cac155ed49..d7d708e7d2 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -102,7 +102,7 @@ pub mod pallet { Ok(()) } - #[pallet::call_index(43)] + #[pallet::call_index(45)] #[pallet::weight((0, DispatchClass::Operational, Pays::No))] pub fn sudo_set_tx_delegate_take_rate_limit(origin: OriginFor, tx_rate_limit: u64) -> DispatchResult { ensure_root(origin)?; diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 6769848bcb..7c8f744743 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "512"] +#![allow(non_snake_case, non_camel_case_types)] // Edit this file to define custom logic or remove it if it is not needed. // Learn more about FRAME and the core library of Substrate FRAME pallets: // @@ -1026,6 +1027,7 @@ pub mod pallet { StakeTooLowForRoot, // --- Thrown when a hotkey attempts to join the root subnet with too little stake AllNetworksInImmunity, // --- Thrown when all subnets are in the immunity period NotEnoughBalance, + InvalidTake, // --- Thrown when delegate take is being set out of bounds } // ================== @@ -1395,7 +1397,7 @@ pub mod pallet { // * 'InvalidTransaction': // - The delegate is setting a take which is not lower than the previous. // - #[pallet::call_index(63)] + #[pallet::call_index(65)] #[pallet::weight((0, DispatchClass::Normal, Pays::No))] pub fn decrease_take(origin: OriginFor, hotkey: T::AccountId, take: u16) -> DispatchResult { Self::do_decrease_take(origin, hotkey, take) @@ -1431,7 +1433,7 @@ pub mod pallet { // * 'InvalidTransaction': // - The delegate is setting a take which is not lower than the previous. // - #[pallet::call_index(64)] + #[pallet::call_index(66)] #[pallet::weight((0, DispatchClass::Normal, Pays::No))] pub fn increase_take(origin: OriginFor, hotkey: T::AccountId, take: u16) -> DispatchResult { Self::do_decrease_take(origin, hotkey, take) diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index a97071e85b..1bfc95c35f 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -319,28 +319,6 @@ impl Pallet { Ok(()) } - // ======================== - // ===== Take checks ====== - // ======================== - pub fn do_take_checks( - coldkey: &T::AccountId, - hotkey: &T::AccountId, - ) -> Result<(), Error> { - // Ensure we are delegating a known key. - ensure!( - Self::hotkey_account_exists(hotkey), - Error::::NotRegistered - ); - - // Ensure that the coldkey is the owner. - ensure!( - Self::coldkey_owns_hotkey(coldkey, hotkey), - Error::::NonAssociatedColdKey - ); - - Ok(()) - } - // ======================== // ==== Rate Limiting ===== // ======================== diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 2f2a5bc42e..fb6ff9aad9 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -125,12 +125,10 @@ parameter_types! { pub const InitialStakePruningMin: u16 = 0; pub const InitialFoundationDistribution: u64 = 0; pub const InitialDefaultTake: u16 = 32_767; // 50% for tests (18% honest number is used in production (see runtime)) - pub const InitialDefaultTake: u16 = 32_767; // 50% for tests (18% honest number is used in production (see runtime)) pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable delegate take rate limit for testing - pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable delegate take rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; @@ -344,7 +342,6 @@ impl pallet_subtensor::Config for Test { type InitialServingRateLimit = InitialServingRateLimit; type InitialTxRateLimit = InitialTxRateLimit; type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; - type InitialTxDelegateTakeRateLimit = InitialTxDelegateTakeRateLimit; type InitialBurn = InitialBurn; type InitialMaxBurn = InitialMaxBurn; type InitialMinBurn = InitialMinBurn; diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index af86ecc278..8972cc3ef5 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2735,7 +2735,7 @@ fn test_faucet_ok() { // Verify that InitialDefaultTake is between 50% and u16::MAX-1, this is important for other tests #[test] fn test_delegate_take_limit() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { assert_eq!(InitialDefaultTake::get() >= u16::MAX/2, true); assert_eq!(InitialDefaultTake::get() <= u16::MAX-1, true); }); @@ -2744,7 +2744,7 @@ fn test_delegate_take_limit() { // Verify delegate take can be decreased #[test] fn test_delegate_take_can_be_decreased() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { // Make account let hotkey0 = U256::from(1); let coldkey0 = U256::from(3); @@ -2778,7 +2778,7 @@ fn test_delegate_take_can_be_decreased() { // Verify delegate take can not be increased with do_decrease_take #[test] fn test_delegate_take_can_not_be_increased_with_decrease_take() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { // Make account let hotkey0 = U256::from(1); let coldkey0 = U256::from(3); @@ -2815,7 +2815,7 @@ fn test_delegate_take_can_not_be_increased_with_decrease_take() { // Verify delegate take can be increased #[test] fn test_delegate_take_can_be_increased() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { // Make account let hotkey0 = U256::from(1); let coldkey0 = U256::from(3); @@ -2849,7 +2849,7 @@ fn test_delegate_take_can_be_increased() { // Verify delegate take can not be decreased with increase_take #[test] fn test_delegate_take_can_not_be_decreased_with_increase_take() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { // Make account let hotkey0 = U256::from(1); let coldkey0 = U256::from(3); @@ -2886,7 +2886,7 @@ fn test_delegate_take_can_not_be_decreased_with_increase_take() { // Verify delegate take can be increased up to InitialDefaultTake (18%) #[test] fn test_delegate_take_can_be_increased_to_limit() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { // Make account let hotkey0 = U256::from(1); let coldkey0 = U256::from(3); @@ -2920,7 +2920,7 @@ fn test_delegate_take_can_be_increased_to_limit() { // Verify delegate take can not be set above InitialDefaultTake #[test] fn test_delegate_take_can_not_be_set_beyond_limit() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { // Make account let hotkey0 = U256::from(1); let coldkey0 = U256::from(3); @@ -2953,7 +2953,7 @@ fn test_delegate_take_can_not_be_set_beyond_limit() { // Verify delegate take can not be increased above InitialDefaultTake (18%) #[test] fn test_delegate_take_can_not_be_increased_beyond_limit() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { // Make account let hotkey0 = U256::from(1); let coldkey0 = U256::from(3); @@ -2993,7 +2993,7 @@ fn test_delegate_take_can_not_be_increased_beyond_limit() { // Verify delegate take affects emission distribution #[test] fn test_delegate_take_affects_distribution() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { let netuid = 1; // Make two accounts. let hotkey0 = U256::from(1); @@ -3069,7 +3069,7 @@ fn test_delegate_take_affects_distribution() { // Verify changing delegate take also changes emission distribution #[test] fn test_changing_delegate_take_changes_distribution() { - new_test_ext().execute_with(|| { + new_test_ext(1).execute_with(|| { let netuid = 1; // Make two accounts. let hotkey0 = U256::from(1); diff --git a/pallets/subtensor/tests/weights.rs b/pallets/subtensor/tests/weights.rs index 0b29af8909..89a51a13d8 100644 --- a/pallets/subtensor/tests/weights.rs +++ b/pallets/subtensor/tests/weights.rs @@ -3,7 +3,6 @@ use frame_support::{ assert_ok, dispatch::{DispatchClass, GetDispatchInfo, Pays}, }; -use frame_system::Config; use mock::*; use pallet_subtensor::Error; use sp_core::U256; From 78466fbba98534942267a5b4e82d5a3a02e9968a Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 11 Apr 2024 18:00:25 -0400 Subject: [PATCH 20/24] Fix tests after rebasing to stao --- pallets/admin-utils/tests/tests.rs | 44 ++++++++++++------------- pallets/subtensor/tests/mock.rs | 2 +- pallets/subtensor/tests/staking.rs | 53 ++++++++++++++++-------------- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/pallets/admin-utils/tests/tests.rs b/pallets/admin-utils/tests/tests.rs index 1976942eb8..c30f94d0b7 100644 --- a/pallets/admin-utils/tests/tests.rs +++ b/pallets/admin-utils/tests/tests.rs @@ -65,7 +65,7 @@ fn test_sudo_set_min_difficulty() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_min_difficulty(netuid); assert_eq!( AdminUtils::sudo_set_min_difficulty( @@ -98,7 +98,7 @@ fn test_sudo_set_max_difficulty() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_max_difficulty(netuid); assert_eq!( AdminUtils::sudo_set_max_difficulty( @@ -131,7 +131,7 @@ fn test_sudo_set_weights_version_key() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_weights_version_key(netuid); assert_eq!( AdminUtils::sudo_set_weights_version_key( @@ -164,7 +164,7 @@ fn test_sudo_set_weights_set_rate_limit() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_weights_set_rate_limit(netuid); assert_eq!( AdminUtils::sudo_set_weights_set_rate_limit( @@ -203,7 +203,7 @@ fn test_sudo_set_adjustment_interval() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_adjustment_interval(netuid); assert_eq!( AdminUtils::sudo_set_adjustment_interval( @@ -236,7 +236,7 @@ fn test_sudo_set_adjustment_alpha() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_adjustment_alpha(netuid); assert_eq!( AdminUtils::sudo_set_adjustment_alpha( @@ -290,7 +290,7 @@ fn test_sudo_set_max_weight_limit() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_max_weight_limit(netuid); assert_eq!( AdminUtils::sudo_set_max_weight_limit( @@ -342,7 +342,7 @@ fn test_sudo_set_immunity_period() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_immunity_period(netuid); assert_eq!( AdminUtils::sudo_set_immunity_period( @@ -375,7 +375,7 @@ fn test_sudo_set_min_allowed_weights() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_min_allowed_weights(netuid); assert_eq!( AdminUtils::sudo_set_min_allowed_weights( @@ -408,7 +408,7 @@ fn test_sudo_set_max_allowed_uids() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_max_allowed_uids(netuid); assert_eq!( AdminUtils::sudo_set_max_allowed_uids( @@ -441,7 +441,7 @@ fn test_sudo_set_and_decrease_max_allowed_uids() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_max_allowed_uids(netuid); assert_eq!( AdminUtils::sudo_set_max_allowed_uids( @@ -478,7 +478,7 @@ fn test_sudo_set_kappa() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_kappa(netuid); assert_eq!( AdminUtils::sudo_set_kappa( @@ -511,7 +511,7 @@ fn test_sudo_set_rho() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_rho(netuid); assert_eq!( AdminUtils::sudo_set_rho( @@ -544,7 +544,7 @@ fn test_sudo_set_activity_cutoff() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_activity_cutoff(netuid); assert_eq!( AdminUtils::sudo_set_activity_cutoff( @@ -577,7 +577,7 @@ fn test_sudo_set_target_registrations_per_interval() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_target_registrations_per_interval(netuid); assert_eq!( AdminUtils::sudo_set_target_registrations_per_interval( @@ -616,7 +616,7 @@ fn test_sudo_set_difficulty() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_difficulty_as_u64(netuid); assert_eq!( AdminUtils::sudo_set_difficulty( @@ -649,7 +649,7 @@ fn test_sudo_set_max_allowed_validators() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_max_allowed_validators(netuid); assert_eq!( AdminUtils::sudo_set_max_allowed_validators( @@ -751,7 +751,7 @@ fn test_sudo_set_bonds_moving_average() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_bonds_moving_average(netuid); assert_eq!( AdminUtils::sudo_set_bonds_moving_average( @@ -787,7 +787,7 @@ fn test_sudo_set_rao_recycled() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_rao_recycled(netuid); // Need to run from genesis block @@ -852,7 +852,7 @@ fn test_sudo_set_subnet_limit() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u16 = 10; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u16 = SubtensorModule::get_max_subnets(); assert_eq!( @@ -876,7 +876,7 @@ fn test_sudo_set_network_lock_reduction_interval() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: u64 = 7200; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: u64 = SubtensorModule::get_lock_reduction_interval(); assert_eq!( @@ -900,7 +900,7 @@ fn test_sudo_set_network_pow_registration_allowed() { new_test_ext().execute_with(|| { let netuid: u16 = 1; let to_be_set: bool = true; - add_network(netuid, 10); + add_network(netuid, 10, 0); let init_value: bool = SubtensorModule::get_network_pow_registration_allowed(netuid); assert_eq!( diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index fb6ff9aad9..f3ce5379d0 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -155,7 +155,7 @@ parameter_types! { pub const InitialNetworkLockReductionInterval: u64 = 2; // 2 blocks. pub const InitialSubnetLimit: u16 = 10; // Max 10 subnets. pub const InitialNetworkRateLimit: u64 = 0; - pub const InitialTargetStakesPerInterval: u16 = 1; + pub const InitialTargetStakesPerInterval: u16 = 2; } // Configure collective pallet for council diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index 8972cc3ef5..ac1563acde 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -1828,11 +1828,12 @@ fn test_full_with_delegating() { step_block(3); + // 100% take is not a valid business case, changing the rest of this test to 50% assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey3), hotkey3, - u16::MAX - )); // Full take. + u16::MAX / 2 + )); // 50% take. assert_ok!(SubtensorModule::add_subnet_stake( <::RuntimeOrigin>::signed(coldkey0), hotkey3, @@ -1872,20 +1873,20 @@ fn test_full_with_delegating() { SubtensorModule::emit_inflation_through_hotkey_account(&hotkey3, netuid, 0, 1000); assert_eq!( SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey3, netuid), - 1000 - ); + 1125 + ); // 1000 + 50% * 1000 * 1000/4000 = 1125 assert_eq!( SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey3, netuid), - 1000 - ); + 1125 + ); // 1000 + 50% * 1000 * 1000/4000 = 1125 assert_eq!( SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey2, &hotkey3, netuid), - 1000 - ); + 1125 + ); // 1000 + 50% * 1000 * 1000/4000 = 1125 assert_eq!( SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey3, &hotkey3, netuid), - 2000 - ); + 1625 + ); // 1000 + 125 * 3 + 1000 * 1000/4000 = 1625 assert_eq!(SubtensorModule::get_total_stake(), 11_500); // before + 1_000 = 10_500 + 1_000 = 11_500 }); } @@ -3015,13 +3016,14 @@ fn test_delegate_take_affects_distribution() { register_ok_neuron(netuid, hotkey1, coldkey1, 987907); // Stake 100 from coldkey/hotkey 0 - assert_ok!(SubtensorModule::add_stake( + assert_ok!(SubtensorModule::add_subnet_stake( <::RuntimeOrigin>::signed(coldkey0), hotkey0, + netuid, 100 )); assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0, netuid), 100 ); @@ -3034,17 +3036,18 @@ fn test_delegate_take_affects_distribution() { // Hotkey 1 adds 100 delegated stake to coldkey/hotkey 0 assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0, netuid), 0 ); assert_eq!(SubtensorModule::get_total_stake(), 100); - assert_ok!(SubtensorModule::add_stake( + assert_ok!(SubtensorModule::add_subnet_stake( <::RuntimeOrigin>::signed(coldkey1), hotkey0, + netuid, 100 )); assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0, netuid), 100 ); assert_eq!(SubtensorModule::get_total_stake(), 200); @@ -3058,9 +3061,9 @@ fn test_delegate_take_affects_distribution() { // Total initial stake is 200 // Delegate's initial stake is 100, which is 50% of total stake // => Delegate will receive 50% of emission (200) + 50% take (100) of nominator reward (200) - SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, 0, 400); + SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, netuid, 0, 400); assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0, netuid), 400 ); // 100 + 50% * 400 + 50% * 200 = 400 }); @@ -3091,13 +3094,14 @@ fn test_changing_delegate_take_changes_distribution() { register_ok_neuron(netuid, hotkey1, coldkey1, 987907); // Stake 100 from coldkey/hotkey 0 - assert_ok!(SubtensorModule::add_stake( + assert_ok!(SubtensorModule::add_subnet_stake( <::RuntimeOrigin>::signed(coldkey0), hotkey0, + netuid, 100 )); assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0, netuid), 100 ); @@ -3110,17 +3114,18 @@ fn test_changing_delegate_take_changes_distribution() { // Hotkey 1 adds 100 delegated stake to coldkey/hotkey 0 assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0, netuid), 0 ); assert_eq!(SubtensorModule::get_total_stake(), 100); - assert_ok!(SubtensorModule::add_stake( + assert_ok!(SubtensorModule::add_subnet_stake( <::RuntimeOrigin>::signed(coldkey1), hotkey0, + netuid, 100 )); assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey1, &hotkey0, netuid), 100 ); assert_eq!(SubtensorModule::get_total_stake(), 200); @@ -3141,9 +3146,9 @@ fn test_changing_delegate_take_changes_distribution() { // Total initial stake is 200 // Delegate's initial stake is 100, which is 50% of total stake // => Delegate will receive 50% of emission (200) + 10% take (20) of nominator reward (200) - SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, 0, 400); + SubtensorModule::emit_inflation_through_hotkey_account(&hotkey0, netuid, 0, 400); assert_eq!( - SubtensorModule::get_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0), + SubtensorModule::get_subnet_stake_for_coldkey_and_hotkey(&coldkey0, &hotkey0, netuid), 320 ); // 100 + 50% * 400 + 10% * 200 = 320 }); From fadce26743ba33d13f30249894bea3dd560722b3 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Thu, 11 Apr 2024 19:25:03 -0400 Subject: [PATCH 21/24] Fix warnings --- pallets/registry/src/tests.rs | 3 --- pallets/registry/src/types.rs | 1 - pallets/subtensor/src/lib.rs | 1 - pallets/subtensor/src/registration.rs | 2 +- pallets/subtensor/tests/migration.rs | 2 -- pallets/subtensor/tests/mock.rs | 2 +- pallets/subtensor/tests/root.rs | 18 +++++++++--------- pallets/subtensor/tests/staking.rs | 3 --- 8 files changed, 11 insertions(+), 21 deletions(-) diff --git a/pallets/registry/src/tests.rs b/pallets/registry/src/tests.rs index 36161f82e6..d233fe0783 100644 --- a/pallets/registry/src/tests.rs +++ b/pallets/registry/src/tests.rs @@ -1,4 +1 @@ -use crate::{Error, Event}; -use frame_support::{assert_noop, assert_ok}; - // Testing diff --git a/pallets/registry/src/types.rs b/pallets/registry/src/types.rs index 73e3ee1dc7..5db1371ae6 100644 --- a/pallets/registry/src/types.rs +++ b/pallets/registry/src/types.rs @@ -15,7 +15,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::*; use codec::{Decode, Encode, MaxEncodedLen}; use enumflags2::{bitflags, BitFlags}; use frame_support::{ diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 7c8f744743..4997966b68 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1537,7 +1537,6 @@ pub mod pallet { pub fn remove_stake( origin: OriginFor, hotkey: T::AccountId, - netuid: u16, amount_unstaked: u64, ) -> DispatchResult { Self::do_remove_stake(origin, hotkey, Self::get_root_netuid(), amount_unstaked) diff --git a/pallets/subtensor/src/registration.rs b/pallets/subtensor/src/registration.rs index 79cd3c374a..29c016ba7b 100644 --- a/pallets/subtensor/src/registration.rs +++ b/pallets/subtensor/src/registration.rs @@ -1,5 +1,5 @@ use super::*; -use frame_support::pallet_prelude::{DispatchResult, DispatchResultWithPostInfo}; +use frame_support::pallet_prelude::DispatchResultWithPostInfo; use frame_support::storage::IterableStorageDoubleMap; use sp_core::{Get, H256, U256}; use sp_io::hashing::{keccak_256, sha2_256}; diff --git a/pallets/subtensor/tests/migration.rs b/pallets/subtensor/tests/migration.rs index d7ff926a7f..e87c0e20b3 100644 --- a/pallets/subtensor/tests/migration.rs +++ b/pallets/subtensor/tests/migration.rs @@ -151,8 +151,6 @@ fn test_total_issuance_global() { // Test the migration's effect on total issuance after adding balance to a coldkey account. let account_balance: u64 = 20000; - let hotkey_account_id_1 = U256::from(1); // Define a hotkey account ID for further operations. - let coldkey_account_id_1 = U256::from(1); // Define a coldkey account ID for further operations. assert_eq!(SubtensorModule::get_total_issuance(), lockcost); // Ensure the total issuance starts at 0 before the migration. SubtensorModule::add_balance_to_coldkey_account(&coldkey, account_balance); // Add a balance of 20000 to the coldkey account. pallet_subtensor::migration::migration5_total_issuance::(true); // Execute the migration to update total issuance. diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index f3ce5379d0..528009f259 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -1,5 +1,5 @@ #![allow(non_snake_case, non_camel_case_types)] -use frame_support::traits::{Hash, StorageMapShim}; +use frame_support::traits::Hash; use frame_support::{ assert_ok, parameter_types, traits::{Everything, Hooks}, diff --git a/pallets/subtensor/tests/root.rs b/pallets/subtensor/tests/root.rs index 80d902c60f..14e65a32e1 100644 --- a/pallets/subtensor/tests/root.rs +++ b/pallets/subtensor/tests/root.rs @@ -785,13 +785,13 @@ fn test_issance_bounds() { // Simulate 100 halvings convergence to 21M. Note that the total issuance never reaches 21M because of rounding errors. // We converge to 20_999_999_989_500_000 (< 1 TAO away). let n_halvings: usize = 100; - let mut total_issuance: u64 = 0; - for i in 0..n_halvings { - let block_emission_10_500_000x: u64 = - SubtensorModule::get_block_emission_for_issuance(total_issuance).unwrap() - * 10_500_000; - total_issuance += block_emission_10_500_000x; - } + let total_issuance = (0..n_halvings) + .into_iter() + .fold(0, |total, _| { + let block_emission_10_500_000x: u64 = + SubtensorModule::get_block_emission_for_issuance(total).unwrap() * 10_500_000; + total + block_emission_10_500_000x + }); assert_eq!(total_issuance, 20_999_999_989_500_000); }) } @@ -868,7 +868,7 @@ fn test_get_emission_across_entire_issuance_range() { let total_supply: u64 = pallet_subtensor::TotalSupply::::get(); let original_emission: u64 = pallet_subtensor::DefaultBlockEmission::::get(); let halving_issuance: u64 = total_supply / 2; - let mut step: usize = original_emission as usize; + let step: usize = original_emission as usize; for issuance in (0..=total_supply).step_by(step) { SubtensorModule::set_total_issuance(issuance); @@ -889,7 +889,7 @@ fn test_get_emission_across_entire_issuance_range() { "Issuance: {}", issuance_f64 ); - step = expected_emission as usize; + assert_eq!(expected_emission <= usize::MAX as u64, true); } }); } diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index ac1563acde..cb150996f9 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -494,7 +494,6 @@ fn test_remove_stake_under_limit() { let call = pallet_subtensor::Call::remove_stake { hotkey: hotkey_account_id, amount_unstaked: 1, - netuid, }; let info: DispatchInfo = DispatchInfoOf::<::RuntimeCall>::default(); @@ -557,7 +556,6 @@ fn test_remove_stake_rate_limit_exceeded() { let call = pallet_subtensor::Call::remove_stake { hotkey: hotkey_account_id, amount_unstaked: 1, - netuid, }; let info: DispatchInfo = DispatchInfoOf::<::RuntimeCall>::default(); @@ -3293,7 +3291,6 @@ fn test_subnet_stake_calculation() { assert_ok!(SubtensorModule::remove_stake( <::RuntimeOrigin>::signed(coldkey), hotkey, - netuid, ROOT_STAKE_PER_NEURON )); From e68533865e2d2051c86818c066e682c1987d2c4a Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 12 Apr 2024 09:48:02 -0400 Subject: [PATCH 22/24] Fix warning on Vec import --- pallets/subtensor/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/types.rs b/pallets/subtensor/src/types.rs index 4b1a7b8e2d..beea5f416c 100644 --- a/pallets/subtensor/src/types.rs +++ b/pallets/subtensor/src/types.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; +#[allow(unused_imports)] use alloc::vec::Vec; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; From 840d8d3579ee5d4d6c3df094a98f20fb4e1791ec Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 12 Apr 2024 11:04:08 -0400 Subject: [PATCH 23/24] Add rate limit test, fix rate limiting --- pallets/subtensor/src/staking.rs | 2 +- pallets/subtensor/src/utils.rs | 8 ++++++ pallets/subtensor/tests/mock.rs | 2 +- pallets/subtensor/tests/staking.rs | 39 +++++++++++++++++++++++++++++- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/staking.rs b/pallets/subtensor/src/staking.rs index a820061251..474f193d6e 100644 --- a/pallets/subtensor/src/staking.rs +++ b/pallets/subtensor/src/staking.rs @@ -223,7 +223,7 @@ impl Pallet { // --- 5. Enforce the rate limit (independently on do_add_stake rate limits) let block: u64 = Self::get_current_block_as_u64(); ensure!( - !Self::exceeds_tx_rate_limit(Self::get_last_tx_block_delegate_take(&coldkey), block), + !Self::exceeds_tx_delegate_take_rate_limit(Self::get_last_tx_block_delegate_take(&coldkey), block), Error::::TxRateLimitExceeded ); diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index 1bfc95c35f..c51d12d24e 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -342,6 +342,14 @@ impl Pallet { return current_block - prev_tx_block <= rate_limit; } + pub fn exceeds_tx_delegate_take_rate_limit(prev_tx_block: u64, current_block: u64) -> bool { + let rate_limit: u64 = Self::get_tx_delegate_take_rate_limit(); + if rate_limit == 0 || prev_tx_block == 0 { + return false; + } + + return current_block - prev_tx_block <= rate_limit; + } // ======================== // === Token Management === diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index 528009f259..0c2c32f498 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -128,7 +128,7 @@ parameter_types! { pub const InitialWeightsVersionKey: u16 = 0; pub const InitialServingRateLimit: u64 = 0; // No limit. pub const InitialTxRateLimit: u64 = 0; // Disable rate limit for testing - pub const InitialTxDelegateTakeRateLimit: u64 = 0; // Disable delegate take rate limit for testing + pub const InitialTxDelegateTakeRateLimit: u64 = 1; // 1 block take rate limit for testing pub const InitialBurn: u64 = 0; pub const InitialMinBurn: u64 = 0; pub const InitialMaxBurn: u64 = 1_000_000_000; diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index a96807065c..c4960c9815 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2827,7 +2827,7 @@ fn test_delegate_take_can_be_increased() { add_network(netuid, 0, 0); register_ok_neuron(netuid, hotkey0, coldkey0, 124124); - // Coldkey / hotkey 0 become delegates with 50% take + // Coldkey / hotkey 0 become delegates with 5% take assert_ok!(SubtensorModule::do_become_delegate( <::RuntimeOrigin>::signed(coldkey0), hotkey0, @@ -3519,3 +3519,40 @@ fn test_substake_increases_stake_of_only_targeted_neuron() { } }); } + +// Test rate-limiting on increase_take +#[test] +fn test_rate_limits_enforced_on_increase_take() { + new_test_ext(1).execute_with(|| { + // Make account + let hotkey0 = U256::from(1); + let coldkey0 = U256::from(3); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account(&coldkey0, 100000); + + // Register the neuron to a new network + let netuid = 1; + add_network(netuid, 0, 0); + register_ok_neuron(netuid, hotkey0, coldkey0, 124124); + + // Coldkey / hotkey 0 become delegates with 5% take + assert_ok!(SubtensorModule::do_become_delegate( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 20 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + + // Coldkey / hotkey 0 increases take to 10% + assert_eq!( + SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + ), + Err(Error::::TxRateLimitExceeded.into()) + ); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + }); +} From fa00e83f5e488225b924f96a4cd03838c3c2d76b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Fri, 12 Apr 2024 14:19:57 -0400 Subject: [PATCH 24/24] Add tests for delegate take rate limit, fix rate limiting issues --- pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/tests/staking.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d81d886b88..88355d96e4 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -626,7 +626,7 @@ pub mod pallet { #[pallet::storage] // --- ITEM ( tx_rate_limit ) pub(super) type TxRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; #[pallet::storage] // --- ITEM ( tx_rate_limit ) - pub(super) type TxDelegateTakeRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxRateLimit>; + pub(super) type TxDelegateTakeRateLimit = StorageValue<_, u64, ValueQuery, DefaultTxDelegateTakeRateLimit>; #[pallet::storage] // --- MAP ( key ) --> last_block pub(super) type LastTxBlock = StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultLastTxBlock>; diff --git a/pallets/subtensor/tests/staking.rs b/pallets/subtensor/tests/staking.rs index c4960c9815..9dbeedaa5b 100644 --- a/pallets/subtensor/tests/staking.rs +++ b/pallets/subtensor/tests/staking.rs @@ -2835,6 +2835,8 @@ fn test_delegate_take_can_be_increased() { )); assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + // Coldkey / hotkey 0 decreases take to 10% assert_ok!(SubtensorModule::do_increase_take( <::RuntimeOrigin>::signed(coldkey0), @@ -2906,6 +2908,8 @@ fn test_delegate_take_can_be_increased_to_limit() { )); assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + // Coldkey / hotkey 0 tries to increase take to InitialDefaultTake+1 assert_ok!(SubtensorModule::do_increase_take( <::RuntimeOrigin>::signed(coldkey0), @@ -3554,5 +3558,16 @@ fn test_rate_limits_enforced_on_increase_take() { Err(Error::::TxRateLimitExceeded.into()) ); assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 20); + + step_block(1 + InitialTxDelegateTakeRateLimit::get() as u16); + + // Can increase after waiting + assert_ok!(SubtensorModule::do_increase_take( + <::RuntimeOrigin>::signed(coldkey0), + hotkey0, + u16::MAX / 10 + )); + assert_eq!(SubtensorModule::get_hotkey_take(&hotkey0), u16::MAX / 10); + }); }