Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subnet Identities #736

Merged
merged 14 commits into from
Aug 23, 2024
2 changes: 1 addition & 1 deletion pallets/admin-utils/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1243,7 +1243,7 @@ fn test_sudo_get_set_alpha() {
DispatchError::BadOrigin
);

assert_ok!(SubtensorModule::register_network(signer.clone()));
assert_ok!(SubtensorModule::register_network(signer.clone(), None));

assert_ok!(AdminUtils::sudo_set_alpha_values(
signer.clone(),
Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ benchmarks! {
let amount: u64 = 1;
let amount_to_be_staked = 100_000_000_000_000u64;
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), amount_to_be_staked);
}: register_network(RawOrigin::Signed(coldkey))
}: register_network(RawOrigin::Signed(coldkey), None)

benchmark_dissolve_network {
let seed : u32 = 1;
Expand All @@ -311,7 +311,7 @@ benchmarks! {
let amount: u64 = 1;
let amount_to_be_staked = 100_000_000_000_000u64;
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey.clone(), amount_to_be_staked);
assert_ok!(Subtensor::<T>::register_network(RawOrigin::Signed(coldkey.clone()).into()));
assert_ok!(Subtensor::<T>::register_network(RawOrigin::Signed(coldkey.clone()).into(), None));
}: dissolve_network(RawOrigin::Signed(coldkey), 1)

