Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions pallets/subtensor/src/benchmarks/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,15 @@ mod pallet_benchmarks {
Subtensor::<T>::set_network_rate_limit(1);
let amount: u64 = 100_000_000_000_000u64.saturating_mul(2);
add_balance_to_coldkey_account::<T>(&coldkey, amount.into());
let netuid = Subtensor::<T>::get_next_netuid();

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), hotkey.clone());

assert_eq!(
SubnetState::<T>::get(netuid),
Some(SubnetLifecycleState::Registered)
);
}

#[benchmark]
Expand Down Expand Up @@ -804,6 +810,11 @@ mod pallet_benchmarks {

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), netuid);

assert_eq!(
SubnetState::<T>::get(netuid),
Some(SubnetLifecycleState::Started)
);
}

#[benchmark]
Expand Down Expand Up @@ -1542,13 +1553,19 @@ mod pallet_benchmarks {
Subtensor::<T>::set_network_rate_limit(1);
let amount: u64 = 9_999_999_999_999;
add_balance_to_coldkey_account::<T>(&coldkey, amount.into());
let netuid = Subtensor::<T>::get_next_netuid();

#[extrinsic_call]
_(
RawOrigin::Signed(coldkey.clone()),
hotkey.clone(),
identity.clone(),
);

assert_eq!(
SubnetState::<T>::get(netuid),
Some(SubnetLifecycleState::Registered)
);
}

#[benchmark]
Expand Down Expand Up @@ -2873,6 +2890,11 @@ mod pallet_benchmarks {

#[extrinsic_call]
_(RawOrigin::Root, coldkey.clone(), netuid);

assert_eq!(
SubnetState::<T>::get(netuid),
Some(SubnetLifecycleState::PendingDissolution)
);
}

#[benchmark]
Expand All @@ -2883,6 +2905,11 @@ mod pallet_benchmarks {

#[extrinsic_call]
_(RawOrigin::Root, netuid);

assert_eq!(
SubnetState::<T>::get(netuid),
Some(SubnetLifecycleState::PendingDissolution)
);
}

#[benchmark]
Expand Down
40 changes: 40 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,38 @@ pub mod pallet {
pub placeholder2: u8,
}

/// User-visible lifecycle state of a subnet.
///
/// This state is deliberately separate from [`NetworksAdded`]. The latter remains the
/// authority for whether runtime operations may target a subnet, while this state keeps a
/// dissolved subnet visible to reporting APIs until its deferred cleanup has settled stake.
#[derive(
Encode,
Decode,
DecodeWithMemTracking,
TypeInfo,
MaxEncodedLen,
Clone,
Copy,
PartialEq,
Eq,
Debug,
)]
pub enum SubnetLifecycleState {
/// The subnet exists but its start call has not completed.
#[codec(index = 0)]
Registered,
/// The subnet's start call has completed.
#[codec(index = 1)]
Started,
/// The subnet has left the active set and is waiting for cleanup.
#[codec(index = 2)]
PendingDissolution,
/// The subnet is currently being cleaned up by `on_idle`.
#[codec(index = 3)]
Dissolving,
}

