From 18ad00abd9f7b69533eeb4f0e39d30ec53f35f2e Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 12 Sep 2025 16:42:53 +0800 Subject: [PATCH 01/32] owner set validator cut --- pallets/admin-utils/src/lib.rs | 26 ++++ pallets/admin-utils/src/tests/mod.rs | 136 ++++++++++++++++++ .../subtensor/src/coinbase/run_coinbase.rs | 4 +- pallets/subtensor/src/lib.rs | 5 + pallets/subtensor/src/staking/helpers.rs | 20 +++ 5 files changed, 190 insertions(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 4af202132..1fe143974 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -1898,6 +1898,32 @@ pub mod pallet { ); Ok(()) } + + /// Sets the validator cut for a subnet. + /// Only callable by subnet owner or root. + #[pallet::call_index(78)] + #[pallet::weight(Weight::from_parts(15_000_000, 0) + .saturating_add(::DbWeight::get().reads(1_u64)) + .saturating_add(::DbWeight::get().writes(1_u64)))] + pub fn sudo_set_validator_cut( + origin: OriginFor, + netuid: NetUid, + cut: u16, + ) -> DispatchResult { + let maybe_owner = pallet_subtensor::Pallet::::ensure_sn_owner_or_root_with_limits( + origin, + netuid, + &[TransactionType::OwnerHyperparamUpdate], + )?; + pallet_subtensor::Pallet::::set_validator_cut(netuid, cut); + log::debug!("ValidatorCutSet( netuid: {netuid:?}, cut: {cut:?} ) "); + pallet_subtensor::Pallet::::record_owner_rl( + maybe_owner, + netuid, + &[TransactionType::OwnerHyperparamUpdate], + ); + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 25b1b8960..0f04b4a03 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2269,3 +2269,139 @@ fn test_sudo_set_subsubnet_count() { )); }); } + +#[test] +fn test_get_validator_cut() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); + let expected_cut: u16 = 5000; // 50% cut + + // Set up a network + add_network(netuid, 10); + + // Set a validator cut value + SubtensorModule::set_validator_cut(netuid, expected_cut); + + // Test that we can retrieve the value + let retrieved_cut = SubtensorModule::get_validator_cut(netuid); + assert_eq!(retrieved_cut, expected_cut); + }); +} + +#[test] +fn test_set_validator_cut() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(2); + let initial_cut: u16 = 0; + let new_cut: u16 = 7500; // 75% cut + + // Set up a network + add_network(netuid, 10); + + // Verify initial value + assert_eq!(SubtensorModule::get_validator_cut(netuid), initial_cut); + + // Set new validator cut + SubtensorModule::set_validator_cut(netuid, new_cut); + + // Verify the value was set correctly + assert_eq!(SubtensorModule::get_validator_cut(netuid), new_cut); + }); +} + +#[test] +fn test_sudo_set_validator_cut() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(3); + let to_be_set: u16 = 4200; // 42% cut + + // Set up a network + add_network(netuid, 10); + + let init_value = SubtensorModule::get_validator_cut(netuid); + + // Test that non-authorized origin fails (using a regular signed origin) + assert_eq!( + AdminUtils::sudo_set_validator_cut( + <::RuntimeOrigin>::signed(U256::from(1)), + netuid, + to_be_set + ), + Err(DispatchError::BadOrigin) + ); + // Value should remain unchanged + assert_eq!(SubtensorModule::get_validator_cut(netuid), init_value); + + // Test that subnet owner can set the validator cut successfully + let subnet_owner = U256::from(123); + SubtensorModule::set_subnet_owner(netuid, subnet_owner); + + assert_ok!(AdminUtils::sudo_set_validator_cut( + <::RuntimeOrigin>::signed(subnet_owner), + netuid, + to_be_set + )); + + // Verify the value was set correctly + assert_eq!(SubtensorModule::get_validator_cut(netuid), to_be_set); + }); +} + +#[test] +fn test_sudo_set_validator_cut_root() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(4); + let to_be_set: u16 = 10000; // 100% cut + + // Set up a network + add_network(netuid, 10); + + // Test that root can set the validator cut successfully + assert_ok!(AdminUtils::sudo_set_validator_cut( + <::RuntimeOrigin>::root(), + netuid, + to_be_set + )); + + // Verify the value was set correctly + assert_eq!(SubtensorModule::get_validator_cut(netuid), to_be_set); + }); +} + +#[test] +fn test_sudo_set_validator_cut_invalid_netuid() { + new_test_ext().execute_with(|| { + let invalid_netuid = NetUid::from(999); // Non-existent netuid + let cut_value: u16 = 2500; // 25% cut + + // Test that setting validator cut on invalid netuid fails + assert_eq!( + AdminUtils::sudo_set_validator_cut( + <::RuntimeOrigin>::root(), + invalid_netuid, + cut_value + ), + Err(DispatchError::BadOrigin) // This will likely fail due to subnet not existing + ); + }); +} + +#[test] +fn test_validator_cut_bounds() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(5); + let min_cut: u16 = 0; // 0% cut + let max_cut: u16 = u16::MAX; // 100% cut + + // Set up a network + add_network(netuid, 10); + + // Test minimum value + SubtensorModule::set_validator_cut(netuid, min_cut); + assert_eq!(SubtensorModule::get_validator_cut(netuid), min_cut); + + // Test maximum value + SubtensorModule::set_validator_cut(netuid, max_cut); + assert_eq!(SubtensorModule::get_validator_cut(netuid), max_cut); + }); +} diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index c11ed5658..da7f22150 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -663,10 +663,12 @@ impl Pallet { }); log::debug!("incentive_sum: {incentive_sum:?}"); + let validator_cut = Self::get_validator_cut(netuid); let pending_validator_alpha = if !incentive_sum.is_zero() { pending_alpha .saturating_add(pending_swapped) - .saturating_div(2.into()) + .saturating_div(u16::MAX.into()) + .saturating_mul(validator_cut.into()) .saturating_sub(pending_swapped) } else { // If the incentive is 0, then Validators get 100% of the alpha. diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index dd12a9b76..89c42f0fa 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1501,6 +1501,11 @@ pub mod pallet { pub type ImmuneOwnerUidsLimit = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmuneOwnerUidsLimit>; + #[pallet::storage] + /// --- MAP ( netuid ) --> Validator cut + pub type ValidatorCut = + StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultValidatorCut>; + /// ======================================= /// ==== Subnetwork Consensus Storage ==== /// ======================================= diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 7b1b644e8..ff30718cc 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -327,4 +327,24 @@ impl Pallet { *total = total.saturating_sub(amount); }); } + + /// Gets the validator cut for a given subnet. + /// + /// # Arguments + /// * `netuid` - The network UID. + /// + /// # Returns + /// The validator cut value for the subnet. + pub fn get_validator_cut(netuid: NetUid) -> u16 { + ValidatorCut::::get(netuid) + } + + /// Sets the validator cut for a given subnet. + /// + /// # Arguments + /// * `netuid` - The network UID. + /// * `cut` - The validator cut value to set. + pub fn set_validator_cut(netuid: NetUid, cut: u16) { + ValidatorCut::::insert(netuid, cut); + } } From 30485546473ed84490a8b4c9970c466ecd5fcbda Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 12 Sep 2025 16:52:20 +0800 Subject: [PATCH 02/32] commit Cargo.lock --- pallets/subtensor/src/coinbase/run_coinbase.rs | 4 ++-- pallets/subtensor/src/lib.rs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index da7f22150..152ed55fd 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -667,8 +667,8 @@ impl Pallet { let pending_validator_alpha = if !incentive_sum.is_zero() { pending_alpha .saturating_add(pending_swapped) - .saturating_div(u16::MAX.into()) - .saturating_mul(validator_cut.into()) + .saturating_div(AlphaCurrency::from(u16::MAX as u64)) + .saturating_mul(AlphaCurrency::from(validator_cut as u64)) .saturating_sub(pending_swapped) } else { // If the incentive is 0, then Validators get 100% of the alpha. diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 89c42f0fa..77e0060e8 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1501,6 +1501,12 @@ pub mod pallet { pub type ImmuneOwnerUidsLimit = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmuneOwnerUidsLimit>; + #[pallet::type_value] + /// Default validator cut 50% + pub fn DefaultValidatorCut() -> u16 { + u16::MAX / 2 + } + #[pallet::storage] /// --- MAP ( netuid ) --> Validator cut pub type ValidatorCut = From 5372967bd290a63964aa3d4f73b2575cc7c81a97 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 12 Sep 2025 16:58:09 +0800 Subject: [PATCH 03/32] commit Cargo.lock --- pallets/admin-utils/src/tests/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 0f04b4a03..fa367726b 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2329,12 +2329,11 @@ fn test_sudo_set_validator_cut() { ), Err(DispatchError::BadOrigin) ); - // Value should remain unchanged + // Value should remain unchangeds assert_eq!(SubtensorModule::get_validator_cut(netuid), init_value); // Test that subnet owner can set the validator cut successfully - let subnet_owner = U256::from(123); - SubtensorModule::set_subnet_owner(netuid, subnet_owner); + let subnet_owner = pallet_subtensor::SubnetOwner::::get(netuid.into()); assert_ok!(AdminUtils::sudo_set_validator_cut( <::RuntimeOrigin>::signed(subnet_owner), From 6de0f2333bf6aac8dadbb924c4fb031c4b1c0f64 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 12 Sep 2025 17:00:19 +0800 Subject: [PATCH 04/32] commit Cargo.lock --- pallets/admin-utils/src/tests/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index fa367726b..4bcfad736 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2318,6 +2318,10 @@ fn test_sudo_set_validator_cut() { // Set up a network add_network(netuid, 10); + let sn_owner = U256::from(1324); + // Set the Subnet Owner + SubnetOwner::::insert(netuid, sn_owner); + let init_value = SubtensorModule::get_validator_cut(netuid); // Test that non-authorized origin fails (using a regular signed origin) @@ -2332,11 +2336,8 @@ fn test_sudo_set_validator_cut() { // Value should remain unchangeds assert_eq!(SubtensorModule::get_validator_cut(netuid), init_value); - // Test that subnet owner can set the validator cut successfully - let subnet_owner = pallet_subtensor::SubnetOwner::::get(netuid.into()); - assert_ok!(AdminUtils::sudo_set_validator_cut( - <::RuntimeOrigin>::signed(subnet_owner), + <::RuntimeOrigin>::signed(sn_owner), netuid, to_be_set )); From 634079ed651ab9ad8da014388538efaf42421df4 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 12 Sep 2025 17:20:24 +0800 Subject: [PATCH 05/32] fix unit test --- pallets/admin-utils/src/tests/mod.rs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 4bcfad736..d443d98b2 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2292,7 +2292,7 @@ fn test_get_validator_cut() { fn test_set_validator_cut() { new_test_ext().execute_with(|| { let netuid = NetUid::from(2); - let initial_cut: u16 = 0; + let initial_cut: u16 = pallet_subtensor::DefaultValidatorCut::::get(); let new_cut: u16 = 7500; // 75% cut // Set up a network @@ -2368,24 +2368,6 @@ fn test_sudo_set_validator_cut_root() { }); } -#[test] -fn test_sudo_set_validator_cut_invalid_netuid() { - new_test_ext().execute_with(|| { - let invalid_netuid = NetUid::from(999); // Non-existent netuid - let cut_value: u16 = 2500; // 25% cut - - // Test that setting validator cut on invalid netuid fails - assert_eq!( - AdminUtils::sudo_set_validator_cut( - <::RuntimeOrigin>::root(), - invalid_netuid, - cut_value - ), - Err(DispatchError::BadOrigin) // This will likely fail due to subnet not existing - ); - }); -} - #[test] fn test_validator_cut_bounds() { new_test_ext().execute_with(|| { From 15fd19cbed0d7b4fcfc8767f11d08292b820b79c Mon Sep 17 00:00:00 2001 From: open-junius Date: Sun, 14 Sep 2025 15:54:13 +0800 Subject: [PATCH 06/32] precision improvement --- .../subtensor/src/coinbase/run_coinbase.rs | 21 ++++++++++++++----- pallets/subtensor/src/lib.rs | 6 +++--- pallets/subtensor/src/staking/helpers.rs | 4 ++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 152ed55fd..7f1cc5f16 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -664,12 +664,23 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let validator_cut = Self::get_validator_cut(netuid); + log::error!("validator_cut: {validator_cut:?}"); + // log::error!("incentive_sum: {:?}"), ; let pending_validator_alpha = if !incentive_sum.is_zero() { - pending_alpha - .saturating_add(pending_swapped) - .saturating_div(AlphaCurrency::from(u16::MAX as u64)) - .saturating_mul(AlphaCurrency::from(validator_cut as u64)) - .saturating_sub(pending_swapped) + let pending_alpha_f = U96F32::from(pending_alpha.to_u64()); + let result = pending_alpha_f + .saturating_add(U96F32::from(pending_swapped.to_u64())) + .saturating_div(u64::MAX.into()) + .saturating_mul(U96F32::from(validator_cut)) + .saturating_sub(U96F32::from(pending_swapped.to_u64())); + // log::error!("result: {result:?}"); + // pending_alpha + // .saturating_add(pending_swapped) + // // .saturating_div(2.into()) + // .saturating_div(AlphaCurrency::from(u16::MAX as u64)) + // .saturating_mul(AlphaCurrency::from(validator_cut as u64)) + // .saturating_sub(pending_swapped) + result.saturating_to_num::().into() } else { // If the incentive is 0, then Validators get 100% of the alpha. pending_alpha diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 77e0060e8..32b0aff1a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1503,14 +1503,14 @@ pub mod pallet { #[pallet::type_value] /// Default validator cut 50% - pub fn DefaultValidatorCut() -> u16 { - u16::MAX / 2 + pub fn DefaultValidatorCut() -> u64 { + u64::MAX / 2 } #[pallet::storage] /// --- MAP ( netuid ) --> Validator cut pub type ValidatorCut = - StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultValidatorCut>; + StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultValidatorCut>; /// ======================================= /// ==== Subnetwork Consensus Storage ==== diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index ff30718cc..904bac32d 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -335,7 +335,7 @@ impl Pallet { /// /// # Returns /// The validator cut value for the subnet. - pub fn get_validator_cut(netuid: NetUid) -> u16 { + pub fn get_validator_cut(netuid: NetUid) -> u64 { ValidatorCut::::get(netuid) } @@ -344,7 +344,7 @@ impl Pallet { /// # Arguments /// * `netuid` - The network UID. /// * `cut` - The validator cut value to set. - pub fn set_validator_cut(netuid: NetUid, cut: u16) { + pub fn set_validator_cut(netuid: NetUid, cut: u64) { ValidatorCut::::insert(netuid, cut); } } From 6730799b8efc00428258f958e07b5e5ac32daa95 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 2 Oct 2025 22:08:58 +0800 Subject: [PATCH 07/32] commit Cargo.lock --- pallets/admin-utils/src/lib.rs | 11 ++++++----- pallets/subtensor/src/utils/rate_limiting.rs | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index d0a94849d..6db34daf8 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2022,26 +2022,27 @@ pub mod pallet { /// Sets the validator cut for a subnet. /// Only callable by subnet owner or root. - #[pallet::call_index(78)] + #[pallet::call_index(81)] #[pallet::weight(Weight::from_parts(15_000_000, 0) .saturating_add(::DbWeight::get().reads(1_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_set_validator_cut( origin: OriginFor, netuid: NetUid, - cut: u16, + cut: u64, ) -> DispatchResult { let maybe_owner = pallet_subtensor::Pallet::::ensure_sn_owner_or_root_with_limits( - origin, + origin.clone(), netuid, - &[TransactionType::OwnerHyperparamUpdate], + &[TransactionType::SetValidatorCut], )?; + pallet_subtensor::Pallet::::set_validator_cut(netuid, cut); log::debug!("ValidatorCutSet( netuid: {netuid:?}, cut: {cut:?} ) "); pallet_subtensor::Pallet::::record_owner_rl( maybe_owner, netuid, - &[TransactionType::OwnerHyperparamUpdate], + &[TransactionType::SetValidatorCut], ); Ok(()) } diff --git a/pallets/subtensor/src/utils/rate_limiting.rs b/pallets/subtensor/src/utils/rate_limiting.rs index e7028bc4e..8b86c46d7 100644 --- a/pallets/subtensor/src/utils/rate_limiting.rs +++ b/pallets/subtensor/src/utils/rate_limiting.rs @@ -16,6 +16,7 @@ pub enum TransactionType { MechanismCountUpdate, MechanismEmission, MaxUidsTrimming, + SetValidatorCut, } impl TransactionType { @@ -141,6 +142,7 @@ impl From for u16 { TransactionType::MechanismCountUpdate => 7, TransactionType::MechanismEmission => 8, TransactionType::MaxUidsTrimming => 9, + TransactionType::SetValidatorCut => 10, } } } @@ -158,6 +160,7 @@ impl From for TransactionType { 7 => TransactionType::MechanismCountUpdate, 8 => TransactionType::MechanismEmission, 9 => TransactionType::MaxUidsTrimming, + 10 => TransactionType::SetValidatorCut, _ => TransactionType::Unknown, } } From 31e62ba7aedbc39debee46e6719a5d987e292f7f Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 2 Oct 2025 22:15:10 +0800 Subject: [PATCH 08/32] commit Cargo.lock --- pallets/admin-utils/src/tests/mod.rs | 126 ++++++++++++++------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 2171beaaa..2aa95ef38 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2423,6 +2423,7 @@ fn test_sudo_set_mechanism_count_and_emissions() { fn test_trim_to_max_allowed_uids() { new_test_ext().execute_with(|| { + let netuid = NetUid::from(1); let sn_owner = U256::from(1); let sn_owner_hotkey1 = U256::from(2); let sn_owner_hotkey2 = U256::from(3); @@ -2856,72 +2857,73 @@ fn test_sudo_set_min_allowed_uids() { }); } -#[test] -fn test_validator_cut_bounds() { - new_test_ext().execute_with(|| { - let netuid = NetUid::from(5); - let min_cut: u16 = 0; // 0% cut - let max_cut: u16 = u16::MAX; // 100% cut - - // Set up a network - add_network(netuid, 10); - - // Test minimum value - SubtensorModule::set_validator_cut(netuid, min_cut); - assert_eq!(SubtensorModule::get_validator_cut(netuid), min_cut); - - // Test maximum value - SubtensorModule::set_validator_cut(netuid, max_cut); - assert_eq!(SubtensorModule::get_validator_cut(netuid), max_cut); - assert_eq!(SubtensorModule::get_min_allowed_uids(netuid), to_be_set); - - // Non root - assert_err!( - AdminUtils::sudo_set_min_allowed_uids( - <::RuntimeOrigin>::signed(U256::from(0)), - netuid, - to_be_set - ), - DispatchError::BadOrigin - ); +// #[test] +// fn test_validator_cut_bounds() { +// new_test_ext().execute_with(|| { +// let netuid = NetUid::from(5); +// let min_cut: u64 = 0; // 0% cut +// let max_cut: u64 = u64::MAX; // 100% cut +// let to_be_set: u64 = 10000; // 100% cut - // Non existent subnet - assert_err!( - AdminUtils::sudo_set_min_allowed_uids( - <::RuntimeOrigin>::root(), - NetUid::from(42), - to_be_set - ), - Error::::SubnetDoesNotExist - ); +// // Set up a network +// add_network(netuid, 10); - // Min allowed uids greater than max allowed uids - assert_err!( - AdminUtils::sudo_set_min_allowed_uids( - <::RuntimeOrigin>::root(), - netuid, - SubtensorModule::get_max_allowed_uids(netuid) + 1 - ), - Error::::MinAllowedUidsGreaterThanMaxAllowedUids - ); +// // Test minimum value +// SubtensorModule::set_validator_cut(netuid, min_cut); +// assert_eq!(SubtensorModule::get_validator_cut(netuid), min_cut); - // Min allowed uids greater than current uids - assert_err!( - AdminUtils::sudo_set_min_allowed_uids( - <::RuntimeOrigin>::root(), - netuid, - SubtensorModule::get_subnetwork_n(netuid) + 1 - ), - Error::::MinAllowedUidsGreaterThanCurrentUids - ); - }); -} +// // Test maximum value +// SubtensorModule::set_validator_cut(netuid, max_cut); +// assert_eq!(SubtensorModule::get_validator_cut(netuid), max_cut); +// assert_eq!(SubtensorModule::get_min_allowed_uids(netuid), to_be_set); + +// // Non root +// assert_err!( +// AdminUtils::sudo_set_min_allowed_uids( +// <::RuntimeOrigin>::signed(U256::from(0)), +// netuid, +// to_be_set +// ), +// DispatchError::BadOrigin +// ); + +// // Non existent subnet +// assert_err!( +// AdminUtils::sudo_set_min_allowed_uids( +// <::RuntimeOrigin>::root(), +// NetUid::from(42), +// to_be_set +// ), +// Error::::SubnetDoesNotExist +// ); + +// // Min allowed uids greater than max allowed uids +// assert_err!( +// AdminUtils::sudo_set_min_allowed_uids( +// <::RuntimeOrigin>::root(), +// netuid, +// SubtensorModule::get_max_allowed_uids(netuid) + 1 +// ), +// Error::::MinAllowedUidsGreaterThanMaxAllowedUids +// ); + +// // Min allowed uids greater than current uids +// assert_err!( +// AdminUtils::sudo_set_min_allowed_uids( +// <::RuntimeOrigin>::root(), +// netuid, +// SubtensorModule::get_subnetwork_n(netuid) + 1 +// ), +// Error::::MinAllowedUidsGreaterThanCurrentUids +// ); +// }); +// } #[test] fn test_get_validator_cut() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); - let expected_cut: u16 = 5000; // 50% cut + let expected_cut: u64 = 5000; // 50% cut // Set up a network add_network(netuid, 10); @@ -2939,8 +2941,8 @@ fn test_get_validator_cut() { fn test_set_validator_cut() { new_test_ext().execute_with(|| { let netuid = NetUid::from(2); - let initial_cut: u16 = pallet_subtensor::DefaultValidatorCut::::get(); - let new_cut: u16 = 7500; // 75% cut + let initial_cut: u64 = pallet_subtensor::DefaultValidatorCut::::get(); + let new_cut: u64 = 7500; // 75% cut // Set up a network add_network(netuid, 10); @@ -2960,7 +2962,7 @@ fn test_set_validator_cut() { fn test_sudo_set_validator_cut() { new_test_ext().execute_with(|| { let netuid = NetUid::from(3); - let to_be_set: u16 = 4200; // 42% cut + let to_be_set: u64 = 4200; // 42% cut // Set up a network add_network(netuid, 10); @@ -2998,7 +3000,7 @@ fn test_sudo_set_validator_cut() { fn test_sudo_set_validator_cut_root() { new_test_ext().execute_with(|| { let netuid = NetUid::from(4); - let to_be_set: u16 = 10000; // 100% cut + let to_be_set: u64 = 10000; // 100% cut // Set up a network add_network(netuid, 10); From 35785da230105652e286404baaeebeff86a9f4b0 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 2 Oct 2025 22:49:33 +0800 Subject: [PATCH 09/32] clean up code --- pallets/admin-utils/src/tests/mod.rs | 95 ++++--------------- .../subtensor/src/coinbase/run_coinbase.rs | 7 -- 2 files changed, 17 insertions(+), 85 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 2aa95ef38..e88d5be40 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2421,6 +2421,7 @@ fn test_sudo_set_mechanism_count_and_emissions() { }); } +#[test] fn test_trim_to_max_allowed_uids() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); @@ -2857,68 +2858,6 @@ fn test_sudo_set_min_allowed_uids() { }); } -// #[test] -// fn test_validator_cut_bounds() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(5); -// let min_cut: u64 = 0; // 0% cut -// let max_cut: u64 = u64::MAX; // 100% cut -// let to_be_set: u64 = 10000; // 100% cut - -// // Set up a network -// add_network(netuid, 10); - -// // Test minimum value -// SubtensorModule::set_validator_cut(netuid, min_cut); -// assert_eq!(SubtensorModule::get_validator_cut(netuid), min_cut); - -// // Test maximum value -// SubtensorModule::set_validator_cut(netuid, max_cut); -// assert_eq!(SubtensorModule::get_validator_cut(netuid), max_cut); -// assert_eq!(SubtensorModule::get_min_allowed_uids(netuid), to_be_set); - -// // Non root -// assert_err!( -// AdminUtils::sudo_set_min_allowed_uids( -// <::RuntimeOrigin>::signed(U256::from(0)), -// netuid, -// to_be_set -// ), -// DispatchError::BadOrigin -// ); - -// // Non existent subnet -// assert_err!( -// AdminUtils::sudo_set_min_allowed_uids( -// <::RuntimeOrigin>::root(), -// NetUid::from(42), -// to_be_set -// ), -// Error::::SubnetDoesNotExist -// ); - -// // Min allowed uids greater than max allowed uids -// assert_err!( -// AdminUtils::sudo_set_min_allowed_uids( -// <::RuntimeOrigin>::root(), -// netuid, -// SubtensorModule::get_max_allowed_uids(netuid) + 1 -// ), -// Error::::MinAllowedUidsGreaterThanMaxAllowedUids -// ); - -// // Min allowed uids greater than current uids -// assert_err!( -// AdminUtils::sudo_set_min_allowed_uids( -// <::RuntimeOrigin>::root(), -// netuid, -// SubtensorModule::get_subnetwork_n(netuid) + 1 -// ), -// Error::::MinAllowedUidsGreaterThanCurrentUids -// ); -// }); -// } - #[test] fn test_get_validator_cut() { new_test_ext().execute_with(|| { @@ -3017,22 +2956,22 @@ fn test_sudo_set_validator_cut_root() { }); } -// #[test] -// fn test_validator_cut_bounds() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(5); -// let min_cut: u16 = 0; // 0% cut -// let max_cut: u16 = u16::MAX; // 100% cut +#[test] +fn test_validator_cut_bounds() { + new_test_ext().execute_with(|| { + let netuid = NetUid::from(5); + let min_cut: u64 = 0; // 0% cut + let max_cut: u64 = u64::MAX; // 100% cut -// // Set up a network -// add_network(netuid, 10); + // Set up a network + add_network(netuid, 10); -// // Test minimum value -// SubtensorModule::set_validator_cut(netuid, min_cut); -// assert_eq!(SubtensorModule::get_validator_cut(netuid), min_cut); + // Test minimum value + SubtensorModule::set_validator_cut(netuid, min_cut); + assert_eq!(SubtensorModule::get_validator_cut(netuid), min_cut); -// // Test maximum value -// SubtensorModule::set_validator_cut(netuid, max_cut); -// assert_eq!(SubtensorModule::get_validator_cut(netuid), max_cut); -// }); -// } + // Test maximum value + SubtensorModule::set_validator_cut(netuid, max_cut); + assert_eq!(SubtensorModule::get_validator_cut(netuid), max_cut); + }); +} diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index ee137ed89..7cdf7d080 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -684,13 +684,6 @@ impl Pallet { .saturating_div(u64::MAX.into()) .saturating_mul(U96F32::from(validator_cut)) .saturating_sub(U96F32::from(pending_swapped.to_u64())); - // log::error!("result: {result:?}"); - // pending_alpha - // .saturating_add(pending_swapped) - // // .saturating_div(2.into()) - // .saturating_div(AlphaCurrency::from(u16::MAX as u64)) - // .saturating_mul(AlphaCurrency::from(validator_cut as u64)) - // .saturating_sub(pending_swapped) result.saturating_to_num::().into() } else { // If the incentive is 0, then Validators get 100% of the alpha. From a04b2cc6f1cfeb3a8dbae6710f976520eb7d9f98 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 3 Oct 2025 22:08:21 +0800 Subject: [PATCH 10/32] fix unit test --- pallets/subtensor/src/coinbase/run_coinbase.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 7cdf7d080..730cc6f6a 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -675,14 +675,14 @@ impl Pallet { log::debug!("incentive_sum: {incentive_sum:?}"); let validator_cut = Self::get_validator_cut(netuid); - log::error!("validator_cut: {validator_cut:?}"); - // log::error!("incentive_sum: {:?}"), ; + log::debug!("validator_cut: {validator_cut:?}"); + let pending_validator_alpha = if !incentive_sum.is_zero() { let pending_alpha_f = U96F32::from(pending_alpha.to_u64()); + let rate = U96F32::from(validator_cut).saturating_div(u64::MAX.into()); let result = pending_alpha_f .saturating_add(U96F32::from(pending_swapped.to_u64())) - .saturating_div(u64::MAX.into()) - .saturating_mul(U96F32::from(validator_cut)) + .saturating_mul(rate) .saturating_sub(U96F32::from(pending_swapped.to_u64())); result.saturating_to_num::().into() } else { From ee6957a5cbb8b59f11e7f811a25b34808a03ebf1 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 09:42:53 +0800 Subject: [PATCH 11/32] add separate test file --- pallets/subtensor/src/tests/validator_cut.rs | 221 +++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 pallets/subtensor/src/tests/validator_cut.rs diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs new file mode 100644 index 000000000..ba2de2b36 --- /dev/null +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -0,0 +1,221 @@ +#![allow(clippy::unwrap_used)] +#![allow(clippy::arithmetic_side_effects)] + +use approx::assert_abs_diff_eq; +use frame_support::assert_ok; +use sp_core::U256; +use subtensor_runtime_common::{Currency as CurrencyT, NetUid, NetUidStorageIndex}; + +use super::mock::*; +use crate::*; + +#[test] +fn test_emission_with_different_cut() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake = 100_000_000_000; + + let cut_list = [ + 0, + 10000, + 1000000, + u64::MAX / 100, + u64::MAX / 10, + u64::MAX / 4, + u64::MAX / 2, + u64::MAX, + ]; + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + // There are one validator and two neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + // set weight for two minder as same value, miner 1 and miner 2 + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + // Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + // BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, false]); + + // Run run_coinbase until emissions are drained + + for cut in cut_list { + let validator_stake_before = + SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); + let miner_stake_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); + SubtensorModule::set_validator_cut(netuid, cut); + + step_block(subnet_tempo); + + // Verify how emission is split between keys + // - Owner cut is zero => 50% goes to miners and 50% goes to validators + // - Validator gets 25% because there are two validators + // - Valiminer gets 25% as a validator and 25% as miner + // - Miner gets 25% as miner + let validator_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) + - validator_stake_before; + let miner_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_stake_before; + let total_emission = validator_emission + miner_emission; + let total_emission_u128: u128 = total_emission.to_u64() as u128; + + let expected_validator_emission = + ((total_emission_u128 * (cut as u128) / (u64::MAX as u128)) as u64).into(); + + assert_abs_diff_eq!( + validator_emission, + expected_validator_emission, + epsilon = 10.into() + ); + assert_abs_diff_eq!( + miner_emission, + total_emission - expected_validator_emission, + epsilon = 10.into() + ); + } + }); +} + +#[test] +fn test_emission_with_defualt_cut_2_validators_2_miners() { + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let validator_miner_coldkey = U256::from(3); + let validator_miner_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake = 100_000_000_000; + + // Add network, register hotkeys, and setup network parameters + add_network(netuid, subnet_tempo, 0); + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, validator_miner_hotkey, validator_miner_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator_miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + // There are two validators and three neurons + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Setup stakes: + // Stake from validator + // Stake from valiminer + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_miner_coldkey), + validator_miner_hotkey, + netuid, + stake.into() + )); + + // Setup YUMA so that it creates emissions + // set weight for two minder as same value, miner 1 and miner 2 + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); // makes all stake active + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + // Run run_coinbase until emissions are drained + let validator_stake_before = + SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); + let valiminer_stake_before = + SubtensorModule::get_total_stake_for_coldkey(&validator_miner_coldkey); + let miner_stake_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); + + step_block(subnet_tempo); + + // Verify how emission is split between keys + // - Owner cut is zero => 50% goes to miners and 50% goes to validators + // - Validator gets 25% because there are two validators + // - Valiminer gets 25% as a validator and 25% as miner + // - Miner gets 25% as miner + let validator_emission = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) + - validator_stake_before; + let valiminer_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator_miner_coldkey) + - valiminer_stake_before; + let miner_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_stake_before; + let total_emission = validator_emission + valiminer_emission + miner_emission; + + assert_abs_diff_eq!( + validator_emission, + total_emission / 4.into(), + epsilon = 10.into() + ); + assert_abs_diff_eq!( + valiminer_emission, + total_emission / 2.into(), + epsilon = 10.into() + ); + assert_abs_diff_eq!( + miner_emission, + total_emission / 4.into(), + epsilon = 10.into() + ); + }); +} From e792af1ddcbc8b5156f48df0eb30173162b23273 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 09:47:11 +0800 Subject: [PATCH 12/32] update emisson accroding to rate --- .../subtensor/src/coinbase/run_coinbase.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 730cc6f6a..571df53ae 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -677,9 +677,26 @@ impl Pallet { let validator_cut = Self::get_validator_cut(netuid); log::debug!("validator_cut: {validator_cut:?}"); + let rate = U96F32::from(validator_cut).saturating_div(u64::MAX.into()); + log::debug!("validator rate: {rate:?}"); + let miner_rate = U96F32::from(1_u64) + .saturating_sub(rate) + .saturating_mul(U96F32::from(2_u64)); + + // Update incentive according to the validator cut + let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = hotkey_emission + .iter() + .map(|(hotkey, incentive, reward)| { + let result: AlphaCurrency = U96F32::from(incentive.to_u64()) + .saturating_mul(miner_rate) + .saturating_to_num::() + .into(); + (hotkey.clone(), result.clone(), reward.clone()) + }) + .collect(); + let pending_validator_alpha = if !incentive_sum.is_zero() { let pending_alpha_f = U96F32::from(pending_alpha.to_u64()); - let rate = U96F32::from(validator_cut).saturating_div(u64::MAX.into()); let result = pending_alpha_f .saturating_add(U96F32::from(pending_swapped.to_u64())) .saturating_mul(rate) From 8e07a52e93f739b6b4290f98a6d24ff6f6371bf6 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 09:48:49 +0800 Subject: [PATCH 13/32] add test file to mod --- pallets/subtensor/src/tests/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index a0105a6ff..0f8657280 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -28,4 +28,5 @@ mod swap_coldkey; mod swap_hotkey; mod swap_hotkey_with_subnet; mod uids; +mod validator_cut; mod weights; From d5f44c536cdbd91c33044d920db6ad9a0905e547 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 11:09:39 +0800 Subject: [PATCH 14/32] more test cases --- pallets/subtensor/src/tests/validator_cut.rs | 720 ++++++++++++++++++- 1 file changed, 718 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index ba2de2b36..77f5fe7da 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -22,8 +22,6 @@ fn test_emission_with_different_cut() { let cut_list = [ 0, - 10000, - 1000000, u64::MAX / 100, u64::MAX / 10, u64::MAX / 4, @@ -219,3 +217,721 @@ fn test_emission_with_defualt_cut_2_validators_2_miners() { ); }); } + +#[test] +fn test_emission_with_multiple_validators_varying_cuts() { + // Test with 3 validators and 2 miners with different validator cut values + new_test_ext(1).execute_with(|| { + let validator1_coldkey = U256::from(1); + let validator1_hotkey = U256::from(2); + let validator2_coldkey = U256::from(3); + let validator2_hotkey = U256::from(4); + let validator3_coldkey = U256::from(5); + let validator3_hotkey = U256::from(6); + let miner1_coldkey = U256::from(7); + let miner1_hotkey = U256::from(8); + let miner2_coldkey = U256::from(9); + let miner2_hotkey = U256::from(10); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake = 100_000_000_000; + + let cut_list = [0, u64::MAX / 4, u64::MAX / 2, u64::MAX]; + + // Add network and register all neurons + add_network(netuid, subnet_tempo, 0); + SubtensorModule::set_target_registrations_per_interval(netuid, 10); + SubtensorModule::set_max_registrations_per_block(netuid, 10); + RegistrationsThisInterval::::set(netuid, 0); + RegistrationsThisBlock::::set(netuid, 0); + register_ok_neuron(netuid, validator1_hotkey, validator1_coldkey, 0); + register_ok_neuron(netuid, validator2_hotkey, validator2_coldkey, 1); + register_ok_neuron(netuid, validator3_hotkey, validator3_coldkey, 2); + register_ok_neuron(netuid, miner1_hotkey, miner1_coldkey, 3); + register_ok_neuron(netuid, miner2_hotkey, miner2_coldkey, 4); + + // Add balance to all coldkeys + for coldkey in [ + validator1_coldkey, + validator2_coldkey, + validator3_coldkey, + miner1_coldkey, + miner2_coldkey, + ] { + SubtensorModule::add_balance_to_coldkey_account( + &coldkey, + stake + ExistentialDeposit::get(), + ); + } + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + MaxAllowedUids::::set(netuid, 5); + SubtensorModule::set_max_allowed_validators(netuid, 3); + + // All validators stake equally + for (coldkey, hotkey) in [ + (validator1_coldkey, validator1_hotkey), + (validator2_coldkey, validator2_hotkey), + (validator3_coldkey, validator3_hotkey), + ] { + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(coldkey), + hotkey, + netuid, + stake.into() + )); + } + + // Setup weights - all validators assign equal weight to miners + Weights::::insert( + NetUidStorageIndex::from(netuid), + 0, + vec![(3, 0x7FFF), (4, 0x7FFF)], + ); + Weights::::insert( + NetUidStorageIndex::from(netuid), + 1, + vec![(3, 0x7FFF), (4, 0x7FFF)], + ); + Weights::::insert( + NetUidStorageIndex::from(netuid), + 2, + vec![(3, 0x7FFF), (4, 0x7FFF)], + ); + + for i in 0..5 { + BlockAtRegistration::::set(netuid, i, 1); + } + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); + ValidatorPermit::::insert(netuid, vec![true, true, true, false, false]); + + for cut in cut_list { + let val1_before = SubtensorModule::get_total_stake_for_coldkey(&validator1_coldkey); + let val2_before = SubtensorModule::get_total_stake_for_coldkey(&validator2_coldkey); + let val3_before = SubtensorModule::get_total_stake_for_coldkey(&validator3_coldkey); + let miner1_before = SubtensorModule::get_total_stake_for_coldkey(&miner1_coldkey); + let miner2_before = SubtensorModule::get_total_stake_for_coldkey(&miner2_coldkey); + + SubtensorModule::set_validator_cut(netuid, cut); + step_block(subnet_tempo); + + let val1_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator1_coldkey) - val1_before; + let val2_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator2_coldkey) - val2_before; + let val3_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator3_coldkey) - val3_before; + let miner1_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner1_coldkey) - miner1_before; + let miner2_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner2_coldkey) - miner2_before; + + let total_emission = + val1_emission + val2_emission + val3_emission + miner1_emission + miner2_emission; + let total_emission_u128: u128 = total_emission.to_u64() as u128; + + let expected_validator_total = + ((total_emission_u128 * (cut as u128) / (u64::MAX as u128)) as u64).into(); + let expected_miner_total = total_emission - expected_validator_total; + + // Each validator should get 1/3 of validator emissions + let validator_total_emission = val1_emission + val2_emission + val3_emission; + assert_abs_diff_eq!( + validator_total_emission, + expected_validator_total, + epsilon = 30.into() + ); + + // Each miner should get 1/2 of miner emissions + let miner_total_emission = miner1_emission + miner2_emission; + assert_abs_diff_eq!( + miner_total_emission, + expected_miner_total, + epsilon = 30.into() + ); + } + }); +} + +#[test] +fn test_emission_with_child_keys_and_varying_cuts() { + // Test delegation scenario: validator with child keys + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let delegate_coldkey1 = U256::from(3); + let delegate_coldkey2 = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let validator_stake = 100_000_000_000; + let delegate_stake = 50_000_000_000; + + let cut_list = [0, u64::MAX / 10, u64::MAX / 2, u64::MAX]; + + // Setup network + add_network(netuid, subnet_tempo, 0); + SubtensorModule::set_target_registrations_per_interval(netuid, 10); + SubtensorModule::set_max_registrations_per_block(netuid, 10); + RegistrationsThisInterval::::set(netuid, 0); + RegistrationsThisBlock::::set(netuid, 0); + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 1); + + // Add balance + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + validator_stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &delegate_coldkey1, + delegate_stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &delegate_coldkey2, + delegate_stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + validator_stake + ExistentialDeposit::get(), + ); + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + MaxAllowedUids::::set(netuid, 2); + SubtensorModule::set_max_allowed_validators(netuid, 1); + + // Validator stakes on their own hotkey + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + validator_stake.into() + )); + + // Delegates stake on validator's hotkey (child keys) + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegate_coldkey1), + validator_hotkey, + netuid, + delegate_stake.into() + )); + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(delegate_coldkey2), + validator_hotkey, + netuid, + delegate_stake.into() + )); + + // Setup weights + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); + ValidatorPermit::::insert(netuid, vec![true, false]); + + for cut in cut_list { + let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); + let delegate1_before = SubtensorModule::get_total_stake_for_coldkey(&delegate_coldkey1); + let delegate2_before = SubtensorModule::get_total_stake_for_coldkey(&delegate_coldkey2); + let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); + + SubtensorModule::set_validator_cut(netuid, cut); + step_block(subnet_tempo); + + let validator_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) - validator_before; + let delegate1_emission = + SubtensorModule::get_total_stake_for_coldkey(&delegate_coldkey1) - delegate1_before; + let delegate2_emission = + SubtensorModule::get_total_stake_for_coldkey(&delegate_coldkey2) - delegate2_before; + let miner_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_before; + + let total_emission = + validator_emission + delegate1_emission + delegate2_emission + miner_emission; + let total_emission_u128: u128 = total_emission.to_u64() as u128; + + let expected_validator_total_emission = + ((total_emission_u128 * (cut as u128) / (u64::MAX as u128)) as u64).into(); + + // Validator emissions should be split proportionally to stake + // Total validator stake = 100 + 50 + 50 = 200 + // Validator: 100/200 = 50%, Delegate1: 50/200 = 25%, Delegate2: 50/200 = 25% + let total_validator_stake = validator_stake + delegate_stake + delegate_stake; + let validator_portion = validator_emission + delegate1_emission + delegate2_emission; + + assert_abs_diff_eq!( + validator_portion, + expected_validator_total_emission, + epsilon = 30.into() + ); + + // Check proportional distribution + let expected_validator_emission = + (expected_validator_total_emission.to_u64() as u128 * validator_stake as u128 + / total_validator_stake as u128) as u64; + let expected_delegate_emission = + (expected_validator_total_emission.to_u64() as u128 * delegate_stake as u128 + / total_validator_stake as u128) as u64; + + assert_abs_diff_eq!( + validator_emission, + expected_validator_emission.into(), + epsilon = 600_000_000.into() + ); + assert_abs_diff_eq!( + delegate1_emission, + expected_delegate_emission.into(), + epsilon = 600_000_000.into() + ); + assert_abs_diff_eq!( + delegate2_emission, + expected_delegate_emission.into(), + epsilon = 600_000_000.into() + ); + } + }); +} + +#[test] +fn test_emission_with_unequal_validator_stakes_varying_cuts() { + // Test with validators having different stake amounts + new_test_ext(1).execute_with(|| { + let validator1_coldkey = U256::from(1); + let validator1_hotkey = U256::from(2); + let validator2_coldkey = U256::from(3); + let validator2_hotkey = U256::from(4); + let miner_coldkey = U256::from(5); + let miner_hotkey = U256::from(6); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let large_stake = 200_000_000_000; + let small_stake = 50_000_000_000; + + let cut_list = [u64::MAX / 10, u64::MAX / 4, u64::MAX / 2, u64::MAX]; + + // Setup network + add_network(netuid, subnet_tempo, 0); + SubtensorModule::set_target_registrations_per_interval(netuid, 10); + SubtensorModule::set_max_registrations_per_block(netuid, 10); + RegistrationsThisInterval::::set(netuid, 0); + RegistrationsThisBlock::::set(netuid, 0); + register_ok_neuron(netuid, validator1_hotkey, validator1_coldkey, 0); + register_ok_neuron(netuid, validator2_hotkey, validator2_coldkey, 1); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 2); + + SubtensorModule::add_balance_to_coldkey_account( + &validator1_coldkey, + large_stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &validator2_coldkey, + small_stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + large_stake + ExistentialDeposit::get(), + ); + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 2); + + // Validator 1 stakes large amount + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator1_coldkey), + validator1_hotkey, + netuid, + large_stake.into() + )); + + // Validator 2 stakes small amount + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator2_coldkey), + validator2_hotkey, + netuid, + small_stake.into() + )); + + // Setup weights + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(2, 0xFFFF)]); + Weights::::insert(NetUidStorageIndex::from(netuid), 1, vec![(2, 0xFFFF)]); + + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + BlockAtRegistration::::set(netuid, 2, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); + ValidatorPermit::::insert(netuid, vec![true, true, false]); + + for cut in cut_list { + let val1_before = SubtensorModule::get_total_stake_for_coldkey(&validator1_coldkey); + let val2_before = SubtensorModule::get_total_stake_for_coldkey(&validator2_coldkey); + let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); + + SubtensorModule::set_validator_cut(netuid, cut); + step_block(subnet_tempo); + + let val1_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator1_coldkey) - val1_before; + let val2_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator2_coldkey) - val2_before; + let miner_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_before; + + let total_emission = val1_emission + val2_emission + miner_emission; + let total_emission_u128: u128 = total_emission.to_u64() as u128; + + let expected_validator_total = + ((total_emission_u128 * (cut as u128) / (u64::MAX as u128)) as u64).into(); + + let validator_total = val1_emission + val2_emission; + assert_abs_diff_eq!( + validator_total, + expected_validator_total, + epsilon = 20.into() + ); + + // Validator 1 should receive more due to higher stake + // Ratio is 200:50 = 4:1 + let total_validator_stake = large_stake + small_stake; + let expected_val1 = (expected_validator_total.to_u64() as u128 * large_stake as u128 + / total_validator_stake as u128) as u64; + let expected_val2 = (expected_validator_total.to_u64() as u128 * small_stake as u128 + / total_validator_stake as u128) as u64; + + assert_abs_diff_eq!(val1_emission, expected_val1.into(), epsilon = 40_000.into()); + assert_abs_diff_eq!(val2_emission, expected_val2.into(), epsilon = 40_000.into()); + } + }); +} + +#[test] +fn test_emission_single_validator_multiple_miners_varying_cuts() { + // Test with 1 validator and 4 miners + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let miner_coldkeys = vec![U256::from(3), U256::from(5), U256::from(7), U256::from(9)]; + let miner_hotkeys = vec![U256::from(4), U256::from(6), U256::from(8), U256::from(10)]; + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake = 100_000_000_000; + + let cut_list = [0, u64::MAX / 3, u64::MAX / 2, u64::MAX / 4 * 3, u64::MAX]; + + // Setup network + add_network(netuid, subnet_tempo, 0); + SubtensorModule::set_target_registrations_per_interval(netuid, 10); + SubtensorModule::set_max_registrations_per_block(netuid, 10); + RegistrationsThisInterval::::set(netuid, 0); + RegistrationsThisBlock::::set(netuid, 0); + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + for i in 0..4 { + register_ok_neuron(netuid, miner_hotkeys[i], miner_coldkeys[i], (i + 1) as u64); + } + + // Add balance + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + for coldkey in &miner_coldkeys { + SubtensorModule::add_balance_to_coldkey_account( + coldkey, + stake + ExistentialDeposit::get(), + ); + } + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + MaxAllowedUids::::set(netuid, 5); + SubtensorModule::set_max_allowed_validators(netuid, 1); + + // Validator stakes + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + + // Validator assigns equal weights to all miners + Weights::::insert( + NetUidStorageIndex::from(netuid), + 0, + vec![(1, 0x3FFF), (2, 0x3FFF), (3, 0x3FFF), (4, 0x3FFF)], + ); + + for i in 0..5 { + BlockAtRegistration::::set(netuid, i, 1); + } + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); + ValidatorPermit::::insert(netuid, vec![true, false, false, false, false]); + + for cut in cut_list { + let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); + let miners_before: Vec<_> = miner_coldkeys + .iter() + .map(|ck| SubtensorModule::get_total_stake_for_coldkey(ck)) + .collect(); + + SubtensorModule::set_validator_cut(netuid, cut); + step_block(subnet_tempo); + + let validator_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) - validator_before; + let miners_emissions: Vec<_> = miner_coldkeys + .iter() + .enumerate() + .map(|(i, ck)| SubtensorModule::get_total_stake_for_coldkey(ck) - miners_before[i]) + .collect(); + + let total_miner_emission = miners_emissions.iter().fold(0.into(), |acc, e| acc + *e); + let total_emission = validator_emission + total_miner_emission; + let total_emission_u128: u128 = total_emission.to_u64() as u128; + + let expected_validator_emission = + ((total_emission_u128 * (cut as u128) / (u64::MAX as u128)) as u64).into(); + let expected_miner_total = total_emission - expected_validator_emission; + + assert_abs_diff_eq!( + validator_emission, + expected_validator_emission, + epsilon = 20.into() + ); + assert_abs_diff_eq!( + total_miner_emission, + expected_miner_total, + epsilon = 20.into() + ); + + // Each miner should get approximately 1/4 of miner emissions (equal weights) + for miner_emission in &miners_emissions { + assert_abs_diff_eq!( + *miner_emission * 4.into(), + expected_miner_total, + epsilon = 100.into() + ); + } + } + }); +} + +#[test] +fn test_emission_all_validator_miners_varying_cuts() { + // Test where all participants are both validators and miners + new_test_ext(1).execute_with(|| { + let participants = vec![ + (U256::from(1), U256::from(2)), + (U256::from(3), U256::from(4)), + (U256::from(5), U256::from(6)), + ]; + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake = 100_000_000_000; + + let cut_list = [u64::MAX / 10, u64::MAX / 4, u64::MAX / 2, u64::MAX]; + + // Setup network + add_network(netuid, subnet_tempo, 0); + for (i, (coldkey, hotkey)) in participants.iter().enumerate() { + register_ok_neuron(netuid, *hotkey, *coldkey, i as u64); + SubtensorModule::add_balance_to_coldkey_account( + coldkey, + stake + ExistentialDeposit::get(), + ); + } + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + MaxAllowedUids::::set(netuid, 3); + SubtensorModule::set_max_allowed_validators(netuid, 3); + + // All stake + for (coldkey, hotkey) in &participants { + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(*coldkey), + *hotkey, + netuid, + stake.into() + )); + } + + // Each validator assigns equal weight to all others + Weights::::insert( + NetUidStorageIndex::from(netuid), + 0, + vec![(1, 0x7FFF), (2, 0x7FFF)], + ); + Weights::::insert( + NetUidStorageIndex::from(netuid), + 1, + vec![(0, 0x7FFF), (2, 0x7FFF)], + ); + Weights::::insert( + NetUidStorageIndex::from(netuid), + 2, + vec![(0, 0x7FFF), (1, 0x7FFF)], + ); + + for i in 0..3 { + BlockAtRegistration::::set(netuid, i, 1); + } + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); + ValidatorPermit::::insert(netuid, vec![true, true, true]); + + for cut in cut_list { + let stakes_before: Vec<_> = participants + .iter() + .map(|(ck, _)| SubtensorModule::get_total_stake_for_coldkey(ck)) + .collect(); + + SubtensorModule::set_validator_cut(netuid, cut); + step_block(subnet_tempo); + + let emissions: Vec<_> = participants + .iter() + .enumerate() + .map(|(i, (ck, _))| { + SubtensorModule::get_total_stake_for_coldkey(ck) - stakes_before[i] + }) + .collect(); + + let total_emission: u64 = emissions.iter().map(|e| e.to_u64()).sum(); + let total_emission_u128: u128 = total_emission as u128; + + // With equal stakes and everyone being both validator and miner, + // total emission should be split equally regardless of cut + // (each gets 1/3 as validator and 1/3 as miner) + let expected_per_participant: u64 = total_emission / 3; + + for emission in &emissions { + assert_abs_diff_eq!( + *emission, + expected_per_participant.into(), + epsilon = 30.into() + ); + } + + // Verify total matches expected + let expected_validator_total_u64 = + (total_emission_u128 * (cut as u128) / (u64::MAX as u128)) as u64; + let expected_miner_total_u64: u64 = total_emission - expected_validator_total_u64; + + // Sum of validator and miner portions should equal total + assert_abs_diff_eq!( + expected_validator_total_u64 + expected_miner_total_u64, + total_emission, + epsilon = 10 + ); + } + }); +} + +#[test] +fn test_emission_extreme_cuts_edge_cases() { + // Test extreme validator cut values: 0 (all to miners) and MAX (all to validators) + new_test_ext(1).execute_with(|| { + let validator_coldkey = U256::from(1); + let validator_hotkey = U256::from(2); + let miner_coldkey = U256::from(3); + let miner_hotkey = U256::from(4); + let netuid = NetUid::from(1); + let subnet_tempo = 10; + let stake = 100_000_000_000; + + // Setup network + add_network(netuid, subnet_tempo, 0); + register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); + register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 1); + + SubtensorModule::add_balance_to_coldkey_account( + &validator_coldkey, + stake + ExistentialDeposit::get(), + ); + SubtensorModule::add_balance_to_coldkey_account( + &miner_coldkey, + stake + ExistentialDeposit::get(), + ); + + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + step_block(subnet_tempo); + SubnetOwnerCut::::set(0); + MaxAllowedUids::::set(netuid, 2); + SubtensorModule::set_max_allowed_validators(netuid, 1); + + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(validator_coldkey), + validator_hotkey, + netuid, + stake.into() + )); + + Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); + + BlockAtRegistration::::set(netuid, 0, 1); + BlockAtRegistration::::set(netuid, 1, 1); + LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2]); + Kappa::::set(netuid, u16::MAX / 5); + ActivityCutoff::::set(netuid, u16::MAX); + ValidatorPermit::::insert(netuid, vec![true, false]); + + // Test cut = 0: all emissions should go to miners + { + let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); + let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); + + SubtensorModule::set_validator_cut(netuid, 0); + step_block(subnet_tempo); + + let validator_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) - validator_before; + let miner_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_before; + + // Validator should get nearly nothing (within rounding error) + assert_abs_diff_eq!(validator_emission, 0.into(), epsilon = 5.into()); + // Miner should get almost all emissions + assert!(miner_emission.to_u64() > 0); + } + + // Test cut = u64::MAX: all emissions should go to validators + { + let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); + let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); + + SubtensorModule::set_validator_cut(netuid, u64::MAX); + step_block(subnet_tempo); + + let validator_emission = + SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) - validator_before; + let miner_emission = + SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_before; + + // Miner should get nearly nothing (within rounding error) + assert_abs_diff_eq!(miner_emission, 0.into(), epsilon = 5.into()); + // Validator should get almost all emissions + assert!(validator_emission.to_u64() > 0); + } + }); +} From b2dd663d7288f9712f49ee60bd9a9280b4b61585 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 11:11:03 +0800 Subject: [PATCH 15/32] cargo clippy --- pallets/subtensor/src/coinbase/run_coinbase.rs | 2 +- pallets/subtensor/src/tests/validator_cut.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 571df53ae..001466fe2 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -691,7 +691,7 @@ impl Pallet { .saturating_mul(miner_rate) .saturating_to_num::() .into(); - (hotkey.clone(), result.clone(), reward.clone()) + (hotkey.clone(), result, *reward) }) .collect(); diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index 77f5fe7da..57cd8f77b 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -626,7 +626,7 @@ fn test_emission_single_validator_multiple_miners_varying_cuts() { let validator_coldkey = U256::from(1); let validator_hotkey = U256::from(2); let miner_coldkeys = vec![U256::from(3), U256::from(5), U256::from(7), U256::from(9)]; - let miner_hotkeys = vec![U256::from(4), U256::from(6), U256::from(8), U256::from(10)]; + let miner_hotkeys = [U256::from(4), U256::from(6), U256::from(8), U256::from(10)]; let netuid = NetUid::from(1); let subnet_tempo = 10; let stake = 100_000_000_000; @@ -689,7 +689,7 @@ fn test_emission_single_validator_multiple_miners_varying_cuts() { let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); let miners_before: Vec<_> = miner_coldkeys .iter() - .map(|ck| SubtensorModule::get_total_stake_for_coldkey(ck)) + .map(SubtensorModule::get_total_stake_for_coldkey) .collect(); SubtensorModule::set_validator_cut(netuid, cut); From 1cb952e22f2e5375e51571a2d737b5f46cc154fb Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 11:12:02 +0800 Subject: [PATCH 16/32] cargo fix --- pallets/subtensor/src/tests/validator_cut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index 57cd8f77b..fa1ff7dbc 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -640,8 +640,8 @@ fn test_emission_single_validator_multiple_miners_varying_cuts() { RegistrationsThisInterval::::set(netuid, 0); RegistrationsThisBlock::::set(netuid, 0); register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - for i in 0..4 { - register_ok_neuron(netuid, miner_hotkeys[i], miner_coldkeys[i], (i + 1) as u64); + for (i, (hotkey, coldkey)) in miner_hotkeys.iter().zip(miner_coldkeys.iter()).enumerate() { + register_ok_neuron(netuid, *hotkey, *coldkey, (i + 1) as u64); } // Add balance From a1bc8b5c9d48519921e8f3ffaaa49c40e2a1781b Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 11:48:27 +0800 Subject: [PATCH 17/32] cargo fix --- pallets/subtensor/src/tests/validator_cut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index fa1ff7dbc..8de14a523 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -699,8 +699,8 @@ fn test_emission_single_validator_multiple_miners_varying_cuts() { SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) - validator_before; let miners_emissions: Vec<_> = miner_coldkeys .iter() - .enumerate() - .map(|(i, ck)| SubtensorModule::get_total_stake_for_coldkey(ck) - miners_before[i]) + .zip(miners_before.iter()) + .map(|(ck, before)| SubtensorModule::get_total_stake_for_coldkey(ck) - *before) .collect(); let total_miner_emission = miners_emissions.iter().fold(0.into(), |acc, e| acc + *e); From 279cdf30c33fb99d469e877a27bcb9d955246985 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 11:50:19 +0800 Subject: [PATCH 18/32] cargo fix --- pallets/subtensor/src/tests/validator_cut.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index 8de14a523..dd4cfad31 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -811,10 +811,8 @@ fn test_emission_all_validator_miners_varying_cuts() { let emissions: Vec<_> = participants .iter() - .enumerate() - .map(|(i, (ck, _))| { - SubtensorModule::get_total_stake_for_coldkey(ck) - stakes_before[i] - }) + .zip(stakes_before.iter()) + .map(|((ck, _), before)| SubtensorModule::get_total_stake_for_coldkey(ck) - *before) .collect(); let total_emission: u64 = emissions.iter().map(|e| e.to_u64()).sum(); From e358179da53338c75d5696e29a4458977d14b016 Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 7 Oct 2025 11:53:59 +0800 Subject: [PATCH 19/32] bump version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 51932583d..fc3effd68 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -223,7 +223,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 326, + spec_version: 327, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, From cc9304d8eec8f16e3f2a3cae2fc8c8af31f20185 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 8 Oct 2025 12:58:27 +0000 Subject: [PATCH 20/32] auto-update benchmark weights --- pallets/admin-utils/src/lib.rs | 2 +- pallets/subtensor/src/macros/dispatches.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index f3cb4af77..402998bb2 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -731,7 +731,7 @@ pub mod pallet { /// It is only callable by the root account. /// The extrinsic will call the Subtensor pallet to set the target registrations per interval. #[pallet::call_index(21)] - #[pallet::weight(Weight::from_parts(44_320_000, 0) + #[pallet::weight(Weight::from_parts(25_550_000, 0) .saturating_add(::DbWeight::get().reads(3_u64)) .saturating_add(::DbWeight::get().writes(1_u64)))] pub fn sudo_set_target_registrations_per_interval( diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d16b93e32..c9dadd273 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1058,7 +1058,7 @@ mod dispatches { /// The extrinsic for user to change its hotkey in subnet or all subnets. #[pallet::call_index(70)] #[pallet::weight((Weight::from_parts(275_300_000, 0) - .saturating_add(T::DbWeight::get().reads(47)) + .saturating_add(T::DbWeight::get().reads(49_u64)) .saturating_add(T::DbWeight::get().writes(37)), DispatchClass::Normal, Pays::No))] pub fn swap_hotkey( origin: OriginFor, @@ -2261,7 +2261,7 @@ mod dispatches { /// * commit_reveal_version (`u16`): /// - The client (bittensor-drand) version #[pallet::call_index(113)] - #[pallet::weight((Weight::from_parts(63_160_000, 0) + #[pallet::weight((Weight::from_parts(92_350_000, 0) .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(2)), DispatchClass::Normal, Pays::No))] pub fn commit_timelocked_weights( @@ -2293,8 +2293,8 @@ mod dispatches { /// - The hotkey account to designate as the autostake destination. #[pallet::call_index(114)] #[pallet::weight((Weight::from_parts(29_930_000, 0) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::No))] + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)), DispatchClass::Normal, Pays::No))] pub fn set_coldkey_auto_stake_hotkey( origin: T::RuntimeOrigin, netuid: NetUid, From 85b082ab933d70a53abd2d669bf48a7744c268a4 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 07:49:35 +0800 Subject: [PATCH 21/32] minx max limit --- pallets/admin-utils/src/tests/mod.rs | 20 ++++++++++++++------ pallets/subtensor/src/lib.rs | 13 +++++++++++++ pallets/subtensor/src/macros/errors.rs | 2 ++ pallets/subtensor/src/staking/helpers.rs | 4 ++++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index e99b70250..fac0dc67a 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2939,7 +2939,7 @@ fn test_set_validator_cut() { fn test_sudo_set_validator_cut() { new_test_ext().execute_with(|| { let netuid = NetUid::from(3); - let to_be_set: u64 = 4200; // 42% cut + let to_be_set: u64 = u64::MAX / 3; // Set up a network add_network(netuid, 10); @@ -2977,7 +2977,7 @@ fn test_sudo_set_validator_cut() { fn test_sudo_set_validator_cut_root() { new_test_ext().execute_with(|| { let netuid = NetUid::from(4); - let to_be_set: u64 = 10000; // 100% cut + let to_be_set: u64 = u64::MAX / 3; // Set up a network add_network(netuid, 10); @@ -3005,11 +3005,19 @@ fn test_validator_cut_bounds() { add_network(netuid, 10); // Test minimum value - SubtensorModule::set_validator_cut(netuid, min_cut); - assert_eq!(SubtensorModule::get_validator_cut(netuid), min_cut); + assert_err!( + SubtensorModule::set_validator_cut(netuid, min_cut), + Error::::InvalidValidatorCut + ); // Test maximum value - SubtensorModule::set_validator_cut(netuid, max_cut); - assert_eq!(SubtensorModule::get_validator_cut(netuid), max_cut); + assert_err!( + SubtensorModule::set_validator_cut(netuid, max_cut), + Error::::InvalidValidatorCut + ); + assert_eq!( + SubtensorModule::get_validator_cut(netuid), + DefaultValidatorCut::::get() + ); }); } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 12d6d2b0a..417f8dd2b 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1559,6 +1559,19 @@ pub mod pallet { pub type ImmuneOwnerUidsLimit = StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmuneOwnerUidsLimit>; + #[pallet::type_value] + pub fn MinValidatorCut() -> u64 { + /// Min validator cut 25% + u64::MAX + / 4 + } + + #[pallet::type_value] + /// Max validator cut 75% + pub fn MaxValidatorCut() -> u64 { + u64::MAX / 4 * 3 + } + #[pallet::type_value] /// Default validator cut 50% pub fn DefaultValidatorCut() -> u64 { diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index e241ff068..e630f1eab 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -262,5 +262,7 @@ mod errors { UidMapCouldNotBeCleared, /// Trimming would exceed the max immune neurons percentage TrimmingWouldExceedMaxImmunePercentage, + /// Invalid validator cut + InvalidValidatorCut, } } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 30050a7c9..2bd68105f 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -343,6 +343,10 @@ impl Pallet { /// * `netuid` - The network UID. /// * `cut` - The validator cut value to set. pub fn set_validator_cut(netuid: NetUid, cut: u64) { + ensure!( + cut >= MinValidatorCut::::get() && cut <= MaxValidatorCut::::get(), + Error::::InvalidValidatorCut + ); ValidatorCut::::insert(netuid, cut); } From 43fce2e1535e60f0d3407eecd1f6db9ce78cffe2 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 18:37:18 +0800 Subject: [PATCH 22/32] add min/max value for validator cut --- 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 417f8dd2b..413e79788 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1561,13 +1561,13 @@ pub mod pallet { #[pallet::type_value] pub fn MinValidatorCut() -> u64 { - /// Min validator cut 25% + /// Min validator cut 25%, placeholder final value TBD u64::MAX / 4 } #[pallet::type_value] - /// Max validator cut 75% + /// Max validator cut 75%, placeholder final value TBD pub fn MaxValidatorCut() -> u64 { u64::MAX / 4 * 3 } From 3d6180f0370c2da46ec3765be67a6b30d087f034 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 18:42:04 +0800 Subject: [PATCH 23/32] commit Cargo.lock --- pallets/admin-utils/src/lib.rs | 2 +- pallets/subtensor/src/lib.rs | 5 ++--- pallets/subtensor/src/staking/helpers.rs | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 402998bb2..092d9687e 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -2160,7 +2160,7 @@ pub mod pallet { &[TransactionType::SetValidatorCut], )?; - pallet_subtensor::Pallet::::set_validator_cut(netuid, cut); + pallet_subtensor::Pallet::::set_validator_cut(netuid, cut)?; log::debug!("ValidatorCutSet( netuid: {netuid:?}, cut: {cut:?} ) "); pallet_subtensor::Pallet::::record_owner_rl( maybe_owner, diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 413e79788..aa49d380e 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1560,10 +1560,9 @@ pub mod pallet { StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmuneOwnerUidsLimit>; #[pallet::type_value] + /// Min validator cut 25%, placeholder final value TBD pub fn MinValidatorCut() -> u64 { - /// Min validator cut 25%, placeholder final value TBD - u64::MAX - / 4 + u64::MAX / 4 } #[pallet::type_value] diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 2bd68105f..cbd962a56 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -342,12 +342,13 @@ impl Pallet { /// # Arguments /// * `netuid` - The network UID. /// * `cut` - The validator cut value to set. - pub fn set_validator_cut(netuid: NetUid, cut: u64) { + pub fn set_validator_cut(netuid: NetUid, cut: u64) -> DispatchResult { ensure!( cut >= MinValidatorCut::::get() && cut <= MaxValidatorCut::::get(), Error::::InvalidValidatorCut ); ValidatorCut::::insert(netuid, cut); + Ok(()) } pub fn burn_subnet_alpha(_netuid: NetUid, _amount: AlphaCurrency) { From e6b261259ec57054b0891cd8b1629d565fa3fc98 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 18:50:26 +0800 Subject: [PATCH 24/32] commit Cargo.lock --- pallets/admin-utils/src/tests/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index fac0dc67a..6054cc948 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -3007,7 +3007,7 @@ fn test_validator_cut_bounds() { // Test minimum value assert_err!( SubtensorModule::set_validator_cut(netuid, min_cut), - Error::::InvalidValidatorCut + DispatchError::from(Error::::InvalidValidatorCut) ); // Test maximum value From fd40e3dea2b3eb103523fb261c192862f8f073ce Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 18:51:19 +0800 Subject: [PATCH 25/32] commit Cargo.lock --- pallets/admin-utils/src/tests/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 6054cc948..a62b204f2 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -3007,13 +3007,13 @@ fn test_validator_cut_bounds() { // Test minimum value assert_err!( SubtensorModule::set_validator_cut(netuid, min_cut), - DispatchError::from(Error::::InvalidValidatorCut) + DispatchError::from(pallet_subtensor::Error::::InvalidValidatorCut) ); // Test maximum value assert_err!( SubtensorModule::set_validator_cut(netuid, max_cut), - Error::::InvalidValidatorCut + DispatchError::from(pallet_subtensor::Error::::InvalidValidatorCut) ); assert_eq!( SubtensorModule::get_validator_cut(netuid), From 5a9c7f90a624cda72db6f4dd3cdb5a2caa1115d5 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 18:52:34 +0800 Subject: [PATCH 26/32] cargo clippy --- pallets/subtensor/src/tests/validator_cut.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index dd4cfad31..8f7c106d8 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -77,7 +77,7 @@ fn test_emission_with_different_cut() { let validator_stake_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); let miner_stake_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); - SubtensorModule::set_validator_cut(netuid, cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, cut)); step_block(subnet_tempo); @@ -316,7 +316,7 @@ fn test_emission_with_multiple_validators_varying_cuts() { let miner1_before = SubtensorModule::get_total_stake_for_coldkey(&miner1_coldkey); let miner2_before = SubtensorModule::get_total_stake_for_coldkey(&miner2_coldkey); - SubtensorModule::set_validator_cut(netuid, cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, cut)); step_block(subnet_tempo); let val1_emission = @@ -445,7 +445,7 @@ fn test_emission_with_child_keys_and_varying_cuts() { let delegate2_before = SubtensorModule::get_total_stake_for_coldkey(&delegate_coldkey2); let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); - SubtensorModule::set_validator_cut(netuid, cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, cut)); step_block(subnet_tempo); let validator_emission = @@ -582,7 +582,7 @@ fn test_emission_with_unequal_validator_stakes_varying_cuts() { let val2_before = SubtensorModule::get_total_stake_for_coldkey(&validator2_coldkey); let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); - SubtensorModule::set_validator_cut(netuid, cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, cut)); step_block(subnet_tempo); let val1_emission = @@ -692,7 +692,7 @@ fn test_emission_single_validator_multiple_miners_varying_cuts() { .map(SubtensorModule::get_total_stake_for_coldkey) .collect(); - SubtensorModule::set_validator_cut(netuid, cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, cut)); step_block(subnet_tempo); let validator_emission = From c3077aa185ff590b7cc91001ed24955fb5aaa67b Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 18:53:06 +0800 Subject: [PATCH 27/32] commit Cargo.lock --- pallets/subtensor/src/tests/validator_cut.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index 8f7c106d8..4b2a287b0 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -806,7 +806,7 @@ fn test_emission_all_validator_miners_varying_cuts() { .map(|(ck, _)| SubtensorModule::get_total_stake_for_coldkey(ck)) .collect(); - SubtensorModule::set_validator_cut(netuid, cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, cut)); step_block(subnet_tempo); let emissions: Vec<_> = participants @@ -918,7 +918,7 @@ fn test_emission_extreme_cuts_edge_cases() { let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); - SubtensorModule::set_validator_cut(netuid, u64::MAX); + assert_ok!(SubtensorModule::set_validator_cut(netuid, u64::MAX)); step_block(subnet_tempo); let validator_emission = From dbaf5c2cdc81ac2531cd11d7e61b68bbb4c0d846 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 18:53:47 +0800 Subject: [PATCH 28/32] commit Cargo.lock --- pallets/admin-utils/src/tests/mod.rs | 4 ++-- pallets/subtensor/src/tests/validator_cut.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index a62b204f2..6877fb59c 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2906,7 +2906,7 @@ fn test_get_validator_cut() { add_network(netuid, 10); // Set a validator cut value - SubtensorModule::set_validator_cut(netuid, expected_cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, expected_cut)); // Test that we can retrieve the value let retrieved_cut = SubtensorModule::get_validator_cut(netuid); @@ -2928,7 +2928,7 @@ fn test_set_validator_cut() { assert_eq!(SubtensorModule::get_validator_cut(netuid), initial_cut); // Set new validator cut - SubtensorModule::set_validator_cut(netuid, new_cut); + assert_ok!(SubtensorModule::set_validator_cut(netuid, new_cut)); // Verify the value was set correctly assert_eq!(SubtensorModule::get_validator_cut(netuid), new_cut); diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index 4b2a287b0..226272793 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -899,7 +899,7 @@ fn test_emission_extreme_cuts_edge_cases() { let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); - SubtensorModule::set_validator_cut(netuid, 0); + assert_ok!(SubtensorModule::set_validator_cut(netuid, 0)); step_block(subnet_tempo); let validator_emission = From 51e97e2fd03417ca1ed7b197f431e7aa7dfbb999 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 19:43:42 +0800 Subject: [PATCH 29/32] fix unit tests --- pallets/admin-utils/src/tests/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 6877fb59c..9183e75b1 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2900,7 +2900,7 @@ fn test_sudo_set_min_allowed_uids() { fn test_get_validator_cut() { new_test_ext().execute_with(|| { let netuid = NetUid::from(1); - let expected_cut: u64 = 5000; // 50% cut + let expected_cut: u64 = u64::MAX / 2; // 50% cut // Set up a network add_network(netuid, 10); @@ -2919,7 +2919,7 @@ fn test_set_validator_cut() { new_test_ext().execute_with(|| { let netuid = NetUid::from(2); let initial_cut: u64 = pallet_subtensor::DefaultValidatorCut::::get(); - let new_cut: u64 = 7500; // 75% cut + let new_cut: u64 = u64::MAX / 3; // 33% cut // Set up a network add_network(netuid, 10); From c551ca2b5898fe446d07d77d88530cf8a73ee228 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 21:28:04 +0800 Subject: [PATCH 30/32] update boundary --- pallets/subtensor/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index aa49d380e..1cb1e47fa 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1560,15 +1560,15 @@ pub mod pallet { StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultImmuneOwnerUidsLimit>; #[pallet::type_value] - /// Min validator cut 25%, placeholder final value TBD + /// Min validator cut 1%, placeholder final value TBD pub fn MinValidatorCut() -> u64 { - u64::MAX / 4 + u64::MAX / 100 } #[pallet::type_value] - /// Max validator cut 75%, placeholder final value TBD + /// Max validator cut 99%, placeholder final value TBD pub fn MaxValidatorCut() -> u64 { - u64::MAX / 4 * 3 + u64::MAX / 100 * 99 } #[pallet::type_value] From e974265e4755814b9ceb750a57617428941ab4bc Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 9 Oct 2025 23:22:06 +0800 Subject: [PATCH 31/32] fix unit tests --- pallets/subtensor/src/tests/validator_cut.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index 226272793..27fe1dd95 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -20,14 +20,7 @@ fn test_emission_with_different_cut() { let subnet_tempo = 10; let stake = 100_000_000_000; - let cut_list = [ - 0, - u64::MAX / 100, - u64::MAX / 10, - u64::MAX / 4, - u64::MAX / 2, - u64::MAX, - ]; + let cut_list = [u64::MAX / 50, u64::MAX / 10, u64::MAX / 4, u64::MAX / 2]; // Add network, register hotkeys, and setup network parameters add_network(netuid, subnet_tempo, 0); @@ -236,7 +229,7 @@ fn test_emission_with_multiple_validators_varying_cuts() { let subnet_tempo = 10; let stake = 100_000_000_000; - let cut_list = [0, u64::MAX / 4, u64::MAX / 2, u64::MAX]; + let cut_list = [u64::MAX / 4, u64::MAX / 2, u64::MAX / 10]; // Add network and register all neurons add_network(netuid, subnet_tempo, 0); @@ -372,7 +365,7 @@ fn test_emission_with_child_keys_and_varying_cuts() { let validator_stake = 100_000_000_000; let delegate_stake = 50_000_000_000; - let cut_list = [0, u64::MAX / 10, u64::MAX / 2, u64::MAX]; + let cut_list = [u64::MAX / 10, u64::MAX / 4, u64::MAX / 2]; // Setup network add_network(netuid, subnet_tempo, 0); @@ -518,7 +511,7 @@ fn test_emission_with_unequal_validator_stakes_varying_cuts() { let large_stake = 200_000_000_000; let small_stake = 50_000_000_000; - let cut_list = [u64::MAX / 10, u64::MAX / 4, u64::MAX / 2, u64::MAX]; + let cut_list = [u64::MAX / 10, u64::MAX / 4, u64::MAX / 2]; // Setup network add_network(netuid, subnet_tempo, 0); @@ -631,7 +624,7 @@ fn test_emission_single_validator_multiple_miners_varying_cuts() { let subnet_tempo = 10; let stake = 100_000_000_000; - let cut_list = [0, u64::MAX / 3, u64::MAX / 2, u64::MAX / 4 * 3, u64::MAX]; + let cut_list = [u64::MAX / 3, u64::MAX / 2, u64::MAX / 4 * 3]; // Setup network add_network(netuid, subnet_tempo, 0); @@ -747,7 +740,7 @@ fn test_emission_all_validator_miners_varying_cuts() { let subnet_tempo = 10; let stake = 100_000_000_000; - let cut_list = [u64::MAX / 10, u64::MAX / 4, u64::MAX / 2, u64::MAX]; + let cut_list = [u64::MAX / 10, u64::MAX / 4, u64::MAX / 2]; // Setup network add_network(netuid, subnet_tempo, 0); From 466cf5d696295d6045c2cf6feec90e80b8939de8 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 10 Oct 2025 19:42:17 +0800 Subject: [PATCH 32/32] remove invalid edge case --- pallets/subtensor/src/tests/validator_cut.rs | 88 -------------------- 1 file changed, 88 deletions(-) diff --git a/pallets/subtensor/src/tests/validator_cut.rs b/pallets/subtensor/src/tests/validator_cut.rs index 27fe1dd95..595555cae 100644 --- a/pallets/subtensor/src/tests/validator_cut.rs +++ b/pallets/subtensor/src/tests/validator_cut.rs @@ -838,91 +838,3 @@ fn test_emission_all_validator_miners_varying_cuts() { } }); } - -#[test] -fn test_emission_extreme_cuts_edge_cases() { - // Test extreme validator cut values: 0 (all to miners) and MAX (all to validators) - new_test_ext(1).execute_with(|| { - let validator_coldkey = U256::from(1); - let validator_hotkey = U256::from(2); - let miner_coldkey = U256::from(3); - let miner_hotkey = U256::from(4); - let netuid = NetUid::from(1); - let subnet_tempo = 10; - let stake = 100_000_000_000; - - // Setup network - add_network(netuid, subnet_tempo, 0); - register_ok_neuron(netuid, validator_hotkey, validator_coldkey, 0); - register_ok_neuron(netuid, miner_hotkey, miner_coldkey, 1); - - SubtensorModule::add_balance_to_coldkey_account( - &validator_coldkey, - stake + ExistentialDeposit::get(), - ); - SubtensorModule::add_balance_to_coldkey_account( - &miner_coldkey, - stake + ExistentialDeposit::get(), - ); - - SubtensorModule::set_weights_set_rate_limit(netuid, 0); - step_block(subnet_tempo); - SubnetOwnerCut::::set(0); - MaxAllowedUids::::set(netuid, 2); - SubtensorModule::set_max_allowed_validators(netuid, 1); - - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(validator_coldkey), - validator_hotkey, - netuid, - stake.into() - )); - - Weights::::insert(NetUidStorageIndex::from(netuid), 0, vec![(1, 0xFFFF)]); - - BlockAtRegistration::::set(netuid, 0, 1); - BlockAtRegistration::::set(netuid, 1, 1); - LastUpdate::::set(NetUidStorageIndex::from(netuid), vec![2, 2]); - Kappa::::set(netuid, u16::MAX / 5); - ActivityCutoff::::set(netuid, u16::MAX); - ValidatorPermit::::insert(netuid, vec![true, false]); - - // Test cut = 0: all emissions should go to miners - { - let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); - let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); - - assert_ok!(SubtensorModule::set_validator_cut(netuid, 0)); - step_block(subnet_tempo); - - let validator_emission = - SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) - validator_before; - let miner_emission = - SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_before; - - // Validator should get nearly nothing (within rounding error) - assert_abs_diff_eq!(validator_emission, 0.into(), epsilon = 5.into()); - // Miner should get almost all emissions - assert!(miner_emission.to_u64() > 0); - } - - // Test cut = u64::MAX: all emissions should go to validators - { - let validator_before = SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey); - let miner_before = SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey); - - assert_ok!(SubtensorModule::set_validator_cut(netuid, u64::MAX)); - step_block(subnet_tempo); - - let validator_emission = - SubtensorModule::get_total_stake_for_coldkey(&validator_coldkey) - validator_before; - let miner_emission = - SubtensorModule::get_total_stake_for_coldkey(&miner_coldkey) - miner_before; - - // Miner should get nearly nothing (within rounding error) - assert_abs_diff_eq!(miner_emission, 0.into(), epsilon = 5.into()); - // Validator should get almost all emissions - assert!(validator_emission.to_u64() > 0); - } - }); -}