diff --git a/Cargo.lock b/Cargo.lock index 62cb7a7a7..ed3b5ae45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11133,8 +11133,10 @@ version = "0.0.2" dependencies = [ "frame-support", "pallet-subtensor", + "parity-scale-codec", "serde", "sp-api", + "sp-runtime", ] [[package]] diff --git a/pallets/subtensor/rpc/Cargo.toml b/pallets/subtensor/rpc/Cargo.toml index 861c313d8..c2d631e1a 100644 --- a/pallets/subtensor/rpc/Cargo.toml +++ b/pallets/subtensor/rpc/Cargo.toml @@ -12,9 +12,7 @@ publish = false workspace = true [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ - "derive", -] } +codec = { workspace = true } jsonrpsee = { workspace = true, features = ["client-core", "server", "macros"] } serde = { workspace = true, features = ["derive"] } diff --git a/pallets/subtensor/rpc/src/lib.rs b/pallets/subtensor/rpc/src/lib.rs index cdbcebcac..7233a16c7 100644 --- a/pallets/subtensor/rpc/src/lib.rs +++ b/pallets/subtensor/rpc/src/lib.rs @@ -1,12 +1,13 @@ //! RPC interface for the custom Subtensor rpc methods +use codec::{Decode, Encode}; use jsonrpsee::{ core::RpcResult, proc_macros::rpc, types::{error::ErrorObject, ErrorObjectOwned}, }; use sp_blockchain::HeaderBackend; -use sp_runtime::traits::Block as BlockT; +use sp_runtime::{traits::Block as BlockT, AccountId32}; use std::sync::Arc; use sp_api::ProvideRuntimeApi; @@ -116,9 +117,12 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_delegates(at).map_err(|e| { - Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into() - }) + match api.get_delegates(at) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into()) + } + } } fn get_delegate( @@ -129,9 +133,20 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_delegate(at, delegate_account_vec).map_err(|e| { - Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into() - }) + let delegate_account = match AccountId32::decode(&mut &delegate_account_vec[..]) { + Ok(delegate_account) => delegate_account, + Err(e) => { + return Err( + Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into(), + ) + } + }; + match api.get_delegate(at, delegate_account) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into()) + } + } } fn get_delegated( @@ -142,9 +157,20 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_delegated(at, delegatee_account_vec).map_err(|e| { - Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into() - }) + let delegatee_account = match AccountId32::decode(&mut &delegatee_account_vec[..]) { + Ok(delegatee_account) => delegatee_account, + Err(e) => { + return Err( + Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into(), + ) + } + }; + match api.get_delegated(at, delegatee_account) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get delegates info: {:?}", e)).into()) + } + } } fn get_neurons_lite( @@ -155,9 +181,12 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_neurons_lite(at, netuid).map_err(|e| { - Error::RuntimeError(format!("Unable to get neurons lite info: {:?}", e)).into() - }) + match api.get_neurons_lite(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get neurons lite info: {:?}", e)).into()) + } + } } fn get_neuron_lite( @@ -169,17 +198,24 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_neuron_lite(at, netuid, uid).map_err(|e| { - Error::RuntimeError(format!("Unable to get neurons lite info: {:?}", e)).into() - }) + match api.get_neuron_lite(at, netuid, uid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get neurons lite info: {:?}", e)).into()) + } + } } fn get_neurons(&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_neurons(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get neurons info: {:?}", e)).into()) + match api.get_neurons(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get neurons info: {:?}", e)).into()) + } + } } fn get_neuron( @@ -191,8 +227,12 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_neuron(at, netuid, uid) - .map_err(|e| Error::RuntimeError(format!("Unable to get neuron info: {:?}", e)).into()) + match api.get_neuron(at, netuid, uid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get neuron info: {:?}", e)).into()) + } + } } fn get_subnet_info( @@ -203,8 +243,12 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_subnet_info(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + match api.get_subnet_info(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + } + } } fn get_subnet_hyperparams( @@ -215,23 +259,36 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_subnet_hyperparams(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + match api.get_subnet_hyperparams(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + } + } } fn get_all_dynamic_info(&self, at: Option<::Hash>) -> RpcResult> { let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_all_dynamic_info(at).map_err(|e| { - Error::RuntimeError(format!("Unable to get dynamic subnets info: {:?}", e)).into() - }) + + match api.get_all_dynamic_info(at) { + Ok(result) => Ok(result.encode()), + Err(e) => Err(Error::RuntimeError(format!( + "Unable to get dynamic subnets info: {:?}", + e + )) + .into()), + } } fn get_all_metagraphs(&self, at: Option<::Hash>) -> RpcResult> { let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_all_metagraphs(at) - .map_err(|e| Error::RuntimeError(format!("Unable to get metagraps: {:?}", e)).into()) + + match api.get_all_metagraphs(at) { + Ok(result) => Ok(result.encode()), + Err(e) => Err(Error::RuntimeError(format!("Unable to get metagraps: {:?}", e)).into()), + } } fn get_dynamic_info( @@ -241,9 +298,15 @@ where ) -> RpcResult> { let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_dynamic_info(at, netuid).map_err(|e| { - Error::RuntimeError(format!("Unable to get dynamic subnets info: {:?}", e)).into() - }) + + match api.get_dynamic_info(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => Err(Error::RuntimeError(format!( + "Unable to get dynamic subnets info: {:?}", + e + )) + .into()), + } } fn get_metagraph( @@ -253,9 +316,14 @@ where ) -> RpcResult> { let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_metagraph(at, netuid).map_err(|e| { - Error::RuntimeError(format!("Unable to get dynamic subnets info: {:?}", e)).into() - }) + match api.get_metagraph(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => Err(Error::RuntimeError(format!( + "Unable to get dynamic subnets info: {:?}", + e + )) + .into()), + } } fn get_subnet_state( @@ -265,17 +333,25 @@ where ) -> RpcResult> { let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_subnet_state(at, netuid).map_err(|e| { - Error::RuntimeError(format!("Unable to get subnet state info: {:?}", e)).into() - }) + + match api.get_subnet_state(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get subnet state info: {:?}", e)).into()) + } + } } fn get_subnets_info(&self, at: Option<::Hash>) -> RpcResult> { let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_subnets_info(at) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + match api.get_subnets_info(at) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + } + } } fn get_subnet_info_v2( @@ -286,16 +362,24 @@ where let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_subnet_info_v2(at, netuid) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + match api.get_subnet_info_v2(at, netuid) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get subnet info: {:?}", e)).into()) + } + } } fn get_subnets_info_v2(&self, at: Option<::Hash>) -> RpcResult> { let api = self.client.runtime_api(); let at = at.unwrap_or_else(|| self.client.info().best_hash); - api.get_subnets_info_v2(at) - .map_err(|e| Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + match api.get_subnets_info_v2(at) { + Ok(result) => Ok(result.encode()), + Err(e) => { + Err(Error::RuntimeError(format!("Unable to get subnets info: {:?}", e)).into()) + } + } } fn get_network_lock_cost(&self, at: Option<::Hash>) -> RpcResult { diff --git a/pallets/subtensor/runtime-api/Cargo.toml b/pallets/subtensor/runtime-api/Cargo.toml index ef3e04947..a5300d759 100644 --- a/pallets/subtensor/runtime-api/Cargo.toml +++ b/pallets/subtensor/runtime-api/Cargo.toml @@ -13,9 +13,10 @@ workspace = true [dependencies] sp-api = { workspace = true } +sp-runtime = { workspace = true } frame-support = { workspace = true } serde = { workspace = true, features = ["derive"] } - +codec = { workspace = true } # local pallet-subtensor = { version = "4.0.0-dev", path = "../../subtensor", default-features = false } @@ -23,8 +24,10 @@ pallet-subtensor = { version = "4.0.0-dev", path = "../../subtensor", default-fe default = ["std"] std = [ "sp-api/std", + "sp-runtime/std", "frame-support/std", "pallet-subtensor/std", - "serde/std" + "serde/std", + "codec/std" ] pow-faucet = [] diff --git a/pallets/subtensor/runtime-api/src/lib.rs b/pallets/subtensor/runtime-api/src/lib.rs index 31f351aeb..9f6d96040 100644 --- a/pallets/subtensor/runtime-api/src/lib.rs +++ b/pallets/subtensor/runtime-api/src/lib.rs @@ -1,40 +1,51 @@ #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; use alloc::vec::Vec; +use codec::Compact; +use pallet_subtensor::rpc_info::{ + delegate_info::DelegateInfo, + dynamic_info::DynamicInfo, + metagraph::Metagraph, + neuron_info::{NeuronInfo, NeuronInfoLite}, + show_subnet::SubnetState, + stake_info::StakeInfo, + subnet_info::{SubnetHyperparams, SubnetInfo, SubnetInfov2}, +}; +use sp_runtime::AccountId32; // Here we declare the runtime API. It is implemented it the `impl` block in // src/neuron_info.rs, src/subnet_info.rs, and src/delegate_info.rs sp_api::decl_runtime_apis! { pub trait DelegateInfoRuntimeApi { - fn get_delegates() -> Vec; - fn get_delegate( delegate_account_vec: Vec ) -> Vec; - fn get_delegated( delegatee_account_vec: Vec ) -> Vec; + fn get_delegates() -> Vec>; + fn get_delegate( delegate_account: AccountId32 ) -> Option>; + fn get_delegated( delegatee_account: AccountId32 ) -> Vec<(DelegateInfo, Compact)>; } pub trait NeuronInfoRuntimeApi { - fn get_neurons(netuid: u16) -> Vec; - fn get_neuron(netuid: u16, uid: u16) -> Vec; - fn get_neurons_lite(netuid: u16) -> Vec; - fn get_neuron_lite(netuid: u16, uid: u16) -> Vec; + fn get_neurons(netuid: u16) -> Vec>; + fn get_neuron(netuid: u16, uid: u16) -> Option>; + fn get_neurons_lite(netuid: u16) -> Vec>; + fn get_neuron_lite(netuid: u16, uid: u16) -> Option>; } pub trait SubnetInfoRuntimeApi { - fn get_subnet_info(netuid: u16) -> Vec; - fn get_subnets_info() -> Vec; - fn get_subnet_info_v2(netuid: u16) -> Vec; - fn get_subnets_info_v2() -> Vec; - fn get_subnet_hyperparams(netuid: u16) -> Vec; - fn get_all_dynamic_info() -> Vec; - fn get_all_metagraphs() -> Vec; - fn get_metagraph(netuid: u16) -> Vec; - fn get_dynamic_info(netuid: u16) -> Vec; - fn get_subnet_state(netuid: u16) -> Vec; + fn get_subnet_info(netuid: u16) -> Option>; + fn get_subnets_info() -> Vec>>; + fn get_subnet_info_v2(netuid: u16) -> Option>; + fn get_subnets_info_v2() -> Vec>>; + fn get_subnet_hyperparams(netuid: u16) -> Option; + fn get_all_dynamic_info() -> Vec>>; + fn get_all_metagraphs() -> Vec>>; + fn get_metagraph(netuid: u16) -> Option>; + fn get_dynamic_info(netuid: u16) -> Option>; + fn get_subnet_state(netuid: u16) -> Option>; } pub trait StakeInfoRuntimeApi { - fn get_stake_info_for_coldkey( coldkey_account_vec: Vec ) -> Vec; - fn get_stake_info_for_coldkeys( coldkey_account_vecs: Vec> ) -> Vec; - fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec, coldkey_account_vec: Vec, netuid: u16 ) -> Vec; + fn get_stake_info_for_coldkey( coldkey_account: AccountId32 ) -> Vec>; + fn get_stake_info_for_coldkeys( coldkey_accounts: Vec ) -> Vec<(AccountId32, Vec>)>; + fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account: AccountId32, coldkey_account: AccountId32, netuid: u16 ) -> Option>; } pub trait SubnetRegistrationRuntimeApi { diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index bcfd1a37b..f9dc0022d 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -16,10 +16,8 @@ impl Pallet { log::debug!("Block emission: {:?}", block_emission); // --- 3. Run emission through network. Self::run_coinbase(block_emission); - // --- 4. Set pending children on the epoch; but only after the coinbase has been run. Self::try_set_pending_children(block_number); - // Return ok. Ok(()) } diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 3f83f934f..5968a7376 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/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index b57a08503..bb463e556 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -18,256 +18,184 @@ pub struct WeightsTlockPayload { pub version_key: u64, } -impl Pallet { - pub fn get_root_divs_in_alpha( - netuid: u16, - alpha_out_emission: I96F32, - validator_proportion: I96F32, - ) -> I96F32 { - // Get total TAO on root. - let total_root_tao: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(0)); - // Get total ALPHA on subnet. - let total_alpha_issuance: I96F32 = - I96F32::saturating_from_num(Self::get_alpha_issuance(netuid)); - // Get tao_weight - let tao_weight: I96F32 = total_root_tao.saturating_mul(Self::get_tao_weight()); - // Get root proportional dividends. - let root_proportion: I96F32 = tao_weight - .checked_div(tao_weight.saturating_add(total_alpha_issuance)) - .unwrap_or(I96F32::saturating_from_num(0.0)); - // Get root proportion of alpha_out dividends. - let root_divs_in_alpha: I96F32 = root_proportion - .saturating_mul(alpha_out_emission) - .saturating_mul(validator_proportion); // % of emission that goes to *all* validators. - - // Return - root_divs_in_alpha - } +// Distribute dividends to each hotkey +macro_rules! asfloat { + ($val:expr) => { + I96F32::saturating_from_num($val) + }; +} +macro_rules! tou64 { + ($val:expr) => { + $val.saturating_to_num::() + }; +} + +impl Pallet { pub fn run_coinbase(block_emission: I96F32) { // --- 0. Get current block. let current_block: u64 = Self::get_current_block_as_u64(); log::debug!("Current block: {:?}", current_block); - // --- 1. Get all netuids. - let subnets: Vec = Self::get_all_subnet_netuids(); + // --- 1. Get all netuids (filter out root.) + let subnets: Vec = Self::get_all_subnet_netuids() + .into_iter() + .filter(|netuid| *netuid != 0) + .collect(); log::debug!("All subnet netuids: {:?}", subnets); - // --- 2. Sum all the SubnetTAO associated with the same mechanism. - // Mechanisms get emission based on the proportion of TAO across all their subnets - let mut total_active_tao: I96F32 = I96F32::saturating_from_num(0); - let mut mechanism_tao: BTreeMap = BTreeMap::new(); - for netuid in subnets.iter() { - if *netuid == 0 { - continue; - } // Skip root network - let mechid = SubnetMechanism::::get(*netuid); - let subnet_tao = I96F32::saturating_from_num(SubnetTAO::::get(*netuid)); - let new_subnet_tao = subnet_tao.saturating_add( - *mechanism_tao - .entry(mechid) - .or_insert(I96F32::saturating_from_num(0)), - ); - *mechanism_tao - .entry(mechid) - .or_insert(I96F32::saturating_from_num(0)) = new_subnet_tao; - total_active_tao = total_active_tao.saturating_add(subnet_tao); + // --- 2. Get sum of tao reserves ( in a later version we will switch to prices. ) + let mut total_moving_prices: I96F32 = I96F32::saturating_from_num(0.0); + for netuid_i in subnets.iter() { + // Get and update the moving price of each subnet adding the total together. + total_moving_prices = + total_moving_prices.saturating_add(Self::get_moving_alpha_price(*netuid_i)); } - log::debug!("Mechanism TAO sums: {:?}", mechanism_tao); - - // --- 3. Compute subnet emission values (amount of tao inflation this block). - let mut tao_in_map: BTreeMap = BTreeMap::new(); - for netuid in subnets.iter() { - // Do not emit into root network. - if *netuid == 0 { - continue; - } - // 3.1: Get subnet mechanism ID - let mechid: u16 = SubnetMechanism::::get(*netuid); - log::debug!("Netuid: {:?}, Mechanism ID: {:?}", netuid, mechid); - // 3.2: Get subnet TAO (T_s) - let subnet_tao: I96F32 = I96F32::saturating_from_num(SubnetTAO::::get(*netuid)); - log::debug!("Subnet TAO (T_s) for netuid {:?}: {:?}", netuid, subnet_tao); - // 3.3: Get the denominator as the sum of all TAO associated with a specific mechanism (T_m) - let mech_tao: I96F32 = *mechanism_tao - .get(&mechid) - .unwrap_or(&I96F32::saturating_from_num(0)); - log::debug!( - "Mechanism TAO (T_m) for mechanism ID {:?}: {:?}", - mechid, - mech_tao - ); - // 3.4: Compute the mechanism emission proportion: P_m = T_m / T_total - let mech_proportion: I96F32 = mech_tao - .checked_div(total_active_tao) - .unwrap_or(I96F32::saturating_from_num(0)); - log::debug!( - "Mechanism proportion (P_m) for mechanism ID {:?}: {:?}", - mechid, - mech_proportion - ); - // 3.5: Compute the mechanism emission: E_m = P_m * E_b - let mech_emission: I96F32 = mech_proportion.saturating_mul(block_emission); - log::debug!( - "Mechanism emission (E_m) for mechanism ID {:?}: {:?}", - mechid, - mech_emission - ); - // 3.6: Calculate subnet's proportion of mechanism TAO: P_s = T_s / T_m - let subnet_proportion: I96F32 = subnet_tao - .checked_div(mech_tao) - .unwrap_or(I96F32::saturating_from_num(0)); - log::debug!( - "Subnet proportion (P_s) for netuid {:?}: {:?}", - netuid, - subnet_proportion - ); - + log::debug!("total_moving_prices: {:?}", total_moving_prices); + + // --- 3. Get subnet terms (tao_in, alpha_in, and alpha_out) + // Computation is described in detail in the dtao whitepaper. + let mut tao_in: BTreeMap = BTreeMap::new(); + let mut alpha_in: BTreeMap = BTreeMap::new(); + let mut alpha_out: BTreeMap = BTreeMap::new(); + for netuid_i in subnets.iter() { + // Get subnet price. + let price_i: I96F32 = Self::get_alpha_price(*netuid_i); + log::debug!("price_i: {:?}", price_i); + // Get subnet TAO. + let moving_price_i: I96F32 = Self::get_moving_alpha_price(*netuid_i); + log::debug!("moving_price_i: {:?}", moving_price_i); + // Emission is price over total. + let mut tao_in_i: I96F32 = block_emission + .saturating_mul(moving_price_i) + .checked_div(total_moving_prices) + .unwrap_or(asfloat!(0.0)); + log::debug!("tao_in_i: {:?}", tao_in_i); + // Get alpha_emission total + let alpha_emission_i: I96F32 = asfloat!(Self::get_block_emission_for_issuance( + Self::get_alpha_issuance(*netuid_i) + ) + .unwrap_or(0)); + log::debug!("alpha_emission_i: {:?}", alpha_emission_i); + // Get initial alpha_in + let alpha_in_i: I96F32 = tao_in_i + .checked_div(price_i) + .unwrap_or(alpha_emission_i) + .min(alpha_emission_i); + log::debug!("alpha_in_i: {:?}", alpha_in_i); + // Get alpha_out. + let alpha_out_i = alpha_emission_i; // Only emit TAO if the subnetwork allows registration. - if Self::get_network_registration_allowed(*netuid) - || Self::get_network_pow_registration_allowed(*netuid) + if !Self::get_network_registration_allowed(*netuid_i) + && Self::get_network_pow_registration_allowed(*netuid_i) { - // 3.7: Calculate subnet's TAO emission: E_s = P_s * E_m - let tao_in: u64 = mech_emission - .checked_mul(subnet_proportion) - .unwrap_or(I96F32::saturating_from_num(0)) - .saturating_to_num::(); - log::debug!( - "Subnet TAO emission (E_s) for netuid {:?}: {:?}", - netuid, - tao_in - ); - // 3.8: Store the subnet TAO emission. - *tao_in_map.entry(*netuid).or_insert(0) = tao_in; - // 3.9: Store the block emission for this subnet for chain storage. - EmissionValues::::insert(*netuid, tao_in); + tao_in_i = asfloat!(0.0); } + // Insert values into maps + tao_in.insert(*netuid_i, tao_in_i); + alpha_in.insert(*netuid_i, alpha_in_i); + alpha_out.insert(*netuid_i, alpha_out_i); } - - // == We'll save the owner cuts for each subnet. - let mut owner_cuts: BTreeMap = BTreeMap::new(); - - // --- 4. Distribute subnet emission into subnets based on mechanism type. - for netuid in subnets.iter() { - // Do not emit into root network. - if *netuid == 0 { - continue; - } - // 4.1. Get subnet mechanism ID - let mechid: u16 = SubnetMechanism::::get(*netuid); - log::debug!("{:?} - mechid: {:?}", netuid, mechid); - // 4.2: Get the subnet emission TAO. - let subnet_emission: u64 = *tao_in_map.get(netuid).unwrap_or(&0); - log::debug!("{:?} subnet_emission: {:?}", netuid, subnet_emission); - if mechid == 0 { - // The mechanism is Stable (FOR TESTING PURPOSES ONLY) - // 4.2.1 Increase Tao in the subnet "reserves" unconditionally. - SubnetTAO::::mutate(*netuid, |total| { - *total = total.saturating_add(subnet_emission) - }); - // 4.2.2 Increase total stake across all subnets. - TotalStake::::mutate(|total| *total = total.saturating_add(subnet_emission)); - // 4.2.3 Increase total issuance of Tao. - TotalIssuance::::mutate(|total| *total = total.saturating_add(subnet_emission)); - // 4.2.4 Increase this subnet pending emission. - PendingEmission::::mutate(*netuid, |total| { - *total = total.saturating_add(subnet_emission) - }); - // 4.2.5 Go to next subnet. - continue; - } - // Get the total_alpha_emission for the block - let alpha_block_emission: u64 = - Self::get_block_emission_for_issuance(Self::get_alpha_issuance(*netuid)) - .unwrap_or(0); - - // Compute emission into pool. - let (tao_in_emission, alpha_in_emission, alpha_out_emission): (u64, u64, u64) = - Self::get_dynamic_tao_emission(*netuid, subnet_emission, alpha_block_emission); - - // Set state vars. - SubnetTaoInEmission::::insert(*netuid, tao_in_emission); - SubnetAlphaInEmission::::insert(*netuid, alpha_in_emission); - SubnetAlphaOutEmission::::insert(*netuid, alpha_out_emission); - - // Increase counters. - SubnetAlphaIn::::mutate(*netuid, |total| { - *total = total.saturating_add(alpha_in_emission); - log::debug!("Injected alpha_in into SubnetAlphaIn: {:?}", *total); + log::debug!("tao_in: {:?}", tao_in); + log::debug!("alpha_in: {:?}", alpha_in); + log::debug!("alpha_out: {:?}", alpha_out); + + // --- 4. Injection. + // Actually perform the injection of alpha_in, alpha_out and tao_in into the subnet pool. + // This operation changes the pool liquidity each block. + for netuid_i in subnets.iter() { + // Inject Alpha in. + let alpha_in_i: u64 = tou64!(*alpha_in.get(netuid_i).unwrap_or(&asfloat!(0))); + SubnetAlphaInEmission::::insert(*netuid_i, alpha_in_i); + SubnetAlphaIn::::mutate(*netuid_i, |total| { + *total = total.saturating_add(alpha_in_i); }); - SubnetAlphaOut::::mutate(*netuid, |total| { - *total = total.saturating_add(alpha_out_emission); - log::debug!("Injected alpha_in into SubnetAlphaIn: {:?}", *total); + // Injection Alpha out. + let alpha_out_i: u64 = tou64!(*alpha_out.get(netuid_i).unwrap_or(&asfloat!(0))); + SubnetAlphaOutEmission::::insert(*netuid_i, alpha_out_i); + SubnetAlphaOut::::mutate(*netuid_i, |total| { + *total = total.saturating_add(alpha_out_i); }); - SubnetTAO::::mutate(*netuid, |total| { - *total = total.saturating_add(tao_in_emission); - log::debug!("Increased Tao in SubnetTAO: {:?}", *total); + // Inject TAO in. + let tao_in_i: u64 = tou64!(*tao_in.get(netuid_i).unwrap_or(&asfloat!(0))); + SubnetTaoInEmission::::insert(*netuid_i, tao_in_i); + SubnetTAO::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tao_in_i); }); TotalStake::::mutate(|total| { - *total = total.saturating_add(tao_in_emission); - log::debug!("Increased TotalStake: {:?}", *total); + *total = total.saturating_add(tao_in_i); }); TotalIssuance::::mutate(|total| { - *total = total.saturating_add(tao_in_emission); - log::debug!("Increased TotalIssuance: {:?}", *total); + *total = total.saturating_add(tao_in_i); }); + } + // --- 5. Compute owner cuts and remove them from alpha_out remaining. + // Remove owner cuts here so that we can properly seperate root dividends in the next step. + // Owner cuts are accumulated and then fed to the drain at the end of this func. + let cut_percent: I96F32 = Self::get_float_subnet_owner_cut(); + let mut owner_cuts: BTreeMap = BTreeMap::new(); + for netuid_i in subnets.iter() { + // Get alpha out. + let alpha_out_i: I96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0)); // Calculate the owner cut. - let owner_cut: u64 = I96F32::saturating_from_num(alpha_out_emission) - .saturating_mul(Self::get_float_subnet_owner_cut()) - .saturating_to_num::(); - log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); - // Store the owner cut for this subnet. - *owner_cuts.entry(*netuid).or_insert(0) = owner_cut; - - let remaining_emission: u64 = alpha_out_emission.saturating_sub(owner_cut); - log::debug!( - "Remaining emission for netuid {:?}: {:?}", - netuid, - remaining_emission - ); + let owner_cut_i: I96F32 = alpha_out_i.saturating_mul(cut_percent); + // Save owner cut. + *owner_cuts.entry(*netuid_i).or_insert(asfloat!(0)) = owner_cut_i; + // Save new alpha_out. + alpha_out.insert(*netuid_i, alpha_out_i.saturating_sub(owner_cut_i)); + // Accumulate the owner cut in pending. + PendingOwnerCut::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(owner_cut_i)); + }); + } - // Validators get 50% of remaining emission. - let validator_proportion: I96F32 = I96F32::saturating_from_num(0.5); - // Get proportion of alpha out emission as root divs. - let root_emission_in_alpha: I96F32 = Self::get_root_divs_in_alpha( - *netuid, - I96F32::saturating_from_num(remaining_emission), - validator_proportion, - ); - // Subtract root divs from alpha divs. - let pending_alpha_emission: I96F32 = I96F32::saturating_from_num(remaining_emission) - .saturating_sub(root_emission_in_alpha); + // --- 6. Seperate out root dividends in alpha and sell them into tao. + // Then accumulate those dividends for later. + for netuid_i in subnets.iter() { + // Get remaining alpha out. + let alpha_out_i: I96F32 = *alpha_out.get(netuid_i).unwrap_or(&asfloat!(0.0)); + // Get total TAO on root. + let root_tao: I96F32 = asfloat!(SubnetTAO::::get(0)); + // Get total ALPHA on subnet. + let alpha_issuance: I96F32 = asfloat!(Self::get_alpha_issuance(*netuid_i)); + // Get tao_weight + let tao_weight: I96F32 = root_tao.saturating_mul(Self::get_tao_weight()); + // Get root proportional dividends. + let root_proportion: I96F32 = tao_weight + .checked_div(tao_weight.saturating_add(alpha_issuance)) + .unwrap_or(asfloat!(0.0)); + // Get root proportion of alpha_out dividends. + let root_alpha: I96F32 = root_proportion + .saturating_mul(alpha_out_i) // Total alpha emission per block remaining. + .saturating_mul(asfloat!(0.5)); // 50% to validators. + // Remove root alpha from alpha_out. + let pending_alpha: I96F32 = alpha_out_i.saturating_sub(root_alpha); // Sell root emission through the pool. - let root_emission_in_tao: u64 = Self::swap_alpha_for_tao( - *netuid, - root_emission_in_alpha.saturating_to_num::(), - ); - SubnetAlphaEmissionSell::::insert( - *netuid, - root_emission_in_alpha.saturating_to_num::(), - ); - // Accumulate root divs for subnet. - PendingRootDivs::::mutate(*netuid, |total| { - *total = total.saturating_add(root_emission_in_tao); - }); - // Accumulate alpha that was swapped for the pending root divs. - PendingAlphaSwapped::::mutate(*netuid, |total| { - *total = total.saturating_add(root_emission_in_alpha.saturating_to_num::()); + let root_tao: u64 = Self::swap_alpha_for_tao(*netuid_i, tou64!(root_alpha)); + // Accumulate alpha emission in pending. + PendingAlphaSwapped::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(root_alpha)); }); // Accumulate alpha emission in pending. - PendingEmission::::mutate(*netuid, |total| { - *total = total.saturating_add(pending_alpha_emission.saturating_to_num::()); + PendingEmission::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(pending_alpha)); }); - // Accumulate the owner cut in pending. - PendingOwnerCut::::mutate(*netuid, |total| { - *total = total.saturating_add(owner_cut); + // Accumulate root divs for subnet. + PendingRootDivs::::mutate(*netuid_i, |total| { + *total = total.saturating_add(root_tao); }); } - // --- 5. Drain pending emission through the subnet based on tempo. + // --- 7 Update moving prices after using them in the emission calculation. + for netuid_i in subnets.iter() { + // Update moving prices after using them above. + Self::update_moving_price(*netuid_i); + } + + // --- 7. Drain pending emission through the subnet based on tempo. for &netuid in subnets.iter() { - // 5.1: Pass on subnets that have not reached their tempo. + // Pass on subnets that have not reached their tempo. if Self::should_run_epoch(netuid, current_block) { if let Err(e) = Self::reveal_crv3_commits(netuid) { log::warn!( @@ -281,28 +209,28 @@ impl Pallet { BlocksSinceLastStep::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, current_block); - // 5.2.1 Get and drain the subnet pending emission. - let pending_emission: u64 = PendingEmission::::get(netuid); + // Get and drain the subnet pending emission. + let pending_alpha: u64 = PendingEmission::::get(netuid); PendingEmission::::insert(netuid, 0); - // 5.2.2a Get and drain the subnet pending root divs. - let pending_root_divs: u64 = PendingRootDivs::::get(netuid); + // Get and drain the subnet pending root divs. + let pending_tao: u64 = PendingRootDivs::::get(netuid); PendingRootDivs::::insert(netuid, 0); - // 5.2.2b Get this amount as alpha that was swapped for pending root divs. - let pending_alpha_swapped: u64 = PendingAlphaSwapped::::get(netuid); + // Get this amount as alpha that was swapped for pending root divs. + let pending_swapped: u64 = PendingAlphaSwapped::::get(netuid); PendingAlphaSwapped::::insert(netuid, 0); - // 5.2.3 Get owner cut and drain. + // Get owner cut and drain. let owner_cut: u64 = PendingOwnerCut::::get(netuid); PendingOwnerCut::::insert(netuid, 0); - // 5.2.4 Drain pending root divs, alpha emission, and owner cut. + // Drain pending root divs, alpha emission, and owner cut. Self::drain_pending_emission( netuid, - pending_emission, - pending_root_divs, - pending_alpha_swapped, + pending_alpha, + pending_tao, + pending_swapped, owner_cut, ); } else { @@ -314,360 +242,171 @@ impl Pallet { pub fn drain_pending_emission( netuid: u16, - pending_alpha_emission: u64, - pending_root_divs: u64, - pending_alpha_swapped: u64, + pending_alpha: u64, + pending_tao: u64, + pending_swapped: u64, owner_cut: u64, ) { log::debug!( - "Draining pending alpha emission for netuid {:?}: {:?}, with pending root divs {:?}, pending alpha swapped {:?}, and owner cut {:?}", + "Draining pending alpha emission for netuid {:?}, pending_alpha: {:?}, pending_tao: {:?}, pending_swapped: {:?}, owner_cut: {:?}", netuid, - pending_alpha_emission, - pending_root_divs, - pending_alpha_swapped, + pending_alpha, + pending_tao, + pending_swapped, owner_cut ); - - // === 1. Run the epoch() --> hotkey emission. - // Needs to run on the full emission to the subnet. - let hotkey_emission: Vec<(T::AccountId, u64, u64)> = Self::epoch( - netuid, - pending_alpha_emission.saturating_add(pending_alpha_swapped), - ); - log::debug!( - "Hotkey emission for netuid {:?}: {:?}", - netuid, - hotkey_emission - ); - - // === 2. Calculate the dividend distributions using the current stake. - // Clear maps - let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); - let _ = TaoDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); - - // Initialize maps for parent-child OR miner dividend distributions. - let mut dividends_to_distribute: Vec<(T::AccountId, Vec<(T::AccountId, u64)>)> = Vec::new(); - let mut mining_incentive_to_distribute: Vec<(T::AccountId, u64)> = Vec::new(); - - // 2.1 --- Get dividend distribution from parent-child and miner distributions. - for (hotkey, incentive, dividends) in hotkey_emission { - log::debug!( - "Processing hotkey {:?} with incentive {:?} and dividends {:?}", - hotkey, - incentive, + // Setup. + let zero: I96F32 = asfloat!(0.0); + + // Run the epoch. + let hotkey_emission: Vec<(T::AccountId, u64, u64)> = + Self::epoch(netuid, pending_alpha.saturating_add(pending_swapped)); + + // Accumulate emission of dividends and incentive per hotkey. + let mut incentives: BTreeMap = BTreeMap::new(); + let mut dividends: BTreeMap = BTreeMap::new(); + for (hotkey, incentive, dividend) in hotkey_emission { + // Accumulate incentives to miners. + incentives + .entry(hotkey.clone()) + .and_modify(|e| *e = e.saturating_add(incentive)) + .or_insert(incentive); + // Accumulate dividends to parents. + let div_tuples: Vec<(T::AccountId, u64)> = + Self::get_dividends_distribution(&hotkey, netuid, dividend); + // Accumulate dividends per hotkey. + for (parent, parent_div) in div_tuples { dividends - ); - - // Record mining incentive - mining_incentive_to_distribute.push((hotkey.clone(), incentive)); - - // Get dividend tuples for parents and self based on childkey relationships and child-take. - let dividend_tuples: Vec<(T::AccountId, u64)> = - Self::get_dividends_distribution(&hotkey, netuid, dividends); - log::debug!( - "Dividend tuples for hotkey {:?} on netuid {:?}: {:?}", - hotkey, - netuid, - dividend_tuples - ); - - // Record dividends to distribute - dividends_to_distribute.push((hotkey.clone(), dividend_tuples)); - } - - // Initialize maps for dividend calculations. - let mut root_alpha_divs: BTreeMap = BTreeMap::new(); - let mut alpha_divs: BTreeMap = BTreeMap::new(); - let mut validator_alpha_takes: BTreeMap = BTreeMap::new(); - let mut validator_root_alpha_takes: BTreeMap = BTreeMap::new(); - // 2.2 --- Calculate the validator_take, alpha_divs, and root_alpha_divs using above dividend tuples. - for (hotkey, dividend_tuples) in dividends_to_distribute.iter() { - // Calculate the proportion of root vs alpha divs for each hotkey using their stake. - for (hotkey_j, divs_j) in dividend_tuples.iter() { - log::debug!( - "Processing dividend for child-hotkey {:?} to parent-hotkey {:?}: {:?}", - hotkey, - hotkey_j, - *divs_j - ); - - // 2.1 --- Get the local alpha and root alpha. - let hotkey_tao: I96F32 = I96F32::saturating_from_num( - Self::get_stake_for_hotkey_on_subnet(hotkey_j, Self::get_root_netuid()), - ); - let hotkey_tao_as_alpha: I96F32 = hotkey_tao.saturating_mul(Self::get_tao_weight()); - let hotkey_alpha = I96F32::saturating_from_num( - Self::get_stake_for_hotkey_on_subnet(hotkey_j, netuid), - ); - log::debug!("Hotkey tao for hotkey {:?} on root netuid: {:?}, hotkey tao as alpha: {:?}, hotkey alpha: {:?}", hotkey_j, hotkey_tao, hotkey_tao_as_alpha, hotkey_alpha); - - // 2.2 --- Compute alpha and root proportions. - let alpha_prop: I96F32 = hotkey_alpha - .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::saturating_from_num(0.0)); - let root_prop: I96F32 = hotkey_tao_as_alpha - .checked_div(hotkey_alpha.saturating_add(hotkey_tao_as_alpha)) - .unwrap_or(I96F32::saturating_from_num(0.0)); - log::debug!( - "Alpha proportion: {:?}, root proportion: {:?}", - alpha_prop, - root_prop - ); - - let divs_j: I96F32 = I96F32::saturating_from_num(*divs_j); - // 2.3.1 --- Compute root dividends - let root_alpha_divs_j: I96F32 = divs_j.saturating_mul(root_prop); - // 2.3.2 --- Compute alpha dividends - let alpha_divs_j: I96F32 = divs_j.saturating_sub(root_alpha_divs_j); - log::debug!( - "Alpha dividends: {:?}, Root alpha-dividends: {:?}", - alpha_divs_j, - root_alpha_divs_j - ); - - // 2.4.1 --- Remove the hotkey take from both alpha and root divs. - let take_prop: I96F32 = - I96F32::saturating_from_num(Self::get_hotkey_take(hotkey_j)) - .checked_div(I96F32::saturating_from_num(u16::MAX)) - .unwrap_or(I96F32::saturating_from_num(0.0)); - - let validator_alpha_take: I96F32 = take_prop.saturating_mul(alpha_divs_j); - let validator_root_alpha_take: I96F32 = take_prop.saturating_mul(root_alpha_divs_j); - - let rem_alpha_divs_j: I96F32 = alpha_divs_j.saturating_sub(validator_alpha_take); - let rem_root_alpha_divs_j: I96F32 = - root_alpha_divs_j.saturating_sub(validator_root_alpha_take); - log::debug!( - "Validator take for hotkey {:?}: alpha take: {:?}, remaining alpha: {:?}, root-alpha take: {:?}, remaining root-alpha: {:?}", - hotkey_j, - validator_alpha_take, - rem_alpha_divs_j, - validator_root_alpha_take, - rem_root_alpha_divs_j - ); - - // 2.4.2 --- Store the validator takes. - validator_alpha_takes - .entry(hotkey_j.clone()) - .and_modify(|e| { - *e = e.saturating_add(validator_alpha_take.saturating_to_num::()) - }) - .or_insert(validator_alpha_take.saturating_to_num::()); - validator_root_alpha_takes - .entry(hotkey_j.clone()) - .and_modify(|e| { - *e = e.saturating_add(validator_root_alpha_take.saturating_to_num::()) - }) - .or_insert(validator_root_alpha_take.saturating_to_num::()); - log::debug!( - "Stored validator take for hotkey {:?}: alpha take: {:?}, root-alpha take: {:?}", - hotkey_j, - validator_alpha_take.saturating_to_num::(), - validator_root_alpha_take.saturating_to_num::() - ); - - // 2.5.1 --- Store the root divs under hotkey_j - root_alpha_divs - .entry(hotkey_j.clone()) - .and_modify(|e| { - *e = e.saturating_add(rem_root_alpha_divs_j.saturating_to_num::()) - }) - .or_insert(rem_root_alpha_divs_j.saturating_to_num::()); - log::debug!( - "Stored root alpha dividends for hotkey {:?}: {:?}", - hotkey_j, - rem_root_alpha_divs_j.saturating_to_num::() - ); - - // 2.5.2 --- Store the alpha dividends - alpha_divs - .entry(hotkey_j.clone()) - .and_modify(|e| { - *e = e.saturating_add(rem_alpha_divs_j.saturating_to_num::()) - }) - .or_insert(rem_alpha_divs_j.saturating_to_num::()); - log::debug!( - "Stored alpha dividends for hotkey {:?}: {:?}", - hotkey_j, - rem_alpha_divs_j.saturating_to_num::() - ); + .entry(parent) + .and_modify(|e| *e = e.saturating_add(asfloat!(parent_div))) + .or_insert(asfloat!(parent_div)); } } - let total_root_alpha_divs: u64 = root_alpha_divs - .values() - .sum::() - .saturating_add(validator_root_alpha_takes.values().sum::()); + // Accumulate root divs and alpha_divs. For each hotkye we compute their + // local and root dividend proportion based on their alpha_stake/root_stake + let mut total_root_divs: I96F32 = asfloat!(0); + let mut root_dividends: BTreeMap = BTreeMap::new(); + let mut alpha_dividends: BTreeMap = BTreeMap::new(); + for (hotkey, dividend) in dividends { + // Get hotkey ALPHA on subnet. + let alpha_stake = asfloat!(Self::get_stake_for_hotkey_on_subnet(&hotkey, netuid)); + // Get hotkey TAO on root. + let root_stake: I96F32 = asfloat!(Self::get_stake_for_hotkey_on_subnet( + &hotkey, + Self::get_root_netuid() + )); + // Convert TAO to alpha with weight. + let root_alpha: I96F32 = root_stake.saturating_mul(Self::get_tao_weight()); + // Get total from root and local + let total_alpha: I96F32 = alpha_stake.saturating_add(root_alpha); + // Compute alpha prop. + let alpha_prop: I96F32 = alpha_stake.checked_div(total_alpha).unwrap_or(zero); + // Copmute root prop. + let root_prop: I96F32 = root_alpha.checked_div(total_alpha).unwrap_or(zero); + // Compute alpha dividends + let alpha_divs: I96F32 = dividend.saturating_mul(alpha_prop); + // Compute root dividends + let root_divs: I96F32 = dividend.saturating_mul(root_prop); + // Record the root dividends. + alpha_dividends + .entry(hotkey.clone()) + .and_modify(|e| *e = e.saturating_add(alpha_divs)) + .or_insert(alpha_divs); + // Record the alpha_dividends. + root_dividends + .entry(hotkey.clone()) + .and_modify(|e| *e = e.saturating_add(root_divs)) + .or_insert(root_divs); + // Accumulate total root divs. + total_root_divs = total_root_divs.saturating_add(root_divs); + } - // === 3. Distribute the dividends to the hotkeys. + // Compute root divs as TAO. Here we take + let mut tao_dividends: BTreeMap = BTreeMap::new(); + for (hotkey, root_divs) in root_dividends { + // Root proportion. + let root_share: I96F32 = root_divs.checked_div(total_root_divs).unwrap_or(zero); + // Root proportion in TAO + let root_tao: I96F32 = asfloat!(pending_tao).saturating_mul(root_share); + // Record root dividends as TAO. + tao_dividends + .entry(hotkey) + .and_modify(|e| *e = root_tao) + .or_insert(root_tao); + } - // 3.1 --- Distribute owner cut. - // Check for existence of subnet owner cold/hot pair and distribute emission directly to them. + // Distribute the owner cut. if let Ok(owner_coldkey) = SubnetOwner::::try_get(netuid) { if let Ok(owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { - // Increase stake for both coldkey and hotkey on the subnet + // Increase stake for owner hotkey and coldkey. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &owner_hotkey, &owner_coldkey, netuid, owner_cut, ); - log::debug!("Distributed owner cut for netuid {:?} to owner_hotkey {:?} and owner_coldkey {:?}", netuid, owner_hotkey, owner_coldkey); } } - // 3.2 --- Distribute mining incentive. - for (miner_j, incentive) in mining_incentive_to_distribute { - // Distribute mining incentive immediately. + // Distribute mining incentives. + for (hotkey, incentive) in incentives { + // Increase stake for miner. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &miner_j.clone(), - &Owner::::get(miner_j.clone()), + &hotkey.clone(), + &Owner::::get(hotkey.clone()), netuid, incentive, ); - log::debug!( - "Distributed mining incentive for hotkey {:?} on netuid {:?}: {:?}", - miner_j, - netuid, - incentive - ); } - // 3.3.1 --- Distribute validator alpha takes - for (validator_j, validator_take) in validator_alpha_takes { - // 3.3.1a --- Distribute validator take to the validator. + // Distribute alpha divs. + let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); + for (hotkey, mut alpha_divs) in alpha_dividends { + // Get take prop + let alpha_take: I96F32 = + Self::get_hotkey_take_float(&hotkey).saturating_mul(alpha_divs); + // Remove take prop from alpha_divs + alpha_divs = alpha_divs.saturating_sub(alpha_take); + // Give the validator their take. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &validator_j.clone(), - &Owner::::get(validator_j.clone()), + &hotkey, + &Owner::::get(hotkey.clone()), netuid, - validator_take, + tou64!(alpha_take), ); - log::debug!( - "Distributed validator take for hotkey {:?} on netuid {:?}: {:?}", - validator_j, - netuid, - validator_take - ); - - // 3.3.1b --- Record dividends for this validator on this subnet. - AlphaDividendsPerSubnet::::mutate(netuid, validator_j.clone(), |divs| { - *divs = divs.saturating_add(validator_take); + // Give all other nominators. + Self::increase_stake_for_hotkey_on_subnet(&hotkey.clone(), netuid, tou64!(alpha_divs)); + // Record dividends for this hotkey. + AlphaDividendsPerSubnet::::mutate(netuid, hotkey.clone(), |divs| { + *divs = divs.saturating_add(tou64!(alpha_divs)); }); - log::debug!( - "Recorded dividends for validator {:?} on netuid {:?}: {:?}", - validator_j, - netuid, - validator_take - ); } - // 3.3.2 --- Distribute validator root-alpha takes - for (validator_j, validator_take) in validator_root_alpha_takes { - // 3.3.2a --- Calculate the proportion of root divs to pay out to this validator's take. - let proportion: I96F32 = I96F32::saturating_from_num(validator_take) - .checked_div(I96F32::saturating_from_num(total_root_alpha_divs)) - .unwrap_or(I96F32::saturating_from_num(0)); - // 3.3.2b --- Get the proportion of root divs from the pending root divs. - let take_as_root_divs: u64 = proportion - .saturating_mul(I96F32::saturating_from_num(pending_root_divs)) - .saturating_to_num::(); - log::debug!( - "Root div proportion for validator take {:?}: {:?}, take_as_root_divs: {:?}", - validator_take, - proportion, - take_as_root_divs - ); - - // 3.3.2c --- Distribute validator take to the validator. + // Distribute root tao divs. + let _ = TaoDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); + for (hotkey, mut root_tao) in tao_dividends { + // Get take prop + let tao_take: I96F32 = Self::get_hotkey_take_float(&hotkey).saturating_mul(root_tao); + // Remove take prop from root_tao + root_tao = root_tao.saturating_sub(tao_take); + // Give the validator their take. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &validator_j.clone(), - &Owner::::get(validator_j.clone()), + &hotkey, + &Owner::::get(hotkey.clone()), Self::get_root_netuid(), - take_as_root_divs, - ); - log::debug!( - "Distributed validator take for hotkey {:?} on root netuid {:?}: {:?}", - validator_j, - Self::get_root_netuid(), - take_as_root_divs - ); - - // 3.3.2d --- Record dividends for this validator on this subnet. - TaoDividendsPerSubnet::::mutate(netuid, validator_j.clone(), |divs| { - *divs = divs.saturating_add(take_as_root_divs); - }); - log::debug!( - "Recorded dividends for validator {:?} on netuid {:?}: {:?}", - validator_j, - netuid, - take_as_root_divs - ); - } - - // 3.4 --- Distribute alpha divs - for (hotkey_j, alpha_divs_j) in alpha_divs { - // 3.4.1 --- Distribute alpha divs to the hotkey. - Self::increase_stake_for_hotkey_on_subnet(&hotkey_j.clone(), netuid, alpha_divs_j); - log::debug!( - "Distributed alpha dividends for hotkey {:?} on netuid {:?}: {:?}", - hotkey_j, - netuid, - alpha_divs_j - ); - - // 3.4.2 --- Record dividends for this hotkey on this subnet. - AlphaDividendsPerSubnet::::mutate(netuid, hotkey_j.clone(), |divs| { - *divs = divs.saturating_add(alpha_divs_j); - }); - log::debug!( - "Recorded dividends for hotkey {:?} on netuid {:?}: {:?}", - hotkey_j, - netuid, - alpha_divs_j - ); - } - - // 3.5 --- Distribute root divs - // For all the root-alpha divs give this proportion of the swapped tao to the root participants. - for (hotkey_j, root_alpha_divs_j) in root_alpha_divs.iter() { - // 3.5.1 --- Calculate the proportion of root divs to pay out to this hotkey. - let proportion: I96F32 = I96F32::saturating_from_num(*root_alpha_divs_j) - .checked_div(I96F32::saturating_from_num(total_root_alpha_divs)) - .unwrap_or(I96F32::saturating_from_num(0)); - // 3.5.2 --- Get the proportion of root divs from the pending root divs. - let root_divs_to_pay: u64 = proportion - .saturating_mul(I96F32::saturating_from_num(pending_root_divs)) - .saturating_to_num::(); - log::debug!( - "Proportion for hotkey {:?}: {:?}, root_divs_to_pay: {:?}", - hotkey_j, - proportion, - root_divs_to_pay + tou64!(tao_take), ); - - // 3.5.3 --- Distribute the root divs to the hotkey on the root subnet. + // Give rest to nominators. Self::increase_stake_for_hotkey_on_subnet( - hotkey_j, + &hotkey, Self::get_root_netuid(), - root_divs_to_pay, - ); - log::debug!( - "Paid tao to hotkey {:?} on root netuid from netuid {:?}: {:?}", - hotkey_j, - netuid, - root_divs_to_pay + tou64!(root_tao), ); - - // 3.5.4 --- Record dividends for this hotkey on this subnet. - TaoDividendsPerSubnet::::mutate(netuid, hotkey_j.clone(), |divs| { - *divs = divs.saturating_add(root_divs_to_pay); + // Record root dividends for this validator on this subnet. + TaoDividendsPerSubnet::::mutate(netuid, hotkey.clone(), |divs| { + *divs = divs.saturating_add(tou64!(root_tao)); }); - log::debug!( - "Recorded dividends for hotkey {:?} on netuid {:?}: {:?}", - hotkey_j, - netuid, - root_divs_to_pay - ); } } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 064bb5c6e..d76caf894 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 ==== /// ============================ @@ -703,7 +748,7 @@ pub mod pallet { #[pallet::type_value] /// Default value for applying pending items (e.g. childkeys). pub fn DefaultPendingCooldown() -> u64 { - 7200 + 1 } #[pallet::type_value] @@ -717,7 +762,7 @@ pub mod pallet { /// Default staking fee. /// 500k rao matches $0.25 at $500/TAO pub fn DefaultStakingFee() -> u64 { - 500_000 + 50_000 } #[pallet::type_value] @@ -732,6 +777,18 @@ pub mod pallet { T::InitialDissolveNetworkScheduleDuration::get() } + #[pallet::type_value] + /// Default moving alpha for the moving price. + pub fn DefaultMovingAlpha() -> I96F32 { + // Moving average take 30 days to reach 50% of the price + // and 3.5 months to reach 90%. + I96F32::saturating_from_num(0.000003) + } + #[pallet::type_value] + /// Default subnet moving price. + pub fn DefaultMovingPrice() -> I96F32 { + I96F32::saturating_from_num(0.0) + } #[pallet::type_value] /// Default value for Share Pool variables pub fn DefaultSharePoolZero() -> U64F64 { @@ -910,6 +967,11 @@ pub mod pallet { pub type TotalStake = StorageValue<_, u64, ValueQuery>; #[pallet::storage] // --- ITEM ( dynamic_block ) -- block when dynamic was turned on. pub type DynamicBlock = StorageValue<_, u64, ValueQuery>; + #[pallet::storage] // --- ITEM ( moving_alpha ) -- subnet moving alpha. + pub type SubnetMovingAlpha = StorageValue<_, I96F32, ValueQuery, DefaultMovingAlpha>; + #[pallet::storage] // --- MAP ( netuid ) --> moving_price | The subnet moving price. + pub type SubnetMovingPrice = + StorageMap<_, Identity, u16, I96F32, ValueQuery, DefaultMovingPrice>; #[pallet::storage] // --- MAP ( netuid ) --> total_volume | The total amount of TAO bought and sold since the start of the network. pub type SubnetVolume = StorageMap<_, Identity, u16, u128, ValueQuery, DefaultZeroU128>; @@ -1409,14 +1471,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 2735b6bb7..a6c6ed9eb 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 19f7d317b..e2427dcdc 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 000000000..505b617b2 --- /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 136bd4c59..d33414580 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 5d9aff969..740c21b63 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/delegate_info.rs b/pallets/subtensor/src/rpc_info/delegate_info.rs index f8d641823..acfe28b43 100644 --- a/pallets/subtensor/src/rpc_info/delegate_info.rs +++ b/pallets/subtensor/src/rpc_info/delegate_info.rs @@ -6,15 +6,14 @@ use safe_math::*; use substrate_fixed::types::U64F64; extern crate alloc; use codec::Compact; -use sp_core::hexdisplay::AsBytesRef; -#[freeze_struct("5752e4c650a83e0d")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct DelegateInfo { - delegate_ss58: T::AccountId, +#[freeze_struct("66105c2cfec0608d")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct DelegateInfo { + delegate_ss58: AccountId, take: Compact, - nominators: Vec<(T::AccountId, Compact)>, // map of nominator_ss58 to stake amount - owner_ss58: T::AccountId, + nominators: Vec<(AccountId, Compact)>, // map of nominator_ss58 to stake amount + owner_ss58: AccountId, registrations: Vec>, // Vec of netuid this delegate is registered on validator_permits: Vec>, // Vec of netuid this delegate has validator permit on return_per_1000: Compact, // Delegators current daily return per 1000 TAO staked minus take fee @@ -50,7 +49,7 @@ impl Pallet { Self::return_per_1000_tao(take, total_stake, emissions_per_day) } - fn get_delegate_by_existing_account(delegate: AccountIdOf) -> DelegateInfo { + fn get_delegate_by_existing_account(delegate: AccountIdOf) -> DelegateInfo { let mut nominators = Vec::<(T::AccountId, Compact)>::new(); for (nominator, stake) in @@ -107,13 +106,7 @@ impl Pallet { } } - pub fn get_delegate(delegate_account_vec: Vec) -> Option> { - if delegate_account_vec.len() != 32 { - return None; - } - - let delegate: AccountIdOf = - T::AccountId::decode(&mut delegate_account_vec.as_bytes_ref()).ok()?; + pub fn get_delegate(delegate: T::AccountId) -> Option> { // Check delegate exists if !>::contains_key(delegate.clone()) { return None; @@ -125,8 +118,8 @@ impl Pallet { /// get all delegates info from storage /// - pub fn get_delegates() -> Vec> { - let mut delegates = Vec::>::new(); + pub fn get_delegates() -> Vec> { + let mut delegates = Vec::>::new(); for delegate in as IterableStorageMap>::iter_keys() { let delegate_info = Self::get_delegate_by_existing_account(delegate.clone()); delegates.push(delegate_info); @@ -137,12 +130,10 @@ impl Pallet { /// get all delegate info and staked token amount for a given delegatee account /// - pub fn get_delegated(delegatee_account_vec: Vec) -> Vec<(DelegateInfo, Compact)> { - let Ok(delegatee) = T::AccountId::decode(&mut delegatee_account_vec.as_bytes_ref()) else { - return Vec::new(); // No delegates for invalid account - }; - - let mut delegates: Vec<(DelegateInfo, Compact)> = Vec::new(); + pub fn get_delegated( + delegatee: T::AccountId, + ) -> Vec<(DelegateInfo, Compact)> { + let mut delegates: Vec<(DelegateInfo, Compact)> = Vec::new(); for delegate in as IterableStorageMap>::iter_keys() { // Staked to this delegate, so add to list let delegate_info = Self::get_delegate_by_existing_account(delegate.clone()); diff --git a/pallets/subtensor/src/rpc_info/dynamic_info.rs b/pallets/subtensor/src/rpc_info/dynamic_info.rs index 27d10dc2d..ac6a45cf5 100644 --- a/pallets/subtensor/src/rpc_info/dynamic_info.rs +++ b/pallets/subtensor/src/rpc_info/dynamic_info.rs @@ -4,12 +4,12 @@ use codec::Compact; use frame_support::pallet_prelude::{Decode, Encode}; use subtensor_macros::freeze_struct; -#[freeze_struct("a5cdc80d655398e9")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct DynamicInfo { +#[freeze_struct("f728ab9f6ffbf7f2")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct DynamicInfo { netuid: Compact, - owner_hotkey: T::AccountId, - owner_coldkey: T::AccountId, + owner_hotkey: AccountId, + owner_coldkey: AccountId, subnet_name: Vec>, token_symbol: Vec>, tempo: Compact, @@ -26,11 +26,11 @@ pub struct DynamicInfo { pending_root_emission: Compact, subnet_volume: Compact, network_registered_at: Compact, - subnet_identity: Option, + subnet_identity: Option, } impl Pallet { - pub fn get_dynamic_info(netuid: u16) -> Option> { + pub fn get_dynamic_info(netuid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } @@ -63,12 +63,12 @@ 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>> { + pub fn get_all_dynamic_info() -> Vec>> { let netuids: Vec = Self::get_all_subnet_netuids(); - let mut dynamic_info = Vec::>>::new(); + let mut dynamic_info = Vec::>>::new(); for netuid in netuids.clone().iter() { dynamic_info.push(Self::get_dynamic_info(*netuid)); } diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 267af1d05..315415ca7 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -6,21 +6,21 @@ use frame_support::pallet_prelude::{Decode, Encode}; use substrate_fixed::types::I64F64; use subtensor_macros::freeze_struct; -#[freeze_struct("7c5fe907490c5d5e")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct Metagraph { +#[freeze_struct("4fc6eea3706b9c0c")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct Metagraph { // Subnet index netuid: Compact, // 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. - owner_hotkey: T::AccountId, // hotkey - owner_coldkey: T::AccountId, // coldkey. + owner_hotkey: AccountId, // hotkey + owner_coldkey: AccountId, // coldkey. // Tempo terms. block: Compact, // block at call. @@ -81,32 +81,32 @@ 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<(T::AccountId, Compact)>, // List of dividend payouts in tao via root. - alpha_dividends_per_hotkey: Vec<(T::AccountId, Compact)>, // List of dividend payout in alpha via subnet. + tao_dividends_per_hotkey: Vec<(AccountId, Compact)>, // List of dividend payouts in tao via root. + alpha_dividends_per_hotkey: Vec<(AccountId, Compact)>, // List of dividend payout in alpha via subnet. } impl Pallet { - pub fn get_metagraph(netuid: u16) -> Option> { + pub fn get_metagraph(netuid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } @@ -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. @@ -280,9 +280,9 @@ impl Pallet { alpha_dividends_per_hotkey, }) } - pub fn get_all_metagraphs() -> Vec>> { + pub fn get_all_metagraphs() -> Vec>> { let netuids: Vec = Self::get_all_subnet_netuids(); - let mut metagraphs = Vec::>>::new(); + let mut metagraphs = Vec::>>::new(); for netuid in netuids.clone().iter() { metagraphs.push(Self::get_metagraph(*netuid)); } diff --git a/pallets/subtensor/src/rpc_info/neuron_info.rs b/pallets/subtensor/src/rpc_info/neuron_info.rs index be367a566..4838b376f 100644 --- a/pallets/subtensor/src/rpc_info/neuron_info.rs +++ b/pallets/subtensor/src/rpc_info/neuron_info.rs @@ -3,17 +3,17 @@ use frame_support::pallet_prelude::{Decode, Encode}; extern crate alloc; use codec::Compact; -#[freeze_struct("45e69321f5c74b4b")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct NeuronInfo { - hotkey: T::AccountId, - coldkey: T::AccountId, +#[freeze_struct("d6da7340b3350951")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct NeuronInfo { + hotkey: AccountId, + coldkey: AccountId, uid: Compact, netuid: Compact, active: bool, axon_info: AxonInfo, prometheus_info: PrometheusInfo, - stake: Vec<(T::AccountId, Compact)>, // map of coldkey to stake on this neuron/hotkey (includes delegations) + stake: Vec<(AccountId, Compact)>, // map of coldkey to stake on this neuron/hotkey (includes delegations) rank: Compact, emission: Compact, incentive: Compact, @@ -28,17 +28,17 @@ pub struct NeuronInfo { pruning_score: Compact, } -#[freeze_struct("c21f0f4f22bcb2a1")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct NeuronInfoLite { - hotkey: T::AccountId, - coldkey: T::AccountId, +#[freeze_struct("3e9eed057f379b3b")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct NeuronInfoLite { + hotkey: AccountId, + coldkey: AccountId, uid: Compact, netuid: Compact, active: bool, axon_info: AxonInfo, prometheus_info: PrometheusInfo, - stake: Vec<(T::AccountId, Compact)>, // map of coldkey to stake on this neuron/hotkey (includes delegations) + stake: Vec<(AccountId, Compact)>, // map of coldkey to stake on this neuron/hotkey (includes delegations) rank: Compact, emission: Compact, incentive: Compact, @@ -53,7 +53,7 @@ pub struct NeuronInfoLite { } impl Pallet { - pub fn get_neurons(netuid: u16) -> Vec> { + pub fn get_neurons(netuid: u16) -> Vec> { if !Self::if_subnet_exist(netuid) { return Vec::new(); } @@ -71,7 +71,7 @@ impl Pallet { neurons } - fn get_neuron_subnet_exists(netuid: u16, uid: u16) -> Option> { + fn get_neuron_subnet_exists(netuid: u16, uid: u16) -> Option> { let hotkey = match Self::get_hotkey_for_net_and_uid(netuid, uid) { Ok(h) => h, Err(_) => return None, @@ -146,7 +146,7 @@ impl Pallet { Some(neuron) } - pub fn get_neuron(netuid: u16, uid: u16) -> Option> { + pub fn get_neuron(netuid: u16, uid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } @@ -154,7 +154,10 @@ impl Pallet { Self::get_neuron_subnet_exists(netuid, uid) } - fn get_neuron_lite_subnet_exists(netuid: u16, uid: u16) -> Option> { + fn get_neuron_lite_subnet_exists( + netuid: u16, + uid: u16, + ) -> Option> { let hotkey = match Self::get_hotkey_for_net_and_uid(netuid, uid) { Ok(h) => h, Err(_) => return None, @@ -207,12 +210,12 @@ impl Pallet { Some(neuron) } - pub fn get_neurons_lite(netuid: u16) -> Vec> { + pub fn get_neurons_lite(netuid: u16) -> Vec> { if !Self::if_subnet_exist(netuid) { return Vec::new(); } - let mut neurons: Vec> = Vec::new(); + let mut neurons: Vec> = Vec::new(); let n = Self::get_subnetwork_n(netuid); for uid in 0..n { let neuron = match Self::get_neuron_lite_subnet_exists(netuid, uid) { @@ -225,7 +228,7 @@ impl Pallet { neurons } - pub fn get_neuron_lite(netuid: u16, uid: u16) -> Option> { + pub fn get_neuron_lite(netuid: u16, uid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } diff --git a/pallets/subtensor/src/rpc_info/show_subnet.rs b/pallets/subtensor/src/rpc_info/show_subnet.rs index b10a19359..9b66439fa 100644 --- a/pallets/subtensor/src/rpc_info/show_subnet.rs +++ b/pallets/subtensor/src/rpc_info/show_subnet.rs @@ -5,12 +5,12 @@ use codec::Compact; use frame_support::pallet_prelude::{Decode, Encode}; use substrate_fixed::types::I64F64; -#[freeze_struct("1af112d561741563")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct SubnetState { +#[freeze_struct("7954f39fd0755b28")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct SubnetState { netuid: Compact, - hotkeys: Vec, - coldkeys: Vec, + hotkeys: Vec, + coldkeys: Vec, active: Vec, validator_permit: Vec, pruning_score: Vec>, @@ -77,9 +77,9 @@ impl Pallet { /// /// # Returns /// - /// * `Option>` - An optional `SubnetState` struct containing the collected data for the subnet. + /// * `Option>` - An optional `SubnetState` struct containing the collected data for the subnet. /// Returns `None` if the subnet does not exist. - pub fn get_subnet_state(netuid: u16) -> Option> { + pub fn get_subnet_state(netuid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 0422bc33b..631e3a167 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -2,13 +2,12 @@ use super::*; use frame_support::pallet_prelude::{Decode, Encode}; extern crate alloc; use codec::Compact; -use sp_core::hexdisplay::AsBytesRef; -#[freeze_struct("c5e3871b39062f8e")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct StakeInfo { - hotkey: T::AccountId, - coldkey: T::AccountId, +#[freeze_struct("4f16c654467bc8b6")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct StakeInfo { + hotkey: AccountId, + coldkey: AccountId, netuid: Compact, stake: Compact, locked: Compact, @@ -20,16 +19,16 @@ pub struct StakeInfo { impl Pallet { fn _get_stake_info_for_coldkeys( coldkeys: Vec, - ) -> Vec<(T::AccountId, Vec>)> { + ) -> Vec<(T::AccountId, Vec>)> { if coldkeys.is_empty() { return Vec::new(); // No coldkeys to check } let netuids: Vec = Self::get_all_subnet_netuids(); - let mut stake_info: Vec<(T::AccountId, Vec>)> = Vec::new(); + let mut stake_info: Vec<(T::AccountId, Vec>)> = Vec::new(); for coldkey_i in coldkeys.clone().iter() { // Get all hotkeys associated with this coldkey. let staking_hotkeys = StakingHotkeys::::get(coldkey_i.clone()); - let mut stake_info_for_coldkey: Vec> = Vec::new(); + let mut stake_info_for_coldkey: Vec> = Vec::new(); for netuid_i in netuids.clone().iter() { for hotkey_i in staking_hotkeys.clone().iter() { let alpha: u64 = Self::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -59,35 +58,19 @@ impl Pallet { } pub fn get_stake_info_for_coldkeys( - coldkey_account_vecs: Vec>, - ) -> Vec<(T::AccountId, Vec>)> { - let mut coldkeys: Vec = Vec::new(); - for coldkey_account_vec in coldkey_account_vecs { - if coldkey_account_vec.len() != 32 { - continue; // Invalid coldkey - } - let Ok(coldkey) = T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()) else { - continue; - }; - coldkeys.push(coldkey); + coldkey_accounts: Vec, + ) -> Vec<(T::AccountId, Vec>)> { + if coldkey_accounts.is_empty() { + return Vec::new(); // Empty coldkeys } - if coldkeys.is_empty() { - return Vec::new(); // Invalid coldkey - } - - Self::_get_stake_info_for_coldkeys(coldkeys) + Self::_get_stake_info_for_coldkeys(coldkey_accounts) } - pub fn get_stake_info_for_coldkey(coldkey_account_vec: Vec) -> Vec> { - if coldkey_account_vec.len() != 32 { - return Vec::new(); // Invalid coldkey - } - - let Ok(coldkey) = T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()) else { - return Vec::new(); - }; - let stake_info = Self::_get_stake_info_for_coldkeys(vec![coldkey]); + pub fn get_stake_info_for_coldkey( + coldkey_account: T::AccountId, + ) -> Vec> { + let stake_info = Self::_get_stake_info_for_coldkeys(vec![coldkey_account]); if stake_info.is_empty() { Vec::new() // Invalid coldkey @@ -101,34 +84,21 @@ impl Pallet { } pub fn get_stake_info_for_hotkey_coldkey_netuid( - hotkey_account_vec: Vec, - coldkey_account_vec: Vec, + hotkey_account: T::AccountId, + coldkey_account: T::AccountId, netuid: u16, - ) -> Option> { - if coldkey_account_vec.len() != 32 { - return None; // Invalid coldkey - } - - let Ok(coldkey) = T::AccountId::decode(&mut coldkey_account_vec.as_bytes_ref()) else { - return None; - }; - - if hotkey_account_vec.len() != 32 { - return None; // Invalid hotkey - } - - let Ok(hotkey) = T::AccountId::decode(&mut hotkey_account_vec.as_bytes_ref()) else { - return None; - }; - - let alpha: u64 = - Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); - let emission: u64 = AlphaDividendsPerSubnet::::get(netuid, &hotkey); - let is_registered: bool = Self::is_hotkey_registered_on_network(netuid, &hotkey); + ) -> Option> { + let alpha: u64 = Self::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey_account, + &coldkey_account, + netuid, + ); + let emission: u64 = AlphaDividendsPerSubnet::::get(netuid, &hotkey_account); + let is_registered: bool = Self::is_hotkey_registered_on_network(netuid, &hotkey_account); Some(StakeInfo { - hotkey: hotkey.clone(), - coldkey: coldkey.clone(), + hotkey: hotkey_account, + coldkey: coldkey_account, netuid: (netuid).into(), stake: alpha.into(), locked: 0.into(), diff --git a/pallets/subtensor/src/rpc_info/subnet_info.rs b/pallets/subtensor/src/rpc_info/subnet_info.rs index bdd420821..46644e746 100644 --- a/pallets/subtensor/src/rpc_info/subnet_info.rs +++ b/pallets/subtensor/src/rpc_info/subnet_info.rs @@ -4,9 +4,9 @@ use frame_support::storage::IterableStorageMap; extern crate alloc; use codec::Compact; -#[freeze_struct("fe79d58173da662a")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct SubnetInfo { +#[freeze_struct("1eee6f3911800c6b")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct SubnetInfo { netuid: Compact, rho: Compact, kappa: Compact, @@ -24,12 +24,12 @@ pub struct SubnetInfo { network_connect: Vec<[u16; 2]>, emission_values: Compact, burn: Compact, - owner: T::AccountId, + owner: AccountId, } -#[freeze_struct("65f931972fa13222")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] -pub struct SubnetInfov2 { +#[freeze_struct("a86ee623525247cc")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] +pub struct SubnetInfov2 { netuid: Compact, rho: Compact, kappa: Compact, @@ -45,14 +45,14 @@ pub struct SubnetInfov2 { tempo: Compact, network_modality: Compact, network_connect: Vec<[u16; 2]>, - emission_values: Compact, + emission_value: Compact, burn: Compact, - owner: T::AccountId, - identity: Option, + owner: AccountId, + identity: Option, } -#[freeze_struct("55b472510f10e76a")] -#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)] +#[freeze_struct("7b506df55bd44646")] +#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] pub struct SubnetHyperparams { rho: Compact, kappa: 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, @@ -84,7 +84,7 @@ pub struct SubnetHyperparams { } impl Pallet { - pub fn get_subnet_info(netuid: u16) -> Option> { + pub fn get_subnet_info(netuid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } @@ -132,7 +132,7 @@ impl Pallet { }) } - pub fn get_subnets_info() -> Vec>> { + pub fn get_subnets_info() -> Vec>> { let mut subnet_netuids = Vec::::new(); let mut max_netuid: u16 = 0; for (netuid, added) in as IterableStorageMap>::iter() { @@ -144,7 +144,7 @@ impl Pallet { } } - let mut subnets_info = Vec::>>::new(); + let mut subnets_info = Vec::>>::new(); for netuid_ in 0..=max_netuid { if subnet_netuids.contains(&netuid_) { subnets_info.push(Self::get_subnet_info(netuid_)); @@ -154,7 +154,7 @@ impl Pallet { subnets_info } - pub fn get_subnet_info_v2(netuid: u16) -> Option> { + pub fn get_subnet_info_v2(netuid: u16) -> Option> { if !Self::if_subnet_exist(netuid) { return None; } @@ -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,13 +198,14 @@ 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, }) } - pub fn get_subnets_info_v2() -> Vec>> { + + pub fn get_subnets_info_v2() -> Vec>> { let mut subnet_netuids = Vec::::new(); let mut max_netuid: u16 = 0; for (netuid, added) in as IterableStorageMap>::iter() { @@ -216,15 +217,16 @@ impl Pallet { } } - let mut subnets_info = Vec::>>::new(); + let mut subnets_info = Vec::>>::new(); for netuid_ in 0..=max_netuid { if subnet_netuids.contains(&netuid_) { - subnets_info.push(Self::get_subnet_info(netuid_)); + subnets_info.push(Self::get_subnet_info_v2(netuid_)); } } subnets_info } + pub fn get_subnet_hyperparams(netuid: u16) -> Option { if !Self::if_subnet_exist(netuid) { return None; @@ -280,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/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 0cbe3bccf..c83e303c7 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -120,6 +120,11 @@ impl Pallet { pub fn get_hotkey_take(hotkey: &T::AccountId) -> u16 { Delegates::::get(hotkey) } + pub fn get_hotkey_take_float(hotkey: &T::AccountId) -> I96F32 { + I96F32::saturating_from_num(Self::get_hotkey_take(hotkey)) + .checked_div(I96F32::saturating_from_num(u16::MAX)) + .unwrap_or(I96F32::saturating_from_num(0.0)) + } /// Returns true if the hotkey account has been created. /// diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index b4b650804..4db9bebeb 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -45,6 +45,26 @@ impl Pallet { .unwrap_or(I96F32::saturating_from_num(0)) } } + pub fn get_moving_alpha_price(netuid: u16) -> I96F32 { + if netuid == Self::get_root_netuid() { + // Root. + I96F32::saturating_from_num(1.0) + } else if SubnetMechanism::::get(netuid) == 0 { + // Stable + I96F32::saturating_from_num(1.0) + } else { + SubnetMovingPrice::::get(netuid) + } + } + pub fn update_moving_price(netuid: u16) { + let alpha: I96F32 = SubnetMovingAlpha::::get(); + let minus_alpha: I96F32 = I96F32::saturating_from_num(1.0).saturating_sub(alpha); + let current_price: I96F32 = alpha.saturating_mul(Self::get_alpha_price(netuid)); + let current_moving: I96F32 = + minus_alpha.saturating_mul(Self::get_moving_alpha_price(netuid)); + let new_moving: I96F32 = current_price.saturating_add(current_moving); + SubnetMovingPrice::::insert(netuid, new_moving); + } /// Retrieves the global global weight as a normalized value between 0 and 1. /// diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index ee713cacf..58df2e4ad 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/subnets/symbols.rs b/pallets/subtensor/src/subnets/symbols.rs index 24b28720f..6350c303e 100644 --- a/pallets/subtensor/src/subnets/symbols.rs +++ b/pallets/subtensor/src/subnets/symbols.rs @@ -7,71 +7,71 @@ impl Pallet { SubnetName::::get(netuid) } else { match netuid { - 0 => b"tau".to_vec(), - 1 => b"alpha".to_vec(), - 2 => b"beta".to_vec(), - 3 => b"gamma".to_vec(), - 4 => b"delta".to_vec(), - 5 => b"epsilon".to_vec(), - 6 => b"zeta".to_vec(), - 7 => b"eta".to_vec(), - 8 => b"theta".to_vec(), - 9 => b"iota".to_vec(), - 10 => b"kappa".to_vec(), - 11 => b"lambda".to_vec(), - 12 => b"mu".to_vec(), - 13 => b"nu".to_vec(), - 14 => b"xi".to_vec(), - 15 => b"omicron".to_vec(), - 16 => b"pi".to_vec(), - 17 => b"rho".to_vec(), - 18 => b"sigma".to_vec(), - 19 => b"tau".to_vec(), - 20 => b"upsilon".to_vec(), - 21 => b"phi".to_vec(), - 22 => b"chi".to_vec(), - 23 => b"psi".to_vec(), - 24 => b"omega".to_vec(), - 25 => b"aleph".to_vec(), - 26 => b"bet".to_vec(), - 27 => b"gimel".to_vec(), - 28 => b"dalet".to_vec(), - 29 => b"he".to_vec(), - 30 => b"vav".to_vec(), - 31 => b"zayin".to_vec(), - 32 => b"het".to_vec(), - 33 => b"tet".to_vec(), - 34 => b"yod".to_vec(), - 35 => b"kafso".to_vec(), - 36 => b"kaf".to_vec(), - 37 => b"lamed".to_vec(), - 38 => b"mem".to_vec(), - 39 => b"mem".to_vec(), - 40 => b"nunso".to_vec(), - 41 => b"nun".to_vec(), - 42 => b"samekh".to_vec(), - 43 => b"ayin".to_vec(), - 44 => b"peso".to_vec(), - 45 => b"pe".to_vec(), - 46 => b"tsadiso".to_vec(), - 47 => b"tsadi".to_vec(), - 48 => b"qof".to_vec(), - 49 => b"resh".to_vec(), - 50 => b"shin".to_vec(), - 51 => b"tav".to_vec(), - 52 => b"alif".to_vec(), - 53 => b"ba".to_vec(), - 54 => b"ta".to_vec(), - 55 => b"tha".to_vec(), - 56 => b"jim".to_vec(), - 57 => b"ha".to_vec(), - 58 => b"kha".to_vec(), - 59 => b"dal".to_vec(), - 60 => b"dhal".to_vec(), - 61 => b"ra".to_vec(), - 62 => b"zay".to_vec(), - 63 => b"sin".to_vec(), - 64 => b"shin".to_vec(), + 0 => b"root".to_vec(), // Τ (Upper case Tau) + 1 => b"apex".to_vec(), // α (Alpha) + 2 => b"omron".to_vec(), // β (Beta) + 3 => b"templar".to_vec(), // γ (Gamma) + 4 => b"targon".to_vec(), // δ (Delta) + 5 => b"kaito".to_vec(), // ε (Epsilon) + 6 => b"infinite".to_vec(), // ζ (Zeta) + 7 => b"subvortex".to_vec(), // η (Eta) + 8 => b"ptn".to_vec(), // θ (Theta) + 9 => b"pretrain".to_vec(), // ι (Iota) + 10 => b"sturdy".to_vec(), // κ (Kappa) + 11 => b"dippy".to_vec(), // λ (Lambda) + 12 => b"horde".to_vec(), // μ (Mu) + 13 => b"dataverse".to_vec(), // ν (Nu) + 14 => b"palaidn".to_vec(), // ξ (Xi) + 15 => b"deval".to_vec(), // ο (Omicron) + 16 => b"bitads".to_vec(), // π (Pi) + 17 => b"3gen".to_vec(), // ρ (Rho) + 18 => b"cortex".to_vec(), // σ (Sigma) + 19 => b"inference".to_vec(), // t (Tau) + 20 => b"bitagent".to_vec(), // υ (Upsilon) + 21 => b"any-any".to_vec(), // φ (Phi) + 22 => b"meta".to_vec(), // χ (Chi) + 23 => b"social".to_vec(), // ψ (Psi) + 24 => b"omega".to_vec(), // ω (Omega) + 25 => b"protein".to_vec(), // א (Aleph) + 26 => b"alchemy".to_vec(), // ב (Bet) + 27 => b"compute".to_vec(), // ג (Gimel) + 28 => b"oracle".to_vec(), // ד (Dalet) + 29 => b"coldint".to_vec(), // ה (He) + 30 => b"bet".to_vec(), // ו (Vav) + 31 => b"naschain".to_vec(), // ז (Zayin) + 32 => b"itsai".to_vec(), // ח (Het) + 33 => b"ready".to_vec(), // ט (Tet) + 34 => b"mind".to_vec(), // י (Yod) + 35 => b"logic".to_vec(), // ך (Final Kaf) + 36 => b"automata".to_vec(), // כ (Kaf) + 37 => b"tuning".to_vec(), // ל (Lamed) + 38 => b"distributed".to_vec(), // ם (Final Mem) + 39 => b"edge".to_vec(), // מ (Mem) + 40 => b"chunk".to_vec(), // ן (Final Nun) + 41 => b"sportsensor".to_vec(), // נ (Nun) + 42 => b"masa".to_vec(), // ס (Samekh) + 43 => b"graphite".to_vec(), // ע (Ayin) + 44 => b"score".to_vec(), // ף (Final Pe) + 45 => b"gen42".to_vec(), // פ (Pe) + 46 => b"neural".to_vec(), // ץ (Final Tsadi) + 47 => b"condense".to_vec(), // צ (Tsadi) + 48 => b"nextplace".to_vec(), // ק (Qof) + 49 => b"automl".to_vec(), // ר (Resh) + 50 => b"audio".to_vec(), // ש (Shin) + 51 => b"celium".to_vec(), // ת (Tav) + 52 => b"dojo".to_vec(), // ا (Alif) + 53 => b"frontier".to_vec(), // ب (Ba) + 54 => b"safescan".to_vec(), // ت (Ta) + 55 => b"unknown".to_vec(), // ث (Tha) + 56 => b"gradients".to_vec(), // ج (Jim) + 57 => b"gaia".to_vec(), // ح (Ha) + 58 => b"dippy-speach".to_vec(), // خ (Kha) + 59 => b"agent-arena".to_vec(), // د (Dal) + 60 => b"unknown".to_vec(), // ذ (Dhal) + 61 => b"red team".to_vec(), // ر (Ra) + 62 => b"agentao".to_vec(), // ز (Zay) + 63 => b"lean-in".to_vec(), // س (Sin) + 64 => b"chutes".to_vec(), // ش (Shin) 65 => b"sad".to_vec(), 66 => b"dad".to_vec(), 67 => b"ta".to_vec(), @@ -126,9 +126,207 @@ impl Pallet { 116 => b"dze".to_vec(), 117 => b"alfa".to_vec(), 118 => b"alfas".to_vec(), - 119 => b"vidac".to_vec(), - // Skipping lines 120-318 for brevity as they were omitted in context - 319 => b"kana_a".to_vec(), + 119 => b"vida".to_vec(), // Ⲃ (Vida, 119) + 120 => b"vida_small".to_vec(), // ⲃ (Small Vida, 120) + 121 => b"gamma".to_vec(), // Ⲅ (Gamma, 121) + 122 => b"gamma_small".to_vec(), // ⲅ (Small Gamma, 122) + 123 => b"brahmi_a".to_vec(), // 𑀀 (A, 123) + 124 => b"brahmi_aa".to_vec(), // 𑀁 (Aa, 124) + 125 => b"brahmi_i".to_vec(), // 𑀂 (I, 125) + 126 => b"brahmi_ii".to_vec(), // 𑀃 (Ii, 126) + 127 => b"brahmi_u".to_vec(), // 𑀅 (U, 127) + 128 => b"tifinagh_ya".to_vec(), // ⴰ (Ya, 128) + 129 => b"tifinagh_yab".to_vec(), // ⴱ (Yab, 129) + 130 => b"tifinagh_yabh".to_vec(), // ⴲ (Yabh, 130) + 131 => b"tifinagh_yag".to_vec(), // ⴳ (Yag, 131) + 132 => b"tifinagh_yagh".to_vec(), // ⴴ (Yagh, 132) + 133 => b"tifinagh_yaj".to_vec(), // ⴵ (Yaj, 133) + 134 => b"glagolitic_az".to_vec(), // Ⰰ (Az, 134) + 135 => b"glagolitic_buky".to_vec(), // Ⰱ (Buky, 135) + 136 => b"glagolitic_vede".to_vec(), // Ⰲ (Vede, 136) + 137 => b"glagolitic_glagoli".to_vec(), // Ⰳ (Glagoli, 137) + 138 => b"glagolitic_dobro".to_vec(), // Ⰴ (Dobro, 138) + 139 => b"glagolitic_yest".to_vec(), // Ⰵ (Yest, 139) + 140 => b"glagolitic_zhivete".to_vec(), // Ⰶ (Zhivete, 140) + 141 => b"glagolitic_zemlja".to_vec(), // Ⰷ (Zemlja, 141) + 142 => b"glagolitic_izhe".to_vec(), // Ⰸ (Izhe, 142) + 143 => b"glagolitic_initial_izhe".to_vec(), // Ⰹ (Initial Izhe, 143) + 144 => b"glagolitic_i".to_vec(), // Ⰺ (I, 144) + 145 => b"glagolitic_djerv".to_vec(), // Ⰻ (Djerv, 145) + 146 => b"glagolitic_kako".to_vec(), // Ⰼ (Kako, 146) + 147 => b"glagolitic_ljudije".to_vec(), // Ⰽ (Ljudije, 147) + 148 => b"glagolitic_myse".to_vec(), // Ⰾ (Myse, 148) + 149 => b"glagolitic_nash".to_vec(), // Ⰿ (Nash, 149) + 150 => b"glagolitic_on".to_vec(), // Ⱀ (On, 150) + 151 => b"glagolitic_pokoj".to_vec(), // Ⱁ (Pokoj, 151) + 152 => b"glagolitic_rtsy".to_vec(), // Ⱂ (Rtsy, 152) + 153 => b"glagolitic_slovo".to_vec(), // Ⱃ (Slovo, 153) + 154 => b"glagolitic_tvrido".to_vec(), // Ⱄ (Tvrido, 154) + 155 => b"glagolitic_uku".to_vec(), // Ⱅ (Uku, 155) + 156 => b"glagolitic_fert".to_vec(), // Ⱆ (Fert, 156) + 157 => b"glagolitic_xrivi".to_vec(), // Ⱇ (Xrivi, 157) + 158 => b"glagolitic_ot".to_vec(), // Ⱈ (Ot, 158) + 159 => b"glagolitic_cy".to_vec(), // Ⱉ (Cy, 159) + 160 => b"glagolitic_shcha".to_vec(), // Ⱊ (Shcha, 160) + 161 => b"glagolitic_er".to_vec(), // Ⱋ (Er, 161) + 162 => b"glagolitic_yeru".to_vec(), // Ⱌ (Yeru, 162) + 163 => b"glagolitic_small_yer".to_vec(), // Ⱍ (Small Yer, 163) + 164 => b"glagolitic_yo".to_vec(), // Ⱎ (Yo, 164) + 165 => b"glagolitic_yu".to_vec(), // Ⱏ (Yu, 165) + 166 => b"glagolitic_ja".to_vec(), // Ⱐ (Ja, 166) + 167 => b"thai_ko_kai".to_vec(), // ก (Ko Kai, 167) + 168 => b"thai_kho_khai".to_vec(), // ข (Kho Khai, 168) + 169 => b"thai_kho_khuat".to_vec(), // ฃ (Kho Khuat, 169) + 170 => b"thai_kho_khon".to_vec(), // ค (Kho Khon, 170) + 171 => b"thai_kho_rakhang".to_vec(), // ฅ (Kho Rakhang, 171) + 172 => b"thai_kho_khwai".to_vec(), // ฆ (Kho Khwai, 172) + 173 => b"thai_ngo_ngu".to_vec(), // ง (Ngo Ngu, 173) + 174 => b"thai_cho_chan".to_vec(), // จ (Cho Chan, 174) + 175 => b"thai_cho_ching".to_vec(), // ฉ (Cho Ching, 175) + 176 => b"thai_cho_chang".to_vec(), // ช (Cho Chang, 176) + 177 => b"thai_so_so".to_vec(), // ซ (So So, 177) + 178 => b"thai_cho_choe".to_vec(), // ฌ (Cho Choe, 178) + 179 => b"thai_yo_ying".to_vec(), // ญ (Yo Ying, 179) + 180 => b"thai_do_chada".to_vec(), // ฎ (Do Chada, 180) + 181 => b"thai_to_patak".to_vec(), // ฏ (To Patak, 181) + 182 => b"thai_tho_than".to_vec(), // ฐ (Tho Than, 182) + 183 => b"thai_tho_nangmontho".to_vec(), // ฑ (Tho Nangmontho, 183) + 184 => b"thai_tho_phuthao".to_vec(), // ฒ (Tho Phuthao, 184) + 185 => b"thai_no_nen".to_vec(), // ณ (No Nen, 185) + 186 => b"thai_do_dek".to_vec(), // ด (Do Dek, 186) + 187 => b"thai_to_tao".to_vec(), // ต (To Tao, 187) + 188 => b"thai_tho_thung".to_vec(), // ถ (Tho Thung, 188) + 189 => b"thai_tho_thahan".to_vec(), // ท (Tho Thahan, 189) + 190 => b"thai_tho_thong".to_vec(), // ธ (Tho Thong, 190) + 191 => b"thai_no_nu".to_vec(), // น (No Nu, 191) + 192 => b"thai_bo_baimai".to_vec(), // บ (Bo Baimai, 192) + 193 => b"thai_po_pla".to_vec(), // ป (Po Pla, 193) + 194 => b"thai_pho_phung".to_vec(), // ผ (Pho Phung, 194) + 195 => b"thai_fo_fa".to_vec(), // ฝ (Fo Fa, 195) + 196 => b"thai_pho_phan".to_vec(), // พ (Pho Phan, 196) + 197 => b"thai_fo_fan".to_vec(), // ฟ (Fo Fan, 197) + 198 => b"thai_pho_samphao".to_vec(), // ภ (Pho Samphao, 198) + 199 => b"thai_mo_ma".to_vec(), // ม (Mo Ma, 199) + 200 => b"thai_yo_yak".to_vec(), // ย (Yo Yak, 200) + 201 => b"thai_ro_rua".to_vec(), // ร (Ro Rua, 201) + 202 => b"thai_lo_ling".to_vec(), // ล (Lo Ling, 202) + 203 => b"thai_wo_waen".to_vec(), // ว (Wo Waen, 203) + 204 => b"thai_so_sala".to_vec(), // ศ (So Sala, 204) + 205 => b"thai_so_rusi".to_vec(), // ษ (So Rusi, 205) + 206 => b"thai_so_sua".to_vec(), // ส (So Sua, 206) + 207 => b"thai_ho_hip".to_vec(), // ห (Ho Hip, 207) + 208 => b"thai_lo_chula".to_vec(), // ฬ (Lo Chula, 208) + 209 => b"thai_o_ang".to_vec(), // อ (O Ang, 209) + 210 => b"thai_ho_nokhuk".to_vec(), // ฮ (Ho Nokhuk, 210) + 211 => b"hangul_giyeok".to_vec(), // ㄱ (Giyeok, 211) + 212 => b"hangul_nieun".to_vec(), // ㄴ (Nieun, 212) + 213 => b"hangul_digeut".to_vec(), // ㄷ (Digeut, 213) + 214 => b"hangul_rieul".to_vec(), // ㄹ (Rieul, 214) + 215 => b"hangul_mieum".to_vec(), // ㅁ (Mieum, 215) + 216 => b"hangul_bieup".to_vec(), // ㅂ (Bieup, 216) + 217 => b"hangul_siot".to_vec(), // ㅅ (Siot, 217) + 218 => b"hangul_ieung".to_vec(), // ㅇ (Ieung, 218) + 219 => b"hangul_jieut".to_vec(), // ㅈ (Jieut, 219) + 220 => b"hangul_chieut".to_vec(), // ㅊ (Chieut, 220) + 221 => b"hangul_kieuk".to_vec(), // ㅋ (Kieuk, 221) + 222 => b"hangul_tieut".to_vec(), // ㅌ (Tieut, 222) + 223 => b"hangul_pieup".to_vec(), // ㅍ (Pieup, 223) + 224 => b"hangul_hieut".to_vec(), // ㅎ (Hieut, 224) + 225 => b"hangul_a".to_vec(), // ㅏ (A, 225) + 226 => b"hangul_ae".to_vec(), // ㅐ (Ae, 226) + 227 => b"hangul_ya".to_vec(), // ㅑ (Ya, 227) + 228 => b"hangul_yae".to_vec(), // ㅒ (Yae, 228) + 229 => b"hangul_eo".to_vec(), // ㅓ (Eo, 229) + 230 => b"hangul_e".to_vec(), // ㅔ (E, 230) + 231 => b"hangul_yeo".to_vec(), // ㅕ (Yeo, 231) + 232 => b"hangul_ye".to_vec(), // ㅖ (Ye, 232) + 233 => b"hangul_o".to_vec(), // ㅗ (O, 233) + 234 => b"hangul_wa".to_vec(), // ㅘ (Wa, 234) + 235 => b"hangul_wae".to_vec(), // ㅙ (Wae, 235) + 236 => b"hangul_oe".to_vec(), // ㅚ (Oe, 236) + 237 => b"hangul_yo".to_vec(), // ㅛ (Yo, 237) + 238 => b"hangul_u".to_vec(), // ㅜ (U, 238) + 239 => b"hangul_weo".to_vec(), // ㅝ (Weo, 239) + 240 => b"hangul_we".to_vec(), // ㅞ (We, 240) + 241 => b"hangul_wi".to_vec(), // ㅟ (Wi, 241) + 242 => b"hangul_yu".to_vec(), // ㅠ (Yu, 242) + 243 => b"hangul_eu".to_vec(), // ㅡ (Eu, 243) + 244 => b"hangul_ui".to_vec(), // ㅢ (Ui, 244) + 245 => b"hangul_i".to_vec(), // ㅣ (I, 245) + 246 => b"ethiopic_glottal_a".to_vec(), // አ (Glottal A, 246) + 247 => b"ethiopic_glottal_u".to_vec(), // ኡ (Glottal U, 247) + 248 => b"ethiopic_glottal_i".to_vec(), // ኢ (Glottal I, 248) + 249 => b"ethiopic_glottal_aa".to_vec(), // ኣ (Glottal Aa, 249) + 250 => b"ethiopic_glottal_e".to_vec(), // ኤ (Glottal E, 250) + 251 => b"ethiopic_glottal_ie".to_vec(), // እ (Glottal Ie, 251) + 252 => b"ethiopic_glottal_o".to_vec(), // ኦ (Glottal O, 252) + 253 => b"ethiopic_glottal_wa".to_vec(), // ኧ (Glottal Wa, 253) + 254 => b"ethiopic_wa".to_vec(), // ወ (Wa, 254) + 255 => b"ethiopic_wu".to_vec(), // ዉ (Wu, 255) + 256 => b"ethiopic_wi".to_vec(), // ዊ (Wi, 256) + 257 => b"ethiopic_waa".to_vec(), // ዋ (Waa, 257) + 258 => b"ethiopic_we".to_vec(), // ዌ (We, 258) + 259 => b"ethiopic_wye".to_vec(), // ው (Wye, 259) + 260 => b"ethiopic_wo".to_vec(), // ዎ (Wo, 260) + 261 => b"ethiopic_ko".to_vec(), // ኰ (Ko, 261) + 262 => b"ethiopic_ku".to_vec(), // ኱ (Ku, 262) + 263 => b"ethiopic_ki".to_vec(), // ኲ (Ki, 263) + 264 => b"ethiopic_kua".to_vec(), // ኳ (Kua, 264) + 265 => b"ethiopic_ke".to_vec(), // ኴ (Ke, 265) + 266 => b"ethiopic_kwe".to_vec(), // ኵ (Kwe, 266) + 267 => b"ethiopic_ko_alt".to_vec(), // ኶ (Ko, 267) + 268 => b"ethiopic_go".to_vec(), // ጐ (Go, 268) + 269 => b"ethiopic_gu".to_vec(), // ጑ (Gu, 269) + 270 => b"ethiopic_gi".to_vec(), // ጒ (Gi, 270) + 271 => b"ethiopic_gua".to_vec(), // መ (Gua, 271) + 272 => b"ethiopic_ge".to_vec(), // ጔ (Ge, 272) + 273 => b"ethiopic_gwe".to_vec(), // ጕ (Gwe, 273) + 274 => b"ethiopic_go_alt".to_vec(), // ጖ (Go, 274) + 275 => b"devanagari_a".to_vec(), // अ (A, 275) + 276 => b"devanagari_aa".to_vec(), // आ (Aa, 276) + 277 => b"devanagari_i".to_vec(), // इ (I, 277) + 278 => b"devanagari_ii".to_vec(), // ई (Ii, 278) + 279 => b"devanagari_u".to_vec(), // उ (U, 279) + 280 => b"devanagari_uu".to_vec(), // ऊ (Uu, 280) + 281 => b"devanagari_r".to_vec(), // ऋ (R, 281) + 282 => b"devanagari_e".to_vec(), // ए (E, 282) + 283 => b"devanagari_ai".to_vec(), // ऐ (Ai, 283) + 284 => b"devanagari_o".to_vec(), // ओ (O, 284) + 285 => b"devanagari_au".to_vec(), // औ (Au, 285) + 286 => b"devanagari_ka".to_vec(), // क (Ka, 286) + 287 => b"devanagari_kha".to_vec(), // ख (Kha, 287) + 288 => b"devanagari_ga".to_vec(), // ग (Ga, 288) + 289 => b"devanagari_gha".to_vec(), // घ (Gha, 289) + 290 => b"devanagari_nga".to_vec(), // ङ (Nga, 290) + 291 => b"devanagari_cha".to_vec(), // च (Cha, 291) + 292 => b"devanagari_chha".to_vec(), // छ (Chha, 292) + 293 => b"devanagari_ja".to_vec(), // ज (Ja, 293) + 294 => b"devanagari_jha".to_vec(), // झ (Jha, 294) + 295 => b"devanagari_nya".to_vec(), // ञ (Nya, 295) + 296 => b"devanagari_ta".to_vec(), // ट (Ta, 296) + 297 => b"devanagari_tha".to_vec(), // ठ (Tha, 297) + 298 => b"devanagari_da".to_vec(), // ड (Da, 298) + 299 => b"devanagari_dha".to_vec(), // ढ (Dha, 299) + 300 => b"devanagari_na".to_vec(), // ण (Na, 300) + 301 => b"devanagari_ta_alt".to_vec(), // त (Ta, 301) + 302 => b"devanagari_tha_alt".to_vec(), // थ (Tha, 302) + 303 => b"devanagari_da_alt".to_vec(), // द (Da, 303) + 304 => b"devanagari_dha_alt".to_vec(), // ध (Dha, 304) + 305 => b"devanagari_na_alt".to_vec(), // न (Na, 305) + 306 => b"devanagari_pa".to_vec(), // प (Pa, 306) + 307 => b"devanagari_pha".to_vec(), // फ (Pha, 307) + 308 => b"devanagari_ba".to_vec(), // ब (Ba, 308) + 309 => b"devanagari_bha".to_vec(), // भ (Bha, 309) + 310 => b"devanagari_ma".to_vec(), // म (Ma, 310) + 311 => b"devanagari_ya".to_vec(), // य (Ya, 311) + 312 => b"devanagari_ra".to_vec(), // र (Ra, 312) + 313 => b"devanagari_la".to_vec(), // ल (La, 313) + 314 => b"devanagari_va".to_vec(), // व (Va, 314) + 315 => b"devanagari_sha".to_vec(), // श (Sha, 315) + 316 => b"devanagari_ssa".to_vec(), // ष (Ssa, 316) + 317 => b"devanagari_sa".to_vec(), // स (Sa, 317) + 318 => b"devanagari_ha".to_vec(), // ह (Ha, 318) + 319 => b"katakana_a".to_vec(), // ア (A, 319) 320 => b"kana_i".to_vec(), 321 => b"kana_u".to_vec(), 322 => b"kana_e".to_vec(), diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 075538421..b505cd58e 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/children.rs b/pallets/subtensor/src/tests/children.rs index 7e30a2251..b6181e03c 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -1554,18 +1554,20 @@ fn test_set_network_max_stake_update() { #[test] fn test_children_stake_values() { new_test_ext(1).execute_with(|| { + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let coldkey = U256::from(1); let hotkey = U256::from(2); let child1 = U256::from(3); let child2 = U256::from(4); let child3 = U256::from(5); - let netuid: u16 = 1; let proportion1: u64 = u64::MAX / 4; let proportion2: u64 = u64::MAX / 4; let proportion3: u64 = u64::MAX / 4; // Add network and register hotkey - add_network(netuid, 13, 0); SubtensorModule::set_max_registrations_per_block(netuid, 4); SubtensorModule::set_target_registrations_per_interval(netuid, 4); register_ok_neuron(netuid, hotkey, coldkey, 0); @@ -1580,10 +1582,9 @@ fn test_children_stake_values() { ); // Set multiple children with proportions. - mock_set_children( - &coldkey, - &hotkey, + mock_set_children_no_epochs( netuid, + &hotkey, &[ (proportion1, child1), (proportion2, child2), @@ -1830,20 +1831,23 @@ fn test_get_stake_for_hotkey_on_subnet_multiple_coldkeys() { #[test] fn test_get_stake_for_hotkey_on_subnet_single_parent_child() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); let parent = U256::from(1); let child = U256::from(2); let coldkey = U256::from(3); - - add_network(netuid, 1, 0); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); register_ok_neuron(netuid, parent, coldkey, 0); register_ok_neuron(netuid, child, coldkey, 0); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &parent, &coldkey, netuid, 1000, + &parent, + &coldkey, + netuid, + 1_000_000_000, ); - mock_set_children(&coldkey, &parent, netuid, &[(u64::MAX, child)]); + mock_set_children_no_epochs(netuid, &parent, &[(u64::MAX, child)]); assert_eq!( SubtensorModule::get_inherited_for_hotkey_on_subnet(&parent, netuid), @@ -1851,7 +1855,7 @@ fn test_get_stake_for_hotkey_on_subnet_single_parent_child() { ); assert_eq!( SubtensorModule::get_inherited_for_hotkey_on_subnet(&child, netuid), - 1000 + 1_000_000_000 ); }); } @@ -1867,13 +1871,15 @@ fn test_get_stake_for_hotkey_on_subnet_single_parent_child() { #[test] fn test_get_stake_for_hotkey_on_subnet_multiple_parents_single_child() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let parent1 = U256::from(1); let parent2 = U256::from(2); let child = U256::from(3); let coldkey = U256::from(4); - add_network(netuid, 1, 0); register_ok_neuron(netuid, parent1, coldkey, 0); register_ok_neuron(netuid, parent2, coldkey, 0); register_ok_neuron(netuid, child, coldkey, 0); @@ -1885,8 +1891,8 @@ fn test_get_stake_for_hotkey_on_subnet_multiple_parents_single_child() { &parent2, &coldkey, netuid, 2000, ); - mock_set_children(&coldkey, &parent1, netuid, &[(u64::MAX / 2, child)]); - mock_set_children(&coldkey, &parent2, netuid, &[(u64::MAX / 2, child)]); + mock_set_children_no_epochs(netuid, &parent1, &[(u64::MAX / 2, child)]); + mock_set_children_no_epochs(netuid, &parent2, &[(u64::MAX / 2, child)]); close( SubtensorModule::get_inherited_for_hotkey_on_subnet(&parent1, netuid), @@ -1920,13 +1926,15 @@ fn test_get_stake_for_hotkey_on_subnet_multiple_parents_single_child() { #[test] fn test_get_stake_for_hotkey_on_subnet_single_parent_multiple_children() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let parent = U256::from(1); let child1 = U256::from(2); let child2 = U256::from(3); let coldkey = U256::from(4); - add_network(netuid, 1, 0); register_ok_neuron(netuid, parent, coldkey, 0); register_ok_neuron(netuid, child1, coldkey, 0); register_ok_neuron(netuid, child2, coldkey, 0); @@ -1939,10 +1947,9 @@ fn test_get_stake_for_hotkey_on_subnet_single_parent_multiple_children() { total_stake, ); - mock_set_children( - &coldkey, - &parent, + mock_set_children_no_epochs( netuid, + &parent, &[(u64::MAX / 3, child1), (u64::MAX / 3, child2)], ); @@ -1983,13 +1990,15 @@ fn test_get_stake_for_hotkey_on_subnet_single_parent_multiple_children() { #[test] fn test_get_stake_for_hotkey_on_subnet_edge_cases() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let parent = U256::from(1); let child1 = U256::from(2); let child2 = U256::from(3); let coldkey = U256::from(4); - add_network(netuid, 1, 0); register_ok_neuron(netuid, parent, coldkey, 0); register_ok_neuron(netuid, child1, coldkey, 0); register_ok_neuron(netuid, child2, coldkey, 0); @@ -2007,12 +2016,7 @@ fn test_get_stake_for_hotkey_on_subnet_edge_cases() { ); // Test with 0% and 100% stake allocation - mock_set_children( - &coldkey, - &parent, - netuid, - &[(0, child1), (u64::MAX, child2)], - ); + mock_set_children_no_epochs(netuid, &parent, &[(0, child1), (u64::MAX, child2)]); let parent_stake = SubtensorModule::get_inherited_for_hotkey_on_subnet(&parent, netuid); let child1_stake = SubtensorModule::get_inherited_for_hotkey_on_subnet(&child1, netuid); @@ -2051,7 +2055,10 @@ fn test_get_stake_for_hotkey_on_subnet_edge_cases() { #[test] fn test_get_stake_for_hotkey_on_subnet_complex_hierarchy() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let parent = U256::from(1); let child1 = U256::from(2); let child2 = U256::from(3); @@ -2061,7 +2068,6 @@ fn test_get_stake_for_hotkey_on_subnet_complex_hierarchy() { let coldkey_child2 = U256::from(7); let coldkey_grandchild = U256::from(8); - add_network(netuid, 1, 0); SubtensorModule::set_max_registrations_per_block(netuid, 1000); SubtensorModule::set_target_registrations_per_interval(netuid, 1000); register_ok_neuron(netuid, parent, coldkey_parent, 0); @@ -2096,10 +2102,9 @@ fn test_get_stake_for_hotkey_on_subnet_complex_hierarchy() { ); // Step 1: Set children for parent - mock_set_children( - &coldkey_parent, - &parent, + mock_set_children_no_epochs( netuid, + &parent, &[(u64::MAX / 2, child1), (u64::MAX / 2, child2)], ); @@ -2133,7 +2138,7 @@ fn test_get_stake_for_hotkey_on_subnet_complex_hierarchy() { close(child2_stake_1, 499, 10, "Child2 should have 499 stake"); // Step 2: Set children for child1 - mock_set_children(&coldkey_child1, &child1, netuid, &[(u64::MAX, grandchild)]); + mock_set_children_no_epochs(netuid, &child1, &[(u64::MAX, grandchild)]); log::info!("After setting child1's children:"); log::info!( @@ -2699,12 +2704,14 @@ fn test_set_children_rate_limit_fail_then_succeed() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --test children -- test_childkey_set_weights_single_parent --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_childkey_set_weights_single_parent --exact --show-output --nocapture #[test] fn test_childkey_set_weights_single_parent() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; - add_network(netuid, 1, 0); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + Tempo::::insert(netuid, 1); // Define hotkeys let parent: U256 = U256::from(1); @@ -2744,14 +2751,14 @@ fn test_childkey_set_weights_single_parent() { SubtensorModule::set_weights_set_rate_limit(netuid, 0); // Set parent-child relationship - mock_set_children(&coldkey_parent, &parent, netuid, &[(u64::MAX, child)]); + mock_set_children_no_epochs(netuid, &parent, &[(u64::MAX, child)]); - step_block(7200 + 1); // Set weights on the child using the weight_setter account let origin = RuntimeOrigin::signed(weight_setter); let uids: Vec = vec![1]; // Only set weight for the child (UID 1) let values: Vec = vec![u16::MAX]; // Use maximum value for u16 let version_key = SubtensorModule::get_weights_version_key(netuid); + ValidatorPermit::::insert(netuid, vec![true, true, true, true]); assert_ok!(SubtensorModule::set_weights( origin, netuid, @@ -2810,8 +2817,9 @@ fn test_childkey_set_weights_single_parent() { fn test_set_weights_no_parent() { // Verify that a regular key without a parent delegation is effected by the minimum stake requirements new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; - add_network(netuid, 1, 0); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); let hotkey: U256 = U256::from(2); let spare_hk: U256 = U256::from(3); @@ -3096,228 +3104,6 @@ fn test_childkey_take_drain_validator_take() { }); } -// 43: Test emission distribution between a childkey and multiple parents -// This test verifies the correct distribution of emissions between a child and multiple parents: -// - Sets up a network with two parents, a child, and a weight setter -// - Establishes parent-child relationships with different stake proportions -// - Sets weights on the child and one parent -// - Runs an epoch -// - Checks the emission distribution among parents, child, and weight setter -// - Verifies that all parties received emissions and the total stake increased correctly -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_childkey_multiple_parents_emission --exact --nocapture -#[test] -fn test_childkey_multiple_parents_emission() { - new_test_ext(1).execute_with(|| { - let subnet_owner_coldkey = U256::from(1001); - let subnet_owner_hotkey: U256 = U256::from(1002); - let netuid = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); - Tempo::::insert(netuid, 10); // run epoch every 10 blocks - // Set subnet owner cut to 0 - SubtensorModule::set_subnet_owner_cut(0); - SubtensorModule::set_tao_weight(0); // No TAO weight - - // Set registration parameters and emission tempo - SubtensorModule::set_max_registrations_per_block(netuid, 1000); - SubtensorModule::set_target_registrations_per_interval(netuid, 1000); - - // Define hotkeys and coldkeys - let parent1: U256 = U256::from(1); - let parent2: U256 = U256::from(2); - let child: U256 = U256::from(3); - let weight_setter: U256 = U256::from(4); - let coldkey_parent1: U256 = U256::from(100); - let coldkey_parent2: U256 = U256::from(101); - let coldkey_child: U256 = U256::from(102); - let coldkey_weight_setter: U256 = U256::from(103); - - // Register neurons and add initial stakes - let initial_stakes: Vec<(bool, U256, U256, u64)> = vec![ - (false, coldkey_parent1, parent1, 200_000_000), - (true, coldkey_parent2, parent2, 150_000_000), - (true, coldkey_child, child, 20_000_000), - (true, coldkey_weight_setter, weight_setter, 100_000_000), - ]; - - initial_stakes - .iter() - .for_each(|(register, coldkey, hotkey, stake)| { - SubtensorModule::add_balance_to_coldkey_account(coldkey, *stake); - if *register { - // Register a neuron - register_ok_neuron(netuid, *hotkey, *coldkey, 0); - } else { - // Just create hotkey account - SubtensorModule::create_account_if_non_existent(coldkey, hotkey); - } - assert_ok!(SubtensorModule::add_stake( - RuntimeOrigin::signed(*coldkey), - *hotkey, - netuid, - *stake - )); - }); - - SubtensorModule::set_weights_set_rate_limit(netuid, 0); - step_block(2); - - // Set parent-child relationships - mock_schedule_children(&coldkey_parent1, &parent1, netuid, &[(u64::MAX, child)]); - mock_schedule_children(&coldkey_parent2, &parent2, netuid, &[(u64::MAX / 2, child)]); - wait_and_set_pending_children(netuid); - ChildkeyTake::::insert(child, netuid, u16::MAX / 5); - - // Set pending emission to 0 - PendingEmission::::insert(netuid, 0); - - let initial_actual_stakes: Vec = initial_stakes - .iter() - .map(|(_register, coldkey, hotkey, _stake)| { - // Return actual stake - SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) - }) - .collect(); - - // Set weights (subnet owner is uid 0, ignore him) - let uids: Vec = vec![1, 2]; - let values: Vec = vec![65354, 65354]; - let version_key = SubtensorModule::get_weights_version_key(netuid); - ValidatorPermit::::insert(netuid, vec![true, true, true, true]); - assert_ok!(SubtensorModule::set_weights( - RuntimeOrigin::signed(weight_setter), - netuid, - uids, - values, - version_key - )); - log::info!("Running an epoch"); - // Wait until epoch - let start_block = SubtensorModule::get_current_block_as_u64(); - loop { - let current_block = SubtensorModule::get_current_block_as_u64(); - if SubtensorModule::should_run_epoch(netuid, current_block) { - step_block(1); - break; - } - step_block(1); - } - // We substract one because we are running it *after* the epoch, so we don't expect it to effect the emission. - let blocks_passed = SubtensorModule::get_current_block_as_u64() - start_block - 1; - log::info!("blocks_passed: {:?}", blocks_passed); - let alpha_block_emission: u64 = SubtensorModule::get_block_emission_for_issuance( - SubtensorModule::get_alpha_issuance(netuid), - ) - .unwrap_or(0); - let (_, _, per_block_emission) = SubtensorModule::get_dynamic_tao_emission( - netuid, - SubtensorModule::get_block_emission().unwrap_or(0), - alpha_block_emission, - ); - let total_emission = per_block_emission * blocks_passed; - - // Check emission distribution - let stakes: Vec<(U256, U256, &str)> = vec![ - (coldkey_parent1, parent1, "Parent1"), - (coldkey_parent2, parent2, "Parent2"), - (coldkey_child, child, "Child"), - (coldkey_weight_setter, weight_setter, "Weight setter"), - ]; - - for (coldkey, hotkey, name) in stakes.iter() { - let stake_on_subnet = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, coldkey, netuid, - ); - log::debug!("{} stake on subnet: {:?}", name, stake_on_subnet); - } - - let parent1_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &parent1, - &coldkey_parent1, - netuid, - ); - let parent2_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &parent2, - &coldkey_parent2, - netuid, - ); - let child_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &child, - &coldkey_child, - netuid, - ); - let weight_setter_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( - &weight_setter, - &coldkey_weight_setter, - netuid, - ); - - assert!( - parent1_stake > initial_actual_stakes[0], - "Parent1 should have received emission" - ); - assert!( - parent2_stake > initial_actual_stakes[1], - "Parent2 should have received emission" - ); - assert!( - child_stake > initial_actual_stakes[2], - "Child should have received emission" - ); - assert!( - weight_setter_stake > initial_actual_stakes[3], - "Weight setter should have received emission" - ); - - // Check individual stake increases - let parent1_stake_increase = parent1_stake - initial_actual_stakes[0]; - let parent2_stake_increase = parent2_stake - initial_actual_stakes[1]; - let child_stake_increase = child_stake - initial_actual_stakes[2]; - - log::debug!( - "Stake increases - Parent1: {}, Parent2: {}, Child: {}", - parent1_stake_increase, - parent2_stake_increase, - child_stake_increase - ); - - // Assert that all neurons received some emission - assert!( - parent1_stake_increase > 0, - "Parent1 should have received some emission" - ); - assert!( - parent2_stake_increase > 0, - "Parent2 should have received some emission" - ); - assert!( - child_stake_increase > 0, - "Child should have received some emission" - ); - - let mut total_stake_on_subnet = 0; - let hks = [parent1, parent2, child, weight_setter]; - for (hk, net, alpha) in TotalHotkeyAlpha::::iter() { - if hks.contains(&hk) && net == netuid { - total_stake_on_subnet += alpha; - } else { - log::info!("hk: {:?}, net: {:?}, alpha: {:?}", hk, net, alpha); - } - } - - log::info!("total_stake_on_subnet: {:?}", total_stake_on_subnet); - log::info!("total_stake: {:?}", TotalStake::::get()); - log::info!("total_emission: {:?}", total_emission); - // Check that the total stake has increased by the emission amount - // Allow 1% slippage - let total_stake = parent1_stake + parent2_stake + child_stake + weight_setter_stake; - let initial_total_stake: u64 = initial_actual_stakes.iter().sum::(); - assert_abs_diff_eq!( - total_stake, - initial_total_stake + total_emission, - epsilon = total_emission / 100, - ); - }); -} - // 44: Test with a chain of parent-child relationships (e.g., A -> B -> C) // This test verifies the correct distribution of emissions in a chain of parent-child relationships: // - Sets up a network with three neurons A, B, and C in a chain (A -> B -> C) @@ -3330,8 +3116,15 @@ fn test_childkey_multiple_parents_emission() { #[test] fn test_parent_child_chain_emission() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; - add_network(netuid, 1, 0); + let subnet_owner_coldkey = U256::from(1001); + let subnet_owner_hotkey = U256::from(1002); + let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + Tempo::::insert(netuid, 1); + + // Setup large LPs to prevent slippage + SubnetTAO::::insert(netuid, 1_000_000_000_000_000); + SubnetAlphaIn::::insert(netuid, 1_000_000_000_000_000); + // Set owner cut to 0 SubtensorModule::set_subnet_owner_cut(0_u16); @@ -3354,7 +3147,10 @@ fn test_parent_child_chain_emission() { SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); // Swap to alpha - let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); + let stake_a = 300_000_000_000_u64; + let stake_b = 100_000_000_000_u64; + let stake_c = 50_000_000_000_u64; + let total_tao: I96F32 = I96F32::from_num(stake_a + stake_b + stake_c); let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( netuid, total_tao.to_num::(), @@ -3366,19 +3162,19 @@ fn test_parent_child_chain_emission() { &hotkey_a, &coldkey_a, netuid, - (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::(), + (total_alpha * I96F32::from_num(stake_a) / total_tao).saturating_to_num::(), ); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_b, &coldkey_b, netuid, - (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::(), + (total_alpha * I96F32::from_num(stake_b) / total_tao).saturating_to_num::(), ); SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey_c, &coldkey_c, netuid, - (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::(), + (total_alpha * I96F32::from_num(stake_c) / total_tao).saturating_to_num::(), ); // Get old stakes @@ -3396,17 +3192,16 @@ fn test_parent_child_chain_emission() { log::info!("rel_stake_a: {:?}", rel_stake_a); // 0.6666 -> 2/3 log::info!("rel_stake_b: {:?}", rel_stake_b); // 0.2222 -> 2/9 log::info!("rel_stake_c: {:?}", rel_stake_c); // 0.1111 -> 1/9 - assert_eq!(rel_stake_a, I96F32::from_num(300_000) / total_tao); - assert_eq!(rel_stake_b, I96F32::from_num(100_000) / total_tao); - assert_eq!(rel_stake_c, I96F32::from_num(50_000) / total_tao); + assert!((rel_stake_a - I96F32::from_num(stake_a) / total_tao).abs() < 0.001); + assert!((rel_stake_b - I96F32::from_num(stake_b) / total_tao).abs() < 0.001); + assert!((rel_stake_c - I96F32::from_num(stake_c) / total_tao).abs() < 0.001); // Set parent-child relationships // A -> B (50% of A's stake) - mock_schedule_children(&coldkey_a, &hotkey_a, netuid, &[(u64::MAX / 2, hotkey_b)]); + mock_set_children_no_epochs(netuid, &hotkey_a, &[(u64::MAX / 2, hotkey_b)]); // B -> C (50% of B's stake) - mock_schedule_children(&coldkey_b, &hotkey_b, netuid, &[(u64::MAX / 2, hotkey_c)]); - wait_and_set_pending_children(netuid); // Don't want to run blocks before both children are scheduled + mock_set_children_no_epochs(netuid, &hotkey_b, &[(u64::MAX / 2, hotkey_c)]); // Get old stakes after children are scheduled let stake_a_old: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_a); @@ -3414,6 +3209,10 @@ fn test_parent_child_chain_emission() { let stake_c_old: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_c); let total_stake_old: I96F32 = I96F32::from_num(stake_a_old + stake_b_old + stake_c_old); + log::info!("Old stake for hotkey A: {:?}", stake_a_old); + log::info!("Old stake for hotkey B: {:?}", stake_b_old); + log::info!("Old stake for hotkey C: {:?}", stake_c_old); + log::info!("Total old stake: {:?}", total_stake_old); // Set CHK take rate to 1/9 let chk_take: I96F32 = I96F32::from_num(1_f64 / 9_f64); @@ -3424,15 +3223,13 @@ fn test_parent_child_chain_emission() { // Set the weight of root TAO to be 0%, so only alpha is effective. SubtensorModule::set_tao_weight(0); - let hardcoded_emission: I96F32 = I96F32::from_num(1_000_000); // 1 million (adjust as needed) - let emission_as_alpha = - I96F32::from_num(hardcoded_emission) / SubtensorModule::get_alpha_price(netuid); + let emission: I96F32 = I96F32::from_num(SubtensorModule::get_block_emission().unwrap_or(0)); // Set pending emission to 0 PendingEmission::::insert(netuid, 0); - // Run epoch with a hardcoded emission value - SubtensorModule::run_coinbase(hardcoded_emission); + // Run epoch with emission value + SubtensorModule::run_coinbase(emission); // Log new stake let stake_a_new: u64 = SubtensorModule::get_total_stake_for_hotkey(&hotkey_a); @@ -3460,8 +3257,8 @@ fn test_parent_child_chain_emission() { // Verify the final stake distribution let stake_inc_eps: I96F32 = I96F32::from_num(1e-4); // 4 decimal places - // Each child has chk_take take + // Each child has chk_take take let expected_a = I96F32::from_num(2_f64 / 3_f64) * (I96F32::from_num(1_f64) - (I96F32::from_num(1_f64 / 2_f64) * chk_take)); assert!( @@ -3511,11 +3308,10 @@ fn test_parent_child_chain_emission() { total_stake_new ); - let eps: I96F32 = I96F32::from_num(10_000); - assert!( - (total_stake_new - (total_stake_old + emission_as_alpha)).abs() <= eps, - "Total stake should have increased by the hardcoded emission amount {:?}", - total_stake_new - (total_stake_old + emission_as_alpha) + assert_abs_diff_eq!( + total_stake_inc.to_num::(), + emission.to_num::(), + epsilon = emission.to_num::() / 1000, ); }); } diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 3a2371af8..2ddb73bb8 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -49,529 +49,314 @@ fn test_dynamic_function_various_values() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_dynamic_function_price_equal_emission --exact --show-output --nocapture +// Test the base case of running coinbase with zero emission. +// This test verifies that the coinbase mechanism can handle the edge case +// of zero emission without errors or unexpected behavior. +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_basecase --exact --show-output --nocapture #[test] -fn test_dynamic_function_price_equal_emission() { +fn test_coinbase_basecase() { new_test_ext(1).execute_with(|| { - let netuid = 1; - let tao_subnet_emission: u64 = 100_000_000; - let tao_block_emission: u64 = 1_000_000_000; - let alpha_block_emission: u64 = 1_000_000_000; - SubnetTAO::::insert(netuid, 1_000_000_000); - SubnetAlphaIn::::insert(netuid, 1_000_000_000); - add_network(netuid, 110, 100); - let (tao_in, alpha_in, alpha_out): (u64, u64, u64) = - SubtensorModule::get_dynamic_tao_emission( - netuid, - tao_subnet_emission, - alpha_block_emission, - ); - assert_eq!(tao_in, tao_subnet_emission); // at price == tao_in == tao_subnet_emission - let expected_alpha_in: u64 = - (alpha_block_emission * tao_subnet_emission) / tao_block_emission; - close(alpha_in, expected_alpha_in, 10); - close(alpha_out, alpha_block_emission, 10); + SubtensorModule::run_coinbase(I96F32::from_num(0.0)); }); } -// Verifies that the total stake after the coinbase is only increased by the coinbase emission. -// Avoids TAO weight. -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_total_stake_after_coinbase_no_tao_weight --exact --show-output --nocapture +// Test the emission distribution for a single subnet. +// This test verifies that: +// - A single subnet receives the full emission amount +// - The emission is correctly reflected in SubnetTAO +// - Total issuance and total stake are updated appropriately +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_base --exact --show-output --nocapture #[test] -fn test_total_stake_after_coinbase_no_tao_weight() { +fn test_coinbase_tao_issuance_base() { new_test_ext(1).execute_with(|| { let netuid: u16 = 1; + let emission: u64 = 1_234_567; add_network(netuid, 1, 0); - // Set TAO weight to 0 - SubtensorModule::set_tao_weight(0); - // Set owner cut to ~11.11% - SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); - let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); - let epsilon: u64 = 100; - - // Define hotkeys and coldkeys - let hotkey_a: U256 = U256::from(1); - let hotkey_b: U256 = U256::from(2); - let hotkey_c: U256 = U256::from(3); - let coldkey_a: U256 = U256::from(100); - let coldkey_b: U256 = U256::from(101); - let coldkey_c: U256 = U256::from(102); - - // Register neurons with decreasing stakes - register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); - register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); - register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); - - // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); - - // Swap to alpha - let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); - let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( - netuid, - total_tao.saturating_to_num::(), - )); - - // Set the stakes directly - // This avoids needing to swap tao to alpha, impacting the initial stake distribution. - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::(), - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_b, - &coldkey_b, - netuid, - (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::(), - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_c, - &coldkey_c, - netuid, - (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::(), - ); - - // Get the total stake on the network - let mut total_stake_before = 0; - for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::::iter() { - if netuid_i == netuid { - total_stake_before += alpha; - } else { - assert!( - alpha == 0, - "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", - alpha, - netuid_i - ); - } - } - - log::info!("total_stake_before: {:?}", total_stake_before); - - // Run the coinbase - SubtensorModule::run_coinbase(total_coinbase_emission); - - // Get the total stake on the network - let mut total_stake_after = 0; - for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::::iter() { - if netuid_i == netuid { - total_stake_after += alpha; - } else { - assert!( - alpha == 0, - "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", - alpha, - netuid_i - ); - } - } - assert_abs_diff_eq!( - total_stake_after, - total_stake_before + total_coinbase_emission.saturating_to_num::(), - epsilon = epsilon - ); + assert_eq!(SubnetTAO::::get(netuid), 0); + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + assert_eq!(SubnetTAO::::get(netuid), emission); + assert_eq!(TotalIssuance::::get(), emission); + assert_eq!(TotalStake::::get(), emission); }); } -// Verifies that the total stake after the coinbase is only increased by the coinbase emission. -// Includes TAO weight. -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_total_stake_after_coinbase --exact --show-output --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_base_low --exact --show-output --nocapture #[test] -fn test_total_stake_after_coinbase() { +fn test_coinbase_tao_issuance_base_low() { new_test_ext(1).execute_with(|| { let netuid: u16 = 1; + let emission: u64 = 1; add_network(netuid, 1, 0); - // Set TAO weight to 18% - SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::()); - // Set owner cut to ~11.11% - SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); - let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); - let epsilon: u64 = 100; - - // Define hotkeys and coldkeys - let hotkey_a: U256 = U256::from(1); - let hotkey_b: U256 = U256::from(2); - let hotkey_c: U256 = U256::from(3); - let coldkey_a: U256 = U256::from(100); - let coldkey_b: U256 = U256::from(101); - let coldkey_c: U256 = U256::from(102); - - // Register neurons with decreasing stakes - register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); - register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); - register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); - - // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); - - // Swap to alpha - let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); - let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( - netuid, - total_tao.saturating_to_num::(), - )); - - // Set the stakes directly - // This avoids needing to swap tao to alpha, impacting the initial stake distribution. - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::(), - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_b, - &coldkey_b, - netuid, - (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::(), - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_c, - &coldkey_c, - netuid, - (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::(), - ); - - // Stake some to root - let stake_to_root: u64 = 10_000_000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - stake_to_root, - ); - - let alpha_price = SubtensorModule::get_alpha_price(netuid); - log::info!("alpha_price: {:?}", alpha_price); - - // Get the total stake on the network - let mut total_stake_before = 0; - for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::::iter() { - if netuid_i == netuid { - total_stake_before += alpha; - } else if netuid == SubtensorModule::get_root_netuid() { - let as_alpha: I96F32 = I96F32::from_num(alpha) / alpha_price; - total_stake_before += as_alpha.saturating_to_num::(); - } else { - assert!( - alpha == 0, - "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", - alpha, - netuid_i - ); - } - } - - log::info!("total_stake_before: {:?}", total_stake_before); - - // Run the coinbase - SubtensorModule::run_coinbase(total_coinbase_emission); - - // Get the total stake on the network - let mut total_stake_after = 0; - for (hotkey, netuid_i, alpha) in TotalHotkeyAlpha::::iter() { - if netuid_i == netuid { - total_stake_after += alpha; - } else if netuid == SubtensorModule::get_root_netuid() { - let as_alpha: I96F32 = I96F32::from_num(alpha) / alpha_price; - total_stake_after += as_alpha.saturating_to_num::(); - } else { - assert!( - alpha == 0, - "Alpha should be 0 for non-subnet hotkeys, but is {:?} on netuid {:?}", - alpha, - netuid_i - ); - } - } - assert_abs_diff_eq!( - total_stake_after, - total_stake_before + total_coinbase_emission.saturating_to_num::(), - epsilon = epsilon - ); + assert_eq!(SubnetTAO::::get(netuid), 0); + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + assert_eq!(SubnetTAO::::get(netuid), emission); + assert_eq!(TotalIssuance::::get(), emission); + assert_eq!(TotalStake::::get(), emission); }); } -// Verifies that the total issuance after the coinbase is only increased by the coinbase emission. -// Includes TAO weight. -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_total_issuance_after_coinbase --exact --show-output --nocapture +// Test emission distribution across multiple subnets. +// This test verifies that: +// - Multiple subnets receive equal portions of the total emission +// - Each subnet's TAO balance is updated correctly +// - Total issuance and total stake reflect the full emission amount +// - The emission is split evenly between all subnets +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_multiple --exact --show-output --nocapture #[test] -fn test_total_issuance_after_coinbase() { +fn test_coinbase_tao_issuance_multiple() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; - add_network(netuid, 1, 0); - // Set TAO weight to 18% - SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::()); - // Set owner cut to ~11.11% - SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); - let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); - let epsilon: u64 = 100; - - // Define hotkeys and coldkeys - let hotkey_a: U256 = U256::from(1); - let hotkey_b: U256 = U256::from(2); - let hotkey_c: U256 = U256::from(3); - let coldkey_a: U256 = U256::from(100); - let coldkey_b: U256 = U256::from(101); - let coldkey_c: U256 = U256::from(102); - - // Register neurons with decreasing stakes - register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); - register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); - register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); - - // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); - - // Swap to alpha - let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); - let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( - netuid, - total_tao.saturating_to_num::(), - )); - - // Set the stakes directly - // This avoids needing to swap tao to alpha, impacting the initial stake distribution. - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::(), - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_b, - &coldkey_b, - netuid, - (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::(), - ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_c, - &coldkey_c, - netuid, - (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::(), - ); - - // Stake some to root - let stake_to_root: u64 = 10_000_000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - stake_to_root, - ); - - let alpha_price = SubtensorModule::get_alpha_price(netuid); - log::info!("alpha_price: {:?}", alpha_price); - - // Get the total issuance - let mut total_issuance_before = TotalIssuance::::get(); - log::info!("total_issuance_before: {:?}", total_issuance_before); - - // Run the coinbase - SubtensorModule::run_coinbase(total_coinbase_emission); + let netuid1: u16 = 1; + let netuid2: u16 = 2; + let netuid3: u16 = 3; + let emission: u64 = 3_333_333; + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + add_network(netuid3, 1, 0); + assert_eq!(SubnetTAO::::get(netuid1), 0); + assert_eq!(SubnetTAO::::get(netuid2), 0); + assert_eq!(SubnetTAO::::get(netuid3), 0); + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + assert_eq!(SubnetTAO::::get(netuid1), emission / 3); + assert_eq!(SubnetTAO::::get(netuid2), emission / 3); + assert_eq!(SubnetTAO::::get(netuid3), emission / 3); + assert_eq!(TotalIssuance::::get(), emission); + assert_eq!(TotalStake::::get(), emission); + }); +} - // Compare - let total_issuance_after = TotalIssuance::::get(); - assert_abs_diff_eq!( - total_issuance_after, - total_issuance_before + total_coinbase_emission.saturating_to_num::(), - epsilon = epsilon - ); +// Test emission distribution with different subnet prices. +// This test verifies that: +// - Subnets with different prices receive proportional emission shares +// - A subnet with double the price receives double the emission +// - Total issuance and total stake reflect the full emission amount +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_tao_issuance_different_prices --exact --show-output --nocapture +#[test] +fn test_coinbase_tao_issuance_different_prices() { + new_test_ext(1).execute_with(|| { + let netuid1: u16 = 1; + let netuid2: u16 = 2; + let emission: u64 = 100_000_000; + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + // Make subnets dynamic. + SubnetMechanism::::insert(netuid1, 1); + SubnetMechanism::::insert(netuid2, 1); + // Set subnet prices. + SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); + SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); + // Assert initial TAO reserves. + assert_eq!(SubnetTAO::::get(netuid1), 0); + assert_eq!(SubnetTAO::::get(netuid2), 0); + // Run the coinbase with the emission amount. + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + // Assert tao emission is split evenly. + assert_eq!(SubnetTAO::::get(netuid1), emission / 3); + assert_eq!(SubnetTAO::::get(netuid2), emission / 3 + emission / 3); + close(TotalIssuance::::get(), emission, 2); + close(TotalStake::::get(), emission, 2); }); } -// Verifies that the total issuance after the coinbase is not changed when registration is disabled. -// Includes TAO weight. -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_registration_disabled_total_issuance_same --exact --show-output --nocapture +// Test moving price updates with different alpha values. +// This test verifies that: +// - Moving price stays constant when alpha is 1.0 +// - Moving price converges to real price at expected rate with alpha 0.1 +// - Moving price updates correctly over multiple iterations +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_moving_prices --exact --show-output --nocapture #[test] -fn test_registration_disabled_total_issuance_same() { +fn test_coinbase_moving_prices() { new_test_ext(1).execute_with(|| { let netuid: u16 = 1; add_network(netuid, 1, 0); - // Set TAO weight to 18% - SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::()); - // Set owner cut to ~11.11% - SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); - let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); - let epsilon: u64 = 100; - - // Define hotkeys and coldkeys - let hotkey_a: U256 = U256::from(1); - let hotkey_b: U256 = U256::from(2); - let hotkey_c: U256 = U256::from(3); - let coldkey_a: U256 = U256::from(100); - let coldkey_b: U256 = U256::from(101); - let coldkey_c: U256 = U256::from(102); - - // Register neurons with decreasing stakes - register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); - register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); - register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); - - // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); - - // Swap to alpha - let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); - let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( - netuid, - total_tao.saturating_to_num::(), - )); - - // Set the stakes directly - // This avoids needing to swap tao to alpha, impacting the initial stake distribution. - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::(), + // Set price to 1.0 + SubnetTAO::::insert(netuid, 1_000_000); + SubnetAlphaIn::::insert(netuid, 1_000_000); + SubnetMechanism::::insert(netuid, 1); + SubnetMovingPrice::::insert(netuid, I96F32::from_num(1)); + // Updating the moving price keeps it the same. + assert_eq!( + SubtensorModule::get_moving_alpha_price(netuid), + I96F32::from_num(1) ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_b, - &coldkey_b, - netuid, - (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::(), + SubtensorModule::update_moving_price(netuid); + assert_eq!( + SubtensorModule::get_moving_alpha_price(netuid), + I96F32::from_num(1) ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_c, - &coldkey_c, - netuid, - (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::(), + // Check alpha of 1. + // Set price to zero. + SubnetMovingPrice::::insert(netuid, I96F32::from_num(0)); + SubnetMovingAlpha::::set(I96F32::from_num(1.0)); + // Run moving 1 times. + SubtensorModule::update_moving_price(netuid); + // Assert price is == 100% of the real price. + assert_eq!( + SubtensorModule::get_moving_alpha_price(netuid), + I96F32::from_num(1.0) ); - - // Stake some to root - let stake_to_root: u64 = 10_000_000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - stake_to_root, + // Set price to zero. + SubnetMovingPrice::::insert(netuid, I96F32::from_num(0)); + SubnetMovingAlpha::::set(I96F32::from_num(0.1)); + // Run moving 6 times. + SubtensorModule::update_moving_price(netuid); + SubtensorModule::update_moving_price(netuid); + SubtensorModule::update_moving_price(netuid); + SubtensorModule::update_moving_price(netuid); + SubtensorModule::update_moving_price(netuid); + SubtensorModule::update_moving_price(netuid); + // Assert price is > 50% of the real price. + assert_eq!( + SubtensorModule::get_moving_alpha_price(netuid), + I96F32::from_num(0.468559) ); + }); +} - let alpha_price = SubtensorModule::get_alpha_price(netuid); - log::info!("alpha_price: {:?}", alpha_price); - - // Get the total issuance - let mut total_issuance_before = TotalIssuance::::get(); - log::info!("total_issuance_before: {:?}", total_issuance_before); - - // Disable registration on the network - SubtensorModule::set_network_registration_allowed(netuid, false); - SubtensorModule::set_network_pow_registration_allowed(netuid, false); - - // Run the coinbase - SubtensorModule::run_coinbase(total_coinbase_emission); +// Test basic alpha issuance in coinbase mechanism. +// This test verifies that: +// - Alpha issuance is initialized to 0 for new subnets +// - Alpha issuance is split evenly between subnets during coinbase +// - Each subnet receives the expected fraction of total emission +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_alpha_issuance_base --exact --show-output --nocapture +#[test] +fn test_coinbase_alpha_issuance_base() { + new_test_ext(1).execute_with(|| { + let netuid1: u16 = 1; + let netuid2: u16 = 2; + let emission: u64 = 1_000_000; + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + // Set up prices 1 and 1 + let initial: u64 = 1_000_000; + SubnetTAO::::insert(netuid1, initial); + SubnetAlphaIn::::insert(netuid1, initial); + SubnetTAO::::insert(netuid2, initial); + SubnetAlphaIn::::insert(netuid2, initial); + // Check initial + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + // tao_in = 500_000 + // alpha_in = 500_000/price = 500_000 + assert_eq!(SubnetAlphaIn::::get(netuid1), initial + emission / 2); + assert_eq!(SubnetAlphaIn::::get(netuid2), initial + emission / 2); + }); +} - // Should be the same - let total_issuance_after = TotalIssuance::::get(); - assert_abs_diff_eq!( - total_issuance_after, - total_issuance_before, - epsilon = epsilon +// Test alpha issuance with different subnet prices. +// This test verifies that: +// - Alpha issuance is proportional to subnet prices +// - Higher priced subnets receive more TAO emission +// - Alpha issuance is correctly calculated based on price ratios +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_alpha_issuance_different --exact --show-output --nocapture +#[test] +fn test_coinbase_alpha_issuance_different() { + new_test_ext(1).execute_with(|| { + let netuid1: u16 = 1; + let netuid2: u16 = 2; + let emission: u64 = 1_000_000; + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + // Make subnets dynamic. + SubnetMechanism::::insert(netuid1, 1); + SubnetMechanism::::insert(netuid2, 1); + // Setup prices 1 and 1 + let initial: u64 = 1_000_000; + SubnetTAO::::insert(netuid1, initial); + SubnetAlphaIn::::insert(netuid1, initial); + SubnetTAO::::insert(netuid2, initial); + SubnetAlphaIn::::insert(netuid2, initial); + // Set subnet prices. + SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); + SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); + // Run coinbase + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + // tao_in = 333_333 + // alpha_in = 333_333/price = 333_333 + initial + assert_eq!(SubnetAlphaIn::::get(netuid1), initial + emission / 3); + // tao_in = 666_666 + // alpha_in = 666_666/price = 666_666 + initial + assert_eq!( + SubnetAlphaIn::::get(netuid2), + initial + emission / 3 + emission / 3 ); }); } -// Verifies that the TAO-in after the coinbase is not changed when registration is disabled. -// Includes TAO weight. -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_registration_disabled_tao_in_same --exact --show-output --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_alpha_issuance_with_cap_trigger --exact --show-output --nocapture #[test] -fn test_registration_disabled_tao_in_same() { +fn test_coinbase_alpha_issuance_with_cap_trigger() { new_test_ext(1).execute_with(|| { - let netuid: u16 = 1; - add_network(netuid, 1, 0); - // Set TAO weight to 18% - SubtensorModule::set_tao_weight(I96F32::from_num(0.18).saturating_to_num::()); - // Set owner cut to ~11.11% - SubtensorModule::set_subnet_owner_cut(u16::MAX / 9); - let total_coinbase_emission: I96F32 = I96F32::from_num(1_123_456_789); - let epsilon: u64 = 100; - - // Define hotkeys and coldkeys - let hotkey_a: U256 = U256::from(1); - let hotkey_b: U256 = U256::from(2); - let hotkey_c: U256 = U256::from(3); - let coldkey_a: U256 = U256::from(100); - let coldkey_b: U256 = U256::from(101); - let coldkey_c: U256 = U256::from(102); - - // Register neurons with decreasing stakes - register_ok_neuron(netuid, hotkey_a, coldkey_a, 0); - register_ok_neuron(netuid, hotkey_b, coldkey_b, 0); - register_ok_neuron(netuid, hotkey_c, coldkey_c, 0); - - // Add initial stakes - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_b, 1_000); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_c, 1_000); - - // Swap to alpha - let total_tao: I96F32 = I96F32::from_num(300_000 + 100_000 + 50_000); - let total_alpha: I96F32 = I96F32::from_num(SubtensorModule::swap_tao_for_alpha( - netuid, - total_tao.saturating_to_num::(), - )); - - // Set the stakes directly - // This avoids needing to swap tao to alpha, impacting the initial stake distribution. - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - (total_alpha * I96F32::from_num(300_000) / total_tao).saturating_to_num::(), + let netuid1: u16 = 1; + let netuid2: u16 = 2; + let emission: u64 = 1_000_000; + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + // Make subnets dynamic. + SubnetMechanism::::insert(netuid1, 1); + SubnetMechanism::::insert(netuid2, 1); + // Setup prices 1000000 + let initial: u64 = 1_000; + let initial_alpha: u64 = initial * 1000000; + SubnetTAO::::insert(netuid1, initial); + SubnetAlphaIn::::insert(netuid1, initial_alpha); // Make price extremely low. + SubnetTAO::::insert(netuid2, initial); + SubnetAlphaIn::::insert(netuid2, initial_alpha); // Make price extremely low. + // Set subnet prices. + SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); + SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); + // Run coinbase + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + // tao_in = 333_333 + // alpha_in = 333_333/price > 1_000_000_000 --> 1_000_000_000 + initial_alpha + assert_eq!( + SubnetAlphaIn::::get(netuid1), + initial_alpha + 1_000_000_000 ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_b, - &coldkey_b, - netuid, - (total_alpha * I96F32::from_num(100_000) / total_tao).saturating_to_num::(), + assert_eq!(SubnetAlphaOut::::get(netuid2), 1_000_000_000); + // tao_in = 666_666 + // alpha_in = 666_666/price > 1_000_000_000 --> 1_000_000_000 + initial_alpha + assert_eq!( + SubnetAlphaIn::::get(netuid2), + initial_alpha + 1_000_000_000 ); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_c, - &coldkey_c, - netuid, - (total_alpha * I96F32::from_num(50_000) / total_tao).saturating_to_num::(), - ); - - // Stake some to root - let stake_to_root: u64 = 10_000_000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_a, stake_to_root); - SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey_a, - &coldkey_a, - netuid, - stake_to_root, - ); - - let alpha_price = SubtensorModule::get_alpha_price(netuid); - log::info!("alpha_price: {:?}", alpha_price); - - // Get the total issuance - let mut tao_in_before = SubnetTAO::::get(netuid); - log::info!("tao_in_before: {:?}", tao_in_before); - - // Disable registration on the network - SubtensorModule::set_network_registration_allowed(netuid, false); - SubtensorModule::set_network_pow_registration_allowed(netuid, false); - - // Run the coinbase - SubtensorModule::run_coinbase(total_coinbase_emission); + assert_eq!(SubnetAlphaOut::::get(netuid2), 1_000_000_000); // Gets full block emission. + }); +} - // Should be the same - let tao_in_after = SubnetTAO::::get(netuid); - assert_abs_diff_eq!(tao_in_after, tao_in_before, epsilon = epsilon); +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission --exact --show-output --nocapture +#[test] +fn test_coinbase_alpha_issuance_with_cap_trigger_and_block_emission() { + new_test_ext(1).execute_with(|| { + let netuid1: u16 = 1; + let netuid2: u16 = 2; + let emission: u64 = 1_000_000; + add_network(netuid1, 1, 0); + add_network(netuid2, 1, 0); + // Make subnets dynamic. + SubnetMechanism::::insert(netuid1, 1); + SubnetMechanism::::insert(netuid2, 1); + // Setup prices 1000000 + let initial: u64 = 1_000; + let initial_alpha: u64 = initial * 1000000; + SubnetTAO::::insert(netuid1, initial); + SubnetAlphaIn::::insert(netuid1, initial_alpha); // Make price extremely low. + SubnetTAO::::insert(netuid2, initial); + SubnetAlphaIn::::insert(netuid2, initial_alpha); // Make price extremely low. + // Set issuance to greater than 21M + SubnetAlphaOut::::insert(netuid1, 22_000_000_000_000_000); // Set issuance above 21M + SubnetAlphaOut::::insert(netuid2, 22_000_000_000_000_000); // Set issuance above 21M + // Set subnet prices. + SubnetMovingPrice::::insert(netuid1, I96F32::from_num(1)); + SubnetMovingPrice::::insert(netuid2, I96F32::from_num(2)); + // Run coinbase + SubtensorModule::run_coinbase(I96F32::from_num(emission)); + // tao_in = 333_333 + // alpha_in = 333_333/price > 1_000_000_000 --> 0 + initial_alpha + assert_eq!(SubnetAlphaIn::::get(netuid1), initial_alpha); + assert_eq!(SubnetAlphaOut::::get(netuid2), 22_000_000_000_000_000); + // tao_in = 666_666 + // alpha_in = 666_666/price > 1_000_000_000 --> 0 + initial_alpha + assert_eq!(SubnetAlphaIn::::get(netuid2), initial_alpha); + assert_eq!(SubnetAlphaOut::::get(netuid2), 22_000_000_000_000_000); + // No emission. }); } diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index b0d7c461a..be2292623 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -727,6 +727,15 @@ pub fn mock_set_children(coldkey: &U256, parent: &U256, netuid: u16, child_vec: wait_and_set_pending_children(netuid); } +#[allow(dead_code)] +pub fn mock_set_children_no_epochs(netuid: u16, parent: &U256, child_vec: &[(u64, U256)]) { + let backup_block = SubtensorModule::get_current_block_as_u64(); + PendingChildKeys::::insert(netuid, parent, (child_vec, 0)); + System::set_block_number(1); + SubtensorModule::do_set_pending_children(netuid); + System::set_block_number(backup_block); +} + // Helper function to wait for the rate limit #[allow(dead_code)] pub fn step_rate_limit(transaction_type: &TransactionType, netuid: u16) { diff --git a/pallets/subtensor/src/tests/serving.rs b/pallets/subtensor/src/tests/serving.rs index 32b604ba5..52b8f8b08 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 f21af56cf..776100578 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 7babb04f4..0e83205cc 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 b3f617edd..84283b034 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -11,7 +11,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub mod check_nonce; mod migrations; -use codec::{Decode, Encode, MaxEncodedLen}; +use codec::{Compact, Decode, Encode, MaxEncodedLen}; use frame_support::traits::Imbalance; use frame_support::{ dispatch::DispatchResultWithPostInfo, @@ -30,6 +30,15 @@ use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; use pallet_registry::CanRegisterIdentity; +use pallet_subtensor::rpc_info::{ + delegate_info::DelegateInfo, + dynamic_info::DynamicInfo, + metagraph::Metagraph, + neuron_info::{NeuronInfo, NeuronInfoLite}, + show_subnet::SubnetState, + stake_info::StakeInfo, + subnet_info::{SubnetHyperparams, SubnetInfo, SubnetInfov2}, +}; use scale_info::TypeInfo; use smallvec::smallvec; use sp_api::impl_runtime_apis; @@ -220,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, @@ -1043,7 +1052,7 @@ parameter_types! { pub const SubtensorInitialNetworkMaxStake: u64 = u64::MAX; // Maximum possible value for u64, this make the make stake infinity pub const InitialColdkeySwapScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days - pub const SubtensorInitialTaoWeight: u64 = 332_041_393_326_771_929; // 18% global weigh. + pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. } impl pallet_subtensor::Config for Runtime { @@ -2008,155 +2017,90 @@ impl_runtime_apis! { } impl subtensor_custom_rpc_runtime_api::DelegateInfoRuntimeApi for Runtime { - fn get_delegates() -> Vec { - let result = SubtensorModule::get_delegates(); - result.encode() + fn get_delegates() -> Vec> { + SubtensorModule::get_delegates() } - fn get_delegate(delegate_account_vec: Vec) -> Vec { - let _result = SubtensorModule::get_delegate(delegate_account_vec); - if _result.is_some() { - let result = _result.expect("Could not get DelegateInfo"); - result.encode() - } else { - vec![] - } + fn get_delegate(delegate_account: AccountId32) -> Option> { + SubtensorModule::get_delegate(delegate_account) } - fn get_delegated(delegatee_account_vec: Vec) -> Vec { - let result = SubtensorModule::get_delegated(delegatee_account_vec); - result.encode() + fn get_delegated(delegatee_account: AccountId32) -> Vec<(DelegateInfo, Compact)> { + SubtensorModule::get_delegated(delegatee_account) } } impl subtensor_custom_rpc_runtime_api::NeuronInfoRuntimeApi for Runtime { - fn get_neurons_lite(netuid: u16) -> Vec { - let result = SubtensorModule::get_neurons_lite(netuid); - result.encode() + fn get_neurons_lite(netuid: u16) -> Vec> { + SubtensorModule::get_neurons_lite(netuid) } - fn get_neuron_lite(netuid: u16, uid: u16) -> Vec { - let _result = SubtensorModule::get_neuron_lite(netuid, uid); - if _result.is_some() { - let result = _result.expect("Could not get NeuronInfoLite"); - result.encode() - } else { - vec![] - } + fn get_neuron_lite(netuid: u16, uid: u16) -> Option> { + SubtensorModule::get_neuron_lite(netuid, uid) } - fn get_neurons(netuid: u16) -> Vec { - let result = SubtensorModule::get_neurons(netuid); - result.encode() + fn get_neurons(netuid: u16) -> Vec> { + SubtensorModule::get_neurons(netuid) } - fn get_neuron(netuid: u16, uid: u16) -> Vec { - let _result = SubtensorModule::get_neuron(netuid, uid); - if _result.is_some() { - let result = _result.expect("Could not get NeuronInfo"); - result.encode() - } else { - vec![] - } + fn get_neuron(netuid: u16, uid: u16) -> Option> { + SubtensorModule::get_neuron(netuid, uid) } } impl subtensor_custom_rpc_runtime_api::SubnetInfoRuntimeApi for Runtime { - fn get_subnet_info(netuid: u16) -> Vec { - let _result = SubtensorModule::get_subnet_info(netuid); - if _result.is_some() { - let result = _result.expect("Could not get SubnetInfo"); - result.encode() - } else { - vec![] - } + fn get_subnet_info(netuid: u16) -> Option> { + SubtensorModule::get_subnet_info(netuid) } - fn get_subnets_info() -> Vec { - let result = SubtensorModule::get_subnets_info(); - result.encode() + fn get_subnets_info() -> Vec>> { + SubtensorModule::get_subnets_info() } - fn get_subnet_info_v2(netuid: u16) -> Vec { - let _result = SubtensorModule::get_subnet_info_v2(netuid); - if _result.is_some() { - let result = _result.expect("Could not get SubnetInfo"); - result.encode() - } else { - vec![] - } + fn get_subnet_info_v2(netuid: u16) -> Option> { + SubtensorModule::get_subnet_info_v2(netuid) } - fn get_subnets_info_v2() -> Vec { - let result = SubtensorModule::get_subnets_info_v2(); - result.encode() + fn get_subnets_info_v2() -> Vec>> { + SubtensorModule::get_subnets_info_v2() } - fn get_subnet_hyperparams(netuid: u16) -> Vec { - let _result = SubtensorModule::get_subnet_hyperparams(netuid); - if _result.is_some() { - let result = _result.expect("Could not get SubnetHyperparams"); - result.encode() - } else { - vec![] - } + fn get_subnet_hyperparams(netuid: u16) -> Option { + SubtensorModule::get_subnet_hyperparams(netuid) } - fn get_dynamic_info(netuid: u16) -> Vec { - let _result = SubtensorModule::get_dynamic_info(netuid); - if _result.is_some() { - let result = _result.expect("Could not get DynamicInfo."); - result.encode() - } else { - vec![] - } + fn get_dynamic_info(netuid: u16) -> Option> { + SubtensorModule::get_dynamic_info(netuid) } - fn get_metagraph(netuid: u16) -> Vec { - let _result = SubtensorModule::get_metagraph(netuid); - if _result.is_some() { - let result = _result.expect("Could not get Metagraph."); - result.encode() - } else { - vec![] - } + fn get_metagraph(netuid: u16) -> Option> { + SubtensorModule::get_metagraph(netuid) } - fn get_subnet_state(netuid: u16) -> Vec { - let _result = SubtensorModule::get_subnet_state(netuid); - if _result.is_some() { - let result = _result.expect("Could not get SubnetState."); - result.encode() - } else { - vec![] - } + fn get_subnet_state(netuid: u16) -> Option> { + SubtensorModule::get_subnet_state(netuid) } - fn get_all_metagraphs() -> Vec { - let result = SubtensorModule::get_all_metagraphs(); - result.encode() + fn get_all_metagraphs() -> Vec>> { + SubtensorModule::get_all_metagraphs() } - fn get_all_dynamic_info() -> Vec { - let result = SubtensorModule::get_all_dynamic_info(); - result.encode() + fn get_all_dynamic_info() -> Vec>> { + SubtensorModule::get_all_dynamic_info() } } impl subtensor_custom_rpc_runtime_api::StakeInfoRuntimeApi for Runtime { - fn get_stake_info_for_coldkey( coldkey_account_vec: Vec ) -> Vec { - let result = SubtensorModule::get_stake_info_for_coldkey( coldkey_account_vec ); - result.encode() + fn get_stake_info_for_coldkey( coldkey_account: AccountId32 ) -> Vec> { + SubtensorModule::get_stake_info_for_coldkey( coldkey_account ) } - fn get_stake_info_for_coldkeys( coldkey_account_vecs: Vec> ) -> Vec { - let result = SubtensorModule::get_stake_info_for_coldkeys( coldkey_account_vecs ); - result.encode() + fn get_stake_info_for_coldkeys( coldkey_accounts: Vec ) -> Vec<(AccountId32, Vec>)> { + SubtensorModule::get_stake_info_for_coldkeys( coldkey_accounts ) } - fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec: Vec, coldkey_account_vec: Vec, netuid: u16 ) -> Vec { - let result = SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account_vec, coldkey_account_vec, netuid ); - result.encode() + fn get_stake_info_for_hotkey_coldkey_netuid( hotkey_account: AccountId32, coldkey_account: AccountId32, netuid: u16 ) -> Option> { + SubtensorModule::get_stake_info_for_hotkey_coldkey_netuid( hotkey_account, coldkey_account, netuid ) } } diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 9944572c5..d62a0efd0 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, + )) } }