// swap_hotkey {
Expand Down
48 changes: 38 additions & 10 deletions pallets/subtensor/src/coinbase/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,17 +894,24 @@ impl<T: Config> Pallet<T> {
/// Facilitates user registration of a new subnetwork.
///
/// # Args:
/// * 'origin': ('T::RuntimeOrigin'): The calling origin. Must be signed.
/// * `origin` (`T::RuntimeOrigin`): The calling origin. Must be signed.
/// * `identity` (`Option<SubnetIdentityOf>`): Optional identity to be associated with the new subnetwork.
///
/// # Event:
/// * 'NetworkAdded': Emitted when a new network is successfully added.
/// # Events:
/// * `NetworkAdded(netuid, modality)`: Emitted when a new network is successfully added.
/// * `SubnetIdentitySet(netuid)`: Emitted when a custom identity is set for a new subnetwork.
/// * `NetworkRemoved(netuid)`: Emitted when an existing network is removed to make room for the new one.
/// * `SubnetIdentityRemoved(netuid)`: Emitted when the identity of a removed network is also deleted.
///
/// # Raises:
/// * 'TxRateLimitExceeded': If the rate limit for network registration is exceeded.
/// * 'NotEnoughBalanceToStake': If there isn't enough balance to stake for network registration.
/// * 'BalanceWithdrawalError': If an error occurs during balance withdrawal for network registration.
///
pub fn user_add_network(origin: T::RuntimeOrigin) -> dispatch::DispatchResult {
pub fn user_add_network(
origin: T::RuntimeOrigin,
identity: Option<SubnetIdentityOf>,
distributedstatemachine marked this conversation as resolved.
Show resolved Hide resolved
) -> dispatch::DispatchResult {
// --- 0. Ensure the caller is a signed user.
let coldkey = ensure_signed(origin)?;

Expand Down Expand Up @@ -948,6 +955,11 @@ impl<T: Config> Pallet<T> {
Self::remove_network(netuid_to_prune);
log::debug!("remove_network: {:?}", netuid_to_prune,);
Self::deposit_event(Event::NetworkRemoved(netuid_to_prune));

if SubnetIdentities::<T>::take(netuid_to_prune).is_some() {
Self::deposit_event(Event::SubnetIdentityRemoved(netuid_to_prune));
}

netuid_to_prune
}
};
Expand All @@ -961,21 +973,32 @@ impl<T: Config> Pallet<T> {
Self::init_new_network(netuid_to_register, 360);
log::debug!("init_new_network: {:?}", netuid_to_register,);

// --- 7. Set netuid storage.
// --- 7. Remove the identity if it exists
if let Some(identity_value) = identity {
ensure!(
Self::is_valid_subnet_identity(&identity_value),
Error::<T>::InvalidIdentity
);

SubnetIdentities::<T>::insert(netuid_to_register, identity_value);
Self::deposit_event(Event::SubnetIdentitySet(netuid_to_register));
}

// --- 8. Set netuid storage.
let current_block_number: u64 = Self::get_current_block_as_u64();
NetworkLastRegistered::<T>::set(current_block_number);
NetworkRegisteredAt::<T>::insert(netuid_to_register, current_block_number);
SubnetOwner::<T>::insert(netuid_to_register, coldkey);

// --- 8. Emit the NetworkAdded event.
// --- 9. Emit the NetworkAdded event.
log::info!(
"NetworkAdded( netuid:{:?}, modality:{:?} )",
netuid_to_register,
0
);
Self::deposit_event(Event::NetworkAdded(netuid_to_register, 0));

// --- 9. Return success.
// --- 10. Return success.
Ok(())
}

Expand Down Expand Up @@ -1008,14 +1031,19 @@ impl<T: Config> Pallet<T> {
Error::<T>::NotSubnetOwner
);

// --- 4. Explicitly erase the network and all its parameters.
// --- 4. Remove the subnet identity if it exists.
if SubnetIdentities::<T>::take(netuid).is_some() {
Self::deposit_event(Event::SubnetIdentityRemoved(netuid));
}

// --- 5. Explicitly erase the network and all its parameters.
Self::remove_network(netuid);

// --- 5. Emit the NetworkRemoved event.
// --- 6. Emit the NetworkRemoved event.
log::info!("NetworkRemoved( netuid:{:?} )", netuid);
Self::deposit_event(Event::NetworkRemoved(netuid));

// --- 6. Return success.
// --- 7. Return success.
Ok(())
}

Expand Down
23 changes: 20 additions & 3 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ extern crate alloc;
#[frame_support::pallet]
pub mod pallet {

use crate::migrations;
use crate::{freeze_struct, migrations};
JohnReedV marked this conversation as resolved.
Show resolved Hide resolved
use frame_support::{
dispatch::GetDispatchInfo,
pallet_prelude::{DispatchResult, StorageMap, ValueQuery, *},
Expand Down Expand Up @@ -133,9 +133,9 @@ pub mod pallet {
pub ip_type: u8,
}

/// Struct for Prometheus.
/// Struct for ChainIdentities.
pub type ChainIdentityOf = ChainIdentity;
/// Data structure for Prometheus information.
/// Data structure for Chain Identities.
#[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)]
pub struct ChainIdentity {
/// The name of the chain identity
Expand All @@ -152,6 +152,19 @@ pub mod pallet {
pub additional: Vec<u8>,
}

/// Struct for SubnetIdentities.
pub type SubnetIdentityOf = SubnetIdentity;
/// Data structure for Subnet Identities
#[freeze_struct("f448dc3dad763108")]
#[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)]
pub struct SubnetIdentity {
JohnReedV marked this conversation as resolved.
Show resolved Hide resolved
/// The name of the subnet
pub subnet_name: Vec<u8>,
/// The github repository associated with the chain identity
pub github_repo: Vec<u8>,
/// The subnet's contact
pub subnet_contact: Vec<u8>,
}
/// ============================
/// ==== Staking + Accounts ====
/// ============================
Expand Down Expand Up @@ -1080,6 +1093,10 @@ pub mod pallet {
pub type Identities<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOf, OptionQuery>;

#[pallet::storage] // --- MAP ( netuid ) --> identity
pub type SubnetIdentities<T: Config> =
StorageMap<_, Blake2_128Concat, u16, SubnetIdentityOf, OptionQuery>;

/// =================================
/// ==== Axon / Promo Endpoints =====
/// =================================
Expand Down
37 changes: 35 additions & 2 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -798,8 +798,11 @@ mod dispatches {
#[pallet::weight((Weight::from_parts(157_000_000, 0)
.saturating_add(T::DbWeight::get().reads(16))
.saturating_add(T::DbWeight::get().writes(30)), DispatchClass::Operational, Pays::No))]
pub fn register_network(origin: OriginFor<T>) -> DispatchResult {
Self::user_add_network(origin)
pub fn register_network(
origin: OriginFor<T>,
identity: Option<SubnetIdentityOf>,
) -> DispatchResult {
Self::user_add_network(origin, identity)
}

/// Facility extrinsic for user to get taken from faucet
Expand Down Expand Up @@ -937,5 +940,35 @@ mod dispatches {
) -> DispatchResult {
Self::do_set_identity(origin, name, url, image, discord, description, additional)
}

/// ---- Set the identity information for a subnet.
/// # Args:
/// * `origin` - (<T as frame_system::Config>::Origin):
/// - The signature of the calling coldkey, which must be the owner of the subnet.
///
/// * `netuid` (u16):
/// - The unique network identifier of the subnet.
///
/// * `subnet_name` (Vec<u8>):
/// - The name of the subnet.
///
/// * `github_repo` (Vec<u8>):
/// - The GitHub repository associated with the subnet identity.
///
/// * `subnet_contact` (Vec<u8>):
/// - The contact information for the subnet.
#[pallet::call_index(69)]
#[pallet::weight((Weight::from_parts(45_000_000, 0)
.saturating_add(T::DbWeight::get().reads(4))
.saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::Yes))]
pub fn set_subnet_identity(
origin: OriginFor<T>,
netuid: u16,
subnet_name: Vec<u8>,
github_repo: Vec<u8>,
subnet_contact: Vec<u8>,
) -> DispatchResult {
Self::do_set_subnet_identity(origin, netuid, subnet_name, github_repo, subnet_contact)
}
}
}
4 changes: 4 additions & 0 deletions pallets/subtensor/src/macros/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,5 +179,9 @@ mod events {
NetworkMaxStakeSet(u16, u64),
/// The identity of a coldkey has been set
ChainIdentitySet(T::AccountId),
/// The identity of a subnet has been set
SubnetIdentitySet(u16),
/// The identity of a subnet has been removed
SubnetIdentityRemoved(u16),
}
}
5 changes: 4 additions & 1 deletion pallets/subtensor/src/rpc_info/subnet_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use frame_support::storage::IterableStorageMap;
extern crate alloc;
use codec::Compact;

