From a363fe325a1e09823ba43d661cba3f946bb4bfc1 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 14 Aug 2023 14:48:32 -0400 Subject: [PATCH 01/11] add associated ips for validators --- pallets/subtensor/src/lib.rs | 20 ++++ pallets/subtensor/src/serving.rs | 164 +++++++++++++++++++++++++------ pallets/subtensor/src/uids.rs | 37 ++++--- pallets/subtensor/src/utils.rs | 112 +++++++++++---------- runtime/src/lib.rs | 2 + 5 files changed, 242 insertions(+), 93 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d614781605..fe11237dce 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -165,6 +165,12 @@ pub mod pallet { type InitialSenateRequiredStakePercentage: Get; #[pallet::constant] // Initial adjustment alpha on burn and pow. type InitialAdjustmentAlpha: Get; + + // ================================= + // ===== Bounded Vec Max Sizes ===== + // ================================= + #[pallet::constant] // Maximum number of associated IPs per validator hotkey. + type AssociatedIPsMaxSize: Get; } pub type AccountIdOf = ::AccountId; @@ -425,6 +431,15 @@ pub mod pallet { pub placeholder2: u8, // --- Axon proto placeholder 1. } + // --- Struct for IP Info. + pub type IPInfoOf = IPInfo; + #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + pub struct IPInfo { + pub ip: u128, // --- u128 encoded IP address of type v6 or v4. + pub ip_type: u8, // --- IP type, 4 for ipv4 and 6 for ipv6. + pub protocol: u8, // --- Axon protocol. TCP, UDP, other. + } + // --- Struct for Prometheus. pub type PrometheusInfoOf = PrometheusInfo; #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] @@ -463,6 +478,9 @@ pub mod pallet { #[pallet::storage] // --- MAP ( netuid, hotkey ) --> axon_info pub(super) type Axons = StorageDoubleMap<_, Identity, u16, Blake2_128Concat, T::AccountId, AxonInfoOf, OptionQuery>; + #[pallet::storage] // --- MAP ( netuid, hotkey ) --> (block_last_updated, Vec) + pub(super) type AssociatedIPInfo = + StorageDoubleMap<_, Identity, u16, Blake2_128Concat, T::AccountId, (u64, BoundedVec), OptionQuery>; #[pallet::storage] // --- MAP ( netuid, hotkey ) --> prometheus_info pub(super) type Prometheus = StorageDoubleMap< _, @@ -761,6 +779,7 @@ pub mod pallet { RAORecycledForRegistrationSet(u16, u64), // Event created when setting the RAO recycled for registration. SenateRequiredStakePercentSet(u64), // Event created when setting the minimum required stake amount for senate registration. AdjustmentAlphaSet(u16, u64), // Event created when setting the adjustment alpha on a subnet. + IPInfoSet(u16, T::AccountId, Vec), // Event created when setting the IP info for a neuron. } // Errors inform users that something went wrong. @@ -817,6 +836,7 @@ pub mod pallet { BelowStakeThreshold, // --- Thrown when a hotkey attempts to join the senate without enough stake NotDelegate, // --- Thrown when a hotkey attempts to join the senate without being a delegate first IncorrectNetuidsLength, // --- Thrown when an incorrect amount of Netuids are passed as input + AssociatedIPsMaxSizeExceeded, // --- Thrown when the number of associated IPs exceeds the maximum allowed } // ================== diff --git a/pallets/subtensor/src/serving.rs b/pallets/subtensor/src/serving.rs index c340fa89c8..2120b1780f 100644 --- a/pallets/subtensor/src/serving.rs +++ b/pallets/subtensor/src/serving.rs @@ -1,6 +1,5 @@ use super::*; -use frame_support::inherent::Vec; -use frame_support::sp_std::vec; +use frame_support::{inherent::Vec, sp_std::vec, BoundedVec}; impl Pallet { @@ -22,12 +21,12 @@ impl Pallet { // // * 'port' (u16): // - The endpoint port information as a u16 encoded integer. - // + // // * 'ip_type' (u8): // - The endpoint ip version as a u8, 4 or 6. // // * 'protocol' (u8): - // - UDP:1 or TCP:0 + // - UDP:1 or TCP:0 // // * 'placeholder1' (u8): // - Placeholder for further extra params. @@ -55,31 +54,31 @@ impl Pallet { // * 'ServingRateLimitExceeded': // - Attempting to set prometheus information withing the rate limit min. // - pub fn do_serve_axon( - origin: T::RuntimeOrigin, + pub fn do_serve_axon( + origin: T::RuntimeOrigin, netuid: u16, - version: u32, - ip: u128, - port: u16, + version: u32, + ip: u128, + port: u16, ip_type: u8, - protocol: u8, - placeholder1: u8, + protocol: u8, + placeholder1: u8, placeholder2: u8, ) -> dispatch::DispatchResult { // --- 1. We check the callers (hotkey) signature. let hotkey_id = ensure_signed(origin)?; // --- 2. Ensure the hotkey is registered somewhere. - ensure!( Self::is_hotkey_registered_on_any_network( &hotkey_id ), Error::::NotRegistered ); - + ensure!( Self::is_hotkey_registered_on_any_network( &hotkey_id ), Error::::NotRegistered ); + // --- 3. Check the ip signature validity. ensure!( Self::is_valid_ip_type(ip_type), Error::::InvalidIpType ); ensure!( Self::is_valid_ip_address(ip_type, ip), Error::::InvalidIpAddress ); - + // --- 4. Get the previous axon information. let mut prev_axon = Self::get_axon_info( netuid, &hotkey_id ); let current_block:u64 = Self::get_current_block_as_u64(); - ensure!( Self::axon_passes_rate_limit( netuid, &prev_axon, current_block ), Error::::ServingRateLimitExceeded ); + ensure!( Self::axon_passes_rate_limit( netuid, &prev_axon, current_block ), Error::::ServingRateLimitExceeded ); // --- 6. We insert the axon meta. prev_axon.block = Self::get_current_block_as_u64(); @@ -101,7 +100,95 @@ impl Pallet { log::info!("AxonServed( hotkey:{:?} ) ", hotkey_id.clone() ); Self::deposit_event(Event::AxonServed( netuid, hotkey_id )); - // --- 9. Return is successful dispatch. + // --- 9. Return is successful dispatch. + Ok(()) + } + + // ---- The implementation for the extrinsic associate_ips which sets the outgoing IP information of a uid on a network. + // + // # Args: + // * 'origin': (RuntimeOrigin): + // - The signature of the caller. + // + // * 'netuid' (u16): + // - The u16 network identifier. + // + // * 'version' (u64): + // - The bittensor version identifier. + // + // * 'ip' (u64): + // - The endpoint ip information as a u128 encoded integer. + // + // * 'port' (u16): + // - The endpoint port information as a u16 encoded integer. + // + // * 'ip_type' (u8): + // - The endpoint ip version as a u8, 4 or 6. + // + // * 'protocol' (u8): + // - UDP:1 or TCP:0 + // + // * 'placeholder1' (u8): + // - Placeholder for further extra params. + // + // * 'placeholder2' (u8): + // - Placeholder for further extra params. + // + // # Event: + // * IPInfoSet; + // - On successfully setting the ip info. + // + // # Raises: + // * 'NetworkDoesNotExist': + // - Attempting to set weights on a non-existent network. + // + // * 'NotAValidator': + // - Attempting to add associated IPs without a vpermit. + // + // * 'InvalidIpType': + // - The ip type is not 4 or 6. + // + // * 'InvalidIpAddress': + // - The numerically encoded ip address does not resolve to a proper ip. + // + // * 'ServingRateLimitExceeded': + // - Attempting to set prometheus information withing the rate limit min. + // + pub fn do_associate_ips( + origin: T::RuntimeOrigin, + netuid: u16, + associated_ips: Vec, + ) -> dispatch::DispatchResult { + // --- 1. We check the callers (hotkey) signature. + let hotkey_id = ensure_signed(origin)?; + + // --- 2. Ensure the hotkey is a validator, i.e. has a vpermit + ensure!( Self::has_vpermit_on_any_network( &hotkey_id ), Error::::NoValidatorPermit); + + // --- 3. Check the ip validity. + for ip_info in &associated_ips { + ensure!( Self::is_valid_ip_type(ip_info.ip_type), Error::::InvalidIpType ); + ensure!( Self::is_valid_ip_address(ip_info.ip_type, ip_info.ip), Error::::InvalidIpAddress ); + } + + // --- 4. Get the previous associated IP information. + let prev_associated_ip_info = Self::get_associated_ip_info( netuid, &hotkey_id ); + let last_update_block = prev_associated_ip_info.0; + let current_block:u64 = Self::get_current_block_as_u64(); + ensure!( Self::associate_ip_info_passes_rate_limit( netuid, last_update_block, current_block ), Error::::ServingRateLimitExceeded ); + + // --- 5. Check conversion to bounded vec. + let bounded_associated_ips = BoundedVec::::try_from(associated_ips.clone()); + ensure!( bounded_associated_ips.is_ok(), Error::::AssociatedIPsMaxSizeExceeded); + + // --- 5. We insert the associated IP Info and update the block. + AssociatedIPInfo::::insert( netuid, hotkey_id.clone(), (current_block, bounded_associated_ips.unwrap() ) ); + + // --- 6. We deposit Associated IPs set event. + log::info!("IPInfoSet( hotkey:{:?} ) ", hotkey_id.clone() ); + Self::deposit_event(Event::IPInfoSet( netuid, hotkey_id, associated_ips)); + + // --- 7. Return is successful dispatch. Ok(()) } @@ -122,7 +209,7 @@ impl Pallet { // // * 'port' (u16): // - The prometheus port information as a u16 encoded integer. - // + // // * 'ip_type' (u8): // - The prometheus ip version as a u8, 4 or 6. // @@ -146,28 +233,28 @@ impl Pallet { // * 'ServingRateLimitExceeded': // - Attempting to set prometheus information withing the rate limit min. // - pub fn do_serve_prometheus( - origin: T::RuntimeOrigin, + pub fn do_serve_prometheus( + origin: T::RuntimeOrigin, netuid: u16, - version: u32, - ip: u128, - port: u16, + version: u32, + ip: u128, + port: u16, ip_type: u8, ) -> dispatch::DispatchResult { // --- 1. We check the callers (hotkey) signature. let hotkey_id = ensure_signed(origin)?; // --- 2. Ensure the hotkey is registered somewhere. - ensure!( Self::is_hotkey_registered_on_any_network( &hotkey_id ), Error::::NotRegistered ); + ensure!( Self::is_hotkey_registered_on_any_network( &hotkey_id ), Error::::NotRegistered ); // --- 3. Check the ip signature validity. ensure!( Self::is_valid_ip_type(ip_type), Error::::InvalidIpType ); ensure!( Self::is_valid_ip_address(ip_type, ip), Error::::InvalidIpAddress ); - + // --- 5. We get the previous axon info assoicated with this ( netuid, uid ) let mut prev_prometheus = Self::get_prometheus_info( netuid, &hotkey_id ); let current_block:u64 = Self::get_current_block_as_u64(); - ensure!( Self::prometheus_passes_rate_limit( netuid, &prev_prometheus, current_block ), Error::::ServingRateLimitExceeded ); + ensure!( Self::prometheus_passes_rate_limit( netuid, &prev_prometheus, current_block ), Error::::ServingRateLimitExceeded ); // --- 6. We insert the prometheus meta. prev_prometheus.block = Self::get_current_block_as_u64(); @@ -187,7 +274,7 @@ impl Pallet { log::info!("PrometheusServed( hotkey:{:?} ) ", hotkey_id.clone() ); Self::deposit_event(Event::PrometheusServed( netuid, hotkey_id )); - // --- 10. Return is successful dispatch. + // --- 10. Return is successful dispatch. Ok(()) } @@ -195,6 +282,12 @@ impl Pallet { --==[[ Helper functions ]]==-- *********************************/ + pub fn associate_ip_info_passes_rate_limit( netuid: u16, last_updated_block: u64, current_block: u64 ) -> bool { + let rate_limit: u64 = Self::get_serving_rate_limit(netuid); + let last_serve = last_updated_block; + return rate_limit == 0 || last_serve == 0 || current_block - last_serve >= rate_limit; + } + pub fn axon_passes_rate_limit( netuid: u16, prev_axon_info: &AxonInfoOf, current_block: u64 ) -> bool { let rate_limit: u64 = Self::get_serving_rate_limit(netuid); let last_serve = prev_axon_info.block; @@ -211,6 +304,10 @@ impl Pallet { return Axons::::contains_key( netuid, hotkey ); } + pub fn has_associated_ip_info( netuid: u16, hotkey: &T::AccountId ) -> bool { + return AssociatedIPInfo::::contains_key( netuid, hotkey ); + } + pub fn has_prometheus_info( netuid: u16, hotkey: &T::AccountId ) -> bool { return Prometheus::::contains_key( netuid, hotkey ); } @@ -219,7 +316,7 @@ impl Pallet { if Self::has_axon_info( netuid, hotkey ) { return Axons::::get( netuid, hotkey ).unwrap(); } else{ - return AxonInfo { + return AxonInfo { block: 0, version: 0, ip: 0, @@ -233,11 +330,20 @@ impl Pallet { } } + pub fn get_associated_ip_info( netuid: u16, hotkey: &T::AccountId ) -> (u64, Vec){ + if Self::has_associated_ip_info( netuid, hotkey ) { + let result = AssociatedIPInfo::::get( netuid, hotkey ).unwrap(); + return (result.0, result.1.into_inner()); + } else{ + return (0, vec![]) + } + } + pub fn get_prometheus_info( netuid: u16, hotkey: &T::AccountId ) -> PrometheusInfoOf { if Self::has_prometheus_info( netuid, hotkey ) { return Prometheus::::get( netuid, hotkey ).unwrap(); } else { - return PrometheusInfo { + return PrometheusInfo { block: 0, version: 0, ip: 0, @@ -290,4 +396,4 @@ impl Pallet { Ok(true) } -} \ No newline at end of file +} diff --git a/pallets/subtensor/src/uids.rs b/pallets/subtensor/src/uids.rs index 9beaaa432c..3846027a0d 100644 --- a/pallets/subtensor/src/uids.rs +++ b/pallets/subtensor/src/uids.rs @@ -5,12 +5,12 @@ use frame_support::storage::IterableStorageMap; use frame_support::pallet_prelude::DispatchError; use frame_support::storage::IterableStorageDoubleMap; -impl Pallet { +impl Pallet { // Returns the number of filled slots on a network. /// - pub fn get_subnetwork_n( netuid:u16 ) -> u16 { - return SubnetworkN::::get( netuid ) + pub fn get_subnetwork_n( netuid:u16 ) -> u16 { + return SubnetworkN::::get( netuid ) } // Replace the neuron under this uid. @@ -22,9 +22,9 @@ impl Pallet { let old_hotkey: T::AccountId = Keys::::get( netuid, uid_to_replace ); // 2. Remove previous set memberships. - Uids::::remove( netuid, old_hotkey.clone() ); + Uids::::remove( netuid, old_hotkey.clone() ); IsNetworkMember::::remove( old_hotkey.clone(), netuid ); - Keys::::remove( netuid, uid_to_replace ); + Keys::::remove( netuid, uid_to_replace ); // 2a. Check if the uid is registered in any other subnetworks. let hotkey_is_registered_on_any_network: bool = Self::is_hotkey_registered_on_any_network( &old_hotkey.clone() ); @@ -63,7 +63,7 @@ impl Pallet { PruningScores::::mutate(netuid, |v| v.push(0) ); ValidatorTrust::::mutate(netuid, |v| v.push(0) ); ValidatorPermit::::mutate(netuid, |v| v.push(false) ); - + // 4. Insert new account information. Keys::::insert( netuid, next_uid, new_hotkey.clone() ); // Make hotkey - uid association. Uids::::insert( netuid, new_hotkey.clone(), next_uid ); // Make uid - hotkey association. @@ -79,27 +79,27 @@ impl Pallet { // Returns true if the hotkey holds a slot on the network. // - pub fn is_hotkey_registered_on_network( netuid:u16, hotkey: &T::AccountId ) -> bool { - return Uids::::contains_key( netuid, hotkey ) + pub fn is_hotkey_registered_on_network( netuid:u16, hotkey: &T::AccountId ) -> bool { + return Uids::::contains_key( netuid, hotkey ) } // Returs the hotkey under the network uid as a Result. Ok if the uid is taken. // pub fn get_hotkey_for_net_and_uid( netuid: u16, neuron_uid: u16) -> Result { - Keys::::try_get(netuid, neuron_uid).map_err(|_err| Error::::NotRegistered.into()) + Keys::::try_get(netuid, neuron_uid).map_err(|_err| Error::::NotRegistered.into()) } // Returns the uid of the hotkey in the network as a Result. Ok if the hotkey has a slot. // - pub fn get_uid_for_net_and_hotkey( netuid: u16, hotkey: &T::AccountId) -> Result { - return Uids::::try_get(netuid, &hotkey).map_err(|_err| Error::::NotRegistered.into()) + pub fn get_uid_for_net_and_hotkey( netuid: u16, hotkey: &T::AccountId) -> Result { + return Uids::::try_get(netuid, &hotkey).map_err(|_err| Error::::NotRegistered.into()) } // Returns the stake of the uid on network or 0 if it doesnt exist. // - pub fn get_stake_for_uid_and_subnetwork( netuid: u16, neuron_uid: u16) -> u64 { + pub fn get_stake_for_uid_and_subnetwork( netuid: u16, neuron_uid: u16) -> u64 { if Self::is_uid_exist_on_network( netuid, neuron_uid) { - return Self::get_total_stake_for_hotkey( &Self::get_hotkey_for_net_and_uid( netuid, neuron_uid ).unwrap() ) + return Self::get_total_stake_for_hotkey( &Self::get_hotkey_for_net_and_uid( netuid, neuron_uid ).unwrap() ) } else { return 0; } @@ -134,4 +134,15 @@ impl Pallet { } false } + + pub fn get_registrations_for_hotkey( hotkey: &T::AccountId ) -> Vec<(u16, u16)> { + let mut all_uids: Vec<(u16, u16)> = vec![]; + for ( network, is_registered) in as IterableStorageDoubleMap< T::AccountId, u16, bool >>::iter_prefix( hotkey ){ + if is_registered { + let uid = Self::get_uid_for_net_and_hotkey( network, hotkey ).unwrap(); + all_uids.push( (network, uid) ); + } + } + all_uids + } } diff --git a/pallets/subtensor/src/utils.rs b/pallets/subtensor/src/utils.rs index 4b39e7b01f..9e131add80 100644 --- a/pallets/subtensor/src/utils.rs +++ b/pallets/subtensor/src/utils.rs @@ -6,7 +6,7 @@ use frame_support::pallet_prelude::DispatchResult; use crate::system::ensure_root; impl Pallet { - + // ======================== // ==== Global Setters ==== // ======================== @@ -41,22 +41,32 @@ impl Pallet { pub fn get_validator_trust( netuid:u16 ) -> Vec { ValidatorTrust::::get( netuid ) } pub fn get_validator_permit( netuid:u16 ) -> Vec { ValidatorPermit::::get( netuid ) } + pub fn has_vpermit_on_any_network( hotkey: &T::AccountId ) -> bool { + let registrations = Self::get_registrations_for_hotkey( hotkey ); + for (netuid, uid) in registrations.iter() { + if Self::get_validator_permit_for_uid( *netuid, *uid ) { + return true; + } + } + return false; + } + // ================================== // ==== YumaConsensus UID params ==== // ================================== - pub fn set_last_update_for_uid( netuid:u16, uid: u16, last_update: u64 ) { - let mut updated_last_update_vec = Self::get_last_update( netuid ); - if (uid as usize) < updated_last_update_vec.len() { + pub fn set_last_update_for_uid( netuid:u16, uid: u16, last_update: u64 ) { + let mut updated_last_update_vec = Self::get_last_update( netuid ); + if (uid as usize) < updated_last_update_vec.len() { updated_last_update_vec[uid as usize] = last_update; LastUpdate::::insert( netuid, updated_last_update_vec ); - } + } } - pub fn set_active_for_uid( netuid:u16, uid: u16, active: bool ) { - let mut updated_active_vec = Self::get_active( netuid ); - if (uid as usize) < updated_active_vec.len() { + pub fn set_active_for_uid( netuid:u16, uid: u16, active: bool ) { + let mut updated_active_vec = Self::get_active( netuid ); + if (uid as usize) < updated_active_vec.len() { updated_active_vec[uid as usize] = active; Active::::insert( netuid, updated_active_vec ); - } + } } pub fn set_pruning_score_for_uid( netuid:u16, uid: u16, pruning_score: u16 ) { log::info!("netuid = {:?}", netuid); @@ -65,12 +75,12 @@ impl Pallet { assert!( uid < SubnetworkN::::get( netuid ) ); PruningScores::::mutate( netuid, |v| v[uid as usize] = pruning_score ); } - pub fn set_validator_permit_for_uid( netuid:u16, uid: u16, validator_permit: bool ) { - let mut updated_validator_permit = Self::get_validator_permit( netuid ); - if (uid as usize) < updated_validator_permit.len() { + pub fn set_validator_permit_for_uid( netuid:u16, uid: u16, validator_permit: bool ) { + let mut updated_validator_permit = Self::get_validator_permit( netuid ); + if (uid as usize) < updated_validator_permit.len() { updated_validator_permit[uid as usize] = validator_permit; ValidatorPermit::::insert( netuid, updated_validator_permit ); - } + } } pub fn get_rank_for_uid( netuid:u16, uid: u16) -> u16 { let vec = Rank::::get( netuid ); if (uid as usize) < vec.len() { return vec[uid as usize] } else{ return 0 } } @@ -93,12 +103,12 @@ impl Pallet { pub fn get_pending_emission( netuid:u16 ) -> u64{ PendingEmission::::get( netuid ) } pub fn get_last_adjustment_block( netuid: u16) -> u64 { LastAdjustmentBlock::::get( netuid ) } pub fn get_blocks_since_last_step(netuid:u16 ) -> u64 { BlocksSinceLastStep::::get( netuid ) } - pub fn get_difficulty( netuid: u16 ) -> U256 { U256::from( Self::get_difficulty_as_u64( netuid ) ) } + pub fn get_difficulty( netuid: u16 ) -> U256 { U256::from( Self::get_difficulty_as_u64( netuid ) ) } pub fn get_registrations_this_block( netuid:u16 ) -> u16 { RegistrationsThisBlock::::get( netuid ) } pub fn get_last_mechanism_step_block( netuid: u16 ) -> u64 { LastMechansimStepBlock::::get( netuid ) } - pub fn get_registrations_this_interval( netuid: u16 ) -> u16 { RegistrationsThisInterval::::get( netuid ) } - pub fn get_pow_registrations_this_interval( netuid: u16 ) -> u16 { POWRegistrationsThisInterval::::get( netuid ) } - pub fn get_burn_registrations_this_interval( netuid: u16 ) -> u16 { BurnRegistrationsThisInterval::::get( netuid ) } + pub fn get_registrations_this_interval( netuid: u16 ) -> u16 { RegistrationsThisInterval::::get( netuid ) } + pub fn get_pow_registrations_this_interval( netuid: u16 ) -> u16 { POWRegistrationsThisInterval::::get( netuid ) } + pub fn get_burn_registrations_this_interval( netuid: u16 ) -> u16 { BurnRegistrationsThisInterval::::get( netuid ) } pub fn get_neuron_block_at_registration( netuid: u16, neuron_uid: u16 ) -> u64 { BlockAtRegistration::::get( netuid, neuron_uid )} // ======================== @@ -120,99 +130,99 @@ impl Pallet { // ======================== pub fn get_default_take() -> u16 { DefaultTake::::get() } pub fn set_default_take( default_take: u16 ) { DefaultTake::::put( default_take ) } - pub fn do_sudo_set_default_take( origin: T::RuntimeOrigin, default_take: u16 ) -> DispatchResult { + pub fn do_sudo_set_default_take( origin: T::RuntimeOrigin, default_take: u16 ) -> DispatchResult { ensure_root( origin )?; Self::set_default_take( default_take ); log::info!("DefaultTakeSet( default_take: {:?} ) ", default_take); Self::deposit_event( Event::DefaultTakeSet( default_take ) ); - Ok(()) + Ok(()) } // Configure tx rate limiting pub fn get_tx_rate_limit() -> u64 { TxRateLimit::::get() } pub fn set_tx_rate_limit( tx_rate_limit: u64 ) { TxRateLimit::::put( tx_rate_limit ) } - pub fn do_sudo_set_tx_rate_limit( origin: T::RuntimeOrigin, tx_rate_limit: u64 ) -> DispatchResult { + pub fn do_sudo_set_tx_rate_limit( origin: T::RuntimeOrigin, tx_rate_limit: u64 ) -> DispatchResult { ensure_root( origin )?; Self::set_tx_rate_limit( tx_rate_limit ); log::info!("TxRateLimitSet( tx_rate_limit: {:?} ) ", tx_rate_limit ); Self::deposit_event( Event::TxRateLimitSet( tx_rate_limit ) ); - Ok(()) + Ok(()) } pub fn get_serving_rate_limit( netuid: u16 ) -> u64 { ServingRateLimit::::get(netuid) } pub fn set_serving_rate_limit( netuid: u16, serving_rate_limit: u64 ) { ServingRateLimit::::insert( netuid, serving_rate_limit ) } - pub fn do_sudo_set_serving_rate_limit( origin: T::RuntimeOrigin, netuid: u16, serving_rate_limit: u64 ) -> DispatchResult { + pub fn do_sudo_set_serving_rate_limit( origin: T::RuntimeOrigin, netuid: u16, serving_rate_limit: u64 ) -> DispatchResult { ensure_root( origin )?; Self::set_serving_rate_limit( netuid, serving_rate_limit ); log::info!("ServingRateLimitSet( serving_rate_limit: {:?} ) ", serving_rate_limit ); Self::deposit_event( Event::ServingRateLimitSet( netuid, serving_rate_limit ) ); - Ok(()) + Ok(()) } pub fn get_min_difficulty( netuid: u16) -> u64 { MinDifficulty::::get( netuid ) } pub fn set_min_difficulty( netuid: u16, min_difficulty: u64 ) { MinDifficulty::::insert( netuid, min_difficulty ); } - pub fn do_sudo_set_min_difficulty( origin: T::RuntimeOrigin, netuid: u16, min_difficulty: u64 ) -> DispatchResult { + pub fn do_sudo_set_min_difficulty( origin: T::RuntimeOrigin, netuid: u16, min_difficulty: u64 ) -> DispatchResult { ensure_root( origin )?; ensure!(Self::if_subnet_exist(netuid), Error::::NetworkDoesNotExist); Self::set_min_difficulty( netuid, min_difficulty ); log::info!("MinDifficultySet( netuid: {:?} min_difficulty: {:?} ) ", netuid, min_difficulty); Self::deposit_event( Event::MinDifficultySet( netuid, min_difficulty) ); - Ok(()) + Ok(()) } pub fn get_max_difficulty( netuid: u16) -> u64 { MaxDifficulty::::get( netuid ) } pub fn set_max_difficulty( netuid: u16, max_difficulty: u64 ) { MaxDifficulty::::insert( netuid, max_difficulty ); } - pub fn do_sudo_set_max_difficulty( origin: T::RuntimeOrigin, netuid: u16, max_difficulty: u64 ) -> DispatchResult { + pub fn do_sudo_set_max_difficulty( origin: T::RuntimeOrigin, netuid: u16, max_difficulty: u64 ) -> DispatchResult { ensure_root( origin )?; ensure!(Self::if_subnet_exist(netuid), Error::::NetworkDoesNotExist); Self::set_max_difficulty( netuid, max_difficulty ); log::info!("MaxDifficultySet( netuid: {:?} max_difficulty: {:?} ) ", netuid, max_difficulty); Self::deposit_event( Event::MaxDifficultySet( netuid, max_difficulty) ); - Ok(()) + Ok(()) } pub fn get_weights_version_key( netuid: u16) -> u64 { WeightsVersionKey::::get( netuid ) } pub fn set_weights_version_key( netuid: u16, weights_version_key: u64 ) { WeightsVersionKey::::insert( netuid, weights_version_key ); } - pub fn do_sudo_set_weights_version_key( origin: T::RuntimeOrigin, netuid: u16, weights_version_key: u64 ) -> DispatchResult { + pub fn do_sudo_set_weights_version_key( origin: T::RuntimeOrigin, netuid: u16, weights_version_key: u64 ) -> DispatchResult { ensure_root( origin )?; ensure!(Self::if_subnet_exist(netuid), Error::::NetworkDoesNotExist); Self::set_weights_version_key( netuid, weights_version_key ); log::info!("WeightsVersionKeySet( netuid: {:?} weights_version_key: {:?} ) ", netuid, weights_version_key); Self::deposit_event( Event::WeightsVersionKeySet( netuid, weights_version_key) ); - Ok(()) + Ok(()) } pub fn get_weights_set_rate_limit( netuid: u16) -> u64 { WeightsSetRateLimit::::get( netuid ) } pub fn set_weights_set_rate_limit( netuid: u16, weights_set_rate_limit: u64 ) { WeightsSetRateLimit::::insert( netuid, weights_set_rate_limit ); } - pub fn do_sudo_set_weights_set_rate_limit( origin: T::RuntimeOrigin, netuid: u16, weights_set_rate_limit: u64 ) -> DispatchResult { + pub fn do_sudo_set_weights_set_rate_limit( origin: T::RuntimeOrigin, netuid: u16, weights_set_rate_limit: u64 ) -> DispatchResult { ensure_root( origin )?; ensure!(Self::if_subnet_exist(netuid), Error::::NetworkDoesNotExist); Self::set_weights_set_rate_limit( netuid, weights_set_rate_limit ); log::info!("WeightsSetRateLimitSet( netuid: {:?} weights_set_rate_limit: {:?} ) ", netuid, weights_set_rate_limit); Self::deposit_event( Event::WeightsSetRateLimitSet( netuid, weights_set_rate_limit) ); - Ok(()) + Ok(()) } pub fn get_adjustment_interval( netuid: u16) -> u16 { AdjustmentInterval::::get( netuid ) } pub fn set_adjustment_interval( netuid: u16, adjustment_interval: u16 ) { AdjustmentInterval::::insert( netuid, adjustment_interval ); } - pub fn do_sudo_set_adjustment_interval( origin: T::RuntimeOrigin, netuid: u16, adjustment_interval: u16 ) -> DispatchResult { + pub fn do_sudo_set_adjustment_interval( origin: T::RuntimeOrigin, netuid: u16, adjustment_interval: u16 ) -> DispatchResult { ensure_root( origin )?; ensure!(Self::if_subnet_exist(netuid), Error::::NetworkDoesNotExist); Self::set_adjustment_interval( netuid, adjustment_interval ); log::info!("AdjustmentIntervalSet( netuid: {:?} adjustment_interval: {:?} ) ", netuid, adjustment_interval); Self::deposit_event( Event::AdjustmentIntervalSet( netuid, adjustment_interval) ); - Ok(()) + Ok(()) } pub fn get_adjustment_alpha( netuid: u16 ) -> u64 { AdjustmentAlpha::::get(netuid) } pub fn set_adjustment_alpha( netuid: u16, adjustment_alpha: u64 ) { AdjustmentAlpha::::insert( netuid, adjustment_alpha ) } - pub fn do_sudo_set_adjustment_alpha( origin: T::RuntimeOrigin, netuid: u16, adjustment_alpha: u64 ) -> DispatchResult { + pub fn do_sudo_set_adjustment_alpha( origin: T::RuntimeOrigin, netuid: u16, adjustment_alpha: u64 ) -> DispatchResult { ensure_root( origin )?; ensure!(Self::if_subnet_exist(netuid), Error::::NetworkDoesNotExist); Self::set_adjustment_alpha( netuid, adjustment_alpha ); log::info!("AdjustmentAlphaSet( adjustment_alpha: {:?} ) ", adjustment_alpha ); Self::deposit_event( Event::AdjustmentAlphaSet( netuid, adjustment_alpha ) ); - Ok(()) + Ok(()) } pub fn get_validator_prune_len( netuid: u16 ) -> u64 { ValidatorPruneLen::::get( netuid ) } @@ -238,7 +248,7 @@ impl Pallet { Ok(()) } - pub fn get_max_weight_limit( netuid: u16) -> u16 { MaxWeightsLimit::::get( netuid ) } + pub fn get_max_weight_limit( netuid: u16) -> u16 { MaxWeightsLimit::::get( netuid ) } pub fn set_max_weight_limit( netuid: u16, max_weight_limit: u16 ) { MaxWeightsLimit::::insert( netuid, max_weight_limit ); } pub fn do_sudo_set_max_weight_limit( origin:T::RuntimeOrigin, netuid: u16, max_weight_limit: u16 ) -> DispatchResult { ensure_root( origin )?; @@ -259,7 +269,7 @@ impl Pallet { Self::deposit_event(Event::ImmunityPeriodSet(netuid, immunity_period)); Ok(()) } - + pub fn get_min_allowed_weights( netuid:u16 ) -> u16 { MinAllowedWeights::::get( netuid ) } pub fn set_min_allowed_weights( netuid: u16, min_allowed_weights: u16 ) { MinAllowedWeights::::insert( netuid, min_allowed_weights ); } pub fn do_sudo_set_min_allowed_weights( origin:T::RuntimeOrigin, netuid: u16, min_allowed_weights: u16 ) -> DispatchResult { @@ -293,7 +303,7 @@ impl Pallet { Self::deposit_event( Event::KappaSet( netuid, kappa) ); Ok(()) } - + pub fn get_rho( netuid: u16 ) -> u16 { Rho::::get( netuid ) } pub fn set_rho( netuid: u16, rho: u16 ) { Rho::::insert( netuid, rho ); } pub fn do_sudo_set_rho( origin:T::RuntimeOrigin, netuid: u16, rho: u16 ) -> DispatchResult { @@ -304,7 +314,7 @@ impl Pallet { Self::deposit_event( Event::RhoSet( netuid, rho ) ); Ok(()) } - + pub fn get_activity_cutoff( netuid: u16 ) -> u16 { ActivityCutoff::::get( netuid ) } pub fn set_activity_cutoff( netuid: u16, activity_cutoff: u16 ) { ActivityCutoff::::insert( netuid, activity_cutoff ); } pub fn do_sudo_set_activity_cutoff( origin:T::RuntimeOrigin, netuid: u16, activity_cutoff: u16 ) -> DispatchResult { @@ -319,14 +329,14 @@ impl Pallet { // Registration Toggle utils pub fn get_network_registration_allowed( netuid: u16 ) -> bool { NetworkRegistrationAllowed::::get( netuid ) } pub fn set_network_registration_allowed( netuid: u16, registration_allowed: bool ) { NetworkRegistrationAllowed::::insert( netuid, registration_allowed ) } - pub fn do_sudo_set_network_registration_allowed( origin: T::RuntimeOrigin, netuid: u16, registration_allowed: bool ) -> DispatchResult { + pub fn do_sudo_set_network_registration_allowed( origin: T::RuntimeOrigin, netuid: u16, registration_allowed: bool ) -> DispatchResult { ensure_root( origin )?; Self::set_network_registration_allowed( netuid, registration_allowed ); log::info!("NetworkRegistrationAllowed( registration_allowed: {:?} ) ", registration_allowed ); Self::deposit_event( Event::RegistrationAllowed( netuid, registration_allowed ) ); - Ok(()) + Ok(()) } - + pub fn get_target_registrations_per_interval( netuid: u16 ) -> u16 { TargetRegistrationsPerInterval::::get( netuid ) } pub fn set_target_registrations_per_interval( netuid: u16, target_registrations_per_interval: u16 ) { TargetRegistrationsPerInterval::::insert( netuid, target_registrations_per_interval ); } pub fn do_sudo_set_target_registrations_per_interval( origin:T::RuntimeOrigin, netuid: u16, target_registrations_per_interval: u16 ) -> DispatchResult { @@ -370,7 +380,7 @@ impl Pallet { Self::deposit_event( Event::MaxBurnSet( netuid, max_burn ) ); Ok(()) } - + pub fn get_difficulty_as_u64( netuid: u16 ) -> u64 { Difficulty::::get( netuid ) } pub fn set_difficulty( netuid: u16, difficulty: u64 ) { Difficulty::::insert( netuid, difficulty ); } pub fn do_sudo_set_difficulty( origin:T::RuntimeOrigin, netuid: u16, difficulty: u64 ) -> DispatchResult { @@ -381,7 +391,7 @@ impl Pallet { Self::deposit_event( Event::DifficultySet( netuid, difficulty ) ); Ok(()) } - + pub fn get_max_allowed_validators( netuid: u16 ) -> u16 { MaxAllowedValidators::::get( netuid ) } pub fn set_max_allowed_validators( netuid: u16, max_allowed_validators: u16 ) { MaxAllowedValidators::::insert( netuid, max_allowed_validators ); } pub fn do_sudo_set_max_allowed_validators( origin:T::RuntimeOrigin, netuid: u16, max_allowed_validators: u16 ) -> DispatchResult { @@ -407,8 +417,8 @@ impl Pallet { pub fn get_max_registrations_per_block( netuid: u16 ) -> u16 { MaxRegistrationsPerBlock::::get( netuid ) } pub fn set_max_registrations_per_block( netuid: u16, max_registrations_per_block: u16 ) { MaxRegistrationsPerBlock::::insert( netuid, max_registrations_per_block ); } pub fn do_sudo_set_max_registrations_per_block( - origin: T::RuntimeOrigin, - netuid: u16, + origin: T::RuntimeOrigin, + netuid: u16, max_registrations_per_block: u16 ) -> DispatchResult { ensure_root( origin )?; @@ -419,8 +429,8 @@ impl Pallet { Ok(()) } pub fn do_sudo_set_tempo ( - origin: T::RuntimeOrigin, - netuid: u16, + origin: T::RuntimeOrigin, + netuid: u16, tempo: u16 ) -> DispatchResult { ensure_root( origin )?; @@ -437,13 +447,13 @@ impl Pallet { Ok(()) } - pub fn get_rao_recycled( netuid: u16 ) -> u64 { + pub fn get_rao_recycled( netuid: u16 ) -> u64 { RAORecycledForRegistration::::get( netuid ) } - pub fn set_rao_recycled( netuid: u16, rao_recycled: u64 ) { + pub fn set_rao_recycled( netuid: u16, rao_recycled: u64 ) { RAORecycledForRegistration::::insert( netuid, rao_recycled ); } - pub fn increase_rao_recycled( netuid: u16, inc_rao_recycled: u64 ) { + pub fn increase_rao_recycled( netuid: u16, inc_rao_recycled: u64 ) { let curr_rao_recycled = Self::get_rao_recycled( netuid ); let rao_recycled = curr_rao_recycled.saturating_add( inc_rao_recycled ); Self::set_rao_recycled( netuid, rao_recycled ); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 55cc1813a0..a198006d1d 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -586,6 +586,7 @@ parameter_types! { pub const SubtensorInitialTxRateLimit: u64 = 1000; pub const SubtensorInitialRAORecycledForRegistration: u64 = 0; // 0 rao pub const SubtensorInitialSenateRequiredStakePercentage: u64 = 1; // 1 percent of total stake + pub const SubtensorAssociatedIPsMaxSize: u32 = 100 * (16 + 1 + 1); //100 MAX, 16 bytes for IP, 1 byte for type, 1 byte for protocol } impl pallet_subtensor::Config for Runtime { @@ -627,6 +628,7 @@ impl pallet_subtensor::Config for Runtime { type InitialTxRateLimit = SubtensorInitialTxRateLimit; type InitialRAORecycledForRegistration = SubtensorInitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = SubtensorInitialSenateRequiredStakePercentage; + type AssociatedIPsMaxSize = SubtensorAssociatedIPsMaxSize; } // Create the runtime by composing the FRAME pallets that were previously configured. From af2a7348a4f87628e8454d0717c5e1603419d781 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 16 Aug 2023 12:11:20 -0400 Subject: [PATCH 02/11] use compact ints --- pallets/subtensor/src/lib.rs | 12 +++++++----- pallets/subtensor/src/serving.rs | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index fe11237dce..81b7dcc294 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -14,7 +14,7 @@ use frame_support::{ traits::{tokens::WithdrawReasons, Currency, ExistenceRequirement, IsSubType}, }; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, Compact}; use frame_support::sp_runtime::transaction_validity::ValidTransaction; use scale_info::TypeInfo; use sp_runtime::{ @@ -63,6 +63,7 @@ pub mod pallet { pallet_prelude::{DispatchResult, StorageMap, *}, }; use frame_system::pallet_prelude::*; + use codec::Compact; #[cfg(not(feature = "std"))] use alloc::boxed::Box; @@ -433,11 +434,12 @@ pub mod pallet { // --- Struct for IP Info. pub type IPInfoOf = IPInfo; - #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + #[derive(Encode, Decode, TypeInfo, Clone, PartialEq, Eq, Debug)] pub struct IPInfo { - pub ip: u128, // --- u128 encoded IP address of type v6 or v4. - pub ip_type: u8, // --- IP type, 4 for ipv4 and 6 for ipv6. - pub protocol: u8, // --- Axon protocol. TCP, UDP, other. + pub ip: Compact, // --- u128 encoded IP address of type v6 or v4. + pub ip_type_and_protocol: Compact, // Combined IP Type and Protocol. High bits are IP type, low bits are protocol. + // --- IP type, 4 for ipv4 and 6 for ipv6. (4-bits) + // --- Axon protocol. TCP, UDP, other. (4-bits) } // --- Struct for Prometheus. diff --git a/pallets/subtensor/src/serving.rs b/pallets/subtensor/src/serving.rs index 2120b1780f..543a07a59f 100644 --- a/pallets/subtensor/src/serving.rs +++ b/pallets/subtensor/src/serving.rs @@ -167,8 +167,9 @@ impl Pallet { // --- 3. Check the ip validity. for ip_info in &associated_ips { - ensure!( Self::is_valid_ip_type(ip_info.ip_type), Error::::InvalidIpType ); - ensure!( Self::is_valid_ip_address(ip_info.ip_type, ip_info.ip), Error::::InvalidIpAddress ); + let ip_type: u8 = Self::get_ip_type_from_ip_and_protocol(ip_info.ip_type_and_protocol); + ensure!( Self::is_valid_ip_type(ip_type), Error::::InvalidIpType ); + ensure!( Self::is_valid_ip_address(ip_type, ip_info.ip.0), Error::::InvalidIpAddress ); } // --- 4. Get the previous associated IP information. @@ -339,6 +340,17 @@ impl Pallet { } } + fn get_ip_type_from_ip_and_protocol(ip_and_protocol: Compact) -> u8 { + // IP version is the first 4 bits + return ip_and_protocol.0 >> 4; + } + + #[allow(dead_code)] + fn get_ip_protocol_from_ip_and_protocol(ip_and_protocol: Compact) -> u8 { + // IP protocol is the last 4 bits + return ip_and_protocol.0 & 0b00001111; + } + pub fn get_prometheus_info( netuid: u16, hotkey: &T::AccountId ) -> PrometheusInfoOf { if Self::has_prometheus_info( netuid, hotkey ) { return Prometheus::::get( netuid, hotkey ).unwrap(); From 785041dd21891450bc1e180480fc0f3508c4e18b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 16 Aug 2023 12:11:39 -0400 Subject: [PATCH 03/11] add getter for entire subnet --- pallets/subtensor/src/serving.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pallets/subtensor/src/serving.rs b/pallets/subtensor/src/serving.rs index 543a07a59f..d50f328379 100644 --- a/pallets/subtensor/src/serving.rs +++ b/pallets/subtensor/src/serving.rs @@ -340,6 +340,32 @@ impl Pallet { } } + // Return a vector of IPInfoOf structs for all current Validators associated with the netuid. + pub fn get_associated_validator_ip_info_for_subnet( netuid: u16 ) -> Option> { + if !Self::if_subnet_exist( netuid ) { + return None; + } + + let validator_permits_for_subnet = Self::get_validator_permit( netuid ); + let mut validator_ips: Vec = vec![]; + for _uid in 0..validator_permits_for_subnet.len() { + let uid = _uid as u16; + let has_validator_permit = validator_permits_for_subnet[_uid]; + let _hotkey_for_uid_in_subnet = Self::get_hotkey_for_net_and_uid( netuid, uid ); + if !_hotkey_for_uid_in_subnet.is_ok() { + continue; // Skip when the UID has no hotkey. (e.g. does not exist) + } + + let hotkey_for_uid_in_subnet = _hotkey_for_uid_in_subnet.unwrap(); // Safe to unwrap here. + if has_validator_permit && Self::has_associated_ip_info( netuid, &hotkey_for_uid_in_subnet ) { + let associated_ip_info = Self::get_associated_ip_info( netuid, &hotkey_for_uid_in_subnet ); + validator_ips.extend(associated_ip_info.1); + } + } + + return Some(validator_ips); + } + fn get_ip_type_from_ip_and_protocol(ip_and_protocol: Compact) -> u8 { // IP version is the first 4 bits return ip_and_protocol.0 >> 4; From fe828755865e77e4100902f40a3fac13aadb48fa Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 16 Aug 2023 12:12:11 -0400 Subject: [PATCH 04/11] add rpc for associated IPs --- node/src/rpc.rs | 1 + pallets/subtensor/rpc/src/lib.rs | 20 ++++++++++++++++++++ pallets/subtensor/runtime-api/src/lib.rs | 4 ++++ runtime/src/lib.rs | 12 ++++++++++++ 4 files changed, 37 insertions(+) diff --git a/node/src/rpc.rs b/node/src/rpc.rs index 5632aaa528..8b6c51c806 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -40,6 +40,7 @@ where C::Api: subtensor_custom_rpc_runtime_api::DelegateInfoRuntimeApi, C::Api: subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi, C::Api: subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi, + C::Api: subtensor_custom_rpc_runtime_api::ValidatorIPRuntimeApi, P: TransactionPool + 'static { use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index f636362c3e..9e9dd8b5ad 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -14,6 +14,8 @@ use sp_api::ProvideRuntimeApi; pub use subtensor_custom_rpc_runtime_api::DelegateInfoRuntimeApi; pub use subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi; pub use subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi; +pub use subtensor_custom_rpc_runtime_api::ValidatorIPRuntimeApi; + #[rpc(client, server)] pub trait SubtensorCustomApi { @@ -45,6 +47,9 @@ pub trait SubtensorCustomApi { fn get_subnet_info(&self, netuid: u16, at: Option) -> RpcResult>; #[method(name = "subnetInfo_getSubnetsInfo")] fn get_subnets_info(&self, at: Option) -> RpcResult>; + + #[method(name = "validatorIP_getAssociatedValidatorIPInfoForSubnet")] + fn get_associated_validator_ip_info_for_subnet(&self, netuid: u16, at: Option) -> RpcResult>; } pub struct SubtensorCustom { @@ -84,6 +89,7 @@ where C::Api: DelegateInfoRuntimeApi, C::Api: NeuronInfoRuntimeApi, C::Api: SubnetInfoRuntimeApi, + C::Api: ValidatorIPRuntimeApi, { fn get_delegates(&self, at: Option<::Hash>) -> RpcResult> { let api = self.client.runtime_api(); @@ -236,4 +242,18 @@ where .into() }) } + + fn get_associated_validator_ip_info_for_subnet(&self, netuid: u16, at: Option<::Hash>) -> RpcResult> { + let api = self.client.runtime_api(); + let at = at.unwrap_or_else(|| self.client.info().best_hash); + + api.get_associated_validator_ip_info_for_subnet(at, netuid).map_err(|e| { + CallError::Custom(ErrorObject::owned( + Error::RuntimeError.into(), + "Unable to get associated validator ip info for subnet.", + Some(e.to_string()), + )) + .into() + }) + } } diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index 333f5c164f..a568c7c03d 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -22,4 +22,8 @@ sp_api::decl_runtime_apis! { fn get_subnet_info(netuid: u16) -> Vec; fn get_subnets_info() -> Vec; } + + pub trait ValidatorIPRuntimeApi { + fn get_associated_validator_ip_info_for_subnet(netuid: u16) -> Vec; + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a198006d1d..1aaac5872a 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -999,6 +999,18 @@ impl_runtime_apis! { result.encode() } } + + impl subtensor_custom_rpc_runtime_api::ValidatorIPRuntimeApi for Runtime { + fn get_associated_validator_ip_info_for_subnet(netuid: u16) -> Vec { + let _result = SubtensorModule::get_associated_validator_ip_info_for_subnet(netuid); + if _result.is_some() { + let result = _result.expect("Could not get associated Validator IPs for Subnet"); + result.encode() + } else { + vec![] + } + } + } } #[cfg(test)] From 5c6f04b27cdf6d087aade7dc29d2332225679557 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 18 Aug 2023 17:43:56 -0400 Subject: [PATCH 05/11] add call to associate ips --- pallets/subtensor/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 81b7dcc294..3f7e902147 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1304,6 +1304,14 @@ pub mod pallet { Self::do_serve_prometheus(origin, netuid, version, ip, port, ip_type) } + #[pallet::call_index(59)] + #[pallet::weight((Weight::from_ref_time(15_000_000) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::No))] + pub fn associate_ips(origin: OriginFor, netuid: u16, ip_info_list: Vec) -> DispatchResult { + Self::do_associate_ips(origin, netuid, ip_info_list) + } + // ---- Registers a new neuron to the subnetwork. // // # Args: From cd9a9c1b156ccec00346c1d578562d691056ce5d Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 22 Aug 2023 11:42:33 -0400 Subject: [PATCH 06/11] add new config var to mock --- pallets/subtensor/tests/mock.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/subtensor/tests/mock.rs b/pallets/subtensor/tests/mock.rs index fdf84e220b..0b8cd08f1e 100644 --- a/pallets/subtensor/tests/mock.rs +++ b/pallets/subtensor/tests/mock.rs @@ -154,6 +154,8 @@ parameter_types! { pub const InitialRAORecycledForRegistration: u64 = 0; pub const InitialSenateRequiredStakePercentage: u64 = 2; // 2 percent of total stake + + pub const SubtensorAssociatedIPsMaxSize: u32 = 100 * (16 + 1 + 1); } // Configure collective pallet for council @@ -344,6 +346,8 @@ impl pallet_subtensor::Config for Test { type InitialMinBurn = InitialMinBurn; type InitialRAORecycledForRegistration = InitialRAORecycledForRegistration; type InitialSenateRequiredStakePercentage = InitialSenateRequiredStakePercentage; + + type AssociatedIPsMaxSize = SubtensorAssociatedIPsMaxSize; } impl pallet_utility::Config for Test { From f70579d723f04b9c2830cc35f00705098c13385e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 22 Aug 2023 12:00:52 -0400 Subject: [PATCH 07/11] use options for getter --- pallets/subtensor/src/serving.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/serving.rs b/pallets/subtensor/src/serving.rs index d50f328379..621a44d9b8 100644 --- a/pallets/subtensor/src/serving.rs +++ b/pallets/subtensor/src/serving.rs @@ -173,7 +173,7 @@ impl Pallet { } // --- 4. Get the previous associated IP information. - let prev_associated_ip_info = Self::get_associated_ip_info( netuid, &hotkey_id ); + let prev_associated_ip_info = Self::get_associated_ip_info( netuid, &hotkey_id ).unwrap_or_else(|| (0, vec![])); let last_update_block = prev_associated_ip_info.0; let current_block:u64 = Self::get_current_block_as_u64(); ensure!( Self::associate_ip_info_passes_rate_limit( netuid, last_update_block, current_block ), Error::::ServingRateLimitExceeded ); @@ -331,12 +331,12 @@ impl Pallet { } } - pub fn get_associated_ip_info( netuid: u16, hotkey: &T::AccountId ) -> (u64, Vec){ + pub fn get_associated_ip_info( netuid: u16, hotkey: &T::AccountId ) -> Option<(u64, Vec)>{ if Self::has_associated_ip_info( netuid, hotkey ) { let result = AssociatedIPInfo::::get( netuid, hotkey ).unwrap(); - return (result.0, result.1.into_inner()); + return Some((result.0, result.1.into_inner())); } else{ - return (0, vec![]) + return None; } } From 4c8afa9805cf9f09883ac619f00b912e765ea583 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 22 Aug 2023 12:23:39 -0400 Subject: [PATCH 08/11] add tests for ip info utils --- pallets/subtensor/tests/ip_info.rs | 145 +++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 pallets/subtensor/tests/ip_info.rs diff --git a/pallets/subtensor/tests/ip_info.rs b/pallets/subtensor/tests/ip_info.rs new file mode 100644 index 0000000000..d29f4f79c2 --- /dev/null +++ b/pallets/subtensor/tests/ip_info.rs @@ -0,0 +1,145 @@ +mod mock; +use codec::Compact; +use mock::*; + +use pallet_subtensor::IPInfoOf; +use sp_core::U256; + +#[test] +fn test_get_ip_info_hotkey_does_not_exist() { + new_test_ext().execute_with(|| { + let netuid: u16 = 1; + let hotkey = U256::from(0); + let hotkey2 = U256::from(1); + + // Note, no hotkey is in the map + let ip_info_result = SubtensorModule::get_associated_ip_info(netuid, &hotkey); + assert_eq!(ip_info_result.is_none(), true); + + // Add IP info for hotkey2 + let ip_info = IPInfoOf { + ip: Compact::::from(0), + ip_type_and_protocol: Compact::::from(0), + }; + let ip_info_in_vec = vec![ip_info]; + SubtensorModule::associate_ips_with_hotkey_for_netuid(netuid, hotkey2, 0, ip_info_in_vec.try_into().unwrap()); + + // Note: hotkey is still not in the map, but hotkey2 is + let ip_info_result_2 = SubtensorModule::get_associated_ip_info(netuid, &hotkey); + assert_eq!(ip_info_result_2.is_none(), true); // Still none + }); +} + +#[test] +#[cfg(not(tarpaulin))] +fn test_get_ip_info_some() { + new_test_ext().execute_with(|| { + let netuid: u16 = 1; + let hotkey = U256::from(0); + let hotkey2 = U256::from(1); + + // Note, no hotkey is in the map + let ip_info_result = SubtensorModule::get_associated_ip_info(netuid, &hotkey); + assert_eq!(ip_info_result.is_none(), true); // Not in map, so none + + // Add IP info for hotkey + let ip_info = IPInfoOf { + ip: Compact::::from(0), + ip_type_and_protocol: Compact::::from(0), + }; + let ip_info_in_vec = vec![ip_info]; + SubtensorModule::associate_ips_with_hotkey_for_netuid(netuid, hotkey, 0, ip_info_in_vec.try_into().unwrap()); + + // Note: hotkey is now in the map + let ip_info_result_2 = SubtensorModule::get_associated_ip_info(netuid, &hotkey); + assert_eq!(ip_info_result_2.is_none(), false); // Now in the map, so some + + // Note: hotkey2 is still not in the map + let ip_info_result_3 = SubtensorModule::get_associated_ip_info(netuid, &hotkey2); + assert_eq!(ip_info_result_3.is_none(), true); // Still none + + // Add IP info for hotkey2 + let ip_info2 = IPInfoOf { + ip: Compact::::from(0), + ip_type_and_protocol: Compact::::from(0), + }; + let ip_info_in_vec2 = vec![ip_info2]; + SubtensorModule::associate_ips_with_hotkey_for_netuid(netuid, hotkey2, 0, ip_info_in_vec2.try_into().unwrap()); + + // Note: hotkey2 is now in the map + let ip_info_result_4 = SubtensorModule::get_associated_ip_info(netuid, &hotkey2); + assert_eq!(ip_info_result_4.is_none(), false); // Now in the map, so some + }); +} + +#[test] +fn test_get_validator_ip_info_for_hotkey_list() { + new_test_ext().execute_with(|| { + let netuid: u16 = 1; + + let tempo: u16 = 2; + let modality: u16 = 2; + + let neuron_count: u16 = 3; + + // add network + add_network(netuid, tempo, modality); + // Increase max allowed validators so all neurons can be registered as a validator + SubtensorModule::set_max_allowed_validators(netuid, neuron_count * 3); // More than neuron count + // Increase max allowed uids so all neurons can be registered + SubtensorModule::set_max_allowed_uids(netuid, neuron_count * 3); // More than neuron count + + for index in 0..neuron_count { + let hotkey = U256::from(0 + index); + let coldkey = U256::from(0 + index); + // Register neuron + let nonce: u64 = 39420842 + index as u64; + register_ok_neuron(netuid, hotkey, coldkey, nonce); + + // Add IP info for hotkey + let ip_info = IPInfoOf { + ip: Compact::::from(0), + ip_type_and_protocol: Compact::::from(0), + }; + let ip_info_in_vec = vec![ip_info]; + SubtensorModule::associate_ips_with_hotkey_for_netuid( + netuid, + hotkey, + 0, + ip_info_in_vec.try_into().unwrap(), + ); + } + // Sanity check, make sure all neurons are registered + assert_eq!( + SubtensorModule::get_neurons_lite(netuid).len(), + neuron_count as usize + ); + + // Step until tempo so we can get vpermits + step_block(tempo * 2); + assert_eq!( // Check that all neurons have vpermits + SubtensorModule::get_neurons_lite(netuid) + .into_iter() + .enumerate() + .filter(|(uid, _n)| SubtensorModule::get_validator_permit_for_uid(netuid, *uid as u16) ) + .count(), + neuron_count as usize + ); + + // Note: only returns those with VPermit + let ip_infos = SubtensorModule::get_associated_validator_ip_info_for_subnet(netuid); + assert_eq!(ip_infos.is_none(), false); + assert_eq!(ip_infos.unwrap().len(), neuron_count as usize); + + }); +} + +#[test] +fn test_get_ip_info_empty() { + new_test_ext().execute_with(|| { + let netuid: u16 = 1; + + let ip_infos = SubtensorModule::get_associated_validator_ip_info_for_subnet(netuid); + assert_eq!(ip_infos.is_none(), true); + }); +} From 89e44e8fbc6e28ce9ff2582adb6ba0c46ecd3494 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Tue, 22 Aug 2023 12:23:52 -0400 Subject: [PATCH 09/11] add helper function --- pallets/subtensor/src/serving.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/serving.rs b/pallets/subtensor/src/serving.rs index 621a44d9b8..8f0043b95a 100644 --- a/pallets/subtensor/src/serving.rs +++ b/pallets/subtensor/src/serving.rs @@ -183,7 +183,7 @@ impl Pallet { ensure!( bounded_associated_ips.is_ok(), Error::::AssociatedIPsMaxSizeExceeded); // --- 5. We insert the associated IP Info and update the block. - AssociatedIPInfo::::insert( netuid, hotkey_id.clone(), (current_block, bounded_associated_ips.unwrap() ) ); + Self::associate_ips_with_hotkey_for_netuid(netuid, hotkey_id.clone(), current_block, bounded_associated_ips.unwrap() ); // --- 6. We deposit Associated IPs set event. log::info!("IPInfoSet( hotkey:{:?} ) ", hotkey_id.clone() ); @@ -193,6 +193,10 @@ impl Pallet { Ok(()) } + pub fn associate_ips_with_hotkey_for_netuid(netuid: u16, hotkey: T::AccountId, current_block: u64, associated_ips: BoundedVec) { + AssociatedIPInfo::::insert( netuid, hotkey, (current_block, associated_ips ) ); + } + // ---- The implementation for the extrinsic serve_prometheus. // // # Args: @@ -359,7 +363,8 @@ impl Pallet { let hotkey_for_uid_in_subnet = _hotkey_for_uid_in_subnet.unwrap(); // Safe to unwrap here. if has_validator_permit && Self::has_associated_ip_info( netuid, &hotkey_for_uid_in_subnet ) { let associated_ip_info = Self::get_associated_ip_info( netuid, &hotkey_for_uid_in_subnet ); - validator_ips.extend(associated_ip_info.1); + let block_and_ip_info = associated_ip_info.unwrap_or_else(|| (0, vec![])); + validator_ips.extend(block_and_ip_info.1); } } From 9faf085744b43557ca8ec6c9bca6a7ee0a6fe487 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 1 Sep 2023 12:34:20 -0400 Subject: [PATCH 10/11] add remove associated IP list on neuron replace --- pallets/subtensor/src/uids.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/subtensor/src/uids.rs b/pallets/subtensor/src/uids.rs index 3846027a0d..7abcdfefc9 100644 --- a/pallets/subtensor/src/uids.rs +++ b/pallets/subtensor/src/uids.rs @@ -25,6 +25,7 @@ impl Pallet { Uids::::remove( netuid, old_hotkey.clone() ); IsNetworkMember::::remove( old_hotkey.clone(), netuid ); Keys::::remove( netuid, uid_to_replace ); + AssociatedIPInfo::::remove( netuid, old_hotkey.clone() ); // 2a. Check if the uid is registered in any other subnetworks. let hotkey_is_registered_on_any_network: bool = Self::is_hotkey_registered_on_any_network( &old_hotkey.clone() ); From 38af9cd2abc8439e3fe09642a2b3ab8e44a22e1a Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 1 Sep 2023 12:35:51 -0400 Subject: [PATCH 11/11] fix comment --- pallets/subtensor/src/serving.rs | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/pallets/subtensor/src/serving.rs b/pallets/subtensor/src/serving.rs index 8f0043b95a..47bd92f3b7 100644 --- a/pallets/subtensor/src/serving.rs +++ b/pallets/subtensor/src/serving.rs @@ -113,26 +113,8 @@ impl Pallet { // * 'netuid' (u16): // - The u16 network identifier. // - // * 'version' (u64): - // - The bittensor version identifier. - // - // * 'ip' (u64): - // - The endpoint ip information as a u128 encoded integer. - // - // * 'port' (u16): - // - The endpoint port information as a u16 encoded integer. - // - // * 'ip_type' (u8): - // - The endpoint ip version as a u8, 4 or 6. - // - // * 'protocol' (u8): - // - UDP:1 or TCP:0 - // - // * 'placeholder1' (u8): - // - Placeholder for further extra params. - // - // * 'placeholder2' (u8): - // - Placeholder for further extra params. + // * 'associated_ips' (Vec): + // - The associated IP information as a vector of IPInfoOf structs. // // # Event: // * IPInfoSet; @@ -140,7 +122,7 @@ impl Pallet { // // # Raises: // * 'NetworkDoesNotExist': - // - Attempting to set weights on a non-existent network. + // - Attempting to add associated IPs on a non-existent subnet/netuid. // // * 'NotAValidator': // - Attempting to add associated IPs without a vpermit. @@ -152,7 +134,7 @@ impl Pallet { // - The numerically encoded ip address does not resolve to a proper ip. // // * 'ServingRateLimitExceeded': - // - Attempting to set prometheus information withing the rate limit min. + // - Attempting to set new IP Info withing the rate limit min. // pub fn do_associate_ips( origin: T::RuntimeOrigin,