/// Struct for NeuronCertificate.
pub type NeuronCertificateOf = NeuronCertificate;
/// Data structure for NeuronCertificate information.
Expand Down Expand Up @@ -2129,6 +2161,14 @@ pub mod pallet {
pub type NetworksAdded<T: Config> =
StorageMap<_, Identity, NetUid, bool, ValueQuery, DefaultNeworksAdded<T>>;

/// MAP (netuid) --> user-visible subnet lifecycle state.
///
/// Absence means that the netuid is neither an existing subnet nor one whose deferred
/// dissolution cleanup is still in progress.
#[pallet::storage]
pub type SubnetState<T: Config> =
StorageMap<_, Identity, NetUid, SubnetLifecycleState, OptionQuery>;

/// DMAP ( hotkey, netuid ) --> bool
#[pallet::storage]
pub type IsNetworkMember<T: Config> = StorageDoubleMap<
Expand Down
2 changes: 2 additions & 0 deletions pallets/subtensor/src/macros/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod genesis {

// Set the root network as added.
NetworksAdded::<T>::insert(NetUid::ROOT, true);
SubnetState::<T>::insert(NetUid::ROOT, SubnetLifecycleState::Started);

// Increment the number of total networks.
TotalNetworks::<T>::mutate(|n| *n = n.saturating_add(1));
Expand Down Expand Up @@ -81,6 +82,7 @@ mod genesis {
SubnetAlphaIn::<T>::insert(netuid, AlphaBalance::from(10_000_000_000_u64));
SubnetTAO::<T>::insert(netuid, TaoBalance::from(10_000_000_000_u64));
NetworksAdded::<T>::insert(netuid, true);
SubnetState::<T>::insert(netuid, SubnetLifecycleState::Registered);
TotalNetworks::<T>::mutate(|n| *n = n.saturating_add(1));
SubnetworkN::<T>::insert(netuid, 0);
MaxAllowedUids::<T>::insert(netuid, 256u16);
Expand Down
3 changes: 3 additions & 0 deletions pallets/subtensor/src/macros/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ mod hooks {
// Add pre-tracking burns to the generation-scoped AlphaBurned counters. This
// follows both AlphaOut repair and recycled-generation counter rebasing.
.saturating_add(migrations::migrate_backfill_historical_alpha_burned::migrate_backfill_historical_alpha_burned::<T>())
// Backfill the reporting-only subnet lifecycle map before the first post-upgrade
// idle block can advance an already queued dissolution.
.saturating_add(migrations::migrate_subnet_state::migrate_subnet_state::<T>())
// Schedule the large storage-GC sweep. Actual work is bounded by the remaining
// on_idle weight over subsequent blocks.
.saturating_add(migrations::migrate_storage_bloat_v2::kickoff_storage_bloat_cleanup::<T>())
Expand Down
70 changes: 70 additions & 0 deletions pallets/subtensor/src/migrations/migrate_subnet_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use super::*;
use frame_support::{traits::Get, weights::Weight};
use sp_std::collections::btree_map::BTreeMap;

const MIGRATION_NAME: &[u8] = b"backfill_subnet_lifecycle_state_v1";

/// Backfills the reporting-only subnet lifecycle map from the existing operational state.
pub fn migrate_subnet_state<T: Config>() -> Weight {
let migration_name = MIGRATION_NAME.to_vec();
let mut weight = T::DbWeight::get().reads(1);

if HasMigrationRun::<T>::get(&migration_name) {
return weight;
}

let mut expected = BTreeMap::<NetUid, SubnetLifecycleState>::new();
for (netuid, added) in NetworksAdded::<T>::iter() {
weight.saturating_accrue(T::DbWeight::get().reads(1));
if !added {
continue;
}

let state = if netuid == NetUid::ROOT {
SubnetLifecycleState::Started
} else {
let started = FirstEmissionBlockNumber::<T>::contains_key(netuid)
|| SubtokenEnabled::<T>::get(netuid);
weight.saturating_accrue(T::DbWeight::get().reads(2));
if started {
SubnetLifecycleState::Started
} else {
SubnetLifecycleState::Registered
}
};

expected.insert(netuid, state);
}

let queued = DissolveCleanupQueue::<T>::get();
weight.saturating_accrue(T::DbWeight::get().reads(1));
for netuid in queued {
expected.insert(netuid, SubnetLifecycleState::PendingDissolution);
}

if let Some(status) = CurrentDissolveCleanupStatus::<T>::get() {
expected.insert(status.netuid, SubnetLifecycleState::Dissolving);
}
weight.saturating_accrue(T::DbWeight::get().reads(1));

for (netuid, state) in &expected {
SubnetState::<T>::insert(netuid, state);
weight.saturating_accrue(T::DbWeight::get().writes(1));
}

// Validate the complete expected set before making the migration idempotency marker durable.
// `OptionQuery` guarantees a netuid has at most one state; equality here proves every active,
// queued, or in-progress subnet has exactly the state selected above.
let valid = expected
.iter()
.all(|(netuid, state)| SubnetState::<T>::get(netuid).as_ref() == Some(state));
weight.saturating_accrue(T::DbWeight::get().reads(expected.len() as u64));
if !valid {
log::error!("subnet lifecycle migration validation failed");
return weight;
}

HasMigrationRun::<T>::insert(migration_name, true);
weight.saturating_accrue(T::DbWeight::get().writes(1));
weight
}
1 change: 1 addition & 0 deletions pallets/subtensor/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub mod migrate_storage_bloat_v2;
pub mod migrate_subnet_balances;
pub mod migrate_subnet_limit_to_default;
pub mod migrate_subnet_locked;
pub mod migrate_subnet_state;
pub mod migrate_subnet_symbols;
pub mod migrate_subnet_volume;
pub mod migrate_tao_in_refund_deployment_block;
Expand Down
18 changes: 10 additions & 8 deletions pallets/subtensor/src/rpc_info/delegate_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,17 @@ impl<T: Config> Pallet<T> {
BTreeMap::<T::AccountId, Vec<(Compact<NetUid>, Compact<u64>)>>::new();

if !skip_nominators {
let mut alpha_share_pools = vec![];
for netuid in Self::get_all_subnet_netuids() {
let alpha_share_pool = Self::get_alpha_share_pool(delegate.clone(), netuid);
alpha_share_pools.push(alpha_share_pool);
}
let alpha_share_pools = Self::get_all_reportable_subnet_netuids()
.into_iter()
.map(|netuid| (netuid, Self::get_alpha_share_pool(delegate.clone(), netuid)))
.collect::<BTreeMap<_, _>>();

for (nominator, netuid, alpha_stake) in Self::alpha_iter_single_prefix(&delegate) {
if alpha_stake.is_zero() {
if alpha_stake.is_zero() || !Self::is_hotkey_stake_reportable(netuid, &delegate) {
continue;
}

if let Some(alpha_share_pool) = alpha_share_pools.get(u16::from(netuid) as usize) {
if let Some(alpha_share_pool) = alpha_share_pools.get(&netuid) {
let coldkey_stake = alpha_share_pool.get_value_from_shares(alpha_stake);

nominator_map
Expand Down Expand Up @@ -111,7 +110,7 @@ impl<T: Config> Pallet<T> {
let owner = Self::get_owning_coldkey_for_hotkey(&delegate.clone());
let take: Compact<PerU16> = <Delegates<T>>::get(delegate.clone()).into();

let total_stake: U64F64 = u64::from(Self::get_stake_for_hotkey_on_subnet(
let total_stake: U64F64 = u64::from(Self::get_reported_stake_for_hotkey_on_subnet(
&delegate.clone(),
NetUid::ROOT,
))
Expand Down Expand Up @@ -169,6 +168,9 @@ impl<T: Config> Pallet<T> {
for delegate in <Delegates<T> as IterableStorageMap<T::AccountId, PerU16>>::iter_keys() {
// Staked to this delegate, so add to list
for (netuid, _) in Self::alpha_iter_prefix((&delegate, &delegatee)) {
if !Self::is_hotkey_stake_reportable(netuid, &delegate) {
continue;
}
let delegate_info = Self::get_delegate_by_existing_account(delegate.clone(), true);
delegates.push((
delegate_info,
Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/src/rpc_info/dynamic_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub struct DynamicInfo<AccountId: TypeInfo + Encode + Decode> {

impl<T: Config> Pallet<T> {
pub fn get_dynamic_info(netuid: NetUid) -> Option<DynamicInfo<T::AccountId>> {
if !Self::if_subnet_exist(netuid) {
if !Self::is_subnet_reportable(netuid) {
return None;
}
let last_step: u64 = LastMechansimStepBlock::<T>::get(netuid);
Expand Down Expand Up @@ -73,7 +73,7 @@ impl<T: Config> Pallet<T> {
})
}
pub fn get_all_dynamic_info() -> Vec<Option<DynamicInfo<T::AccountId>>> {
let netuids = Self::get_all_subnet_netuids();
let netuids = Self::get_all_reportable_subnet_netuids();
let mut dynamic_info = Vec::<Option<DynamicInfo<T::AccountId>>>::new();
for netuid in netuids.clone().iter() {
dynamic_info.push(Self::get_dynamic_info(*netuid));
Expand Down
22 changes: 12 additions & 10 deletions pallets/subtensor/src/rpc_info/metagraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ impl SelectiveMetagraphIndex {
}
impl<T: Config> Pallet<T> {
pub fn get_metagraph(netuid: NetUid) -> Option<Metagraph<T::AccountId>> {
if !Self::if_subnet_exist(netuid) {
if !Self::is_subnet_reportable(netuid) {
return None;
}

Expand Down Expand Up @@ -686,7 +686,7 @@ impl<T: Config> Pallet<T> {
Vec<I64F64>,
Vec<I64F64>,
Vec<I64F64>,
) = Self::get_stake_weights_for_network(netuid);
) = Self::get_reported_stake_weights_for_network(netuid);

let subnet_volume = SubnetVolume::<T>::get(netuid);
let (collateral_locked, collateral_min, collateral_earned) =
Expand Down Expand Up @@ -827,7 +827,7 @@ impl<T: Config> Pallet<T> {
})
}
pub fn get_all_metagraphs() -> Vec<Option<Metagraph<T::AccountId>>> {
let netuids = Self::get_all_subnet_netuids();
let netuids = Self::get_all_reportable_subnet_netuids();
let mut metagraphs = Vec::<Option<Metagraph<T::AccountId>>>::new();
for netuid in netuids.clone().iter() {
metagraphs.push(Self::get_metagraph(*netuid));
Expand All @@ -836,7 +836,9 @@ impl<T: Config> Pallet<T> {
}

pub fn get_mechagraph(netuid: NetUid, mecid: MechId) -> Option<Metagraph<T::AccountId>> {
if Self::ensure_mechanism_exists(netuid, mecid).is_err() {
// This is a reporting wrapper, so do not use `ensure_mechanism_exists`: that helper is
// intentionally operational and rejects a subnet as soon as `NetworksAdded` is cleared.
if !Self::is_subnet_reportable(netuid) || MechanismCountCurrent::<T>::get(netuid) <= mecid {
return None;
}

Expand All @@ -863,7 +865,7 @@ impl<T: Config> Pallet<T> {
}

pub fn get_all_mechagraphs() -> Vec<Option<Metagraph<T::AccountId>>> {
let netuids = Self::get_all_subnet_netuids();
let netuids = Self::get_all_reportable_subnet_netuids();
let mut metagraphs = Vec::<Option<Metagraph<T::AccountId>>>::new();
for netuid in netuids.clone().iter() {
let mechanism_count = u8::from(MechanismCountCurrent::<T>::get(netuid));
Expand All @@ -878,7 +880,7 @@ impl<T: Config> Pallet<T> {
netuid: NetUid,
metagraph_indexes: Vec<u16>,
) -> Option<SelectiveMetagraph<T::AccountId>> {
if !Self::if_subnet_exist(netuid) {
if !Self::is_subnet_reportable(netuid) {
None
} else {
let mut result = SelectiveMetagraph::default();
Expand All @@ -895,7 +897,7 @@ impl<T: Config> Pallet<T> {
mecid: MechId,
metagraph_indexes: Vec<u16>,
) -> Option<SelectiveMetagraph<T::AccountId>> {
if !Self::if_subnet_exist(netuid) {
if !Self::is_subnet_reportable(netuid) {
None
} else {
let mut result = SelectiveMetagraph::default();
Expand Down Expand Up @@ -1372,7 +1374,7 @@ impl<T: Config> Pallet<T> {
}
Some(SelectiveMetagraphIndex::AlphaStake) => {
let (_, alpha_stake_fl, _): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
Self::get_stake_weights_for_network(netuid);
Self::get_reported_stake_weights_for_network(netuid);
SelectiveMetagraph {
netuid: netuid.into(),
alpha_stake: Some(
Expand All @@ -1386,7 +1388,7 @@ impl<T: Config> Pallet<T> {
}
Some(SelectiveMetagraphIndex::TaoStake) => {
let (_, _, tao_stake_fl): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
Self::get_stake_weights_for_network(netuid);
Self::get_reported_stake_weights_for_network(netuid);
SelectiveMetagraph {
netuid: netuid.into(),
tao_stake: Some(
Expand All @@ -1400,7 +1402,7 @@ impl<T: Config> Pallet<T> {
}
Some(SelectiveMetagraphIndex::TotalStake) => {
let (total_stake_fl, _, _): (Vec<I64F64>, Vec<I64F64>, Vec<I64F64>) =
Self::get_stake_weights_for_network(netuid);
Self::get_reported_stake_weights_for_network(netuid);
SelectiveMetagraph {
netuid: netuid.into(),
total_stake: Some(
Expand Down
Loading
Loading