#[freeze_struct("fe79d58173da662a")]
#[freeze_struct("ccca539640c3f631")]
#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)]
pub struct SubnetInfo<T: Config> {
netuid: Compact<u16>,
Expand All @@ -25,6 +25,7 @@ pub struct SubnetInfo<T: Config> {
emission_values: Compact<u64>,
burn: Compact<u64>,
owner: T::AccountId,
identity: Option<SubnetIdentity>,
}

#[freeze_struct("55b472510f10e76a")]
Expand Down Expand Up @@ -80,6 +81,7 @@ impl<T: Config> Pallet<T> {
let network_modality = <NetworkModality<T>>::get(netuid);
let emission_values = Self::get_emission_value(netuid);
let burn: Compact<u64> = Self::get_burn_as_u64(netuid).into();
let identity: Option<SubnetIdentity> = SubnetIdentities::<T>::get(netuid);

// DEPRECATED
let network_connect: Vec<[u16; 2]> = Vec::<[u16; 2]>::new();
Expand All @@ -106,6 +108,7 @@ impl<T: Config> Pallet<T> {
emission_values: emission_values.into(),
burn,
owner: Self::get_subnet_owner(netuid),
identity,
})
}

Expand Down
85 changes: 85 additions & 0 deletions pallets/subtensor/src/utils/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,65 @@ impl<T: Config> Pallet<T> {
Ok(())
}

