From 842c2aee503aa2e183f65f74deff10dc49be41ee Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Wed, 5 Nov 2025 18:44:32 +0300 Subject: [PATCH 1/2] Separate admin freeze window check from rate limit check --- pallets/admin-utils/src/lib.rs | 27 ++++++++++++++++++++++ pallets/subtensor/src/tests/ensure.rs | 15 ++++++++---- pallets/subtensor/src/utils/misc.rs | 33 ++++----------------------- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 01e9e7b33e..f8f9a56838 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -236,6 +236,7 @@ pub mod pallet { netuid, &[Hyperparameter::ServingRateLimit.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_serving_rate_limit(netuid, serving_rate_limit); log::debug!("ServingRateLimitSet( serving_rate_limit: {serving_rate_limit:?} ) "); pallet_subtensor::Pallet::::record_owner_rl( @@ -288,6 +289,7 @@ pub mod pallet { netuid, &[Hyperparameter::MaxDifficulty.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -322,6 +324,7 @@ pub mod pallet { netuid, &[TransactionType::SetWeightsVersionKey], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -413,6 +416,7 @@ pub mod pallet { netuid, &[Hyperparameter::AdjustmentAlpha.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -445,6 +449,7 @@ pub mod pallet { netuid, &[Hyperparameter::ImmunityPeriod.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -479,6 +484,7 @@ pub mod pallet { netuid, &[Hyperparameter::MinAllowedWeights.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -513,6 +519,7 @@ pub mod pallet { netuid, &[Hyperparameter::MaxAllowedUids.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -572,6 +579,7 @@ pub mod pallet { netuid, &[Hyperparameter::Rho.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -604,6 +612,7 @@ pub mod pallet { netuid, &[Hyperparameter::ActivityCutoff.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -672,6 +681,7 @@ pub mod pallet { netuid, &[Hyperparameter::PowRegistrationAllowed.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_network_pow_registration_allowed( netuid, @@ -733,6 +743,7 @@ pub mod pallet { netuid, &[Hyperparameter::MinBurn.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -773,6 +784,7 @@ pub mod pallet { netuid, &[Hyperparameter::MaxBurn.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), Error::::SubnetDoesNotExist @@ -868,6 +880,7 @@ pub mod pallet { netuid, &[Hyperparameter::BondsMovingAverage.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; if maybe_owner.is_some() { ensure!( bonds_moving_average <= 975000, @@ -908,6 +921,7 @@ pub mod pallet { netuid, &[Hyperparameter::BondsPenalty.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1245,6 +1259,7 @@ pub mod pallet { netuid, &[Hyperparameter::CommitRevealEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1288,6 +1303,7 @@ pub mod pallet { netuid, &[Hyperparameter::LiquidAlphaEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_liquid_alpha_enabled(netuid, enabled); log::debug!("LiquidAlphaEnableToggled( netuid: {netuid:?}, Enabled: {enabled:?} ) "); pallet_subtensor::Pallet::::record_owner_rl( @@ -1318,6 +1334,7 @@ pub mod pallet { netuid, &[Hyperparameter::AlphaValues.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; let res = pallet_subtensor::Pallet::::do_set_alpha_values( origin, netuid, alpha_low, alpha_high, ); @@ -1437,6 +1454,7 @@ pub mod pallet { netuid, &[Hyperparameter::WeightCommitInterval.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1535,6 +1553,7 @@ pub mod pallet { netuid, &[Hyperparameter::TransferEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; let res = pallet_subtensor::Pallet::::toggle_transfer(netuid, toggle); if res.is_ok() { pallet_subtensor::Pallet::::record_owner_rl( @@ -1567,6 +1586,7 @@ pub mod pallet { netuid, &[Hyperparameter::RecycleOrBurn.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_recycle_or_burn(netuid, recycle_or_burn); pallet_subtensor::Pallet::::record_owner_rl( @@ -1734,6 +1754,7 @@ pub mod pallet { netuid, &[Hyperparameter::AlphaSigmoidSteepness.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; ensure!( pallet_subtensor::Pallet::::if_subnet_exist(netuid), @@ -1784,6 +1805,7 @@ pub mod pallet { netuid, &[Hyperparameter::Yuma3Enabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_yuma3_enabled(netuid, enabled); Self::deposit_event(Event::Yuma3EnableToggled { netuid, enabled }); @@ -1823,6 +1845,7 @@ pub mod pallet { netuid, &[Hyperparameter::BondsResetEnabled.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_bonds_reset(netuid, enabled); Self::deposit_event(Event::BondsResetToggled { netuid, enabled }); @@ -1948,6 +1971,7 @@ pub mod pallet { netuid, &[Hyperparameter::ImmuneNeuronLimit.into()], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::set_owner_immune_neuron_limit(netuid, immune_neurons)?; pallet_subtensor::Pallet::::record_owner_rl( maybe_owner, @@ -2021,6 +2045,7 @@ pub mod pallet { netuid, &[TransactionType::MechanismCountUpdate], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::do_set_mechanism_count(netuid, mechanism_count)?; @@ -2047,6 +2072,7 @@ pub mod pallet { netuid, &[TransactionType::MechanismEmission], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::do_set_emission_split(netuid, maybe_split)?; @@ -2077,6 +2103,7 @@ pub mod pallet { netuid, &[TransactionType::MaxUidsTrimming], )?; + pallet_subtensor::Pallet::::ensure_admin_window_open(netuid)?; pallet_subtensor::Pallet::::trim_to_max_allowed_uids(netuid, max_n)?; diff --git a/pallets/subtensor/src/tests/ensure.rs b/pallets/subtensor/src/tests/ensure.rs index a59bfd7484..3b4db87f4e 100644 --- a/pallets/subtensor/src/tests/ensure.rs +++ b/pallets/subtensor/src/tests/ensure.rs @@ -111,6 +111,7 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { ) .expect("should pass"); assert_eq!(res, Some(owner)); + assert_ok!(crate::Pallet::::ensure_admin_window_open(netuid)); // Simulate previous update at current block -> next call should fail due to rate limit let now = crate::Pallet::::get_current_block_as_u64(); @@ -127,11 +128,14 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { // Advance beyond RL and ensure passes again run_to_block(now + 3); + TransactionType::from(Hyperparameter::Kappa) + .set_last_block_on_subnet::(&owner, netuid, 0); assert_ok!(crate::Pallet::::ensure_sn_owner_or_root_with_limits( <::RuntimeOrigin>::signed(owner), netuid, &[Hyperparameter::Kappa.into()] )); + assert_ok!(crate::Pallet::::ensure_admin_window_open(netuid)); // Now advance into the freeze window; ensure blocks // (using loop for clarity, because epoch calculation function uses netuid) @@ -148,12 +152,13 @@ fn ensure_owner_or_root_with_limits_checks_rl_and_freeze() { } run_to_block(cur + 1); } + assert_ok!(crate::Pallet::::ensure_sn_owner_or_root_with_limits( + <::RuntimeOrigin>::signed(owner), + netuid, + &[Hyperparameter::Kappa.into()] + )); assert_noop!( - crate::Pallet::::ensure_sn_owner_or_root_with_limits( - <::RuntimeOrigin>::signed(owner), - netuid, - &[Hyperparameter::Kappa.into()], - ), + crate::Pallet::::ensure_admin_window_open(netuid), crate::Error::::AdminActionProhibitedDuringWeightsWindow ); }); diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 0ba3df1103..7a14fafd6d 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -42,23 +42,16 @@ impl Pallet { netuid: NetUid, ) -> Result<(), DispatchError> { ensure_root(o)?; - let now = Self::get_current_block_as_u64(); - Self::ensure_not_in_admin_freeze_window(netuid, now)?; - Ok(()) + Self::ensure_admin_window_open(netuid) } /// Ensure owner-or-root with a set of TransactionType rate checks (owner only). - /// - Root: only freeze window is enforced; no TransactionType checks. - /// - Owner (Signed): freeze window plus all rate checks in `limits` using signer extracted from - /// origin. pub fn ensure_sn_owner_or_root_with_limits( o: T::RuntimeOrigin, netuid: NetUid, limits: &[crate::utils::rate_limiting::TransactionType], ) -> Result, DispatchError> { let maybe_who = Self::ensure_subnet_owner_or_root(o, netuid)?; - let now = Self::get_current_block_as_u64(); - Self::ensure_not_in_admin_freeze_window(netuid, now)?; if let Some(who) = maybe_who.as_ref() { for tx in limits.iter() { ensure!( @@ -70,26 +63,6 @@ impl Pallet { Ok(maybe_who) } - /// Ensure the caller is the subnet owner and passes all provided rate limits. - /// This does NOT allow root; it is strictly owner-only. - /// Returns the signer (owner) on success so callers may record last-blocks. - pub fn ensure_sn_owner_with_limits( - o: T::RuntimeOrigin, - netuid: NetUid, - limits: &[crate::utils::rate_limiting::TransactionType], - ) -> Result { - let who = Self::ensure_subnet_owner(o, netuid)?; - let now = Self::get_current_block_as_u64(); - Self::ensure_not_in_admin_freeze_window(netuid, now)?; - for tx in limits.iter() { - ensure!( - tx.passes_rate_limit_on_subnet::(&who, netuid), - Error::::TxRateLimitExceeded - ); - } - Ok(who) - } - /// Returns true if the current block is within the terminal freeze window of the tempo for the /// given subnet. During this window, admin ops are prohibited to avoid interference with /// validator weight submissions. @@ -103,7 +76,9 @@ impl Pallet { remaining < window } - fn ensure_not_in_admin_freeze_window(netuid: NetUid, now: u64) -> Result<(), DispatchError> { + /// Ensures the admin freeze window is not currently active for the given subnet. + pub fn ensure_admin_window_open(netuid: NetUid) -> Result<(), DispatchError> { + let now = Self::get_current_block_as_u64(); ensure!( !Self::is_in_admin_freeze_window(netuid, now), Error::::AdminActionProhibitedDuringWeightsWindow From 34dc6e73738eb2446abe5dc4b5542ed8f3bc3a2c Mon Sep 17 00:00:00 2001 From: Aliaksandr Tsurko Date: Wed, 5 Nov 2025 18:57:46 +0300 Subject: [PATCH 2/2] Update spec 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 266a755708..d95eb45ee9 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,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: 338, + spec_version: 339, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,