diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 3f83f934f1..5968a73761 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -479,7 +479,7 @@ impl Pallet { ); // --- 4. Remove the subnet identity if it exists. - if SubnetIdentities::::take(netuid).is_some() { + if SubnetIdentitiesV2::::take(netuid).is_some() { Self::deposit_event(Event::SubnetIdentityRemoved(netuid)); } @@ -590,8 +590,8 @@ impl Pallet { SubnetOwner::::remove(netuid); // --- 13. Remove subnet identity if it exists. - if SubnetIdentities::::contains_key(netuid) { - SubnetIdentities::::remove(netuid); + if SubnetIdentitiesV2::::contains_key(netuid) { + SubnetIdentitiesV2::::remove(netuid); Self::deposit_event(Event::SubnetIdentityRemoved(netuid)); } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 064bb5c6e4..8fcd437243 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -189,10 +189,10 @@ pub mod pallet { pub ip_type: u8, } - /// Struct for ChainIdentities. + /// Struct for ChainIdentities. (DEPRECATED for V2) pub type ChainIdentityOf = ChainIdentity; - /// Data structure for Chain Identities. + /// Data structure for Chain Identities. (DEPRECATED for V2) #[crate::freeze_struct("bbfd00438dbe2b58")] #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] pub struct ChainIdentity { @@ -210,9 +210,32 @@ pub mod pallet { pub additional: Vec, } - /// Struct for SubnetIdentities. + /// Struct for ChainIdentities. + pub type ChainIdentityOfV2 = ChainIdentityV2; + + /// Data structure for Chain Identities. + #[crate::freeze_struct("ad72a270be7b59d7")] + #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + pub struct ChainIdentityV2 { + /// The name of the chain identity + pub name: Vec, + /// The URL associated with the chain identity + pub url: Vec, + /// The github repository associated with the identity + pub github_repo: Vec, + /// The image representation of the chain identity + pub image: Vec, + /// The Discord information for the chain identity + pub discord: Vec, + /// A description of the chain identity + pub description: Vec, + /// Additional information about the chain identity + pub additional: Vec, + } + + /// Struct for SubnetIdentities. (DEPRECATED for V2) pub type SubnetIdentityOf = SubnetIdentity; - /// Data structure for Subnet Identities + /// Data structure for Subnet Identities. (DEPRECATED for V2) #[crate::freeze_struct("f448dc3dad763108")] #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] pub struct SubnetIdentity { @@ -223,6 +246,28 @@ pub mod pallet { /// The subnet's contact pub subnet_contact: Vec, } + + /// Struct for SubnetIdentitiesV2. + pub type SubnetIdentityOfV2 = SubnetIdentityV2; + /// Data structure for Subnet Identities + #[crate::freeze_struct("e002be4cd05d7b3e")] + #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + pub struct SubnetIdentityV2 { + /// The name of the subnet + pub subnet_name: Vec, + /// The github repository associated with the subnet + pub github_repo: Vec, + /// The subnet's contact + pub subnet_contact: Vec, + /// The subnet's website + pub subnet_url: Vec, + /// The subnet's discord + pub discord: Vec, + /// The subnet's description + pub description: Vec, + /// Additional information about the subnet + pub additional: Vec, + } /// ============================ /// ==== Staking + Accounts ==== /// ============================ @@ -1409,14 +1454,22 @@ pub mod pallet { PrometheusInfoOf, OptionQuery, >; - #[pallet::storage] // --- MAP ( coldkey ) --> identity + #[pallet::storage] // --- MAP ( coldkey ) --> identity. (DEPRECATED for V2) pub type Identities = StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOf, OptionQuery>; - #[pallet::storage] // --- MAP ( netuid ) --> identity + #[pallet::storage] // --- MAP ( coldkey ) --> identity + pub type IdentitiesV2 = + StorageMap<_, Blake2_128Concat, T::AccountId, ChainIdentityOfV2, OptionQuery>; + + #[pallet::storage] // --- MAP ( netuid ) --> identity. (DEPRECATED for V2) pub type SubnetIdentities = StorageMap<_, Blake2_128Concat, u16, SubnetIdentityOf, OptionQuery>; + #[pallet::storage] // --- MAP ( netuid ) --> identityV2 + pub type SubnetIdentitiesV2 = + StorageMap<_, Blake2_128Concat, u16, SubnetIdentityOfV2, OptionQuery>; + /// ================================= /// ==== Axon / Promo Endpoints ===== /// ================================= diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2735b6bb79..a6c6ed9eb1 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1453,12 +1453,22 @@ mod dispatches { origin: OriginFor, name: Vec, url: Vec, + github_repo: Vec, image: Vec, discord: Vec, description: Vec, additional: Vec, ) -> DispatchResult { - Self::do_set_identity(origin, name, url, image, discord, description, additional) + Self::do_set_identity( + origin, + name, + url, + github_repo, + image, + discord, + description, + additional, + ) } /// ---- Set the identity information for a subnet. @@ -1487,8 +1497,22 @@ mod dispatches { subnet_name: Vec, github_repo: Vec, subnet_contact: Vec, + subnet_url: Vec, + discord: Vec, + description: Vec, + additional: Vec, ) -> DispatchResult { - Self::do_set_subnet_identity(origin, netuid, subnet_name, github_repo, subnet_contact) + Self::do_set_subnet_identity( + origin, + netuid, + subnet_name, + github_repo, + subnet_contact, + subnet_url, + discord, + description, + additional, + ) } /// User register a new subnetwork @@ -1499,7 +1523,7 @@ mod dispatches { pub fn register_network_with_identity( origin: OriginFor, hotkey: T::AccountId, - identity: Option, + identity: Option, ) -> DispatchResult { Self::do_register_network(origin, &hotkey, 1, identity) } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 19f7d317b2..e2427dcdcc 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -77,7 +77,9 @@ mod hooks { .saturating_add(migrations::migrate_rao::migrate_rao::()) // Fix the IsNetworkMember map to be consistent with other storage maps .saturating_add(migrations::migrate_fix_is_network_member::migrate_fix_is_network_member::()) - .saturating_add(migrations::migrate_subnet_volume::migrate_subnet_volume::()); + .saturating_add(migrations::migrate_subnet_volume::migrate_subnet_volume::()) + // Upgrade identities to V2 + .saturating_add(migrations::migrate_identities_v2::migrate_identities_to_v2::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_identities_v2.rs b/pallets/subtensor/src/migrations/migrate_identities_v2.rs new file mode 100644 index 0000000000..505b617b2f --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_identities_v2.rs @@ -0,0 +1,91 @@ +use super::*; +use frame_support::weights::Weight; +use log; +use scale_info::prelude::{string::String, vec::Vec}; + +pub fn migrate_identities_to_v2() -> Weight { + use frame_support::traits::Get; + let migration_name = b"migrate_identities_to_v2".to_vec(); + + // Start counting weight + let mut weight = T::DbWeight::get().reads(1); + + // Check if we already ran this migration + if HasMigrationRun::::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + target: "runtime", + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // ----------------------------- + // 1) Migrate Chain Identities + // ----------------------------- + let old_identities = Identities::::iter().collect::>(); + for (account_id, old_identity) in old_identities.clone() { + let new_identity = ChainIdentityV2 { + name: old_identity.name, + url: old_identity.url, + github_repo: Vec::new(), + image: old_identity.image, + discord: old_identity.discord, + description: old_identity.description, + additional: old_identity.additional, + }; + + // Insert into the new storage map + IdentitiesV2::::insert(&account_id, &new_identity); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + Identities::::remove(&account_id); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + + weight = weight.saturating_add(T::DbWeight::get().reads(old_identities.len() as u64)); + + // ----------------------------- + // 2) Migrate Subnet Identities + // ----------------------------- + let old_subnet_identities = SubnetIdentities::::iter().collect::>(); + for (netuid, old_subnet_identity) in old_subnet_identities.clone() { + let new_subnet_identity = SubnetIdentityV2 { + subnet_name: old_subnet_identity.subnet_name, + github_repo: old_subnet_identity.github_repo, + subnet_contact: old_subnet_identity.subnet_contact, + subnet_url: Vec::new(), + discord: Vec::new(), + description: Vec::new(), + additional: Vec::new(), + }; + + // Insert into the new storage map + SubnetIdentitiesV2::::insert(netuid, &new_subnet_identity); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + SubnetIdentities::::remove(netuid); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + weight = weight.saturating_add(T::DbWeight::get().reads(old_subnet_identities.len() as u64)); + + // ----------------------------- + // Mark the migration as done + // ----------------------------- + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + target: "runtime", + "Migration '{}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index 136bd4c59a..d334145804 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -107,19 +107,20 @@ pub fn migrate_rao() -> Weight { let _neuron_uid: u16 = Pallet::::register_neuron(*netuid, &owner_coldkey); } // Register the neuron immediately. - if !Identities::::contains_key(owner_coldkey.clone()) { + if !IdentitiesV2::::contains_key(owner_coldkey.clone()) { // Set the identitiy for the Owner coldkey if non existent. - let identity = ChainIdentityOf { + let identity = ChainIdentityOfV2 { name: format!("Owner{}", netuid).as_bytes().to_vec(), url: Vec::new(), image: Vec::new(), + github_repo: Vec::new(), discord: Vec::new(), description: Vec::new(), additional: Vec::new(), }; // Validate the created identity and set it. if Pallet::::is_valid_identity(&identity) { - Identities::::insert(owner_coldkey.clone(), identity.clone()); + IdentitiesV2::::insert(owner_coldkey.clone(), identity.clone()); } } } diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 5d9aff969e..740c21b636 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -6,6 +6,7 @@ pub mod migrate_delete_subnet_21; pub mod migrate_delete_subnet_3; pub mod migrate_fix_is_network_member; pub mod migrate_fix_total_coldkey_stake; +pub mod migrate_identities_v2; pub mod migrate_init_total_issuance; pub mod migrate_populate_owned_hotkeys; pub mod migrate_populate_staking_hotkeys; diff --git a/pallets/subtensor/src/rpc_info/dynamic_info.rs b/pallets/subtensor/src/rpc_info/dynamic_info.rs index bd24844e1a..ac6a45cf56 100644 --- a/pallets/subtensor/src/rpc_info/dynamic_info.rs +++ b/pallets/subtensor/src/rpc_info/dynamic_info.rs @@ -4,7 +4,7 @@ use codec::Compact; use frame_support::pallet_prelude::{Decode, Encode}; use subtensor_macros::freeze_struct; -#[freeze_struct("70be0b07db585696")] +#[freeze_struct("f728ab9f6ffbf7f2")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct DynamicInfo { netuid: Compact, @@ -26,7 +26,7 @@ pub struct DynamicInfo { pending_root_emission: Compact, subnet_volume: Compact, network_registered_at: Compact, - subnet_identity: Option, + subnet_identity: Option, } impl Pallet { @@ -63,7 +63,7 @@ impl Pallet { pending_root_emission: PendingRootDivs::::get(netuid).into(), subnet_volume: SubnetVolume::::get(netuid).into(), network_registered_at: NetworkRegisteredAt::::get(netuid).into(), - subnet_identity: SubnetIdentities::::get(netuid), + subnet_identity: SubnetIdentitiesV2::::get(netuid), }) } pub fn get_all_dynamic_info() -> Vec>> { diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 69b21b0e9f..315415ca73 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -6,7 +6,7 @@ use frame_support::pallet_prelude::{Decode, Encode}; use substrate_fixed::types::I64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("a92e51d2046f4be8")] +#[freeze_struct("4fc6eea3706b9c0c")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct Metagraph { // Subnet index @@ -15,7 +15,7 @@ pub struct Metagraph { // Name and symbol name: Vec>, // name symbol: Vec>, // token symbol - identity: Option, // identity information. + identity: Option, // identity information. network_registered_at: Compact, // block at registration // Keys for owner. @@ -81,24 +81,24 @@ pub struct Metagraph { bonds_moving_avg: Compact, // Bonds moving avg // Metagraph info. - hotkeys: Vec, // hotkey per UID - coldkeys: Vec, // coldkey per UID - identities: Vec>, // coldkeys identities - axons: Vec, // UID axons. - active: Vec, // Avtive per UID - validator_permit: Vec, // Val permit per UID - pruning_score: Vec>, // Pruning per UID - last_update: Vec>, // Last update per UID - emission: Vec>, // Emission per UID - dividends: Vec>, // Dividends per UID - incentives: Vec>, // Mining incentives per UID - consensus: Vec>, // Consensus per UID - trust: Vec>, // Trust per UID - rank: Vec>, // Rank per UID - block_at_registration: Vec>, // Reg block per UID - alpha_stake: Vec>, // Alpha staked per UID - tao_stake: Vec>, // TAO staked per UID - total_stake: Vec>, // Total stake per UID + hotkeys: Vec, // hotkey per UID + coldkeys: Vec, // coldkey per UID + identities: Vec>, // coldkeys identities + axons: Vec, // UID axons. + active: Vec, // Avtive per UID + validator_permit: Vec, // Val permit per UID + pruning_score: Vec>, // Pruning per UID + last_update: Vec>, // Last update per UID + emission: Vec>, // Emission per UID + dividends: Vec>, // Dividends per UID + incentives: Vec>, // Mining incentives per UID + consensus: Vec>, // Consensus per UID + trust: Vec>, // Trust per UID + rank: Vec>, // Rank per UID + block_at_registration: Vec>, // Reg block per UID + alpha_stake: Vec>, // Alpha staked per UID + tao_stake: Vec>, // TAO staked per UID + total_stake: Vec>, // Total stake per UID // Dividend break down. tao_dividends_per_hotkey: Vec<(AccountId, Compact)>, // List of dividend payouts in tao via root. @@ -115,7 +115,7 @@ impl Pallet { let mut hotkeys: Vec = vec![]; let mut coldkeys: Vec = vec![]; let mut block_at_registration: Vec> = vec![]; - let mut identities: Vec> = vec![]; + let mut identities: Vec> = vec![]; let mut axons: Vec = vec![]; for uid in 0..n { let hotkey = Keys::::get(netuid, uid); @@ -123,7 +123,7 @@ impl Pallet { hotkeys.push(hotkey.clone()); coldkeys.push(coldkey.clone()); block_at_registration.push(BlockAtRegistration::::get(netuid, uid).into()); - identities.push(Identities::::get(coldkey.clone())); + identities.push(IdentitiesV2::::get(coldkey.clone())); axons.push(Self::get_axon_info(netuid, &hotkey)); } let mut tao_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; @@ -157,7 +157,7 @@ impl Pallet { .into_iter() .map(Compact) .collect(), // Symbol. - identity: SubnetIdentities::::get(netuid), // identity information. + identity: SubnetIdentitiesV2::::get(netuid), // identity information. network_registered_at: NetworkRegisteredAt::::get(netuid).into(), // block at registration // Keys for owner. diff --git a/pallets/subtensor/src/rpc_info/subnet_info.rs b/pallets/subtensor/src/rpc_info/subnet_info.rs index 2d9e3dfa48..46644e7467 100644 --- a/pallets/subtensor/src/rpc_info/subnet_info.rs +++ b/pallets/subtensor/src/rpc_info/subnet_info.rs @@ -27,7 +27,7 @@ pub struct SubnetInfo { owner: AccountId, } -#[freeze_struct("ae2cf407a8d95ef6")] +#[freeze_struct("a86ee623525247cc")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SubnetInfov2 { netuid: Compact, @@ -45,13 +45,13 @@ pub struct SubnetInfov2 { tempo: Compact, network_modality: Compact, network_connect: Vec<[u16; 2]>, - emission_values: Compact, + emission_value: Compact, burn: Compact, owner: AccountId, - identity: Option, + identity: Option, } -#[freeze_struct("4714b5e2336f7b19")] +#[freeze_struct("7b506df55bd44646")] #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SubnetHyperparams { rho: Compact, @@ -76,7 +76,7 @@ pub struct SubnetHyperparams { max_validators: Compact, adjustment_alpha: Compact, difficulty: Compact, - commit_reveal_weights_interval: Compact, + commit_reveal_period: Compact, commit_reveal_weights_enabled: bool, alpha_high: Compact, alpha_low: Compact, @@ -172,9 +172,9 @@ impl Pallet { let blocks_since_last_step = Self::get_blocks_since_last_step(netuid); let tempo = Self::get_tempo(netuid); let network_modality = >::get(netuid); - let emission_values = Self::get_emission_value(netuid); + let emission_value = Self::get_emission_value(netuid); let burn: Compact = Self::get_burn_as_u64(netuid).into(); - let identity: Option = SubnetIdentities::::get(netuid); + let identity: Option = SubnetIdentitiesV2::::get(netuid); // DEPRECATED let network_connect: Vec<[u16; 2]> = Vec::<[u16; 2]>::new(); @@ -198,7 +198,7 @@ impl Pallet { tempo: tempo.into(), network_modality: network_modality.into(), network_connect, - emission_values: emission_values.into(), + emission_value: emission_value.into(), burn, owner: Self::get_subnet_owner(netuid), identity, @@ -282,7 +282,7 @@ impl Pallet { max_validators: max_validators.into(), adjustment_alpha: adjustment_alpha.into(), difficulty: difficulty.into(), - commit_reveal_weights_interval: commit_reveal_periods.into(), + commit_reveal_period: commit_reveal_periods.into(), commit_reveal_weights_enabled, alpha_high: alpha_high.into(), alpha_low: alpha_low.into(), diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index ee713cacfd..58df2e4ade 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -162,7 +162,7 @@ impl Pallet { origin: T::RuntimeOrigin, hotkey: &T::AccountId, mechid: u16, - identity: Option, + identity: Option, ) -> DispatchResult { // --- 1. Ensure the caller is a signed user. let coldkey = ensure_signed(origin)?; @@ -262,7 +262,7 @@ impl Pallet { Error::::InvalidIdentity ); - SubnetIdentities::::insert(netuid_to_register, identity_value); + SubnetIdentitiesV2::::insert(netuid_to_register, identity_value); Self::deposit_event(Event::SubnetIdentitySet(netuid_to_register)); } diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 075538421b..b505cd58e9 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -52,8 +52,8 @@ impl Pallet { weight = weight.saturating_add(T::DbWeight::get().reads(1)); // 5. Swap the identity if the old coldkey has one - if let Some(identity) = Identities::::take(old_coldkey) { - Identities::::insert(new_coldkey, identity); + if let Some(identity) = IdentitiesV2::::take(old_coldkey) { + IdentitiesV2::::insert(new_coldkey, identity); } // 6. Ensure sufficient balance for the swap cost diff --git a/pallets/subtensor/src/tests/serving.rs b/pallets/subtensor/src/tests/serving.rs index 32b604ba5a..52b8f8b08f 100644 --- a/pallets/subtensor/src/tests/serving.rs +++ b/pallets/subtensor/src/tests/serving.rs @@ -625,6 +625,7 @@ fn test_do_set_identity() { // Prepare identity data let name = b"Alice".to_vec(); let url = b"https://alice.com".to_vec(); + let git = b"https://github.com".to_vec(); let image = b"alice.jpg".to_vec(); let discord = b"alice#1234".to_vec(); let description = b"Alice's identity".to_vec(); @@ -635,6 +636,7 @@ fn test_do_set_identity() { <::RuntimeOrigin>::signed(coldkey), name.clone(), url.clone(), + git.clone(), image.clone(), discord.clone(), description.clone(), @@ -642,7 +644,7 @@ fn test_do_set_identity() { )); // Check if identity is set correctly - let stored_identity = Identities::::get(coldkey).expect("Identity should be set"); + let stored_identity = IdentitiesV2::::get(coldkey).expect("Identity should be set"); assert_eq!(stored_identity.name, name); assert_eq!(stored_identity.url, url); assert_eq!(stored_identity.image, image); @@ -657,6 +659,7 @@ fn test_do_set_identity() { <::RuntimeOrigin>::signed(coldkey_without_hotkey), name.clone(), url.clone(), + git.clone(), image.clone(), discord.clone(), description.clone(), @@ -672,6 +675,7 @@ fn test_do_set_identity() { <::RuntimeOrigin>::signed(coldkey), new_name.clone(), new_url.clone(), + git.clone(), image.clone(), discord.clone(), description.clone(), @@ -679,7 +683,7 @@ fn test_do_set_identity() { )); let updated_identity = - Identities::::get(coldkey).expect("Updated identity should be set"); + IdentitiesV2::::get(coldkey).expect("Updated identity should be set"); assert_eq!(updated_identity.name, new_name); assert_eq!(updated_identity.url, new_url); @@ -693,6 +697,7 @@ fn test_do_set_identity() { long_data.clone(), long_data.clone(), long_data.clone(), + long_data.clone(), long_data.clone() ), Error::::InvalidIdentity @@ -705,9 +710,10 @@ fn test_do_set_identity() { fn test_is_valid_identity() { new_test_ext(1).execute_with(|| { // Test valid identity - let valid_identity = ChainIdentity { + let valid_identity = ChainIdentityV2 { name: vec![0; 256], url: vec![0; 256], + github_repo: vec![0; 256], image: vec![0; 1024], discord: vec![0; 256], description: vec![0; 1024], @@ -716,9 +722,10 @@ fn test_is_valid_identity() { assert!(SubtensorModule::is_valid_identity(&valid_identity)); // Test identity with total length exactly at the maximum - let max_length_identity = ChainIdentity { + let max_length_identity = ChainIdentityV2 { name: vec![0; 256], url: vec![0; 256], + github_repo: vec![0; 256], image: vec![0; 1024], discord: vec![0; 256], description: vec![0; 1024], @@ -727,9 +734,10 @@ fn test_is_valid_identity() { assert!(SubtensorModule::is_valid_identity(&max_length_identity)); // Test identity with total length exceeding the maximum - let invalid_length_identity = ChainIdentity { + let invalid_length_identity = ChainIdentityV2 { name: vec![0; 257], url: vec![0; 256], + github_repo: vec![0; 256], image: vec![0; 1024], discord: vec![0; 256], description: vec![0; 1024], @@ -740,9 +748,10 @@ fn test_is_valid_identity() { )); // Test identity with one field exceeding its maximum - let invalid_field_identity = ChainIdentity { + let invalid_field_identity = ChainIdentityV2 { name: vec![0; 257], url: vec![0; 256], + github_repo: vec![0; 256], image: vec![0; 1024], discord: vec![0; 256], description: vec![0; 1024], @@ -751,9 +760,10 @@ fn test_is_valid_identity() { assert!(!SubtensorModule::is_valid_identity(&invalid_field_identity)); // Test identity with empty fields - let empty_identity = ChainIdentity { + let empty_identity = ChainIdentityV2 { name: vec![], url: vec![], + github_repo: vec![], image: vec![], discord: vec![], description: vec![], @@ -762,9 +772,10 @@ fn test_is_valid_identity() { assert!(SubtensorModule::is_valid_identity(&empty_identity)); // Test identity with some empty and some filled fields - let mixed_identity = ChainIdentity { + let mixed_identity = ChainIdentityV2 { name: b"Alice".to_vec(), url: b"https://alice.com".to_vec(), + github_repo: vec![], image: vec![], discord: b"alice#1234".to_vec(), description: vec![], @@ -773,9 +784,10 @@ fn test_is_valid_identity() { assert!(SubtensorModule::is_valid_identity(&mixed_identity)); // Test identity with all fields at maximum allowed length - let max_field_identity = ChainIdentity { + let max_field_identity = ChainIdentityV2 { name: vec![0; 256], url: vec![0; 256], + github_repo: vec![0; 256], image: vec![0; 1024], discord: vec![0; 256], description: vec![0; 1024], @@ -800,6 +812,7 @@ fn test_set_and_get_identity() { // Prepare identity data let name = b"Bob".to_vec(); let url = b"https://bob.com".to_vec(); + let git = b"https://github.com".to_vec(); let image = b"bob.jpg".to_vec(); let discord = b"bob#5678".to_vec(); let description = b"Bob's identity".to_vec(); @@ -810,6 +823,7 @@ fn test_set_and_get_identity() { <::RuntimeOrigin>::signed(coldkey), name.clone(), url.clone(), + git.clone(), image.clone(), discord.clone(), description.clone(), @@ -817,7 +831,7 @@ fn test_set_and_get_identity() { )); // Get and verify identity - let stored_identity = Identities::::get(coldkey).expect("Identity should be set"); + let stored_identity = IdentitiesV2::::get(coldkey).expect("Identity should be set"); assert_eq!(stored_identity.name, name); assert_eq!(stored_identity.url, url); assert_eq!(stored_identity.image, image); @@ -832,6 +846,7 @@ fn test_set_and_get_identity() { <::RuntimeOrigin>::signed(coldkey), new_name.clone(), new_url.clone(), + git.clone(), image.clone(), discord.clone(), description.clone(), @@ -840,7 +855,7 @@ fn test_set_and_get_identity() { // Get and verify updated identity let updated_identity = - Identities::::get(coldkey).expect("Updated identity should be set"); + IdentitiesV2::::get(coldkey).expect("Updated identity should be set"); assert_eq!(updated_identity.name, new_name); assert_eq!(updated_identity.url, new_url); assert_eq!(updated_identity.image, image); @@ -850,7 +865,7 @@ fn test_set_and_get_identity() { // Verify non-existent identity let non_existent_coldkey = U256::from(999); - assert!(Identities::::get(non_existent_coldkey).is_none()); + assert!(IdentitiesV2::::get(non_existent_coldkey).is_none()); }); } @@ -884,6 +899,118 @@ fn test_migrate_set_hotkey_identities() { }); } +#[test] +fn test_migrate_identities_to_v2() { + new_test_ext(1).execute_with(|| { + let account_id_1 = U256::from(1); + let account_id_2 = U256::from(2); + + let chainone_name = b"ChainOne".to_vec(); + let chainone_url = b"https://chainone.example".to_vec(); + let chainone_image = b"some_image_data".to_vec(); + let chainone_discord = b"discord#1".to_vec(); + let chainone_description = b"Old chain identity".to_vec(); + let chainone_additional = b"extra-info".to_vec(); + + let chaintwo_name = b"ChainTwo".to_vec(); + let chaintwo_url = b"https://chaintwo.example".to_vec(); + let chaintwo_description = b"Another chain identity".to_vec(); + + Identities::::insert( + account_id_1, + ChainIdentity { + name: chainone_name.clone(), + url: chainone_url.clone(), + image: chainone_image.clone(), + discord: chainone_discord.clone(), + description: chainone_description.clone(), + additional: chainone_additional.clone(), + }, + ); + + Identities::::insert( + account_id_2, + ChainIdentity { + name: chaintwo_name.clone(), + url: chaintwo_url.clone(), + image: b"".to_vec(), + discord: b"".to_vec(), + description: chaintwo_description.clone(), + additional: b"".to_vec(), + }, + ); + + let old_subnet_name = b"SubnetExample".to_vec(); + let old_github_repo = b"https://github.com/org/repo".to_vec(); + let old_subnet_contact = b"subnet@example".to_vec(); + + SubnetIdentities::::insert( + 42u16, + SubnetIdentity { + subnet_name: old_subnet_name.clone(), + github_repo: old_github_repo.clone(), + subnet_contact: old_subnet_contact.clone(), + }, + ); + + assert!(Identities::::get(account_id_1).is_some()); + assert!(Identities::::get(account_id_2).is_some()); + assert!(SubnetIdentities::::get(42u16).is_some()); + assert!(!HasMigrationRun::::get( + b"migrate_identities_to_v2".to_vec() + )); + + let weight = crate::migrations::migrate_identities_v2::migrate_identities_to_v2::(); + + assert!( + HasMigrationRun::::get(b"migrate_identities_to_v2".to_vec()), + "Expected HasMigrationRun to be true after migration" + ); + assert!(Identities::::get(account_id_1).is_none()); + assert!(Identities::::get(account_id_2).is_none()); + assert!(SubnetIdentities::::get(42u16).is_none()); + + let new_identity_1 = IdentitiesV2::::get(account_id_1) + .expect("ChainOne should be migrated to IdentitiesV2"); + let expected_github_repo = b"".to_vec(); + + assert_eq!(new_identity_1.name, chainone_name); + assert_eq!(new_identity_1.url, chainone_url); + assert_eq!(new_identity_1.github_repo, expected_github_repo); + assert_eq!(new_identity_1.image, chainone_image); + assert_eq!(new_identity_1.discord, chainone_discord); + assert_eq!(new_identity_1.description, chainone_description); + assert_eq!(new_identity_1.additional, chainone_additional); + + let new_identity_2 = IdentitiesV2::::get(account_id_2) + .expect("ChainTwo should be migrated to IdentitiesV2"); + assert_eq!(new_identity_2.name, chaintwo_name); + assert_eq!(new_identity_2.url, chaintwo_url); + assert_eq!(new_identity_2.github_repo, b"".to_vec()); + + let new_subnet_identity = SubnetIdentitiesV2::::get(42u16) + .expect("SubnetExample should be migrated to SubnetIdentitiesV2"); + + let expected_subnet_url = b"".to_vec(); + let expected_discord = b"".to_vec(); + let expected_description = b"".to_vec(); + let expected_additional = b"".to_vec(); + + assert_eq!(new_subnet_identity.subnet_name, old_subnet_name); + assert_eq!(new_subnet_identity.github_repo, old_github_repo); + assert_eq!(new_subnet_identity.subnet_contact, old_subnet_contact); + assert_eq!(new_subnet_identity.subnet_url, expected_subnet_url); + assert_eq!(new_subnet_identity.discord, expected_discord); + assert_eq!(new_subnet_identity.description, expected_description); + assert_eq!(new_subnet_identity.additional, expected_additional); + + assert!( + weight != Weight::zero(), + "Migration weight should be non-zero" + ); + }); +} + // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --test serving -- test_do_set_subnet_identity --exact --nocapture #[test] fn test_do_set_subnet_identity() { @@ -903,6 +1030,10 @@ fn test_do_set_subnet_identity() { let subnet_name = b"Test Subnet".to_vec(); let github_repo = b"https://github.com/test/subnet".to_vec(); let subnet_contact = b"contact@testsubnet.com".to_vec(); + let subnet_url = b"subnet.com".to_vec(); + let discord = b"discord.com".to_vec(); + let description = b"I am the describer".to_vec(); + let additional = b"tao foreva".to_vec(); // Set subnet identity assert_ok!(SubtensorModule::do_set_subnet_identity( @@ -910,12 +1041,16 @@ fn test_do_set_subnet_identity() { netuid, subnet_name.clone(), github_repo.clone(), - subnet_contact.clone() + subnet_contact.clone(), + subnet_url.clone(), + discord.clone(), + description.clone(), + additional.clone(), )); // Check if subnet identity is set correctly let stored_identity = - SubnetIdentities::::get(netuid).expect("Subnet identity should be set"); + SubnetIdentitiesV2::::get(netuid).expect("Subnet identity should be set"); assert_eq!(stored_identity.subnet_name, subnet_name); assert_eq!(stored_identity.github_repo, github_repo); assert_eq!(stored_identity.subnet_contact, subnet_contact); @@ -928,7 +1063,11 @@ fn test_do_set_subnet_identity() { netuid, subnet_name.clone(), github_repo.clone(), - subnet_contact.clone() + subnet_contact.clone(), + subnet_url.clone(), + discord.clone(), + description.clone(), + additional.clone(), ), Error::::NotSubnetOwner ); @@ -941,11 +1080,15 @@ fn test_do_set_subnet_identity() { netuid, new_subnet_name.clone(), new_github_repo.clone(), - subnet_contact.clone() + subnet_contact.clone(), + subnet_url.clone(), + discord.clone(), + description.clone(), + additional.clone(), )); let updated_identity = - SubnetIdentities::::get(netuid).expect("Updated subnet identity should be set"); + SubnetIdentitiesV2::::get(netuid).expect("Updated subnet identity should be set"); assert_eq!(updated_identity.subnet_name, new_subnet_name); assert_eq!(updated_identity.github_repo, new_github_repo); @@ -957,7 +1100,11 @@ fn test_do_set_subnet_identity() { netuid, long_data.clone(), long_data.clone(), - long_data.clone() + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone(), + long_data.clone(), ), Error::::InvalidIdentity ); @@ -969,56 +1116,80 @@ fn test_do_set_subnet_identity() { fn test_is_valid_subnet_identity() { new_test_ext(1).execute_with(|| { // Test valid subnet identity - let valid_identity = SubnetIdentity { + let valid_identity = SubnetIdentityV2 { subnet_name: vec![0; 256], github_repo: vec![0; 1024], subnet_contact: vec![0; 1024], + subnet_url: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], }; assert!(SubtensorModule::is_valid_subnet_identity(&valid_identity)); // Test subnet identity with total length exactly at the maximum - let max_length_identity = SubnetIdentity { + let max_length_identity = SubnetIdentityV2 { subnet_name: vec![0; 256], github_repo: vec![0; 1024], subnet_contact: vec![0; 1024], + subnet_url: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], }; assert!(SubtensorModule::is_valid_subnet_identity( &max_length_identity )); // Test subnet identity with total length exceeding the maximum - let invalid_length_identity = SubnetIdentity { + let invalid_length_identity = SubnetIdentityV2 { subnet_name: vec![0; 257], github_repo: vec![0; 1024], subnet_contact: vec![0; 1024], + subnet_url: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], }; assert!(!SubtensorModule::is_valid_subnet_identity( &invalid_length_identity )); // Test subnet identity with one field exceeding its maximum - let invalid_field_identity = SubnetIdentity { + let invalid_field_identity = SubnetIdentityV2 { subnet_name: vec![0; 257], github_repo: vec![0; 1024], subnet_contact: vec![0; 1024], + subnet_url: vec![0; 1024], + discord: vec![0; 256], + description: vec![0; 1024], + additional: vec![0; 1024], }; assert!(!SubtensorModule::is_valid_subnet_identity( &invalid_field_identity )); // Test subnet identity with empty fields - let empty_identity = SubnetIdentity { + let empty_identity = SubnetIdentityV2 { subnet_name: vec![], github_repo: vec![], subnet_contact: vec![], + subnet_url: vec![], + discord: vec![], + description: vec![], + additional: vec![], }; assert!(SubtensorModule::is_valid_subnet_identity(&empty_identity)); // Test subnet identity with some empty and some filled fields - let mixed_identity = SubnetIdentity { + let mixed_identity = SubnetIdentityV2 { subnet_name: b"Test Subnet".to_vec(), github_repo: vec![], subnet_contact: b"contact@testsubnet.com".to_vec(), + subnet_url: b"https://testsubnet.com".to_vec(), + discord: vec![], + description: b"A description".to_vec(), + additional: vec![], }; assert!(SubtensorModule::is_valid_subnet_identity(&mixed_identity)); }); @@ -1034,6 +1205,10 @@ fn test_set_identity_for_non_existent_subnet() { let subnet_name = b"Non-existent Subnet".to_vec(); let github_repo = b"https://github.com/test/nonexistent".to_vec(); let subnet_contact = b"contact@nonexistent.com".to_vec(); + let subnet_url = b"subnet.com".to_vec(); + let discord = b"discord.com".to_vec(); + let description = b"I am the describer".to_vec(); + let additional = b"tao foreva".to_vec(); // Attempt to set identity for a non-existent subnet assert_noop!( @@ -1042,7 +1217,11 @@ fn test_set_identity_for_non_existent_subnet() { netuid, subnet_name.clone(), github_repo.clone(), - subnet_contact.clone() + subnet_contact.clone(), + subnet_url.clone(), + discord.clone(), + description.clone(), + additional.clone(), ), Error::::NotSubnetOwner // Since there's no owner, it should fail ); @@ -1056,12 +1235,20 @@ fn test_set_subnet_identity_dispatch_info_ok() { let subnet_name: Vec = b"JesusSubnet".to_vec(); let github_repo: Vec = b"bible.com".to_vec(); let subnet_contact: Vec = b"https://www.vatican.va".to_vec(); + let subnet_url = b"subnet.com".to_vec(); + let discord = b"discord.com".to_vec(); + let description = b"I am the describer".to_vec(); + let additional = b"tao foreva".to_vec(); let call: RuntimeCall = RuntimeCall::SubtensorModule(SubtensorCall::set_subnet_identity { netuid, subnet_name, github_repo, subnet_contact, + subnet_url, + discord, + description, + additional, }); let dispatch_info: DispatchInfo = call.get_dispatch_info(); diff --git a/pallets/subtensor/src/tests/swap_coldkey.rs b/pallets/subtensor/src/tests/swap_coldkey.rs index f21af56cf9..7761005780 100644 --- a/pallets/subtensor/src/tests/swap_coldkey.rs +++ b/pallets/subtensor/src/tests/swap_coldkey.rs @@ -673,19 +673,20 @@ fn test_do_swap_coldkey_success() { // Insert an Identity let name: Vec = b"The fourth Coolest Identity".to_vec(); - let identity: ChainIdentity = ChainIdentity { + let identity: ChainIdentityV2 = ChainIdentityV2 { name: name.clone(), url: vec![], + github_repo: vec![], image: vec![], discord: vec![], description: vec![], additional: vec![], }; - Identities::::insert(old_coldkey, identity.clone()); + IdentitiesV2::::insert(old_coldkey, identity.clone()); - assert!(Identities::::get(old_coldkey).is_some()); - assert!(Identities::::get(new_coldkey).is_none()); + assert!(IdentitiesV2::::get(old_coldkey).is_some()); + assert!(IdentitiesV2::::get(new_coldkey).is_none()); // Log state after adding stake log::info!( @@ -774,10 +775,10 @@ fn test_do_swap_coldkey_success() { ); // Verify identities were swapped - assert!(Identities::::get(old_coldkey).is_none()); - assert!(Identities::::get(new_coldkey).is_some()); + assert!(IdentitiesV2::::get(old_coldkey).is_none()); + assert!(IdentitiesV2::::get(new_coldkey).is_some()); assert_eq!( - Identities::::get(new_coldkey).expect("Expected an Identity"), + IdentitiesV2::::get(new_coldkey).expect("Expected an Identity"), identity ); @@ -1887,19 +1888,20 @@ fn test_coldkey_swap_delegate_identity_updated() { )); let name: Vec = b"The Third Coolest Identity".to_vec(); - let identity: ChainIdentity = ChainIdentity { + let identity: ChainIdentityV2 = ChainIdentityV2 { name: name.clone(), url: vec![], image: vec![], + github_repo: vec![], discord: vec![], description: vec![], additional: vec![], }; - Identities::::insert(old_coldkey, identity.clone()); + IdentitiesV2::::insert(old_coldkey, identity.clone()); - assert!(Identities::::get(old_coldkey).is_some()); - assert!(Identities::::get(new_coldkey).is_none()); + assert!(IdentitiesV2::::get(old_coldkey).is_some()); + assert!(IdentitiesV2::::get(new_coldkey).is_none()); assert_ok!(SubtensorModule::do_swap_coldkey( &old_coldkey, @@ -1907,10 +1909,10 @@ fn test_coldkey_swap_delegate_identity_updated() { burn_cost )); - assert!(Identities::::get(old_coldkey).is_none()); - assert!(Identities::::get(new_coldkey).is_some()); + assert!(IdentitiesV2::::get(old_coldkey).is_none()); + assert!(IdentitiesV2::::get(new_coldkey).is_some()); assert_eq!( - Identities::::get(new_coldkey).expect("Expected an Identity"), + IdentitiesV2::::get(new_coldkey).expect("Expected an Identity"), identity ); }); @@ -1938,7 +1940,7 @@ fn test_coldkey_swap_no_identity_no_changes() { )); // Ensure the old coldkey does not have an identity before the swap - assert!(Identities::::get(old_coldkey).is_none()); + assert!(IdentitiesV2::::get(old_coldkey).is_none()); // Perform the coldkey swap assert_ok!(SubtensorModule::do_swap_coldkey( @@ -1948,8 +1950,8 @@ fn test_coldkey_swap_no_identity_no_changes() { )); // Ensure no identities have been changed - assert!(Identities::::get(old_coldkey).is_none()); - assert!(Identities::::get(new_coldkey).is_none()); + assert!(IdentitiesV2::::get(old_coldkey).is_none()); + assert!(IdentitiesV2::::get(new_coldkey).is_none()); }); } @@ -1974,19 +1976,20 @@ fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { )); let name: Vec = b"The Coolest Identity".to_vec(); - let identity: ChainIdentity = ChainIdentity { + let identity: ChainIdentityV2 = ChainIdentityV2 { name: name.clone(), url: vec![], + github_repo: vec![], image: vec![], discord: vec![], description: vec![], additional: vec![], }; - Identities::::insert(new_coldkey, identity.clone()); + IdentitiesV2::::insert(new_coldkey, identity.clone()); // Ensure the new coldkey does have an identity before the swap - assert!(Identities::::get(new_coldkey).is_some()); - assert!(Identities::::get(old_coldkey).is_none()); + assert!(IdentitiesV2::::get(new_coldkey).is_some()); + assert!(IdentitiesV2::::get(old_coldkey).is_none()); // Perform the coldkey swap assert_ok!(SubtensorModule::do_swap_coldkey( @@ -1996,8 +1999,8 @@ fn test_coldkey_swap_no_identity_no_changes_newcoldkey_exists() { )); // Ensure no identities have been changed - assert!(Identities::::get(old_coldkey).is_none()); - assert!(Identities::::get(new_coldkey).is_some()); + assert!(IdentitiesV2::::get(old_coldkey).is_none()); + assert!(IdentitiesV2::::get(new_coldkey).is_some()); }); } diff --git a/pallets/subtensor/src/utils/identity.rs b/pallets/subtensor/src/utils/identity.rs index 7babb04f4f..0e83205cc0 100644 --- a/pallets/subtensor/src/utils/identity.rs +++ b/pallets/subtensor/src/utils/identity.rs @@ -27,6 +27,7 @@ impl Pallet { origin: T::RuntimeOrigin, name: Vec, url: Vec, + github_repo: Vec, image: Vec, discord: Vec, description: Vec, @@ -47,9 +48,10 @@ impl Pallet { ); // Create the identity struct with the provided information - let identity = ChainIdentityOf { + let identity = ChainIdentityOfV2 { name, url, + github_repo, image, discord, description, @@ -63,7 +65,7 @@ impl Pallet { ); // Store the validated identity in the blockchain state - Identities::::insert(coldkey.clone(), identity.clone()); + IdentitiesV2::::insert(coldkey.clone(), identity.clone()); // Log the identity set event log::debug!("ChainIdentitySet( coldkey:{:?} ) ", coldkey.clone()); @@ -98,6 +100,10 @@ impl Pallet { subnet_name: Vec, github_repo: Vec, subnet_contact: Vec, + subnet_url: Vec, + discord: Vec, + description: Vec, + additional: Vec, ) -> dispatch::DispatchResult { // Ensure the call is signed and get the signer's (coldkey) account let coldkey = ensure_signed(origin)?; @@ -109,10 +115,14 @@ impl Pallet { ); // Create the identity struct with the provided information - let identity: SubnetIdentityOf = SubnetIdentityOf { + let identity: SubnetIdentityOfV2 = SubnetIdentityOfV2 { subnet_name, github_repo, subnet_contact, + subnet_url, + discord, + description, + additional, }; // Validate the created identity @@ -122,7 +132,7 @@ impl Pallet { ); // Store the validated identity in the blockchain state - SubnetIdentities::::insert(netuid, identity.clone()); + SubnetIdentitiesV2::::insert(netuid, identity.clone()); // Log the identity set event log::info!("SubnetIdentitySet( netuid:{:?} ) ", netuid); @@ -147,7 +157,7 @@ impl Pallet { /// # Returns /// /// * `bool` - Returns true if the Identity is valid, false otherwise. - pub fn is_valid_identity(identity: &ChainIdentityOf) -> bool { + pub fn is_valid_identity(identity: &ChainIdentityOfV2) -> bool { let total_length = identity .name .len() @@ -157,9 +167,18 @@ impl Pallet { .saturating_add(identity.description.len()) .saturating_add(identity.additional.len()); - total_length <= 256 + 256 + 1024 + 256 + 1024 + 1024 + let max_length: usize = 256_usize + .saturating_add(256) + .saturating_add(256) + .saturating_add(1024) + .saturating_add(256) + .saturating_add(1024) + .saturating_add(1024); + + total_length <= max_length && identity.name.len() <= 256 && identity.url.len() <= 256 + && identity.github_repo.len() <= 256 && identity.image.len() <= 1024 && identity.discord.len() <= 256 && identity.description.len() <= 1024 @@ -179,16 +198,28 @@ impl Pallet { /// # Returns /// /// * `bool` - Returns true if the SubnetIdentity is valid, false otherwise. - pub fn is_valid_subnet_identity(identity: &SubnetIdentityOf) -> bool { + pub fn is_valid_subnet_identity(identity: &SubnetIdentityOfV2) -> 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 + let max_length: usize = 256_usize + .saturating_add(1024) + .saturating_add(1024) + .saturating_add(1024) + .saturating_add(256) + .saturating_add(1024) + .saturating_add(1024); + + total_length <= max_length && identity.subnet_name.len() <= 256 && identity.github_repo.len() <= 1024 && identity.subnet_contact.len() <= 1024 + && identity.subnet_url.len() <= 1024 + && identity.discord.len() <= 256 + && identity.description.len() <= 1024 + && identity.additional.len() <= 1024 } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a9a4bd2ca8..25f01692a8 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -229,7 +229,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: 226, + spec_version: 227, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 9944572c51..d62a0efd04 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -5,8 +5,8 @@ use pallet_evm::{ AddressMapping, ExitError, HashedAddressMapping, PrecompileFailure, PrecompileHandle, PrecompileResult, }; -use sp_runtime::traits::BlakeTwo256; use sp_runtime::AccountId32; +use sp_runtime::{traits::BlakeTwo256, Vec}; use sp_std::vec; pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; // bytes with max lenght 1K @@ -62,14 +62,26 @@ impl SubnetPrecompile { ) } 33.. => { - let (hotkey, subnet_name, github_repo, subnet_contact) = - Self::parse_register_network_parameters(data)?; + let ( + hotkey, + subnet_name, + github_repo, + subnet_contact, + subnet_url, + discord, + description, + additional, + ) = Self::parse_register_network_parameters(data)?; - let identity: pallet_subtensor::SubnetIdentityOf = - pallet_subtensor::SubnetIdentityOf { + let identity: pallet_subtensor::SubnetIdentityOfV2 = + pallet_subtensor::SubnetIdentityOfV2 { subnet_name, github_repo, subnet_contact, + subnet_url, + discord, + description, + additional, }; // Create the register_network callcle @@ -98,12 +110,24 @@ impl SubnetPrecompile { fn parse_register_network_parameters( data: &[u8], - ) -> Result<(AccountId32, vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { + ) -> Result< + ( + AccountId32, + Vec, + Vec, + Vec, + Vec, + Vec, + Vec, + Vec, + ), + PrecompileFailure, + > { let (pubkey, dynamic_params) = get_pubkey(data)?; let dynamic_data_len = dynamic_params.len(); let mut buf = [0_u8; 4]; - // get all start point for three data items: name, repo and contact + // get all start points for the data items: name, repo, contact, url, discord, description, additional buf.copy_from_slice(get_slice(data, 60, 64)?); let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; if subnet_name_start > dynamic_data_len { @@ -140,6 +164,54 @@ impl SubnetPrecompile { }); } + buf.copy_from_slice(get_slice(data, 156, 160)?); + let subnet_url_start: usize = u32::from_be_bytes(buf) as usize; + if subnet_url_start > dynamic_data_len { + log::error!( + "the start position of subnet_url as {} is too big ", + subnet_url_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + buf.copy_from_slice(get_slice(data, 188, 192)?); + let discord_start: usize = u32::from_be_bytes(buf) as usize; + if discord_start > dynamic_data_len { + log::error!( + "the start position of discord as {} is too big ", + discord_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + buf.copy_from_slice(get_slice(data, 220, 224)?); + let description_start: usize = u32::from_be_bytes(buf) as usize; + if description_start > dynamic_data_len { + log::error!( + "the start position of description as {} is too big ", + description_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + buf.copy_from_slice(get_slice(data, 252, 256)?); + let additional_start: usize = u32::from_be_bytes(buf) as usize; + if additional_start > dynamic_data_len { + log::error!( + "the start position of additional as {} is too big ", + additional_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + // get name buf.copy_from_slice(get_slice( data, @@ -149,7 +221,10 @@ impl SubnetPrecompile { let subnet_name_len: usize = u32::from_be_bytes(buf) as usize; if subnet_name_len > MAX_SINGLE_PARAMETER_SIZE { - log::error!("the length of subnet nae as {} is too big", subnet_name_len); + log::error!( + "the length of subnet name as {} is too big", + subnet_name_len + ); return Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, }); @@ -210,6 +285,94 @@ impl SubnetPrecompile { subnet_contact_start + subnet_contact_len + 32, )?); - Ok((pubkey, name_vec, repo_vec, contact_vec)) + // get subnet_url + buf.copy_from_slice(get_slice( + data, + subnet_url_start + 28, + subnet_url_start + 32, + )?); + let subnet_url_len: usize = u32::from_be_bytes(buf) as usize; + if subnet_url_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!("the length of subnet_url as {} is too big", subnet_url_len); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut url_vec = vec![0; subnet_url_len]; + url_vec.copy_from_slice(get_slice( + data, + subnet_url_start + 32, + subnet_url_start + subnet_url_len + 32, + )?); + + // get discord + buf.copy_from_slice(get_slice(data, discord_start + 28, discord_start + 32)?); + let discord_len: usize = u32::from_be_bytes(buf) as usize; + if discord_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!("the length of discord as {} is too big", discord_len); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut discord_vec = vec![0; discord_len]; + discord_vec.copy_from_slice(get_slice( + data, + discord_start + 32, + discord_start + discord_len + 32, + )?); + + // get description + buf.copy_from_slice(get_slice( + data, + description_start + 28, + description_start + 32, + )?); + let description_len: usize = u32::from_be_bytes(buf) as usize; + if description_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!( + "the length of description as {} is too big", + description_len + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut description_vec = vec![0; description_len]; + description_vec.copy_from_slice(get_slice( + data, + description_start + 32, + description_start + description_len + 32, + )?); + + // get additional + buf.copy_from_slice(get_slice( + data, + additional_start + 28, + additional_start + 32, + )?); + let additional_len: usize = u32::from_be_bytes(buf) as usize; + if additional_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!("the length of additional as {} is too big", additional_len); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut additional_vec = vec![0; additional_len]; + additional_vec.copy_from_slice(get_slice( + data, + additional_start + 32, + additional_start + additional_len + 32, + )?); + + Ok(( + pubkey, + name_vec, + repo_vec, + contact_vec, + url_vec, + discord_vec, + description_vec, + additional_vec, + )) } }