/// Sets the identity for a subnet.
///
/// This function allows the owner of a subnet to set or update the identity information associated with the subnet.
/// It verifies that the caller is the owner of the specified subnet, validates the provided identity information,
/// and then stores it in the blockchain state.
///
/// # Arguments
///
/// * `origin` - The origin of the call, which should be a signed extrinsic.
/// * `netuid` - The unique identifier for the subnet.
/// * `subnet_name` - The name of the subnet to be associated with the identity.
/// * `github_repo` - The GitHub repository URL associated with the subnet identity.
/// * `subnet_contact` - Contact information for the subnet.
///
/// # Returns
///
/// Returns `Ok(())` if the subnet identity is successfully set, otherwise returns an error.
pub fn do_set_subnet_identity(
origin: T::RuntimeOrigin,
netuid: u16,
subnet_name: Vec<u8>,
github_repo: Vec<u8>,
subnet_contact: Vec<u8>,
) -> dispatch::DispatchResult {
// Ensure the call is signed and get the signer's (coldkey) account
let coldkey = ensure_signed(origin)?;

// Ensure that the coldkey owns the subnet
ensure!(
Self::get_subnet_owner(netuid) == coldkey,
Error::<T>::NotSubnetOwner
);

// Create the identity struct with the provided information
let identity: SubnetIdentityOf = SubnetIdentityOf {
subnet_name,
github_repo,
subnet_contact,
};

// Validate the created identity
ensure!(
Self::is_valid_subnet_identity(&identity),
Error::<T>::InvalidIdentity
);

// Store the validated identity in the blockchain state
SubnetIdentities::<T>::insert(netuid, identity.clone());

// Log the identity set event
log::info!("SubnetIdentitySet( netuid:{:?} ) ", netuid);

// Emit an event to notify that an identity has been set
Self::deposit_event(Event::SubnetIdentitySet(netuid));

// Return Ok to indicate successful execution
Ok(())
}

/// Validates the given ChainIdentityOf struct.
///
/// This function checks if the total length of all fields in the ChainIdentityOf struct
Expand Down Expand Up @@ -106,4 +165,30 @@ impl<T: Config> Pallet<T> {
&& identity.description.len() <= 1024
&& identity.additional.len() <= 1024
}

/// Validates the given SubnetIdentityOf struct.
///
/// This function checks if the total length of all fields in the SubnetIdentityOf struct
/// is less than or equal to 2304 bytes, and if each individual field is also
/// within its respective maximum byte limit.
///
/// # Arguments
///
/// * `identity` - A reference to the SubnetIdentityOf struct to be validated.
///
/// # Returns
///
/// * `bool` - Returns true if the SubnetIdentity is valid, false otherwise.
pub fn is_valid_subnet_identity(identity: &SubnetIdentityOf) -> bool {
let total_length = identity
.subnet_name
.len()
.saturating_add(identity.github_repo.len())
.saturating_add(identity.subnet_contact.len());

total_length <= 256 + 1024 + 1024
&& identity.subnet_name.len() <= 256
&& identity.github_repo.len() <= 1024
&& identity.subnet_contact.len() <= 1024
}
}
4 changes: 2 additions & 2 deletions pallets/subtensor/tests/epoch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1501,7 +1501,7 @@ fn test_set_alpha_disabled() {
assert_ok!(SubtensorModule::root_register(signer.clone(), hotkey,));
assert_ok!(SubtensorModule::add_stake(signer.clone(), hotkey, 1000));
// Only owner can set alpha values
assert_ok!(SubtensorModule::register_network(signer.clone()));
assert_ok!(SubtensorModule::register_network(signer.clone(), None));

// Explicitly set to false
SubtensorModule::set_liquid_alpha_enabled(netuid, false);
Expand Down Expand Up @@ -2584,7 +2584,7 @@ fn test_get_set_alpha() {
DispatchError::BadOrigin
);

assert_ok!(SubtensorModule::register_network(signer.clone()));
assert_ok!(SubtensorModule::register_network(signer.clone(), None));

assert_ok!(SubtensorModule::do_set_alpha_values(
signer.clone(),
Expand Down
3 changes: 2 additions & 1 deletion pallets/subtensor/tests/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ fn test_total_issuance_global() {
SubtensorModule::add_balance_to_coldkey_account(&owner, lockcost); // Add a balance of 20000 to the coldkey account.
assert_eq!(SubtensorModule::get_total_issuance(), 0); // initial is zero.
assert_ok!(SubtensorModule::register_network(
<<Test as Config>::RuntimeOrigin>::signed(owner)
<<Test as Config>::RuntimeOrigin>::signed(owner),
None
));
SubtensorModule::set_max_allowed_uids(netuid, 1); // Set the maximum allowed unique identifiers for the network to 1.
assert_eq!(SubtensorModule::get_total_issuance(), 0); // initial is zero.
Expand Down
Loading
Loading