Skip to content

Commit

Permalink
Merge pull request opentensor#736 from opentensor/subnet-identities
Browse files Browse the repository at this point in the history
Subnet Identities
  • Loading branch information
unconst authored Aug 23, 2024
2 parents 3496c34 + 2c2a15d commit 8c17cfd
Show file tree
Hide file tree
Showing 12 changed files with 488 additions and 39 deletions.
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
7 changes: 4 additions & 3 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,8 +311,9 @@ 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()));
}: dissolve_network(RawOrigin::Root, coldkey, 1)
assert_ok!(Subtensor::<T>::register_network(RawOrigin::Signed(coldkey.clone()).into(), None));
}: dissolve_network(RawOrigin::Signed(coldkey), 1)


// swap_hotkey {
// let seed: u32 = 1;
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>,
) -> 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. Add 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::debug!(
"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 @@ -1005,14 +1028,19 @@ impl<T: Config> Pallet<T> {
Error::<T>::NotSubnetOwner
);

// --- 2. 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);

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

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

Expand Down
21 changes: 19 additions & 2 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ 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.
#[crate::freeze_struct("bbfd00438dbe2b58")]
#[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)]
pub struct ChainIdentity {
Expand All @@ -169,6 +169,19 @@ pub mod pallet {
pub additional: Vec<u8>,
}

/// Struct for SubnetIdentities.
pub type SubnetIdentityOf = SubnetIdentity;
/// Data structure for Subnet Identities
#[crate::freeze_struct("f448dc3dad763108")]
#[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)]
pub struct SubnetIdentity {
/// 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 @@ -1164,6 +1177,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 @@ -901,8 +901,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 @@ -1168,5 +1171,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(78)]
#[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 @@ -187,6 +187,10 @@ 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),
/// A dissolve network extrinsic scheduled.
DissolveNetworkScheduled {
/// The account ID schedule the dissolve network extrisnic
Expand Down
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

0 comments on commit 8c17cfd

Please sign in to comment.