code/pallets/subtensor/src/utils/misc.rs

misc.rs

1,068 lines · 40,740 bytes · 19a6485969RawGitHub
use super::*;
use crate::Error;
use crate::system::{ensure_signed, ensure_signed_or_root, pallet_prelude::BlockNumberFor};
use safe_math::*;
use sp_core::Get;
use sp_core::U256;
use sp_runtime::{PerU16, Saturating};
use substrate_fixed::types::{I32F32, I64F64, U64F64, U96F32};
use subtensor_runtime_common::{AlphaBalance, NetUid, NetUidStorageIndex, TaoBalance};

impl<T: Config> Pallet<T> {
    pub fn ensure_subnet_owner_or_root(
        o: OriginFor<T>,
        netuid: NetUid,
    ) -> Result<Option<T::AccountId>, DispatchError> {
        let coldkey = ensure_signed_or_root(o);
        match coldkey {
            Ok(Some(who)) if SubnetOwner::<T>::get(netuid) == who => Ok(Some(who)),
            Ok(Some(_)) => Err(DispatchError::BadOrigin),
            Ok(None) => Ok(None),
            Err(x) => Err(x.into()),
        }
    }

    pub fn ensure_subnet_owner(
        o: OriginFor<T>,
        netuid: NetUid,
    ) -> Result<T::AccountId, DispatchError> {
        let coldkey = ensure_signed(o);
        match coldkey {
            Ok(who) if SubnetOwner::<T>::get(netuid) == who => Ok(who),
            Ok(_) => Err(DispatchError::BadOrigin),
            Err(x) => Err(x.into()),
        }
    }

    /// Ensure owner-or-root with a set of TransactionType rate checks (owner only).
    pub fn ensure_sn_owner_or_root_with_limits(
        o: OriginFor<T>,
        netuid: NetUid,
        limits: &[crate::utils::rate_limiting::TransactionType],
    ) -> Result<Option<T::AccountId>, DispatchError> {
        let maybe_who = Self::ensure_subnet_owner_or_root(o, netuid)?;
        if let Some(who) = maybe_who.as_ref() {
            for tx in limits.iter() {
                ensure!(
                    tx.passes_rate_limit_on_subnet::<T>(who, netuid),
                    Error::<T>::TxRateLimitExceeded
                );
            }
        }
        Ok(maybe_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. Engages immediately on a pending manual trigger (so the trigger
    /// arms the freeze for the entire countdown to `PendingEpochAt`).
    pub fn is_in_admin_freeze_window(netuid: NetUid, current_block: u64) -> bool {
        let tempo = Self::get_tempo(netuid);
        if tempo == 0 {
            return false;
        }
        let pending = PendingEpochAt::<T>::get(netuid);
        if pending > 0 && pending > current_block {
            return true;
        }
        let remaining = Self::blocks_until_next_auto_epoch(netuid, tempo, current_block);
        let window = AdminFreezeWindow::<T>::get() as u64;
        remaining < window
    }

    /// 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::<T>::AdminActionProhibitedDuringWeightsWindow
        );
        Ok(())
    }

    pub fn set_admin_freeze_window(window: u16) {
        AdminFreezeWindow::<T>::set(window);
        Self::deposit_event(Event::AdminFreezeWindowSet(window));
    }

    pub fn set_owner_hyperparam_rate_limit(epochs: u16) {
        OwnerHyperparamRateLimit::<T>::set(epochs);
        Self::deposit_event(Event::OwnerHyperparamRateLimitSet(epochs));
    }

    /// If owner is `Some`, record last-blocks for the provided `TransactionType`s.
    pub fn record_owner_rl(
        maybe_owner: Option<<T as frame_system::Config>::AccountId>,
        netuid: NetUid,
        txs: &[TransactionType],
    ) {
        if let Some(who) = maybe_owner {
            let now = Self::get_current_block_as_u64();
            for tx in txs {
                tx.set_last_block_on_subnet::<T>(&who, netuid, now);
            }
        }
    }

    // ========================
    // ==== Global Setters ====
    // ========================
    /// Unchecked tempo write used by tests, precompiles, and internal helpers.
    /// Does NOT reset `LastEpochBlock` — that is the responsibility of
    /// `AdminUtils::sudo_set_tempo` (owner-or-root), which performs the cycle
    /// reset explicitly via `apply_tempo_with_cycle_reset`.
    pub fn set_tempo_unchecked(netuid: NetUid, tempo: u16) {
        Tempo::<T>::insert(netuid, tempo);
        Self::deposit_event(Event::TempoSet(netuid, tempo));
    }

    /// Sets `Tempo` and resets the state-based scheduler anchor `LastEpochBlock`
    /// to the current block
    pub fn apply_tempo_with_cycle_reset(netuid: NetUid, tempo: u16) {
        Self::set_tempo_unchecked(netuid, tempo);
        let now = Self::get_current_block_as_u64();
        LastEpochBlock::<T>::insert(netuid, now);
    }

    pub fn set_last_adjustment_block(netuid: NetUid, last_adjustment_block: u64) {
        LastAdjustmentBlock::<T>::insert(netuid, last_adjustment_block);
    }
    pub fn set_blocks_since_last_step(netuid: NetUid, blocks_since_last_step: u64) {
        BlocksSinceLastStep::<T>::insert(netuid, blocks_since_last_step);
    }
    pub fn set_registrations_this_block(netuid: NetUid, registrations_this_block: u16) {
        RegistrationsThisBlock::<T>::insert(netuid, registrations_this_block);
    }
    pub fn set_last_mechanism_step_block(netuid: NetUid, last_mechanism_step_block: u64) {
        LastMechansimStepBlock::<T>::insert(netuid, last_mechanism_step_block);
    }
    pub fn set_registrations_this_interval(netuid: NetUid, registrations_this_interval: u16) {
        RegistrationsThisInterval::<T>::insert(netuid, registrations_this_interval);
    }
    pub fn set_pow_registrations_this_interval(
        netuid: NetUid,
        pow_registrations_this_interval: u16,
    ) {
        POWRegistrationsThisInterval::<T>::insert(netuid, pow_registrations_this_interval);
    }
    pub fn set_burn_registrations_this_interval(
        netuid: NetUid,
        burn_registrations_this_interval: u16,
    ) {
        BurnRegistrationsThisInterval::<T>::insert(netuid, burn_registrations_this_interval);
    }

    // ========================
    // ==== Global Getters ====
    // ========================
    pub fn get_current_block_as_u64() -> u64 {
        TryInto::try_into(<frame_system::Pallet<T>>::block_number())
            .ok()
            .expect("blockchain will not exceed 2^64 blocks; QED.")
    }

    // ==============================
    // ==== YumaConsensus params ====
    // ==============================
    /// Deprecated: Rank is no longer computed during epoch. Always returns empty.
    pub fn get_rank(_netuid: NetUid) -> Vec<u16> {
        Vec::new()
    }
    /// Deprecated: Trust is no longer computed during epoch. Always returns empty.
    pub fn get_trust(_netuid: NetUid) -> Vec<u16> {
        Vec::new()
    }
    pub fn get_active(netuid: NetUid) -> Vec<bool> {
        Active::<T>::get(netuid)
    }
    pub fn get_emission(netuid: NetUid) -> Vec<AlphaBalance> {
        Emission::<T>::get(netuid)
    }
    pub fn get_consensus(netuid: NetUid) -> Vec<u16> {
        Consensus::<T>::get(netuid)
            .into_iter()
            .map(PerU16::deconstruct)
            .collect()
    }
    pub fn get_incentive(netuid: NetUidStorageIndex) -> Vec<u16> {
        Incentive::<T>::get(netuid)
            .into_iter()
            .map(PerU16::deconstruct)
            .collect()
    }
    pub fn get_dividends(netuid: NetUid) -> Vec<u16> {
        Dividends::<T>::get(netuid)
            .into_iter()
            .map(PerU16::deconstruct)
            .collect()
    }
    /// Fetch LastUpdate for `netuid` and ensure its length is at least `get_subnetwork_n(netuid)`,
    /// padding with zeros if needed. Returns the (possibly padded) vector.
    pub fn get_last_update(netuid_index: NetUidStorageIndex) -> Vec<u64> {
        let netuid = Self::get_netuid(netuid_index);
        let target_len = Self::get_subnetwork_n(netuid) as usize;
        let mut v = LastUpdate::<T>::get(netuid_index);
        if v.len() < target_len {
            v.resize(target_len, 0);
        }
        v
    }
    /// Deprecated: PruningScores is no longer computed during epoch. Always returns empty.
    pub fn get_pruning_score(_netuid: NetUid) -> Vec<u16> {
        Vec::new()
    }
    pub fn get_validator_trust(netuid: NetUid) -> Vec<u16> {
        ValidatorTrust::<T>::get(netuid)
            .into_iter()
            .map(PerU16::deconstruct)
            .collect()
    }
    pub fn get_validator_permit(netuid: NetUid) -> Vec<bool> {
        ValidatorPermit::<T>::get(netuid)
    }

    // ==================================
    // ==== YumaConsensus UID params ====
    // ==================================
    pub fn set_last_update_for_uid(netuid: NetUidStorageIndex, uid: u16, last_update: u64) {
        let mut updated_last_update_vec = Self::get_last_update(netuid);
        let Some(updated_last_update) = updated_last_update_vec.get_mut(uid as usize) else {
            return;
        };
        *updated_last_update = last_update;
        LastUpdate::<T>::insert(netuid, updated_last_update_vec);
    }
    pub fn set_active_for_uid(netuid: NetUid, uid: u16, active: bool) {
        let mut updated_active_vec = Self::get_active(netuid);
        let Some(updated_active) = updated_active_vec.get_mut(uid as usize) else {
            return;
        };
        *updated_active = active;
        Active::<T>::insert(netuid, updated_active_vec);
    }
    pub fn set_validator_permit_for_uid(netuid: NetUid, uid: u16, validator_permit: bool) {
        let mut updated_validator_permits = Self::get_validator_permit(netuid);
        let Some(updated_validator_permit) = updated_validator_permits.get_mut(uid as usize) else {
            return;
        };
        *updated_validator_permit = validator_permit;
        ValidatorPermit::<T>::insert(netuid, updated_validator_permits);
    }
    pub fn set_stake_threshold(min_stake: u64) {
        StakeThreshold::<T>::put(min_stake);
        Self::deposit_event(Event::StakeThresholdSet(min_stake));
    }

    /// Deprecated: Rank is no longer computed. Always returns 0.
    pub fn get_rank_for_uid(_netuid: NetUid, _uid: u16) -> u16 {
        0
    }
    /// Deprecated: Trust is no longer computed. Always returns 0.
    pub fn get_trust_for_uid(_netuid: NetUid, _uid: u16) -> u16 {
        0
    }
    pub fn get_emission_for_uid(netuid: NetUid, uid: u16) -> AlphaBalance {
        let vec = Emission::<T>::get(netuid);
        vec.get(uid as usize).copied().unwrap_or_default()
    }
    pub fn get_active_for_uid(netuid: NetUid, uid: u16) -> bool {
        let vec = Active::<T>::get(netuid);
        vec.get(uid as usize).copied().unwrap_or(false)
    }
    pub fn get_consensus_for_uid(netuid: NetUid, uid: u16) -> u16 {
        let vec = Consensus::<T>::get(netuid);
        vec.get(uid as usize)
            .copied()
            .unwrap_or_default()
            .deconstruct()
    }
    pub fn get_incentive_for_uid(netuid: NetUidStorageIndex, uid: u16) -> u16 {
        let vec = Incentive::<T>::get(netuid);
        vec.get(uid as usize)
            .copied()
            .unwrap_or_default()
            .deconstruct()
    }
    pub fn get_dividends_for_uid(netuid: NetUid, uid: u16) -> u16 {
        let vec = Dividends::<T>::get(netuid);
        vec.get(uid as usize)
            .copied()
            .unwrap_or_default()
            .deconstruct()
    }
    pub fn get_last_update_for_uid(netuid: NetUidStorageIndex, uid: u16) -> u64 {
        let vec = LastUpdate::<T>::get(netuid);
        vec.get(uid as usize).copied().unwrap_or(0)
    }
    /// Deprecated: PruningScores is no longer computed. Always returns u16::MAX.
    pub fn get_pruning_score_for_uid(_netuid: NetUid, _uid: u16) -> u16 {
        u16::MAX
    }
    pub fn get_validator_trust_for_uid(netuid: NetUid, uid: u16) -> u16 {
        let vec = ValidatorTrust::<T>::get(netuid);
        vec.get(uid as usize)
            .copied()
            .unwrap_or_default()
            .deconstruct()
    }
    pub fn get_validator_permit_for_uid(netuid: NetUid, uid: u16) -> bool {
        let vec = ValidatorPermit::<T>::get(netuid);
        vec.get(uid as usize).copied().unwrap_or(false)
    }
    pub fn get_stake_threshold() -> u64 {
        StakeThreshold::<T>::get()
    }

    // ============================
    // ==== Subnetwork Getters ====
    // ============================
    pub fn get_tempo(netuid: NetUid) -> u16 {
        Tempo::<T>::get(netuid)
    }
    pub fn get_last_adjustment_block(netuid: NetUid) -> u64 {
        LastAdjustmentBlock::<T>::get(netuid)
    }
    pub fn get_blocks_since_last_step(netuid: NetUid) -> u64 {
        BlocksSinceLastStep::<T>::get(netuid)
    }
    pub fn get_difficulty(netuid: NetUid) -> U256 {
        U256::from(Self::get_difficulty_as_u64(netuid))
    }
    pub fn get_registrations_this_block(netuid: NetUid) -> u16 {
        RegistrationsThisBlock::<T>::get(netuid)
    }
    pub fn get_last_mechanism_step_block(netuid: NetUid) -> u64 {
        LastMechansimStepBlock::<T>::get(netuid)
    }
    pub fn get_registrations_this_interval(netuid: NetUid) -> u16 {
        RegistrationsThisInterval::<T>::get(netuid)
    }
    pub fn get_pow_registrations_this_interval(netuid: NetUid) -> u16 {
        POWRegistrationsThisInterval::<T>::get(netuid)
    }
    pub fn get_burn_registrations_this_interval(netuid: NetUid) -> u16 {
        BurnRegistrationsThisInterval::<T>::get(netuid)
    }
    pub fn get_neuron_block_at_registration(netuid: NetUid, neuron_uid: u16) -> u64 {
        BlockAtRegistration::<T>::get(netuid, neuron_uid)
    }
    /// Returns the minimum number of non-immortal & non-immune UIDs that must remain in a subnet.
    pub fn get_min_non_immune_uids(netuid: NetUid) -> u16 {
        MinNonImmuneUids::<T>::get(netuid)
    }

    /// Sets the minimum number of non-immortal & non-immune UIDs that must remain in a subnet.
    pub fn set_min_non_immune_uids(netuid: NetUid, min: u16) {
        MinNonImmuneUids::<T>::insert(netuid, min);
        Self::deposit_event(Event::MinNonImmuneUidsSet(netuid, min));
    }

    // ========================
    // ===== Take checks ======
    // ========================
    pub fn do_take_checks(coldkey: &T::AccountId, hotkey: &T::AccountId) -> Result<(), Error<T>> {
        // Ensure we are delegating a known key.
        ensure!(
            Self::hotkey_account_exists(hotkey),
            Error::<T>::HotKeyAccountNotExists
        );

        // Ensure that the coldkey is the owner.
        ensure!(
            Self::coldkey_owns_hotkey(coldkey, hotkey),
            Error::<T>::NonAssociatedColdKey
        );

        Ok(())
    }

    // ========================
    // === Token Management ===
    // ========================
    pub fn set_subnet_locked_balance(netuid: NetUid, amount: TaoBalance) {
        SubnetLocked::<T>::insert(netuid, amount);
    }
    pub fn get_subnet_locked_balance(netuid: NetUid) -> TaoBalance {
        SubnetLocked::<T>::get(netuid)
    }
    pub fn get_total_subnet_locked() -> TaoBalance {
        let mut total_subnet_locked: u64 = 0;
        for (_, locked) in SubnetLocked::<T>::iter() {
            total_subnet_locked.saturating_accrue(locked.into());
        }
        total_subnet_locked.into()
    }

    pub fn set_recycle_or_burn(netuid: NetUid, recycle_or_burn: RecycleOrBurnEnum) {
        RecycleOrBurn::<T>::insert(netuid, recycle_or_burn);
    }

    // ========================
    // ========= Sudo =========
    // ========================

    // Per-block epoch cap (dynamic tempo throttle)
    pub fn get_max_epochs_per_block() -> u8 {
        MaxEpochsPerBlock::<T>::get()
    }
    pub fn set_max_epochs_per_block(max_epochs_per_block: u8) {
        MaxEpochsPerBlock::<T>::put(max_epochs_per_block);
        Self::deposit_event(Event::MaxEpochsPerBlockSet(max_epochs_per_block));
    }

    // Configure tx rate limiting
    pub fn get_tx_rate_limit() -> u64 {
        TxRateLimit::<T>::get()
    }
    pub fn set_tx_rate_limit(tx_rate_limit: u64) {
        TxRateLimit::<T>::put(tx_rate_limit);
        Self::deposit_event(Event::TxRateLimitSet(tx_rate_limit));
    }
    pub fn get_tx_delegate_take_rate_limit() -> u64 {
        TxDelegateTakeRateLimit::<T>::get()
    }
    pub fn set_tx_delegate_take_rate_limit(tx_rate_limit: u64) {
        TxDelegateTakeRateLimit::<T>::put(tx_rate_limit);
        Self::deposit_event(Event::TxDelegateTakeRateLimitSet(tx_rate_limit));
    }
    pub fn set_min_delegate_take(take: PerU16) {
        MinDelegateTake::<T>::put(take);
        Self::deposit_event(Event::MinDelegateTakeSet(take));
    }
    pub fn set_max_delegate_take(take: PerU16) {
        MaxDelegateTake::<T>::put(take);
        Self::deposit_event(Event::MaxDelegateTakeSet(take));
    }
    pub fn get_min_delegate_take() -> u16 {
        MinDelegateTake::<T>::get().deconstruct()
    }
    pub fn get_max_delegate_take() -> u16 {
        MaxDelegateTake::<T>::get().deconstruct()
    }
    pub fn get_default_delegate_take() -> u16 {
        // Default to maximum
        MaxDelegateTake::<T>::get().deconstruct()
    }
    // get_default_childkey_take
    pub fn get_default_childkey_take() -> u16 {
        // Default to maximum
        MinChildkeyTake::<T>::get().deconstruct()
    }
    pub fn get_tx_childkey_take_rate_limit() -> u64 {
        TxChildkeyTakeRateLimit::<T>::get()
    }
    pub fn set_tx_childkey_take_rate_limit(tx_rate_limit: u64) {
        TxChildkeyTakeRateLimit::<T>::put(tx_rate_limit);
        Self::deposit_event(Event::TxChildKeyTakeRateLimitSet(tx_rate_limit));
    }
    pub fn set_min_childkey_take(take: PerU16) {
        MinChildkeyTake::<T>::put(take);
        Self::deposit_event(Event::MinChildKeyTakeSet(take));
    }
    pub fn set_min_childkey_take_for_subnet(netuid: NetUid, take: PerU16) {
        MinChildkeyTakePerSubnet::<T>::insert(netuid, take);
        Self::deposit_event(Event::MinChildKeyTakePerSubnetSet(netuid, take));
    }
    pub fn set_max_childkey_take(take: PerU16) {
        MaxChildkeyTake::<T>::put(take);
        Self::deposit_event(Event::MaxChildKeyTakeSet(take));
    }
    pub fn get_min_childkey_take() -> u16 {
        MinChildkeyTake::<T>::get().deconstruct()
    }
    pub fn get_min_childkey_take_for_subnet(netuid: NetUid) -> u16 {
        MinChildkeyTakePerSubnet::<T>::get(netuid).deconstruct()
    }
    pub fn get_effective_min_childkey_take(netuid: NetUid) -> u16 {
        Self::get_min_childkey_take().max(Self::get_min_childkey_take_for_subnet(netuid))
    }

    pub fn get_max_childkey_take() -> u16 {
        MaxChildkeyTake::<T>::get().deconstruct()
    }

    pub fn get_serving_rate_limit(netuid: NetUid) -> u64 {
        ServingRateLimit::<T>::get(netuid)
    }
    pub fn set_serving_rate_limit(netuid: NetUid, serving_rate_limit: u64) {
        ServingRateLimit::<T>::insert(netuid, serving_rate_limit);
        Self::deposit_event(Event::ServingRateLimitSet(netuid, serving_rate_limit));
    }

    pub fn get_min_difficulty(netuid: NetUid) -> u64 {
        MinDifficulty::<T>::get(netuid)
    }
    pub fn set_min_difficulty(netuid: NetUid, min_difficulty: u64) {
        MinDifficulty::<T>::insert(netuid, min_difficulty);
        Self::deposit_event(Event::MinDifficultySet(netuid, min_difficulty));
    }

    pub fn get_max_difficulty(netuid: NetUid) -> u64 {
        MaxDifficulty::<T>::get(netuid)
    }
    pub fn set_max_difficulty(netuid: NetUid, max_difficulty: u64) {
        MaxDifficulty::<T>::insert(netuid, max_difficulty);
        Self::deposit_event(Event::MaxDifficultySet(netuid, max_difficulty));
    }

    pub fn get_weights_version_key(netuid: NetUid) -> u64 {
        WeightsVersionKey::<T>::get(netuid)
    }
    pub fn set_weights_version_key(netuid: NetUid, weights_version_key: u64) {
        WeightsVersionKey::<T>::insert(netuid, weights_version_key);
        Self::deposit_event(Event::WeightsVersionKeySet(netuid, weights_version_key));
    }

    pub fn get_weights_set_rate_limit(netuid: NetUid) -> u64 {
        WeightsSetRateLimit::<T>::get(netuid)
    }
    pub fn set_weights_set_rate_limit(netuid: NetUid, weights_set_rate_limit: u64) {
        WeightsSetRateLimit::<T>::insert(netuid, weights_set_rate_limit);
        Self::deposit_event(Event::WeightsSetRateLimitSet(
            netuid,
            weights_set_rate_limit,
        ));
    }

    pub fn get_adjustment_interval(netuid: NetUid) -> u16 {
        AdjustmentInterval::<T>::get(netuid)
    }
    pub fn set_adjustment_interval(netuid: NetUid, adjustment_interval: u16) {
        AdjustmentInterval::<T>::insert(netuid, adjustment_interval);
        Self::deposit_event(Event::AdjustmentIntervalSet(netuid, adjustment_interval));
    }

    pub fn get_adjustment_alpha(netuid: NetUid) -> u64 {
        AdjustmentAlpha::<T>::get(netuid)
    }
    pub fn set_adjustment_alpha(netuid: NetUid, adjustment_alpha: u64) {
        AdjustmentAlpha::<T>::insert(netuid, adjustment_alpha);
        Self::deposit_event(Event::AdjustmentAlphaSet(netuid, adjustment_alpha));
    }

    pub fn set_validator_prune_len(netuid: NetUid, validator_prune_len: u64) {
        ValidatorPruneLen::<T>::insert(netuid, validator_prune_len);
        Self::deposit_event(Event::ValidatorPruneLenSet(netuid, validator_prune_len));
    }

    pub fn get_scaling_law_power(netuid: NetUid) -> u16 {
        ScalingLawPower::<T>::get(netuid)
    }
    pub fn set_scaling_law_power(netuid: NetUid, scaling_law_power: u16) {
        ScalingLawPower::<T>::insert(netuid, scaling_law_power);
        Self::deposit_event(Event::ScalingLawPowerSet(netuid, scaling_law_power));
    }

    #[inline(always)]
    pub const fn get_max_weight_limit(_netuid: NetUid) -> u16 {
        u16::MAX
    }

    pub fn get_immunity_period(netuid: NetUid) -> u16 {
        ImmunityPeriod::<T>::get(netuid)
    }
    pub fn set_immunity_period(netuid: NetUid, immunity_period: u16) {
        ImmunityPeriod::<T>::insert(netuid, immunity_period);
        Self::deposit_event(Event::ImmunityPeriodSet(netuid, immunity_period));
    }
    /// Check if a neuron is in immunity based on the current block
    pub fn get_neuron_is_immune(netuid: NetUid, uid: u16) -> bool {
        let registered_at = Self::get_neuron_block_at_registration(netuid, uid);
        let current_block = Self::get_current_block_as_u64();
        let immunity_period = Self::get_immunity_period(netuid);
        current_block.saturating_sub(registered_at) < u64::from(immunity_period)
    }

    pub fn get_min_allowed_weights(netuid: NetUid) -> u16 {
        MinAllowedWeights::<T>::get(netuid)
    }
    pub fn set_min_allowed_weights(netuid: NetUid, min_allowed_weights: u16) {
        MinAllowedWeights::<T>::insert(netuid, min_allowed_weights);
        Self::deposit_event(Event::MinAllowedWeightSet(netuid, min_allowed_weights));
    }

    pub fn get_min_allowed_uids(netuid: NetUid) -> u16 {
        MinAllowedUids::<T>::get(netuid)
    }
    pub fn set_min_allowed_uids(netuid: NetUid, min_allowed: u16) {
        MinAllowedUids::<T>::insert(netuid, min_allowed);
        Self::deposit_event(Event::MinAllowedUidsSet(netuid, min_allowed));
    }

    pub fn get_max_allowed_uids(netuid: NetUid) -> u16 {
        MaxAllowedUids::<T>::get(netuid)
    }
    pub fn set_max_allowed_uids(netuid: NetUid, max_allowed: u16) {
        MaxAllowedUids::<T>::insert(netuid, max_allowed);
        Self::deposit_event(Event::MaxAllowedUidsSet(netuid, max_allowed));
    }

    pub fn get_kappa(netuid: NetUid) -> u16 {
        Kappa::<T>::get(netuid)
    }
    pub fn set_kappa(netuid: NetUid, kappa: u16) {
        Kappa::<T>::insert(netuid, kappa);
        Self::deposit_event(Event::KappaSet(netuid, kappa));
    }
    pub fn get_commit_reveal_weights_enabled(netuid: NetUid) -> bool {
        CommitRevealWeightsEnabled::<T>::get(netuid)
    }
    pub fn set_commit_reveal_weights_enabled(netuid: NetUid, enabled: bool) {
        CommitRevealWeightsEnabled::<T>::set(netuid, enabled);
        Self::deposit_event(Event::CommitRevealEnabled(netuid, enabled));
    }
    pub fn get_commit_reveal_weights_version() -> u16 {
        CommitRevealWeightsVersion::<T>::get()
    }
    pub fn set_commit_reveal_weights_version(version: u16) {
        CommitRevealWeightsVersion::<T>::set(version);
        Self::deposit_event(Event::CommitRevealVersionSet(version));
    }
    pub fn get_rho(netuid: NetUid) -> u16 {
        Rho::<T>::get(netuid)
    }
    pub fn set_rho(netuid: NetUid, rho: u16) {
        Rho::<T>::insert(netuid, rho);
    }

    pub fn get_activity_cutoff(netuid: NetUid) -> u16 {
        ActivityCutoff::<T>::get(netuid)
    }
    pub fn set_activity_cutoff(netuid: NetUid, activity_cutoff: u16) {
        ActivityCutoff::<T>::insert(netuid, activity_cutoff);
        Self::deposit_event(Event::ActivityCutoffSet(netuid, activity_cutoff));
    }

    /// Effective activity cutoff in blocks, derived from `ActivityCutoffFactorMilli` and `Tempo`.
    /// `cutoff_blocks = (factor × tempo) / 1000`, clamped to ≥ 1.
    pub fn get_activity_cutoff_blocks(netuid: NetUid) -> u64 {
        let factor_milli = ActivityCutoffFactorMilli::<T>::get(netuid) as u64;
        let tempo = Self::get_tempo(netuid) as u64;
        factor_milli
            .saturating_mul(tempo)
            .checked_div(1000)
            .unwrap_or(0)
            .max(1)
    }

    pub fn get_activity_cutoff_factor_milli(netuid: NetUid) -> u32 {
        ActivityCutoffFactorMilli::<T>::get(netuid)
    }

    pub fn set_activity_cutoff_factor_milli(netuid: NetUid, factor_milli: u32) {
        ActivityCutoffFactorMilli::<T>::insert(netuid, factor_milli);
        Self::deposit_event(Event::ActivityCutoffFactorMilliSet {
            netuid,
            factor_milli,
        });
    }

    // Registration Toggle utils
    pub fn get_network_registration_allowed(netuid: NetUid) -> bool {
        NetworkRegistrationAllowed::<T>::get(netuid)
    }
    pub fn set_network_registration_allowed(netuid: NetUid, registration_allowed: bool) {
        NetworkRegistrationAllowed::<T>::insert(netuid, registration_allowed);
        Self::deposit_event(Event::RegistrationAllowed(netuid, registration_allowed));
    }

    pub fn get_network_pow_registration_allowed(netuid: NetUid) -> bool {
        NetworkPowRegistrationAllowed::<T>::get(netuid)
    }
    pub fn set_network_pow_registration_allowed(netuid: NetUid, registration_allowed: bool) {
        NetworkPowRegistrationAllowed::<T>::insert(netuid, registration_allowed);
        Self::deposit_event(Event::PowRegistrationAllowed(netuid, registration_allowed));
    }

    pub fn get_target_registrations_per_interval(netuid: NetUid) -> u16 {
        TargetRegistrationsPerInterval::<T>::get(netuid)
    }
    pub fn set_target_registrations_per_interval(
        netuid: NetUid,
        target_registrations_per_interval: u16,
    ) {
        TargetRegistrationsPerInterval::<T>::insert(netuid, target_registrations_per_interval);
        Self::deposit_event(Event::RegistrationPerIntervalSet(
            netuid,
            target_registrations_per_interval,
        ));
    }

    pub fn get_burn(netuid: NetUid) -> TaoBalance {
        Burn::<T>::get(netuid)
    }
    pub fn set_burn(netuid: NetUid, burn: TaoBalance) {
        Burn::<T>::insert(netuid, burn);
    }

    pub fn get_min_burn(netuid: NetUid) -> TaoBalance {
        MinBurn::<T>::get(netuid)
    }
    pub fn set_min_burn(netuid: NetUid, min_burn: TaoBalance) {
        MinBurn::<T>::insert(netuid, min_burn);
        Self::deposit_event(Event::MinBurnSet(netuid, min_burn));
    }

    pub fn get_max_burn(netuid: NetUid) -> TaoBalance {
        MaxBurn::<T>::get(netuid)
    }
    pub fn set_max_burn(netuid: NetUid, max_burn: TaoBalance) {
        MaxBurn::<T>::insert(netuid, max_burn);
        Self::deposit_event(Event::MaxBurnSet(netuid, max_burn));
    }

    pub fn get_difficulty_as_u64(netuid: NetUid) -> u64 {
        Difficulty::<T>::get(netuid)
    }
    pub fn set_difficulty(netuid: NetUid, difficulty: u64) {
        Difficulty::<T>::insert(netuid, difficulty);
        Self::deposit_event(Event::DifficultySet(netuid, difficulty));
    }

    pub fn get_max_allowed_validators(netuid: NetUid) -> u16 {
        MaxAllowedValidators::<T>::get(netuid)
    }
    pub fn set_max_allowed_validators(netuid: NetUid, max_allowed_validators: u16) {
        MaxAllowedValidators::<T>::insert(netuid, max_allowed_validators);
        Self::deposit_event(Event::MaxAllowedValidatorsSet(
            netuid,
            max_allowed_validators,
        ));
    }

    pub fn get_bonds_moving_average(netuid: NetUid) -> u64 {
        BondsMovingAverage::<T>::get(netuid)
    }
    pub fn set_bonds_moving_average(netuid: NetUid, bonds_moving_average: u64) {
        BondsMovingAverage::<T>::insert(netuid, bonds_moving_average);
        Self::deposit_event(Event::BondsMovingAverageSet(netuid, bonds_moving_average));
    }

    pub fn get_bonds_penalty(netuid: NetUid) -> u16 {
        BondsPenalty::<T>::get(netuid)
    }
    pub fn set_bonds_penalty(netuid: NetUid, bonds_penalty: u16) {
        BondsPenalty::<T>::insert(netuid, bonds_penalty);
        Self::deposit_event(Event::BondsPenaltySet(netuid, bonds_penalty));
    }

    pub fn get_bonds_reset(netuid: NetUid) -> bool {
        BondsResetOn::<T>::get(netuid)
    }
    pub fn set_bonds_reset(netuid: NetUid, bonds_reset: bool) {
        BondsResetOn::<T>::insert(netuid, bonds_reset);
        Self::deposit_event(Event::BondsResetOnSet(netuid, bonds_reset));
    }

    pub fn get_max_registrations_per_block(netuid: NetUid) -> u16 {
        MaxRegistrationsPerBlock::<T>::get(netuid)
    }
    pub fn set_max_registrations_per_block(netuid: NetUid, max_registrations_per_block: u16) {
        MaxRegistrationsPerBlock::<T>::insert(netuid, max_registrations_per_block);
        Self::deposit_event(Event::MaxRegistrationsPerBlockSet(
            netuid,
            max_registrations_per_block,
        ));
    }

    pub fn get_subnet_owner(netuid: NetUid) -> T::AccountId {
        SubnetOwner::<T>::get(netuid)
    }
    pub fn get_subnet_owner_cut() -> u16 {
        SubnetOwnerCut::<T>::get()
    }
    pub fn get_float_subnet_owner_cut() -> U96F32 {
        U96F32::saturating_from_num(SubnetOwnerCut::<T>::get())
            .safe_div(U96F32::saturating_from_num(u16::MAX))
    }
    pub fn set_subnet_owner_cut(subnet_owner_cut: u16) {
        SubnetOwnerCut::<T>::set(subnet_owner_cut);
        Self::deposit_event(Event::SubnetOwnerCutSet(subnet_owner_cut));
    }

    pub fn get_owned_hotkeys(coldkey: &T::AccountId) -> Vec<T::AccountId> {
        OwnedHotkeys::<T>::get(coldkey)
    }
    pub fn get_all_staked_hotkeys(coldkey: &T::AccountId) -> Vec<T::AccountId> {
        StakingHotkeys::<T>::get(coldkey)
    }

    pub fn get_rao_recycled(netuid: NetUid) -> TaoBalance {
        RAORecycledForRegistration::<T>::get(netuid)
    }
    pub fn set_rao_recycled(netuid: NetUid, rao_recycled: TaoBalance) {
        RAORecycledForRegistration::<T>::insert(netuid, rao_recycled);
        Self::deposit_event(Event::RAORecycledForRegistrationSet(netuid, rao_recycled));
    }
    pub fn increase_rao_recycled(netuid: NetUid, inc_rao_recycled: TaoBalance) {
        let curr_rao_recycled = Self::get_rao_recycled(netuid);
        let rao_recycled = curr_rao_recycled.saturating_add(inc_rao_recycled);
        Self::set_rao_recycled(netuid, rao_recycled);
    }

    pub fn is_subnet_owner(address: &T::AccountId) -> bool {
        SubnetOwner::<T>::iter_values().any(|owner| *address == owner)
    }

    /// The NominatorMinRequiredStake is the factor by which we multiply
    /// the DefaultMinStake to get nominator minimum stake. With DefaulMinStake
    /// of 0.001 TAO and NominatorMinRequiredStake of 100_000_000, the
    /// minimum nomination stake will be 0.1 TAO.
    pub fn get_nominator_min_required_stake() -> u64 {
        // Get the factor (It is stored in per-million format)
        let factor = NominatorMinRequiredStake::<T>::get();

        // Return the default minimum stake multiplied by factor
        // 21M * 10^9 * 10^6 fits u64, hence no need for fixed type usage here
        DefaultMinStake::<T>::get()
            .to_u64()
            .saturating_mul(factor)
            .safe_div(1_000_000)
    }

    pub fn set_nominator_min_required_stake(min_stake: u64) {
        NominatorMinRequiredStake::<T>::put(min_stake);
    }

    pub fn get_key_swap_cost() -> TaoBalance {
        T::KeySwapCost::get().into()
    }

    pub fn get_alpha_values(netuid: NetUid) -> (u16, u16) {
        AlphaValues::<T>::get(netuid)
    }

    pub fn set_alpha_values_32(netuid: NetUid, low: I32F32, high: I32F32) {
        let low =
            (low.saturating_mul(I32F32::saturating_from_num(u16::MAX))).saturating_to_num::<u16>();
        let high =
            (high.saturating_mul(I32F32::saturating_from_num(u16::MAX))).saturating_to_num::<u16>();
        AlphaValues::<T>::insert(netuid, (low, high));
    }

    pub fn get_alpha_values_32(netuid: NetUid) -> (I32F32, I32F32) {
        let (alpha_low, alpha_high): (u16, u16) = AlphaValues::<T>::get(netuid);
        let converted_low =
            I32F32::saturating_from_num(alpha_low).safe_div(I32F32::saturating_from_num(u16::MAX));
        let converted_high =
            I32F32::saturating_from_num(alpha_high).safe_div(I32F32::saturating_from_num(u16::MAX));

        (converted_low, converted_high)
    }

    pub fn set_alpha_sigmoid_steepness(netuid: NetUid, steepness: i16) {
        AlphaSigmoidSteepness::<T>::insert(netuid, steepness);
    }
    pub fn get_alpha_sigmoid_steepness(netuid: NetUid) -> I32F32 {
        let alpha = AlphaSigmoidSteepness::<T>::get(netuid);
        I32F32::saturating_from_num(alpha)
    }

    pub fn set_liquid_alpha_enabled(netuid: NetUid, enabled: bool) {
        LiquidAlphaOn::<T>::set(netuid, enabled);
    }

    pub fn get_liquid_alpha_enabled(netuid: NetUid) -> bool {
        LiquidAlphaOn::<T>::get(netuid)
    }

    pub fn set_yuma3_enabled(netuid: NetUid, enabled: bool) {
        Yuma3On::<T>::set(netuid, enabled);
    }

    pub fn get_yuma3_enabled(netuid: NetUid) -> bool {
        Yuma3On::<T>::get(netuid)
    }

    pub fn get_subtoken_enabled(netuid: NetUid) -> bool {
        SubtokenEnabled::<T>::get(netuid)
    }

    pub fn get_transfer_toggle(netuid: NetUid) -> bool {
        TransferToggle::<T>::get(netuid)
    }

    pub fn set_coldkey_swap_announcement_delay(duration: BlockNumberFor<T>) {
        ColdkeySwapAnnouncementDelay::<T>::set(duration);
        Self::deposit_event(Event::ColdkeySwapAnnouncementDelaySet(duration));
    }

    pub fn set_coldkey_swap_reannouncement_delay(duration: BlockNumberFor<T>) {
        ColdkeySwapReannouncementDelay::<T>::set(duration);
        Self::deposit_event(Event::ColdkeySwapReannouncementDelaySet(duration));
    }

    /// Set the duration for dissolve network
    ///
    /// # Arguments
    ///
    /// * `duration`: The blocks for dissolve network execution.
    ///
    /// # Effects
    ///
    /// * Update the DissolveNetworkScheduleDuration storage.
    /// * Emits a DissolveNetworkScheduleDurationSet evnet.
    pub fn set_dissolve_network_schedule_duration(duration: BlockNumberFor<T>) {
        DissolveNetworkScheduleDuration::<T>::set(duration);
        Self::deposit_event(Event::DissolveNetworkScheduleDurationSet(duration));
    }

    /// Set the owner hotkey for a subnet.
    ///
    /// # Arguments
    ///
    /// * `netuid`: The unique identifier for the subnet.
    /// * `hotkey`: The new hotkey for the subnet owner.
    ///
    /// # Effects
    ///
    /// * Update the SubnetOwnerHotkey storage.
    /// * Emits a SubnetOwnerHotkeySet event.
    pub fn set_subnet_owner_hotkey(netuid: NetUid, hotkey: &T::AccountId) -> DispatchResult {
        // Ensure that hotkey is not a special account
        ensure!(
            Self::is_subnet_account_id(hotkey).is_none(),
            Error::<T>::CannotUseSystemAccount
        );

        SubnetOwnerHotkey::<T>::insert(netuid, hotkey.clone());
        Self::deposit_event(Event::SubnetOwnerHotkeySet(netuid, hotkey.clone()));
        Ok(())
    }

    // Get the uid of the Owner Hotkey for a subnet.
    pub fn get_owner_uid(netuid: NetUid) -> Option<u16> {
        match SubnetOwnerHotkey::<T>::try_get(netuid) {
            Ok(owner_hotkey) => Uids::<T>::get(netuid, &owner_hotkey),
            Err(_) => None,
        }
    }

    /// Set the per-subnet limit (for the given `netuid`) on the number of **owner-immune**
    /// neurons (UIDs).
    ///
    /// The value must lie within the inclusive bounds defined by [`MinImmuneOwnerUidsLimit`]
    /// and [`MaxImmuneOwnerUidsLimit`]. If the bound check fails, this returns
    /// [`Error::<T>::InvalidValue`] and leaves storage unchanged.
    ///
    /// # Arguments
    /// * `netuid`: Identifier of the subnet to update.
    /// * `limit`: New inclusive upper bound for the count of owner-immune UIDs on this subnet.
    ///
    /// # Returns
    /// * `Ok(())` on success (value written to storage).
    /// * `Err(Error::<T>::InvalidValue)` if `limit` is outside `[MinImmuneOwnerUidsLimit, MaxImmuneOwnerUidsLimit]`.
    pub fn set_owner_immune_neuron_limit(netuid: NetUid, limit: u16) -> DispatchResult {
        ensure!(
            limit >= MinImmuneOwnerUidsLimit::<T>::get()
                && limit <= MaxImmuneOwnerUidsLimit::<T>::get(),
            Error::<T>::InvalidValue
        );

        ImmuneOwnerUidsLimit::<T>::insert(netuid, limit);
        Ok(())
    }

    /// Fetches the max number of subnet
    ///
    /// # Returns
    /// * `u16`: The max number of subnet
    ///
    pub fn get_max_subnets() -> u16 {
        SubnetLimit::<T>::get()
    }

    /// Sets the max number of subnet
    pub fn set_max_subnets(limit: u16) {
        SubnetLimit::<T>::put(limit);
        Self::deposit_event(Event::SubnetLimitSet(limit));
    }

    /// Sets TAO flow cutoff value (A)
    pub fn set_tao_flow_cutoff(flow_cutoff: I64F64) {
        TaoFlowCutoff::<T>::set(flow_cutoff);
    }

    /// Sets TAO flow normalization exponent (p)
    pub fn set_tao_flow_normalization_exponent(exponent: U64F64) {
        FlowNormExponent::<T>::set(exponent);
    }

    /// Sets TAO flow smoothing factor (alpha)
    pub fn set_tao_flow_smoothing_factor(smoothing_factor: u64) {
        FlowEmaSmoothingFactor::<T>::set(smoothing_factor);
    }

    /// Enables or disables net TAO flow (protocol cost deduction from emission shares).
    pub fn set_net_tao_flow_enabled(enabled: bool) {
        NetTaoFlowEnabled::<T>::set(enabled);
    }

    /// Multiply an integer `value` by a Q32 fixed-point factor.
    ///
    /// Q32 means:
    ///   1.0 == 1 << 32
    ///   0.5 == 1 << 31
    ///
    /// Safe / non-panicking:
    /// * uses saturating u128 multiplication
    /// * clamps back into u64 range
    pub fn mul_by_q32(value: u64, factor_q32: u64) -> u64 {
        let product: u128 = (value as u128).saturating_mul(factor_q32 as u128);
        let shifted: u128 = product >> 32;
        core::cmp::min(shifted, u64::MAX as u128) as u64
    }

    /// Exponentiation-by-squaring for Q32 values.
    ///
    /// Returns `base_q32 ^ exp` in Q32.
    ///
    /// Safe / non-panicking:
    /// * uses `mul_by_q32`, which is saturating/clamped
    pub fn pow_q32(base_q32: u64, exp: u16) -> u64 {
        let mut result: u64 = 1u64 << 32; // 1.0 in Q32
        let mut factor: u64 = base_q32;
        let mut power: u32 = u32::from(exp);

        while power > 0 {
            if (power & 1) == 1 {
                result = Self::mul_by_q32(result, factor);
            }

            power >>= 1;

            if power > 0 {
                factor = Self::mul_by_q32(factor, factor);
            }
        }

        result
    }

    /// Returns the per-block decay factor `f` in Q32
    pub fn decay_factor_q32(half_life: u16) -> u64 {
        if half_life == 0 {
            return 1u64 << 32; // 1.0
        }

        let one_q32: u64 = 1u64 << 32;
        let half_q32: u64 = 1u64 << 31; // 0.5

        let mut lo: u64 = 0;
        let mut hi: u64 = one_q32;

        while lo.saturating_add(1) < hi {
            let span: u64 = hi.saturating_sub(lo);
            let mid: u64 = lo.saturating_add(span >> 1);
            let mid_pow: u64 = Self::pow_q32(mid, half_life);

            if mid_pow > half_q32 {
                hi = mid;
            } else {
                lo = mid;
            }
        }

        lo
    }
}