From 31a8f7e8789f418a773cbb4bbc68640be436490b Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 29 Jan 2025 13:38:34 -0500 Subject: [PATCH 01/17] add hotkey take float helper --- pallets/subtensor/src/coinbase/run_coinbase.rs | 5 +---- pallets/subtensor/src/staking/helpers.rs | 6 ++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index b57a085034..69e8cab968 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -426,10 +426,7 @@ impl Pallet { ); // 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 take_prop: I96F32 = Self::get_hotkey_take_float(hotkey_j); 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); diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index 0cbe3bccf4..eb16a4af56 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -121,6 +121,12 @@ impl Pallet { Delegates::::get(hotkey) } + pub fn get_hotkey_take_float(hotkey: &T::AccountId) -> I96F32 { + I96F32::from_num(Self::get_hotkey_take(hotkey)) + .checked_div(I96F32::from_num(u16::MAX)) + .unwrap_or(I96F32::from_num(0.0)) + } + /// Returns true if the hotkey account has been created. /// /// # Arguments From 0f2f87338da6fb63f261333c8bb2c1a27b25097c Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 29 Jan 2025 13:38:47 -0500 Subject: [PATCH 02/17] add root claim logic --- pallets/subtensor/src/lib.rs | 22 +++++++ pallets/subtensor/src/staking/stake_utils.rs | 60 ++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index d346b9241a..07d25c7b49 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -995,6 +995,28 @@ pub mod pallet { #[pallet::storage] // --- MAP ( netuid ) --> subnet_name | Returns the name of the subnet. pub type SubnetName = StorageMap<_, Identity, u16, Vec, ValueQuery, DefaultUnicodeVecU8>; + #[pallet::storage] // --- DMAP ( hot, netuid ) --> claimable_dividends | Root claimable dividends. + pub type RootClaimable = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Identity, + u16, + u64, + ValueQuery, + DefaultZeroU64, + >; + #[pallet::storage] // --- NMAP ( hot, cold, netuid ) --> claimable_debt | Returns a keys debt for claimable divs. + pub type RootDebt = StorageNMap< + _, + ( + NMapKey, // hot + NMapKey, // cold + NMapKey, // subnet + ), + I96F32, // Shares + ValueQuery, + >; /// ============================ /// ==== Global Parameters ===== diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 4c446947f5..ede9487075 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -5,6 +5,66 @@ use sp_std::ops::Neg; use substrate_fixed::types::{I110F18, I64F64, I96F32, U64F64}; impl Pallet { + pub fn increase_root_claimable_for_hotkey_and_subnet( + hotkey: &T::AccountId, + netuid: u16, + amount: u64, + ) { + // Get total stake on this hotkey on root. + let total: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + + // Get increment + let increment: I96F32 = I96F32::from_num(amount) + .checked_div(total) + .unwrap_or(I96F32::from_num(0.0)); + + // Convert owed to u64, mapping negative values to 0 + let increment_u64: u64 = if increment.is_negative() { + 0 + } else { + increment.to_num::() + }; + + // Increment claimable for this subnet. + RootClaimable::::mutate(hotkey, netuid, |total| { + *total = total.saturating_add(increment_u64); + }); + } + + pub fn root_claim(hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: u16) { + // Get this keys balance on root. + let balance: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + Self::get_root_netuid(), + )); + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate: I96F32 = I96F32::from_num(RootClaimable::::get(hotkey, netuid)); + + // Compute the proportion owed to this coldkey via balance. + let claimable: I96F32 = claimable_rate.saturating_mul(balance); + + // Attain the claimable debt to avoid overclaiming. + let debt: I96F32 = I96F32::from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Substract the debt. + let owed: I96F32 = claimable - debt; + + // Update your root debt so your rewards are drained. + RootDebt::::insert((hotkey, coldkey, netuid), claimable_rate); + + // Convert owed to u64, mapping negative values to 0 + let owed_u64: u64 = if owed.is_negative() { + 0 + } else { + owed.to_num::() + }; + + // Actually perform the stake operation on the new network. + Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, owed_u64); + } + /// Retrieves the total alpha issuance for a given subnet. /// /// This function calculates the total alpha issuance by summing the alpha From 33a814162cc365032ef07beb52117455ffb93c55 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 29 Jan 2025 18:50:02 -0500 Subject: [PATCH 03/17] initial impl without alpha-claim --- pallets/subtensor/src/staking/stake_utils.rs | 93 ++++++++++++++++---- 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index ede9487075..3ff8d43721 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -11,18 +11,19 @@ impl Pallet { amount: u64, ) { // Get total stake on this hotkey on root. - let total: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + let total: I96F32 = + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); // Get increment - let increment: I96F32 = I96F32::from_num(amount) + let increment: I96F32 = I96F32::saturating_from_num(amount) .checked_div(total) - .unwrap_or(I96F32::from_num(0.0)); + .unwrap_or(I96F32::saturating_from_num(0.0)); - // Convert owed to u64, mapping negative values to 0 + // Convert increment to u64, mapping negative values to 0 let increment_u64: u64 = if increment.is_negative() { 0 } else { - increment.to_num::() + increment.saturating_to_num::() }; // Increment claimable for this subnet. @@ -33,38 +34,98 @@ impl Pallet { pub fn root_claim(hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: u16) { // Get this keys balance on root. - let balance: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - coldkey, - Self::get_root_netuid(), - )); + let balance: I110F18 = + I110F18::saturating_from_num(Self::get_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + Self::get_root_netuid(), + )); // Get the total claimable_rate for this hotkey and this network - let claimable_rate: I96F32 = I96F32::from_num(RootClaimable::::get(hotkey, netuid)); + let claimable_rate: I110F18 = + I110F18::saturating_from_num(RootClaimable::::get(hotkey, netuid)); // Compute the proportion owed to this coldkey via balance. - let claimable: I96F32 = claimable_rate.saturating_mul(balance); + let claimable: I110F18 = claimable_rate.saturating_mul(balance); // Attain the claimable debt to avoid overclaiming. - let debt: I96F32 = I96F32::from_num(RootDebt::::get((hotkey, coldkey, netuid))); + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); // Substract the debt. - let owed: I96F32 = claimable - debt; + let owed: I110F18 = claimable - debt; // Update your root debt so your rewards are drained. - RootDebt::::insert((hotkey, coldkey, netuid), claimable_rate); + RootDebt::::insert( + (hotkey, coldkey, netuid), + claimable.saturating_to_num::(), + ); // Convert owed to u64, mapping negative values to 0 let owed_u64: u64 = if owed.is_negative() { 0 } else { - owed.to_num::() + owed.saturating_to_num::() }; // Actually perform the stake operation on the new network. Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, owed_u64); } + pub fn add_stake_adjust_debt_for_hotkey_and_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + amount: u64, + ) { + // Iterate over all the subnets this hotkey is staked on for root. + for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Increase debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_add( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + } + } + + pub fn remove_stake_adjust_debt_for_hotkey_and_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + amount: u64, + ) { + // Iterate over all the subnets this hotkey is staked on for root. + for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Decrease debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_sub( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + } + } + /// Retrieves the total alpha issuance for a given subnet. /// /// This function calculates the total alpha issuance by summing the alpha From fa3368cba950178407a5ff5b7fd3e79016ff884e Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 29 Jan 2025 18:54:09 -0500 Subject: [PATCH 04/17] pay root alpha to claimable --- pallets/subtensor/src/coinbase/run_coinbase.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 69e8cab968..68959f5703 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -144,9 +144,6 @@ impl Pallet { } } - // == 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. @@ -217,8 +214,6 @@ impl Pallet { .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!( @@ -642,14 +637,14 @@ impl Pallet { root_divs_to_pay ); - // 3.5.3 --- Distribute the root divs to the hotkey on the root subnet. - Self::increase_stake_for_hotkey_on_subnet( - hotkey_j, - Self::get_root_netuid(), + // 3.5.3 --- Increase the root claimable for this hotkey. + Self::increase_root_claimable_for_hotkey_and_subnet( + &hotkey_j.clone(), + netuid, root_divs_to_pay, ); log::debug!( - "Paid tao to hotkey {:?} on root netuid from netuid {:?}: {:?}", + "Paid tao to hotkey claimable {:?} on root netuid from netuid {:?}: {:?}", hotkey_j, netuid, root_divs_to_pay From 08f6e259f48b1daca67292ce9f5bd51e55b5bb00 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Wed, 29 Jan 2025 19:02:14 -0500 Subject: [PATCH 05/17] adjust debt on stake/unstake root --- pallets/subtensor/src/staking/stake_utils.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index 3ff8d43721..d10938d6fc 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -755,6 +755,12 @@ impl Pallet { *total = total.saturating_add(actual_fee); }); + // If this is a root-stake + if netuid == Self::get_root_netuid() { + // 5. Adjust root debt for this hotkey and coldkey. + Self::remove_stake_adjust_debt_for_hotkey_and_coldkey(&hotkey, &coldkey, alpha); + } + // Step 5. Deposit and log the unstaking event. Self::deposit_event(Event::StakeRemoved( coldkey.clone(), @@ -814,6 +820,12 @@ impl Pallet { *total = total.saturating_add(actual_fee); }); + // If this is a root-stake + if netuid == Self::get_root_netuid() { + // 5. Adjust root debt for this hotkey and coldkey. + Self::add_stake_adjust_debt_for_hotkey_and_coldkey(&hotkey, &coldkey, alpha); + } + // Step 6. Deposit and log the staking event. Self::deposit_event(Event::StakeAdded( coldkey.clone(), From 94a7e403ea8c4369accf88b35b1ba0743272e195 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 31 Jan 2025 10:51:51 -0500 Subject: [PATCH 06/17] WIP --- pallets/subtensor/src/lib.rs | 33 ++ pallets/subtensor/src/macros/dispatches.rs | 28 ++ pallets/subtensor/src/macros/events.rs | 5 + pallets/subtensor/src/staking/claim_root.rs | 19 ++ pallets/subtensor/src/staking/mod.rs | 1 + pallets/subtensor/src/staking/stake_utils.rs | 337 ++++++++++++++++--- pallets/subtensor/src/weights.rs | 45 +++ 7 files changed, 420 insertions(+), 48 deletions(-) create mode 100644 pallets/subtensor/src/staking/claim_root.rs create mode 100644 pallets/subtensor/src/weights.rs diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 07d25c7b49..87e00de155 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -34,6 +34,9 @@ use sp_std::marker::PhantomData; // ============================ mod benchmarks; +pub mod weights; +pub use weights::WeightInfo; + // ========================= // ==== Pallet Imports ===== // ========================= @@ -227,6 +230,14 @@ pub mod pallet { /// ==== Staking + Accounts ==== /// ============================ + /// Enum for the per-coldkey root claim setting. + #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + pub enum RootClaimTypeEnum { + #[default] + Swap, // Swap any alpha emission for TAO. + Keep, // Keep all alpha emission. + } + #[pallet::type_value] /// Default value for zero. pub fn DefaultZeroU64() -> u64 { @@ -715,6 +726,16 @@ pub mod pallet { 500_000 } + #[pallet::type_value] + pub fn DefaultMinRootClaimAmount() -> u64 { + 500_000 + } + + #[pallet::type_value] + pub fn DefaultRootClaimType() -> RootClaimTypeEnum { + RootClaimTypeEnum::Swap + } + #[pallet::type_value] /// Default unicode vector for tau symbol. pub fn DefaultUnicodeVecU8() -> Vec { @@ -1017,6 +1038,18 @@ pub mod pallet { I96F32, // Shares ValueQuery, >; + #[pallet::storage] // -- MAP ( cold ) --> root_claim_type enum + pub type RootClaimType = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + RootClaimTypeEnum, + ValueQuery, + DefaultRootClaimType, + >; + #[pallet::storage] // -- MAP ( hot ) --> root_swap_portion | Returns the amount of the root stake that wants to swap their alpha for TAO. + pub type RootSwapPortion = + StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery, DefaultZeroU64>; /// ============================ /// ==== Global Parameters ===== diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 6b8592db8e..2bd09a4354 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1811,5 +1811,33 @@ mod dispatches { allow_partial, ) } + + /// --- Claims the root emissions for a coldkey. + /// # Args: + /// * 'origin': (Origin): + /// - The signature of the caller's coldkey. + /// + /// # Event: + /// * RootClaimed; + /// - On the successfully claiming the root emissions for a coldkey. + /// + /// # Raises: + /// * 'NotRegistered': + /// - Thrown if the account we are attempting to unstake from is non existent. + /// + /// * 'NonAssociatedColdKey': + /// - Thrown if the coldkey does not own the hotkey we are unstaking from. + /// + /// * 'NotEnoughStakeToWithdraw': + /// - Thrown if there is not enough stake on the hotkey to withdwraw this amount. + /// + #[pallet::call_index(90)] + #[pallet::weight((T::DbWeight::get().reads_writes(1, 2) + 200_000, DispatchClass::Normal, Pays::Yes))] + pub fn claim_root(origin: OriginFor) -> DispatchResultWithPostInfo { + let coldkey: T::AccountId = ensure_signed(origin)?; + + let weight = Self::do_root_claim(coldkey); + Ok((Some(weight), Pays::Yes).into()) + } } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 6dd6ddad23..45cd56690a 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -265,5 +265,10 @@ mod events { /// Parameters: /// (coldkey, hotkey, origin_netuid, destination_netuid, amount) StakeSwapped(T::AccountId, T::AccountId, u16, u16, u64), + + /// Root emissions have been claimed for a coldkey on all subnets and hotkeys. + /// Parameters: + /// (coldkey, u8) + RootClaimed(T::AccountId, RootClaimType), } } diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs new file mode 100644 index 0000000000..d0fddea846 --- /dev/null +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -0,0 +1,19 @@ +use super::*; +use frame_support::weights::Weight; +use sp_core::Get; + +impl Pallet { + pub fn do_root_claim(coldkey: T::AccountId) -> Weight { + let mut weight = Weight::default(); + + let hotkeys = StakingHotkeys::::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + hotkeys.iter().for_each(|hotkey| { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + weight.saturating_accrue(Self::root_claim_all(hotkey, &coldkey)); + }); + + weight.into() + } +} diff --git a/pallets/subtensor/src/staking/mod.rs b/pallets/subtensor/src/staking/mod.rs index 2b222036cf..20fe606247 100644 --- a/pallets/subtensor/src/staking/mod.rs +++ b/pallets/subtensor/src/staking/mod.rs @@ -1,5 +1,6 @@ use super::*; pub mod add_stake; +pub mod claim_root; pub mod decrease_take; pub mod helpers; pub mod increase_take; diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index d10938d6fc..e8bafb1122 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,18 +1,103 @@ use super::*; +use frame_support::weights::Weight; use safe_math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; use substrate_fixed::types::{I110F18, I64F64, I96F32, U64F64}; impl Pallet { + pub fn change_root_claim_type(coldkey: &T::AccountId, new_type: RootClaimTypeEnum) -> Weight { + let mut weight = Weight::default(); + + // Get the current claim type. + let current_type: RootClaimTypeEnum = RootClaimType::::get(coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + // Claim now + weight.saturating_accrue(Self::do_root_claim(coldkey)); + + // Execute swap. + match (current_type, new_type) { + (RootClaimTypeEnum::Swap, RootClaimTypeEnum::Keep) => { + // Iterate over all the subnets this hotkey has claimable for root. + let hotkeys = StakingHotkeys::::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + // Swap the claim type. + RootClaimType::::insert(coldkey, new_type); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + hotkeys.iter().for_each(|hotkey| { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + // Clear debt entry for the root netuid. + RootDebt::::remove((hotkey, coldkey, Self::get_root_netuid())); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + + weight.saturating_accrue(T::DbWeight::get().reads(3)); + let stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + Self::get_root_netuid(), + ); + + // Deduct the stake from the root portion. + RootSwapPortion::::mutate(hotkey, |total| { + *total = total.saturating_sub(stake); + }); + + // Adjust debt for the stake with new type + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); // TODO: weights + Self::add_stake_adjust_debt_for_hotkey_and_coldkey(hotkey, coldkey, stake); + }); + } + (RootClaimTypeEnum::Keep, RootClaimTypeEnum::Swap) => { + // Swap the claim type. + RootClaimType::::insert(coldkey, new_type); + weight.saturating_accrue(T::DbWeight::get().writes(1)); + + let hotkeys = StakingHotkeys::::get(&coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + hotkeys.iter().for_each(|hotkey| { + // Clear debt entry for this hotkey on all subnets. + RootDebt::::clear_prefix((hotkey, coldkey), u32::MAX, None); + + weight.saturating_accrue(T::DbWeight::get().reads(1)); + let stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + Self::get_root_netuid(), + ); + + // Adjust debt for the stake with new type + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + Self::add_stake_adjust_debt_for_hotkey_and_coldkey(hotkey, coldkey, stake); + // TODO: weights + }); + } + _ => {} + } + + weight + } + + // Get the amount of stake on this hotkey that has claim type of Swap. + pub fn get_root_swap_portion(hotkey: &T::AccountId) -> u64 { + let swap_portion: u64 = RootSwapPortion::::get(hotkey); + swap_portion + } + pub fn increase_root_claimable_for_hotkey_and_subnet( hotkey: &T::AccountId, netuid: u16, amount: u64, ) { // Get total stake on this hotkey on root. - let total: I96F32 = - I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + let total: I96F32 = if netuid == Self::get_root_netuid() { + I96F32::saturating_from_num(Self::get_root_swap_portion(hotkey)) + } else { + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)) + }; // Get increment let increment: I96F32 = I96F32::saturating_from_num(amount) @@ -32,14 +117,27 @@ impl Pallet { }); } - pub fn root_claim(hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: u16) { + pub fn get_root_claimable_for_hotkey_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: u16, + ) -> I110F18 { // Get this keys balance on root. - let balance: I110F18 = + let balance: I110F18 = if netuid == Self::get_root_netuid() { + // If we are checking for the root netuid, we need to check if the coldkey has a claim type of Swap. + match RootClaimType::::get(coldkey) { + RootClaimTypeEnum::Swap => I110F18::saturating_from_num( + Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid), + ), + _ => 0.into(), + } + } else { I110F18::saturating_from_num(Self::get_stake_for_hotkey_and_coldkey_on_subnet( hotkey, coldkey, Self::get_root_netuid(), - )); + )) + }; // Get the total claimable_rate for this hotkey and this network let claimable_rate: I110F18 = @@ -48,6 +146,16 @@ impl Pallet { // Compute the proportion owed to this coldkey via balance. let claimable: I110F18 = claimable_rate.saturating_mul(balance); + claimable + } + + pub fn get_root_owed_for_hotkey_coldkey_float( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: u16, + ) -> I110F18 { + let claimable = Self::get_root_claimable_for_hotkey_coldkey(hotkey, coldkey, netuid); + // Attain the claimable debt to avoid overclaiming. let debt: I110F18 = I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); @@ -55,11 +163,34 @@ impl Pallet { // Substract the debt. let owed: I110F18 = claimable - debt; - // Update your root debt so your rewards are drained. - RootDebt::::insert( - (hotkey, coldkey, netuid), - claimable.saturating_to_num::(), - ); + owed + } + + pub fn get_root_owed_for_hotkey_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: u16, + ) -> u64 { + let owed = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); + + // Convert owed to u64, mapping negative values to 0 + let owed_u64: u64 = if owed.is_negative() { + 0 + } else { + owed.saturating_to_num::() + }; + + owed_u64 + } + + pub fn root_claim_on_subnet(hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: u16) { + // Substract the debt. + let owed: I110F18 = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); + + // Increase root debt by owed amount. + RootDebt::::mutate((hotkey, coldkey, netuid), |debt| { + *debt = debt.saturating_add(owed.saturating_to_num::()); + }); // Convert owed to u64, mapping negative values to 0 let owed_u64: u64 = if owed.is_negative() { @@ -72,30 +203,100 @@ impl Pallet { Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, owed_u64); } + pub fn root_claim_all(hotkey: &T::AccountId, coldkey: &T::AccountId) -> Weight { + let mut weight = Weight::default(); + + weight.saturating_accrue(T::DbWeight::get().reads(1)); + match RootClaimType::::get(coldkey) { + RootClaimTypeEnum::Swap => { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + weight.saturating_accrue(::WeightInfo::root_claim_on_subnet()); + // Only need to claim on the root netuid. + Self::root_claim_on_subnet(hotkey, coldkey, Self::get_root_netuid()); + } + RootClaimTypeEnum::Keep => { + // Iterate over all the subnets this hotkey has claimable for root. + RootClaimable::::iter_prefix(hotkey).for_each(|(netuid, _)| { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if netuid == Self::get_root_netuid() { + // Skip the root netuid. + weight.saturating_accrue(T::DbWeight::get().reads(1)); + continue; + } + weight.saturating_accrue(::WeightInfo::root_claim_on_subnet()); + + Self::root_claim_on_subnet(hotkey, coldkey, netuid); + }); + } + } + + weight.into() + } + pub fn add_stake_adjust_debt_for_hotkey_and_coldkey( hotkey: &T::AccountId, coldkey: &T::AccountId, amount: u64, ) { - // Iterate over all the subnets this hotkey is staked on for root. - for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { - // Get the total claimable_rate for this hotkey and this network - let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); - - // Get current staker-debt. - let debt: I110F18 = - I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); - - // Increase debt based on the claimable rate. - let new_debt: I110F18 = debt.saturating_add( - claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), - ); - - // Set the new debt. - RootDebt::::insert( - (hotkey, coldkey, netuid), - new_debt.saturating_to_num::(), - ); + // Get the current claim type. + let current_type: RootClaimTypeEnum = RootClaimType::::get(coldkey); + + match current_type { + RootClaimTypeEnum::Swap => { + // Only need to adjust the debt on the root netuid. + let netuid = Self::get_root_netuid(); + + let claimable_rate = RootClaimable::::get(hotkey, netuid); + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Increase debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_add( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + + // Add to the root portion for this hotkey. + RootSwapPortion::::mutate(hotkey, |total| { + *total = total.saturating_add(amount); + }); + } + RootClaimTypeEnum::Keep => { + // Iterate over all the subnets this hotkey is staked on for root. + for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { + if netuid == Self::get_root_netuid() { + continue; + } + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Increase debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_add( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + } + } } } @@ -104,25 +305,65 @@ impl Pallet { coldkey: &T::AccountId, amount: u64, ) { - // Iterate over all the subnets this hotkey is staked on for root. - for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { - // Get the total claimable_rate for this hotkey and this network - let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); - - // Get current staker-debt. - let debt: I110F18 = - I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); - - // Decrease debt based on the claimable rate. - let new_debt: I110F18 = debt.saturating_sub( - claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), - ); - - // Set the new debt. - RootDebt::::insert( - (hotkey, coldkey, netuid), - new_debt.saturating_to_num::(), - ); + // Get the current claim type. + let current_type: RootClaimTypeEnum = RootClaimType::::get(coldkey); + + match current_type { + RootClaimTypeEnum::Swap => { + // Only need to adjust the debt on the root netuid. + let netuid = Self::get_root_netuid(); + + let claimable_rate = RootClaimable::::get(hotkey, netuid); + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Decrease debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_sub( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + + // Substract from the root portion for this hotkey. + RootSwapPortion::::mutate(hotkey, |total| { + *total = total.saturating_sub(amount); + }); + } + RootClaimTypeEnum::Keep => { + // Iterate over all the subnets this hotkey is staked on for root. + for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { + if netuid == Self::get_root_netuid() { + continue; // Skip the root netuid. + } + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Decrease debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_sub( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + } + } } } diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs new file mode 100644 index 0000000000..d57d20db52 --- /dev/null +++ b/pallets/subtensor/src/weights.rs @@ -0,0 +1,45 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +pub trait WeightInfo { + fn root_claim_on_subnet() -> Weight; +} + +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + fn root_claim_on_subnet() -> Weight { + // TODO should be replaced by benchmarked weights + // Weight: + // 100_000 + 5 reads + // 1 read, 1 write + // 3 reads, 3 writes for increase stake + // Total: 100_000 + 9 reads + 4 writes + Weight::default().saturating_add( + T::DbWeight::get().reads_writes(9_u64, 4_u64) + ).saturating_add( + 100_000.into() + ) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + fn root_claim_on_subnet() -> Weight { + // TODO should be replaced by benchmarked weights + // Weight: + // 100_000 + 5 reads + // 1 read, 1 write + // 3 reads, 3 writes for increase stake + // Total: 100_000 + 9 reads + 4 writes + Weight::default().saturating_add( + T::DbWeight::get().reads_writes(9_u64, 4_u64) + ).saturating_add( + 100_000.into() + ) + } +} From 6f9afa9533a1a4d1810b0ce44c36fc299f516df4 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Fri, 31 Jan 2025 13:20:44 -0500 Subject: [PATCH 07/17] wip --- pallets/subtensor/src/coinbase/block_step.rs | 7 +++++++ pallets/subtensor/src/staking/claim_root.rs | 20 ++++++++++++++++++++ pallets/subtensor/src/utils/misc.rs | 10 ++++++++++ 3 files changed, 37 insertions(+) diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index bcfd1a37bc..1854148c79 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -8,6 +8,10 @@ impl Pallet { pub fn block_step() -> Result<(), &'static str> { let block_number: u64 = Self::get_current_block_as_u64(); log::debug!("block_step for block: {:?} ", block_number); + + let last_block_hash: T::Hash = Self::get_last_block_hash(); + log::debug!("last_block_hash: {:?}", last_block_hash); + // --- 1. Adjust difficulties. Self::adjust_registration_terms_for_networks(); // --- 2. Get the current coinbase emission. @@ -20,6 +24,9 @@ impl Pallet { // --- 4. Set pending children on the epoch; but only after the coinbase has been run. Self::try_set_pending_children(block_number); + // --- 5. Run auto-claim root divs. + Self::run_auto_claim_root_divs(last_block_hash); + // Return ok. Ok(()) } diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index d0fddea846..263ef449b1 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -16,4 +16,24 @@ impl Pallet { weight.into() } + + pub fn block_hash_to_indices(block_hash: T::Hash, k: usize, n: usize) -> Vec { + let block_hash_bytes = block_hash.as_bytes_slice(); + let mut indices = Vec::new(); + // k < n + let start_index: usize = block_hash_bytes[0..8].to_vec().into(); + let mut last_idx = start_index; + for i in 0..k { + let bh_idx = (i * 8) % 32; + let idx_step = block_hash_bytes[bh_idx..(bh_idx+8)].to_vec().into(); + let idx = (last_idx + idx_step) % n; + indices.push(idx as usize); + last_idx = idx; + } + indices + } + + pub fn run_auto_claim_root_divs(last_block_hash: T::Hash) { + + } } diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index b27921f27c..190d13e39b 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -1,3 +1,5 @@ +use core::hash::Hash; + use super::*; use crate::{ system::{ensure_root, ensure_signed_or_root, pallet_prelude::BlockNumberFor}, @@ -66,6 +68,14 @@ impl Pallet { .ok() .expect("blockchain will not exceed 2^64 blocks; QED.") } + pub fn get_last_block_hash() -> T::Hash { + let curr_block = >::block_number(); + if curr_block == 0 { + T::Hash::default() // does not exist, so just return default hash + } else { + >::block_hash(curr_block.saturating_sub(1)) + } + } // ============================== // ==== YumaConsensus params ==== From cc625181bb013e853cada7370af689b22c4eaf34 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 1 Feb 2025 15:39:11 -0800 Subject: [PATCH 08/17] wip --- pallets/admin-utils/src/tests/mock.rs | 1 + pallets/subtensor/src/coinbase/block_step.rs | 8 +- pallets/subtensor/src/lib.rs | 67 +++- pallets/subtensor/src/macros/config.rs | 3 + pallets/subtensor/src/macros/dispatches.rs | 31 +- pallets/subtensor/src/macros/events.rs | 12 +- pallets/subtensor/src/staking/claim_root.rs | 282 +++++++++++++- pallets/subtensor/src/staking/stake_utils.rs | 366 +------------------ pallets/subtensor/src/tests/mock.rs | 1 + pallets/subtensor/src/utils/misc.rs | 13 +- pallets/subtensor/src/weights.rs | 24 +- runtime/src/lib.rs | 1 + 12 files changed, 388 insertions(+), 421 deletions(-) diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index 5517196f1f..87b373b18f 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -200,6 +200,7 @@ impl pallet_subtensor::Config for Test { type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; + type WeightInfo = pallet_subtensor::weights::SubstrateWeight; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 1854148c79..d9d71b640e 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -9,8 +9,8 @@ impl Pallet { let block_number: u64 = Self::get_current_block_as_u64(); log::debug!("block_step for block: {:?} ", block_number); - let last_block_hash: T::Hash = Self::get_last_block_hash(); - log::debug!("last_block_hash: {:?}", last_block_hash); + let last_block_hash: T::Hash = Self::get_last_block_hash(); + log::debug!("last_block_hash: {:?}", last_block_hash); // --- 1. Adjust difficulties. Self::adjust_registration_terms_for_networks(); @@ -24,8 +24,8 @@ impl Pallet { // --- 4. Set pending children on the epoch; but only after the coinbase has been run. Self::try_set_pending_children(block_number); - // --- 5. Run auto-claim root divs. - Self::run_auto_claim_root_divs(last_block_hash); + // --- 5. Run auto-claim root divs. + Self::run_auto_claim_root_divs(last_block_hash); // Return ok. Ok(()) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 87e00de155..112b2c781b 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -226,16 +226,28 @@ pub mod pallet { /// The subnet's contact pub subnet_contact: Vec, } - /// ============================ - /// ==== Staking + Accounts ==== - /// ============================ + // ============================ + // ==== Staking + Accounts ==== + // ============================ /// Enum for the per-coldkey root claim setting. #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] pub enum RootClaimTypeEnum { + /// Swap any alpha emission for TAO. #[default] - Swap, // Swap any alpha emission for TAO. - Keep, // Keep all alpha emission. + Swap, + /// Keep all alpha emission. + Keep, + } + + /// Enum for the per-coldkey root claim frequency setting. + #[derive(Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug)] + pub enum RootClaimFrequencyEnum { + /// Claim automatically. + #[default] + Auto, + /// Only claim manually; Never automatically. + Manual, } #[pallet::type_value] @@ -727,13 +739,37 @@ pub mod pallet { } #[pallet::type_value] + /// Default minimum root claim amount. + /// This is the minimum amount of root claim that can be made. + /// Any amount less than this will not be claimed. pub fn DefaultMinRootClaimAmount() -> u64 { 500_000 } #[pallet::type_value] + /// Default root claim type. + /// This is the type of root claim that will be made. + /// This is set by the user. Either swap to TAO or keep as alpha. pub fn DefaultRootClaimType() -> RootClaimTypeEnum { - RootClaimTypeEnum::Swap + RootClaimTypeEnum::default() + } + + #[pallet::type_value] + /// Default root claim frequency. + /// This is the frequency of root claims for a coldkey. + /// This is set by the user. Either auto or manual. + pub fn DefaultRootClaimFrequency() -> RootClaimFrequencyEnum { + RootClaimFrequencyEnum::default() + } + + #[pallet::type_value] + /// Default number of root claims per claim call. + /// Ideally this is calculated using the number of staking coldkey + /// and the block time. + pub fn DefaultNumRootClaim() -> u64 { + // TODO: replace with size of staking coldkeys / 7200 + // i.e. once per day + 15 } #[pallet::type_value] @@ -1047,9 +1083,22 @@ pub mod pallet { ValueQuery, DefaultRootClaimType, >; - #[pallet::storage] // -- MAP ( hot ) --> root_swap_portion | Returns the amount of the root stake that wants to swap their alpha for TAO. - pub type RootSwapPortion = - StorageMap<_, Blake2_128Concat, T::AccountId, u64, ValueQuery, DefaultZeroU64>; + #[pallet::storage] // -- MAP ( cold ) --> root_claim_frequency enum + pub type RootClaimFrequency = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + RootClaimFrequencyEnum, + ValueQuery, + DefaultRootClaimFrequency, + >; + + #[pallet::storage] // --- MAP ( u64 ) --> coldkey | Maps coldkeys that have stake to an index + pub type StakingColdkeys = StorageMap<_, Identity, u64, T::AccountId, OptionQuery>; + #[pallet::storage] // --- Value --> num_staking_coldkeys + pub type NumStakingColdkeys = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; + #[pallet::storage] // --- Value --> num_root_claim | Number of coldkeys to claim each auto-claim. + pub type NumRootClaim = StorageValue<_, u64, ValueQuery, DefaultNumRootClaim>; /// ============================ /// ==== Global Parameters ===== diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index 49bb44dc14..0830bbd180 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -213,5 +213,8 @@ mod config { /// Initial TAO weight. #[pallet::constant] type InitialTaoWeight: Get; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: crate::WeightInfo; } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2bd09a4354..f464a8fdc4 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1822,22 +1822,35 @@ mod dispatches { /// - On the successfully claiming the root emissions for a coldkey. /// /// # Raises: - /// * 'NotRegistered': - /// - Thrown if the account we are attempting to unstake from is non existent. - /// - /// * 'NonAssociatedColdKey': - /// - Thrown if the coldkey does not own the hotkey we are unstaking from. - /// - /// * 'NotEnoughStakeToWithdraw': - /// - Thrown if there is not enough stake on the hotkey to withdwraw this amount. /// #[pallet::call_index(90)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 2) + 200_000, DispatchClass::Normal, Pays::Yes))] + #[pallet::weight((Weight::from_parts(200_000, 0).saturating_add(T::DbWeight::get().reads_writes(1, 2)), DispatchClass::Normal, Pays::Yes))] pub fn claim_root(origin: OriginFor) -> DispatchResultWithPostInfo { let coldkey: T::AccountId = ensure_signed(origin)?; let weight = Self::do_root_claim(coldkey); Ok((Some(weight), Pays::Yes).into()) } + + /// --- Sets the root claim type for the coldkey. + /// # Args: + /// * 'origin': (Origin): + /// - The signature of the caller's coldkey. + /// + /// # Event: + /// * RootClaimTypeSet; + /// - On the successfully setting the root claim type for the coldkey. + /// + #[pallet::call_index(91)] + #[pallet::weight((Weight::from_parts(45_000_000, 0).saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Normal, Pays::Yes))] + pub fn set_root_claim_type( + origin: OriginFor, + new_root_claim_type: RootClaimTypeEnum, + ) -> DispatchResult { + let coldkey: T::AccountId = ensure_signed(origin)?; + + Self::change_root_claim_type(&coldkey, new_root_claim_type); + Ok(()) + } } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 45cd56690a..5b85bc7c17 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -268,7 +268,17 @@ mod events { /// Root emissions have been claimed for a coldkey on all subnets and hotkeys. /// Parameters: + /// (coldkey) + RootClaimed(T::AccountId), + + /// Root claim type for a coldkey has been set. + /// Parameters: + /// (coldkey, u8) + RootClaimTypeSet(T::AccountId, RootClaimTypeEnum), + + /// Root claim frequency for a coldkey has been set. + /// Parameters: /// (coldkey, u8) - RootClaimed(T::AccountId, RootClaimType), + RootClaimFrequencySet(T::AccountId, RootClaimTypeEnum), } } diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 263ef449b1..528365547d 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -1,8 +1,244 @@ use super::*; use frame_support::weights::Weight; use sp_core::Get; +use substrate_fixed::types::{I110F18, I96F32}; impl Pallet { + pub fn block_hash_to_indices(block_hash: T::Hash, k: u64, n: u64) -> Vec { + let block_hash_bytes = block_hash.as_ref(); + let mut indices: Vec = Vec::new(); + // k < n + let start_index: u64 = u64::from_be_bytes( + block_hash_bytes + .get(0..8) + .unwrap_or(&[0; 8]) + .try_into() + .unwrap_or([0; 8]), + ); + let mut last_idx = start_index; + for i in 0..k { + let bh_idx: usize = ((i.saturating_mul(8)) % 32) as usize; + let idx_step = u64::from_be_bytes( + block_hash_bytes + .get(bh_idx..(bh_idx.saturating_add(8))) + .unwrap_or(&[0; 8]) + .try_into() + .unwrap_or([0; 8]), + ); + let idx = last_idx.saturating_add(idx_step).wrapping_rem(n); + indices.push(idx); + last_idx = idx; + } + indices + } + + pub fn increase_root_claimable_for_hotkey_and_subnet( + hotkey: &T::AccountId, + netuid: u16, + amount: u64, + ) { + // Get total stake on this hotkey on root. + let total: I96F32 = + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)); + + // Get increment + let increment: I96F32 = I96F32::saturating_from_num(amount) + .checked_div(total) + .unwrap_or(I96F32::saturating_from_num(0.0)); + + // Convert increment to u64, mapping negative values to 0 + let increment_u64: u64 = if increment.is_negative() { + 0 + } else { + increment.saturating_to_num::() + }; + + // Increment claimable for this subnet. + RootClaimable::::mutate(hotkey, netuid, |total| { + *total = total.saturating_add(increment_u64); + }); + } + + pub fn get_root_claimable_for_hotkey_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: u16, + ) -> I110F18 { + // Get this keys stake balance on root. + let root_stake: I110F18 = + I110F18::saturating_from_num(Self::get_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + Self::get_root_netuid(), + )); + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate: I110F18 = + I110F18::saturating_from_num(RootClaimable::::get(hotkey, netuid)); + + // Compute the proportion owed to this coldkey via balance. + let claimable: I110F18 = claimable_rate.saturating_mul(root_stake); + + claimable + } + + pub fn get_root_owed_for_hotkey_coldkey_float( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: u16, + ) -> I110F18 { + let claimable = Self::get_root_claimable_for_hotkey_coldkey(hotkey, coldkey, netuid); + + // Attain the claimable debt to avoid overclaiming. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Substract the debt. + let owed: I110F18 = claimable.saturating_sub(debt); + + owed + } + + pub fn get_root_owed_for_hotkey_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: u16, + ) -> u64 { + let owed = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); + + // Convert owed to u64, mapping negative values to 0 + let owed_u64: u64 = if owed.is_negative() { + 0 + } else { + owed.saturating_to_num::() + }; + + owed_u64 + } + + pub fn root_claim_on_subnet( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: u16, + root_claim_type: RootClaimTypeEnum, + ) { + // Substract the debt. + let owed: I110F18 = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); + + // Increase root debt by owed amount. + RootDebt::::mutate((hotkey, coldkey, netuid), |debt| { + *debt = debt.saturating_add(owed.saturating_to_num::()); + }); + + // Convert owed to u64, mapping negative values to 0 + let owed_u64: u64 = if owed.is_negative() { + 0 + } else { + owed.saturating_to_num::() + }; + + if owed_u64 == 0 { + return; // no-op + } + + match root_claim_type { + RootClaimTypeEnum::Swap => { + // Swap the alpha owed to TAO and then increase stake on root + let owed_tao: u64 = Self::swap_alpha_for_tao(netuid, owed_u64); + + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + Self::get_root_netuid(), + owed_tao, + ); + } + RootClaimTypeEnum::Keep => { + // Incerase the stake with the alpha owned + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, coldkey, netuid, owed_u64, + ); + } + }; + } + + pub fn root_claim_all(hotkey: &T::AccountId, coldkey: &T::AccountId) -> Weight { + let mut weight = Weight::default(); + + weight.saturating_accrue(T::DbWeight::get().reads(1)); + let root_claim_type = RootClaimType::::get(coldkey); + + // Iterate over all the subnets this hotkey has claimable for root. + RootClaimable::::iter_prefix(hotkey).for_each(|(netuid, _)| { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + weight.saturating_accrue(::WeightInfo::root_claim_on_subnet( + root_claim_type.clone(), + )); + + Self::root_claim_on_subnet(hotkey, coldkey, netuid, root_claim_type.clone()); + }); + + weight + } + + pub fn add_stake_adjust_debt_for_hotkey_and_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + amount: u64, + ) { + // Iterate over all the subnets this hotkey is staked on for root. + for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Increase debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_add( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + } + } + + pub fn remove_stake_adjust_debt_for_hotkey_and_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + amount: u64, + ) { + // Iterate over all the subnets this hotkey is staked on for root. + for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { + if netuid == Self::get_root_netuid() { + continue; // Skip the root netuid. + } + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); + + // Get current staker-debt. + let debt: I110F18 = + I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); + + // Decrease debt based on the claimable rate. + let new_debt: I110F18 = debt.saturating_sub( + claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), + ); + + // Set the new debt. + RootDebt::::insert( + (hotkey, coldkey, netuid), + new_debt.saturating_to_num::(), + ); + } + } + pub fn do_root_claim(coldkey: T::AccountId) -> Weight { let mut weight = Weight::default(); @@ -14,26 +250,36 @@ impl Pallet { weight.saturating_accrue(Self::root_claim_all(hotkey, &coldkey)); }); - weight.into() + Self::deposit_event(Event::RootClaimed(coldkey)); + + weight } - pub fn block_hash_to_indices(block_hash: T::Hash, k: usize, n: usize) -> Vec { - let block_hash_bytes = block_hash.as_bytes_slice(); - let mut indices = Vec::new(); - // k < n - let start_index: usize = block_hash_bytes[0..8].to_vec().into(); - let mut last_idx = start_index; - for i in 0..k { - let bh_idx = (i * 8) % 32; - let idx_step = block_hash_bytes[bh_idx..(bh_idx+8)].to_vec().into(); - let idx = (last_idx + idx_step) % n; - indices.push(idx as usize); - last_idx = idx; - } - indices - } + pub fn run_auto_claim_root_divs(last_block_hash: T::Hash) -> Weight { + let mut weight: Weight = Weight::default(); + + let n = NumStakingColdkeys::::get(); + let k = NumRootClaim::::get(); + weight.saturating_accrue(T::DbWeight::get().reads(2)); - pub fn run_auto_claim_root_divs(last_block_hash: T::Hash) { + let coldkeys_to_claim: Vec = Self::block_hash_to_indices(last_block_hash, k, n); + weight.saturating_accrue(::WeightInfo::block_hash_to_indices(k, n)); - } + for i in coldkeys_to_claim.iter() { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if let Ok(coldkey) = StakingColdkeys::::try_get(i) { + weight.saturating_accrue(Self::do_root_claim(coldkey.clone())); + } + + continue; + } + + weight + } + + pub fn change_root_claim_type(coldkey: &T::AccountId, new_type: RootClaimTypeEnum) { + RootClaimType::::insert(coldkey.clone(), new_type.clone()); + + Self::deposit_event(Event::RootClaimTypeSet(coldkey.clone(), new_type)); + } } diff --git a/pallets/subtensor/src/staking/stake_utils.rs b/pallets/subtensor/src/staking/stake_utils.rs index e8bafb1122..481aa41cc9 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -1,372 +1,10 @@ use super::*; -use frame_support::weights::Weight; use safe_math::*; use share_pool::{SharePool, SharePoolDataOperations}; use sp_std::ops::Neg; use substrate_fixed::types::{I110F18, I64F64, I96F32, U64F64}; impl Pallet { - pub fn change_root_claim_type(coldkey: &T::AccountId, new_type: RootClaimTypeEnum) -> Weight { - let mut weight = Weight::default(); - - // Get the current claim type. - let current_type: RootClaimTypeEnum = RootClaimType::::get(coldkey); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - // Claim now - weight.saturating_accrue(Self::do_root_claim(coldkey)); - - // Execute swap. - match (current_type, new_type) { - (RootClaimTypeEnum::Swap, RootClaimTypeEnum::Keep) => { - // Iterate over all the subnets this hotkey has claimable for root. - let hotkeys = StakingHotkeys::::get(&coldkey); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - - // Swap the claim type. - RootClaimType::::insert(coldkey, new_type); - weight.saturating_accrue(T::DbWeight::get().writes(1)); - - hotkeys.iter().for_each(|hotkey| { - weight.saturating_accrue(T::DbWeight::get().reads(1)); - - // Clear debt entry for the root netuid. - RootDebt::::remove((hotkey, coldkey, Self::get_root_netuid())); - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - - weight.saturating_accrue(T::DbWeight::get().reads(3)); - let stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - coldkey, - Self::get_root_netuid(), - ); - - // Deduct the stake from the root portion. - RootSwapPortion::::mutate(hotkey, |total| { - *total = total.saturating_sub(stake); - }); - - // Adjust debt for the stake with new type - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); // TODO: weights - Self::add_stake_adjust_debt_for_hotkey_and_coldkey(hotkey, coldkey, stake); - }); - } - (RootClaimTypeEnum::Keep, RootClaimTypeEnum::Swap) => { - // Swap the claim type. - RootClaimType::::insert(coldkey, new_type); - weight.saturating_accrue(T::DbWeight::get().writes(1)); - - let hotkeys = StakingHotkeys::::get(&coldkey); - weight.saturating_accrue(T::DbWeight::get().reads(1)); - - hotkeys.iter().for_each(|hotkey| { - // Clear debt entry for this hotkey on all subnets. - RootDebt::::clear_prefix((hotkey, coldkey), u32::MAX, None); - - weight.saturating_accrue(T::DbWeight::get().reads(1)); - let stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - coldkey, - Self::get_root_netuid(), - ); - - // Adjust debt for the stake with new type - weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - Self::add_stake_adjust_debt_for_hotkey_and_coldkey(hotkey, coldkey, stake); - // TODO: weights - }); - } - _ => {} - } - - weight - } - - // Get the amount of stake on this hotkey that has claim type of Swap. - pub fn get_root_swap_portion(hotkey: &T::AccountId) -> u64 { - let swap_portion: u64 = RootSwapPortion::::get(hotkey); - swap_portion - } - - pub fn increase_root_claimable_for_hotkey_and_subnet( - hotkey: &T::AccountId, - netuid: u16, - amount: u64, - ) { - // Get total stake on this hotkey on root. - let total: I96F32 = if netuid == Self::get_root_netuid() { - I96F32::saturating_from_num(Self::get_root_swap_portion(hotkey)) - } else { - I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, netuid)) - }; - - // Get increment - let increment: I96F32 = I96F32::saturating_from_num(amount) - .checked_div(total) - .unwrap_or(I96F32::saturating_from_num(0.0)); - - // Convert increment to u64, mapping negative values to 0 - let increment_u64: u64 = if increment.is_negative() { - 0 - } else { - increment.saturating_to_num::() - }; - - // Increment claimable for this subnet. - RootClaimable::::mutate(hotkey, netuid, |total| { - *total = total.saturating_add(increment_u64); - }); - } - - pub fn get_root_claimable_for_hotkey_coldkey( - hotkey: &T::AccountId, - coldkey: &T::AccountId, - netuid: u16, - ) -> I110F18 { - // Get this keys balance on root. - let balance: I110F18 = if netuid == Self::get_root_netuid() { - // If we are checking for the root netuid, we need to check if the coldkey has a claim type of Swap. - match RootClaimType::::get(coldkey) { - RootClaimTypeEnum::Swap => I110F18::saturating_from_num( - Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid), - ), - _ => 0.into(), - } - } else { - I110F18::saturating_from_num(Self::get_stake_for_hotkey_and_coldkey_on_subnet( - hotkey, - coldkey, - Self::get_root_netuid(), - )) - }; - - // Get the total claimable_rate for this hotkey and this network - let claimable_rate: I110F18 = - I110F18::saturating_from_num(RootClaimable::::get(hotkey, netuid)); - - // Compute the proportion owed to this coldkey via balance. - let claimable: I110F18 = claimable_rate.saturating_mul(balance); - - claimable - } - - pub fn get_root_owed_for_hotkey_coldkey_float( - hotkey: &T::AccountId, - coldkey: &T::AccountId, - netuid: u16, - ) -> I110F18 { - let claimable = Self::get_root_claimable_for_hotkey_coldkey(hotkey, coldkey, netuid); - - // Attain the claimable debt to avoid overclaiming. - let debt: I110F18 = - I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); - - // Substract the debt. - let owed: I110F18 = claimable - debt; - - owed - } - - pub fn get_root_owed_for_hotkey_coldkey( - hotkey: &T::AccountId, - coldkey: &T::AccountId, - netuid: u16, - ) -> u64 { - let owed = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); - - // Convert owed to u64, mapping negative values to 0 - let owed_u64: u64 = if owed.is_negative() { - 0 - } else { - owed.saturating_to_num::() - }; - - owed_u64 - } - - pub fn root_claim_on_subnet(hotkey: &T::AccountId, coldkey: &T::AccountId, netuid: u16) { - // Substract the debt. - let owed: I110F18 = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); - - // Increase root debt by owed amount. - RootDebt::::mutate((hotkey, coldkey, netuid), |debt| { - *debt = debt.saturating_add(owed.saturating_to_num::()); - }); - - // Convert owed to u64, mapping negative values to 0 - let owed_u64: u64 = if owed.is_negative() { - 0 - } else { - owed.saturating_to_num::() - }; - - // Actually perform the stake operation on the new network. - Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, owed_u64); - } - - pub fn root_claim_all(hotkey: &T::AccountId, coldkey: &T::AccountId) -> Weight { - let mut weight = Weight::default(); - - weight.saturating_accrue(T::DbWeight::get().reads(1)); - match RootClaimType::::get(coldkey) { - RootClaimTypeEnum::Swap => { - weight.saturating_accrue(T::DbWeight::get().reads(1)); - weight.saturating_accrue(::WeightInfo::root_claim_on_subnet()); - // Only need to claim on the root netuid. - Self::root_claim_on_subnet(hotkey, coldkey, Self::get_root_netuid()); - } - RootClaimTypeEnum::Keep => { - // Iterate over all the subnets this hotkey has claimable for root. - RootClaimable::::iter_prefix(hotkey).for_each(|(netuid, _)| { - weight.saturating_accrue(T::DbWeight::get().reads(1)); - if netuid == Self::get_root_netuid() { - // Skip the root netuid. - weight.saturating_accrue(T::DbWeight::get().reads(1)); - continue; - } - weight.saturating_accrue(::WeightInfo::root_claim_on_subnet()); - - Self::root_claim_on_subnet(hotkey, coldkey, netuid); - }); - } - } - - weight.into() - } - - pub fn add_stake_adjust_debt_for_hotkey_and_coldkey( - hotkey: &T::AccountId, - coldkey: &T::AccountId, - amount: u64, - ) { - // Get the current claim type. - let current_type: RootClaimTypeEnum = RootClaimType::::get(coldkey); - - match current_type { - RootClaimTypeEnum::Swap => { - // Only need to adjust the debt on the root netuid. - let netuid = Self::get_root_netuid(); - - let claimable_rate = RootClaimable::::get(hotkey, netuid); - - // Get the total claimable_rate for this hotkey and this network - let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); - - // Get current staker-debt. - let debt: I110F18 = - I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); - - // Increase debt based on the claimable rate. - let new_debt: I110F18 = debt.saturating_add( - claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), - ); - - // Set the new debt. - RootDebt::::insert( - (hotkey, coldkey, netuid), - new_debt.saturating_to_num::(), - ); - - // Add to the root portion for this hotkey. - RootSwapPortion::::mutate(hotkey, |total| { - *total = total.saturating_add(amount); - }); - } - RootClaimTypeEnum::Keep => { - // Iterate over all the subnets this hotkey is staked on for root. - for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { - if netuid == Self::get_root_netuid() { - continue; - } - - // Get the total claimable_rate for this hotkey and this network - let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); - - // Get current staker-debt. - let debt: I110F18 = - I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); - - // Increase debt based on the claimable rate. - let new_debt: I110F18 = debt.saturating_add( - claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), - ); - - // Set the new debt. - RootDebt::::insert( - (hotkey, coldkey, netuid), - new_debt.saturating_to_num::(), - ); - } - } - } - } - - pub fn remove_stake_adjust_debt_for_hotkey_and_coldkey( - hotkey: &T::AccountId, - coldkey: &T::AccountId, - amount: u64, - ) { - // Get the current claim type. - let current_type: RootClaimTypeEnum = RootClaimType::::get(coldkey); - - match current_type { - RootClaimTypeEnum::Swap => { - // Only need to adjust the debt on the root netuid. - let netuid = Self::get_root_netuid(); - - let claimable_rate = RootClaimable::::get(hotkey, netuid); - - // Get the total claimable_rate for this hotkey and this network - let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); - - // Get current staker-debt. - let debt: I110F18 = - I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); - - // Decrease debt based on the claimable rate. - let new_debt: I110F18 = debt.saturating_sub( - claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), - ); - - // Set the new debt. - RootDebt::::insert( - (hotkey, coldkey, netuid), - new_debt.saturating_to_num::(), - ); - - // Substract from the root portion for this hotkey. - RootSwapPortion::::mutate(hotkey, |total| { - *total = total.saturating_sub(amount); - }); - } - RootClaimTypeEnum::Keep => { - // Iterate over all the subnets this hotkey is staked on for root. - for (netuid, claimable_rate) in RootClaimable::::iter_prefix(hotkey) { - if netuid == Self::get_root_netuid() { - continue; // Skip the root netuid. - } - - // Get the total claimable_rate for this hotkey and this network - let claimable_rate_float = I110F18::saturating_from_num(claimable_rate); - - // Get current staker-debt. - let debt: I110F18 = - I110F18::saturating_from_num(RootDebt::::get((hotkey, coldkey, netuid))); - - // Decrease debt based on the claimable rate. - let new_debt: I110F18 = debt.saturating_sub( - claimable_rate_float.saturating_mul(I110F18::saturating_from_num(amount)), - ); - - // Set the new debt. - RootDebt::::insert( - (hotkey, coldkey, netuid), - new_debt.saturating_to_num::(), - ); - } - } - } - } - /// Retrieves the total alpha issuance for a given subnet. /// /// This function calculates the total alpha issuance by summing the alpha @@ -999,7 +637,7 @@ impl Pallet { // If this is a root-stake if netuid == Self::get_root_netuid() { // 5. Adjust root debt for this hotkey and coldkey. - Self::remove_stake_adjust_debt_for_hotkey_and_coldkey(&hotkey, &coldkey, alpha); + Self::remove_stake_adjust_debt_for_hotkey_and_coldkey(hotkey, coldkey, alpha); } // Step 5. Deposit and log the unstaking event. @@ -1064,7 +702,7 @@ impl Pallet { // If this is a root-stake if netuid == Self::get_root_netuid() { // 5. Adjust root debt for this hotkey and coldkey. - Self::add_stake_adjust_debt_for_hotkey_and_coldkey(&hotkey, &coldkey, alpha); + Self::add_stake_adjust_debt_for_hotkey_and_coldkey(hotkey, coldkey, alpha); } // Step 6. Deposit and log the staking event. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index b0d7c461ad..f0e0d35ef9 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -409,6 +409,7 @@ impl crate::Config for Test { type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; + type WeightInfo = crate::WeightInfo; } pub struct OriginPrivilegeCmp; diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 190d13e39b..e0e05c0243 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -1,5 +1,3 @@ -use core::hash::Hash; - use super::*; use crate::{ system::{ensure_root, ensure_signed_or_root, pallet_prelude::BlockNumberFor}, @@ -68,14 +66,9 @@ impl Pallet { .ok() .expect("blockchain will not exceed 2^64 blocks; QED.") } - pub fn get_last_block_hash() -> T::Hash { - let curr_block = >::block_number(); - if curr_block == 0 { - T::Hash::default() // does not exist, so just return default hash - } else { - >::block_hash(curr_block.saturating_sub(1)) - } - } + pub fn get_last_block_hash() -> T::Hash { + >::parent_hash() + } // ============================== // ==== YumaConsensus params ==== diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs index d57d20db52..105af29abb 100644 --- a/pallets/subtensor/src/weights.rs +++ b/pallets/subtensor/src/weights.rs @@ -3,16 +3,18 @@ #![allow(unused_imports)] #![allow(missing_docs)] +use crate::RootClaimTypeEnum; use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; pub trait WeightInfo { - fn root_claim_on_subnet() -> Weight; + fn root_claim_on_subnet(claim_type: RootClaimTypeEnum) -> Weight; + fn block_hash_to_indices(k: u64, n: u64) -> Weight; } pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - fn root_claim_on_subnet() -> Weight { + fn root_claim_on_subnet(_claim_type: RootClaimTypeEnum) -> Weight { // TODO should be replaced by benchmarked weights // Weight: // 100_000 + 5 reads @@ -22,14 +24,19 @@ impl WeightInfo for SubstrateWeight { Weight::default().saturating_add( T::DbWeight::get().reads_writes(9_u64, 4_u64) ).saturating_add( - 100_000.into() + Weight::from_parts(100_000, 0) ) } + + fn block_hash_to_indices(k: u64, _n: u64) -> Weight { + // TODO: should be benchmarked + Weight::from_parts(k.saturating_mul(100_000), 0) + } } // For backwards compatibility and tests. impl WeightInfo for () { - fn root_claim_on_subnet() -> Weight { + fn root_claim_on_subnet(_root_claim_type: RootClaimTypeEnum) -> Weight { // TODO should be replaced by benchmarked weights // Weight: // 100_000 + 5 reads @@ -37,9 +44,14 @@ impl WeightInfo for () { // 3 reads, 3 writes for increase stake // Total: 100_000 + 9 reads + 4 writes Weight::default().saturating_add( - T::DbWeight::get().reads_writes(9_u64, 4_u64) + RocksDbWeight::get().reads_writes(9_u64, 4_u64) ).saturating_add( - 100_000.into() + Weight::from_parts(100_000, 0) ) } + + fn block_hash_to_indices(k: u64, _n: u64) -> Weight { + // TODO: should be benchmarked + Weight::from_parts(k.saturating_mul(100_000), 0) + } } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 2a2d2fac47..f7be34a183 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1109,6 +1109,7 @@ impl pallet_subtensor::Config for Runtime { type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; + type WeightInfo = pallet_subtensor::weights::SubstrateWeight; } use sp_runtime::BoundedVec; From c7989b6b66b99d6b04277a22c1f7058a5b8f2859 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 1 Feb 2025 15:42:04 -0800 Subject: [PATCH 09/17] fix mod --- pallets/subtensor/src/staking/claim_root.rs | 5 ++++- pallets/subtensor/src/tests/mock.rs | 2 +- runtime/src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 528365547d..6e785b140b 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -25,7 +25,10 @@ impl Pallet { .try_into() .unwrap_or([0; 8]), ); - let idx = last_idx.saturating_add(idx_step).wrapping_rem(n); + let idx = last_idx + .saturating_add(idx_step) + .checked_rem(n) + .unwrap_or(0); indices.push(idx); last_idx = idx; } diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index f0e0d35ef9..c0d8decb9e 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -409,7 +409,7 @@ impl crate::Config for Test { type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; - type WeightInfo = crate::WeightInfo; + type WeightInfo = crate::weights::SubstrateWeight; } pub struct OriginPrivilegeCmp; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index f7be34a183..85e64e324b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1109,7 +1109,7 @@ impl pallet_subtensor::Config for Runtime { type Preimages = Preimage; type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; - type WeightInfo = pallet_subtensor::weights::SubstrateWeight; + type WeightInfo = pallet_subtensor::weights::SubstrateWeight; } use sp_runtime::BoundedVec; From e33cc4d85ef4879453eba1e8f51bca951e32ca28 Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Sat, 1 Feb 2025 15:49:30 -0800 Subject: [PATCH 10/17] dont claim if below min or zero --- pallets/subtensor/src/staking/claim_root.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index 6e785b140b..ab5819ca71 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -128,6 +128,10 @@ impl Pallet { // Substract the debt. let owed: I110F18 = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); + if owed == 0 || owed < I110F18::saturating_from_num(DefaultMinRootClaimAmount::::get()) { + return; // no-op + } + // Increase root debt by owed amount. RootDebt::::mutate((hotkey, coldkey, netuid), |debt| { *debt = debt.saturating_add(owed.saturating_to_num::()); From e59123f82081b6a35b6bbe4f7678c8d7da97549d Mon Sep 17 00:00:00 2001 From: unconst Date: Mon, 3 Feb 2025 13:49:59 -0500 Subject: [PATCH 11/17] root claim --- .../subtensor/src/coinbase/run_coinbase.rs | 547 ++++-------------- pallets/subtensor/src/lib.rs | 4 +- pallets/subtensor/src/staking/claim_root.rs | 4 +- 3 files changed, 108 insertions(+), 447 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 68959f5703..43e220ded1 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -19,30 +19,6 @@ pub struct WeightsTlockPayload { } 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 - } pub fn run_coinbase(block_emission: I96F32) { // --- 0. Get current block. @@ -55,22 +31,17 @@ impl Pallet { // --- 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 total_active_tao: I96F32 = I96F32::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; + let subnet_tao = I96F32::from_num(SubnetTAO::::get(*netuid)); + let new_subnet_tao = subnet_tao + .saturating_add(*mechanism_tao.entry(mechid).or_insert(I96F32::from_num(0))); + *mechanism_tao.entry(mechid).or_insert(I96F32::from_num(0)) = new_subnet_tao; total_active_tao = total_active_tao.saturating_add(subnet_tao); } log::debug!("Mechanism TAO sums: {:?}", mechanism_tao); @@ -86,12 +57,10 @@ impl Pallet { 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)); + let subnet_tao: I96F32 = I96F32::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)); + let mech_tao: I96F32 = *mechanism_tao.get(&mechid).unwrap_or(&I96F32::from_num(0)); log::debug!( "Mechanism TAO (T_m) for mechanism ID {:?}: {:?}", mechid, @@ -100,7 +69,7 @@ impl Pallet { // 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)); + .unwrap_or(I96F32::from_num(0)); log::debug!( "Mechanism proportion (P_m) for mechanism ID {:?}: {:?}", mechid, @@ -116,32 +85,26 @@ impl Pallet { // 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)); + .unwrap_or(I96F32::from_num(0)); log::debug!( "Subnet proportion (P_s) for netuid {:?}: {:?}", netuid, subnet_proportion ); - - // Only emit TAO if the subnetwork allows registration. - if Self::get_network_registration_allowed(*netuid) - || Self::get_network_pow_registration_allowed(*netuid) - { - // 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); - } + // 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::from_num(0)) + .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); } // --- 4. Distribute subnet emission into subnets based on mechanism type. @@ -208,55 +171,9 @@ impl Pallet { *total = total.saturating_add(tao_in_emission); log::debug!("Increased TotalIssuance: {:?}", *total); }); - - // 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); - - let remaining_emission: u64 = alpha_out_emission.saturating_sub(owner_cut); - log::debug!( - "Remaining emission for netuid {:?}: {:?}", - netuid, - remaining_emission - ); - - // 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); - // 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::()); - }); // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid, |total| { - *total = total.saturating_add(pending_alpha_emission.saturating_to_num::()); - }); - // Accumulate the owner cut in pending. - PendingOwnerCut::::mutate(*netuid, |total| { - *total = total.saturating_add(owner_cut); + *total = total.saturating_add( alpha_out_emission ); }); } @@ -280,25 +197,10 @@ impl Pallet { let pending_emission: 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); - 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); - PendingAlphaSwapped::::insert(netuid, 0); - - // 5.2.3 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. Self::drain_pending_emission( netuid, pending_emission, - pending_root_divs, - pending_alpha_swapped, - owner_cut, ); } else { // Increment @@ -309,25 +211,42 @@ impl Pallet { pub fn drain_pending_emission( netuid: u16, - pending_alpha_emission: u64, - pending_root_divs: u64, - pending_alpha_swapped: u64, - owner_cut: u64, + mut emission: 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 {:?} emission {:?}", netuid, - pending_alpha_emission, - pending_root_divs, - pending_alpha_swapped, - owner_cut + emission, ); + let zero: I96F32 = I96F32::from_num(0.0); - // === 1. Run the epoch() --> hotkey emission. + // Check for existence of owner cold/hot pair and distribute emission directly to them. + if let Ok(owner_coldkey) = SubnetOwner::::try_get(netuid) { + if let Ok(owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { + // Calculate the owner cut. + let owner_cut: u64 = I96F32::from_num(emission).saturating_mul(Self::get_float_subnet_owner_cut()).to_num::(); + log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); + + // Subtract the owner cut from the emission + emission = emission.saturating_sub( owner_cut ); + + // Compute and remove the owner cut. + // Increase stake for both coldkey and hotkey on the subnet + 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); + } + } + + // 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), + emission, ); log::debug!( "Hotkey emission for netuid {:?}: {:?}", @@ -335,331 +254,73 @@ impl Pallet { hotkey_emission ); - // === 2. Calculate the dividend distributions using the current stake. - // Clear maps + // Clear dividend payout accumulators.. 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, - 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, + // First automatically distribute mining emission. + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey.clone(), + &Owner::::get(hotkey.clone()), netuid, - dividend_tuples + incentive, ); - // 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 = Self::get_hotkey_take_float(hotkey_j); - - 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 - ); + // Second distribute the dividends through to parents via parent/children relationships. + let parent_dividends: Vec<(T::AccountId, u64)> = Self::get_dividends_distribution(&hotkey, netuid, dividends); - // 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::() - ); + // Actually perform the distribution + for (parent, divs) in parent_dividends.iter() { - // 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::() + // Set of values. + let mut rem_divs: I96F32 = I96F32::from_num( *divs ); + let owner: T::AccountId = Owner::::get(parent.clone()); + + // Remove the hotkey take straight off the top. + let take: I96F32 = Self::get_hotkey_take_float( parent ).saturating_mul( rem_divs ); + rem_divs = rem_divs.saturating_sub( take ); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + parent, + &owner, + netuid, + take.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::() + // Get the hotkey root TAO. + let root_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( + parent, + Self::get_root_netuid(), + )); + let root_alpha: I96F32 = root_tao.saturating_mul(Self::get_tao_weight()); + let local_alpha: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( parent, netuid )); + let total_alpha: I96F32 = root_alpha.saturating_add( local_alpha ); + + // Compute alpha and root proportions. + let local_prop: I96F32 = local_alpha.checked_div( total_alpha ).unwrap_or( zero ); + let root_prop: I96F32 = root_alpha.checked_div( total_alpha ).unwrap_or( zero ); + + // Compute alpha divs + let local_divs: I96F32 = rem_divs.saturating_mul( local_prop ); + let root_divs: I96F32 = rem_divs.saturating_mul( root_prop ); + + // Pay out alpha divs. + Self::increase_stake_for_hotkey_on_subnet( + parent, + netuid, + local_divs.to_num::(), ); - } - } - - let total_root_alpha_divs: u64 = root_alpha_divs - .values() - .sum::() - .saturating_add(validator_root_alpha_takes.values().sum::()); - - // === 3. Distribute the dividends to the hotkeys. - - // 3.1 --- Distribute owner cut. - // Check for existence of subnet owner cold/hot pair and distribute emission directly to them. - 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 - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &owner_hotkey, - &owner_coldkey, + // Add claimable + Self::increase_root_claimable_for_hotkey_and_subnet( + parent, netuid, - owner_cut, + root_divs.to_num::(), ); - 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. - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &miner_j.clone(), - &Owner::::get(miner_j.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. - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &validator_j.clone(), - &Owner::::get(validator_j.clone()), - netuid, - validator_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); - }); - 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. - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &validator_j.clone(), - &Owner::::get(validator_j.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 - ); - - // 3.5.3 --- Increase the root claimable for this hotkey. - Self::increase_root_claimable_for_hotkey_and_subnet( - &hotkey_j.clone(), - netuid, - root_divs_to_pay, - ); - log::debug!( - "Paid tao to hotkey claimable {:?} on root netuid from netuid {:?}: {:?}", - hotkey_j, - netuid, - root_divs_to_pay - ); + AlphaDividendsPerSubnet::::mutate( netuid, parent.clone(), |total| { + *total = total.saturating_add( rem_divs.to_num::() ); + }); + } - // 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); - }); - 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 112b2c781b..8f6465dab7 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1094,9 +1094,9 @@ pub mod pallet { >; #[pallet::storage] // --- MAP ( u64 ) --> coldkey | Maps coldkeys that have stake to an index - pub type StakingColdkeys = StorageMap<_, Identity, u64, T::AccountId, OptionQuery>; + pub type ColdkeysIndex = StorageMap<_, Identity, u64, T::AccountId, OptionQuery>; #[pallet::storage] // --- Value --> num_staking_coldkeys - pub type NumStakingColdkeys = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; + pub type NumColdkeys = StorageValue<_, u64, ValueQuery, DefaultZeroU64>; #[pallet::storage] // --- Value --> num_root_claim | Number of coldkeys to claim each auto-claim. pub type NumRootClaim = StorageValue<_, u64, ValueQuery, DefaultNumRootClaim>; diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs index ab5819ca71..3aad76b7ee 100644 --- a/pallets/subtensor/src/staking/claim_root.rs +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -265,7 +265,7 @@ impl Pallet { pub fn run_auto_claim_root_divs(last_block_hash: T::Hash) -> Weight { let mut weight: Weight = Weight::default(); - let n = NumStakingColdkeys::::get(); + let n = NumColdkeys::::get(); let k = NumRootClaim::::get(); weight.saturating_accrue(T::DbWeight::get().reads(2)); @@ -274,7 +274,7 @@ impl Pallet { for i in coldkeys_to_claim.iter() { weight.saturating_accrue(T::DbWeight::get().reads(1)); - if let Ok(coldkey) = StakingColdkeys::::try_get(i) { + if let Ok(coldkey) = ColdkeysIndex::::try_get(i) { weight.saturating_accrue(Self::do_root_claim(coldkey.clone())); } From caf7de7cebc46e9ff15c8e5c5c2f77f450d74476 Mon Sep 17 00:00:00 2001 From: unconst Date: Mon, 3 Feb 2025 14:02:40 -0500 Subject: [PATCH 12/17] clean coinbase --- .../subtensor/src/coinbase/run_coinbase.rs | 119 +++++++++--------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 43e220ded1..d66876036c 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -254,74 +254,75 @@ impl Pallet { hotkey_emission ); - // Clear dividend payout accumulators.. - let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); - for (hotkey, incentive, dividends) in hotkey_emission { + // Precompute all parent dividends. + let mut all_parent_dividends: Vec<(T::AccountId, u64)> = vec![]; + for (hotkey, _, dividends) in &hotkey_emission { + all_parent_dividends.extend( Self::get_dividends_distribution( hotkey, netuid, *dividends) ); + } + // Pay out all mining incentives. + for (hotkey, incentive, _) in &hotkey_emission { // First automatically distribute mining emission. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey.clone(), - &Owner::::get(hotkey.clone()), + hotkey, + &Owner::::get( hotkey ), netuid, - incentive, + *incentive, ); + } - // Second distribute the dividends through to parents via parent/children relationships. - let parent_dividends: Vec<(T::AccountId, u64)> = Self::get_dividends_distribution(&hotkey, netuid, dividends); - - // Actually perform the distribution - for (parent, divs) in parent_dividends.iter() { - - // Set of values. - let mut rem_divs: I96F32 = I96F32::from_num( *divs ); - let owner: T::AccountId = Owner::::get(parent.clone()); - - // Remove the hotkey take straight off the top. - let take: I96F32 = Self::get_hotkey_take_float( parent ).saturating_mul( rem_divs ); - rem_divs = rem_divs.saturating_sub( take ); - Self::increase_stake_for_hotkey_and_coldkey_on_subnet( - parent, - &owner, - netuid, - take.to_num::(), - ); - - // Get the hotkey root TAO. - let root_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( - parent, - Self::get_root_netuid(), - )); - let root_alpha: I96F32 = root_tao.saturating_mul(Self::get_tao_weight()); - let local_alpha: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( parent, netuid )); - let total_alpha: I96F32 = root_alpha.saturating_add( local_alpha ); - - // Compute alpha and root proportions. - let local_prop: I96F32 = local_alpha.checked_div( total_alpha ).unwrap_or( zero ); - let root_prop: I96F32 = root_alpha.checked_div( total_alpha ).unwrap_or( zero ); - - // Compute alpha divs - let local_divs: I96F32 = rem_divs.saturating_mul( local_prop ); - let root_divs: I96F32 = rem_divs.saturating_mul( root_prop ); - - // Pay out alpha divs. - Self::increase_stake_for_hotkey_on_subnet( - parent, - netuid, - local_divs.to_num::(), - ); - // Add claimable - Self::increase_root_claimable_for_hotkey_and_subnet( - parent, - netuid, - root_divs.to_num::(), - ); + // Actually perform the distribution to parents. + let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); + for (parent, divs) in all_parent_dividends.iter() { + // Set of values. + let mut rem_divs: I96F32 = I96F32::from_num( *divs ); + let owner: T::AccountId = Owner::::get(parent.clone()); + + // Remove the hotkey take straight off the top. + let take: I96F32 = Self::get_hotkey_take_float( parent ).saturating_mul( rem_divs ); + rem_divs = rem_divs.saturating_sub( take ); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + parent, + &owner, + netuid, + take.to_num::(), + ); - AlphaDividendsPerSubnet::::mutate( netuid, parent.clone(), |total| { - *total = total.saturating_add( rem_divs.to_num::() ); - }); - } + // Get the hotkey root TAO. + let root_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( + parent, + Self::get_root_netuid(), + )); + let root_alpha: I96F32 = root_tao.saturating_mul(Self::get_tao_weight()); + let local_alpha: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( parent, netuid )); + let total_alpha: I96F32 = root_alpha.saturating_add( local_alpha ); + + // Compute alpha and root proportions. + let local_prop: I96F32 = local_alpha.checked_div( total_alpha ).unwrap_or( zero ); + let root_prop: I96F32 = root_alpha.checked_div( total_alpha ).unwrap_or( zero ); + + // Compute alpha divs + let local_divs: I96F32 = rem_divs.saturating_mul( local_prop ); + let root_divs: I96F32 = rem_divs.saturating_mul( root_prop ); + + // Pay out alpha divs. + Self::increase_stake_for_hotkey_on_subnet( + parent, + netuid, + local_divs.to_num::(), + ); + // Add claimable + Self::increase_root_claimable_for_hotkey_and_subnet( + parent, + netuid, + root_divs.to_num::(), + ); + AlphaDividendsPerSubnet::::mutate( netuid, parent.clone(), |total| { + *total = total.saturating_add( rem_divs.to_num::() ); + }); } + } /// Returns the self contribution of a hotkey on a subnet. From b23ec260405c47c01ee6dfea3226ff7d620613c1 Mon Sep 17 00:00:00 2001 From: unconst Date: Mon, 3 Feb 2025 14:12:41 -0500 Subject: [PATCH 13/17] fix initi --- .../subtensor/src/migrations/migrate_rao.rs | 15 ++++----------- pallets/subtensor/src/subnets/subnet.rs | 19 ++++++++----------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index 3c034b7dad..9b87df9ceb 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -73,20 +73,13 @@ pub fn migrate_rao() -> Weight { let lock: u64 = SubnetLocked::::get(netuid); // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha - // The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO. - let pool_initial_tao = 100_000_000_000.min(lock.max(1)); - + // The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 1 TAO. + let pool_initial_tao = 1_000_000_000.min(lock.max(1)); let remaining_lock = lock.saturating_sub(pool_initial_tao); // Refund the owner for the remaining lock. Pallet::::add_balance_to_coldkey_account(&owner, remaining_lock); - SubnetTAO::::insert(netuid, pool_initial_tao); // Set TAO to the lock. - - SubnetAlphaIn::::insert( - netuid, - pool_initial_tao.saturating_mul(netuids.len() as u64), - ); // Set AlphaIn to the initial alpha distribution. - - SubnetAlphaOut::::insert(netuid, 0); // Set zero subnet alpha out. + SubnetTAO::::insert(netuid, pool_initial_tao ); + SubnetAlphaIn::::insert( netuid, pool_initial_tao ); SubnetMechanism::::insert(netuid, 1); // Convert to dynamic immediately with initialization. Tempo::::insert(netuid, DefaultTempo::::get()); // Set the token symbol for this subnet using Self instead of Pallet:: diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 942f6fe8ba..b1da2a957c 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -233,18 +233,15 @@ impl Pallet { Self::get_symbol_for_subnet(netuid_to_register), ); // Set subnet token symbol. - // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha - // The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO. - let pool_initial_tao = 100_000_000_000.min(actual_tao_lock_amount.max(1)); - - let actual_tao_lock_amount_less_pool_tao = - actual_tao_lock_amount.saturating_sub(pool_initial_tao); - SubnetTAO::::insert(netuid_to_register, pool_initial_tao); - SubnetAlphaIn::::insert( + SubnetTAO::::insert( netuid_to_register, actual_tao_lock_amount); + SubnetAlphaIn::::insert( netuid_to_register, actual_tao_lock_amount ); + SubnetAlphaOut::::insert( netuid_to_register, actual_tao_lock_amount ); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, netuid_to_register, - pool_initial_tao.saturating_mul(Self::get_all_subnet_netuids().len() as u64), - ); // Set AlphaIn to the initial alpha distribution. - + actual_tao_lock_amount + ) SubnetOwner::::insert(netuid_to_register, coldkey.clone()); SubnetOwnerHotkey::::insert(netuid_to_register, hotkey.clone()); TotalStakeAtDynamic::::insert(netuid_to_register, TotalStake::::get()); From 556e60e9ec9ac07f653b9d1ef59f968eaf4488eb Mon Sep 17 00:00:00 2001 From: Cameron Fairchild Date: Mon, 3 Feb 2025 12:01:01 -0800 Subject: [PATCH 14/17] typo --- pallets/subtensor/src/subnets/subnet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index b1da2a957c..8f135cf0da 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -241,7 +241,7 @@ impl Pallet { coldkey, netuid_to_register, actual_tao_lock_amount - ) + ); SubnetOwner::::insert(netuid_to_register, coldkey.clone()); SubnetOwnerHotkey::::insert(netuid_to_register, hotkey.clone()); TotalStakeAtDynamic::::insert(netuid_to_register, TotalStake::::get()); From e7f933589eef4078fad2ced20a8eea17f43dec7d Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 3 Feb 2025 16:28:59 -0500 Subject: [PATCH 15/17] Fix tests and unsafe math --- .../subtensor/src/coinbase/run_coinbase.rs | 68 ++++++++++--------- pallets/subtensor/src/staking/helpers.rs | 6 +- pallets/subtensor/src/subnets/subnet.rs | 8 +-- pallets/subtensor/src/tests/children.rs | 8 ++- pallets/subtensor/src/tests/migration.rs | 4 +- pallets/subtensor/src/tests/mock.rs | 2 +- pallets/subtensor/src/tests/networks.rs | 6 +- 7 files changed, 53 insertions(+), 49 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index d66876036c..63aa9d75a7 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -31,17 +31,17 @@ impl Pallet { // --- 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::from_num(0); + 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::from_num(SubnetTAO::::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::from_num(0))); - *mechanism_tao.entry(mechid).or_insert(I96F32::from_num(0)) = new_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); } log::debug!("Mechanism TAO sums: {:?}", mechanism_tao); @@ -57,10 +57,10 @@ impl Pallet { 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::from_num(SubnetTAO::::get(*netuid)); + 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::from_num(0)); + 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, @@ -69,7 +69,7 @@ impl Pallet { // 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::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); log::debug!( "Mechanism proportion (P_m) for mechanism ID {:?}: {:?}", mechid, @@ -85,26 +85,32 @@ impl Pallet { // 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::from_num(0)); + .unwrap_or(I96F32::saturating_from_num(0)); log::debug!( "Subnet proportion (P_s) for netuid {:?}: {:?}", netuid, subnet_proportion ); - // 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::from_num(0)) - .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); + + // Only emit TAO if the subnetwork allows registration. + if Self::get_network_registration_allowed(*netuid) + || Self::get_network_pow_registration_allowed(*netuid) + { + // 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); + } } // --- 4. Distribute subnet emission into subnets based on mechanism type. @@ -218,13 +224,13 @@ impl Pallet { netuid, emission, ); - let zero: I96F32 = I96F32::from_num(0.0); + let zero: I96F32 = I96F32::saturating_from_num(0.0); // Check for existence of owner cold/hot pair and distribute emission directly to them. if let Ok(owner_coldkey) = SubnetOwner::::try_get(netuid) { if let Ok(owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { // Calculate the owner cut. - let owner_cut: u64 = I96F32::from_num(emission).saturating_mul(Self::get_float_subnet_owner_cut()).to_num::(); + let owner_cut: u64 = I96F32::saturating_from_num(emission).saturating_mul(Self::get_float_subnet_owner_cut()).saturating_to_num::(); log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); // Subtract the owner cut from the emission @@ -275,7 +281,7 @@ impl Pallet { let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); for (parent, divs) in all_parent_dividends.iter() { // Set of values. - let mut rem_divs: I96F32 = I96F32::from_num( *divs ); + let mut rem_divs: I96F32 = I96F32::saturating_from_num( *divs ); let owner: T::AccountId = Owner::::get(parent.clone()); // Remove the hotkey take straight off the top. @@ -285,16 +291,16 @@ impl Pallet { parent, &owner, netuid, - take.to_num::(), + take.saturating_to_num::(), ); // Get the hotkey root TAO. - let root_tao: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( + let root_tao: I96F32 = I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( parent, Self::get_root_netuid(), )); let root_alpha: I96F32 = root_tao.saturating_mul(Self::get_tao_weight()); - let local_alpha: I96F32 = I96F32::from_num(Self::get_stake_for_hotkey_on_subnet( parent, netuid )); + let local_alpha: I96F32 = I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( parent, netuid )); let total_alpha: I96F32 = root_alpha.saturating_add( local_alpha ); // Compute alpha and root proportions. @@ -309,17 +315,17 @@ impl Pallet { Self::increase_stake_for_hotkey_on_subnet( parent, netuid, - local_divs.to_num::(), + local_divs.saturating_to_num::(), ); // Add claimable Self::increase_root_claimable_for_hotkey_and_subnet( parent, netuid, - root_divs.to_num::(), + root_divs.saturating_to_num::(), ); AlphaDividendsPerSubnet::::mutate( netuid, parent.clone(), |total| { - *total = total.saturating_add( rem_divs.to_num::() ); + *total = total.saturating_add( rem_divs.saturating_to_num::() ); }); } diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index eb16a4af56..7141f7438a 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -1,4 +1,5 @@ use super::*; +use safe_math::*; use substrate_fixed::types::I96F32; use frame_support::traits::{ @@ -122,9 +123,8 @@ impl Pallet { } pub fn get_hotkey_take_float(hotkey: &T::AccountId) -> I96F32 { - I96F32::from_num(Self::get_hotkey_take(hotkey)) - .checked_div(I96F32::from_num(u16::MAX)) - .unwrap_or(I96F32::from_num(0.0)) + I96F32::saturating_from_num(Self::get_hotkey_take(hotkey)) + .safe_div(I96F32::saturating_from_num(u16::MAX)) } /// Returns true if the hotkey account has been created. diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index b1da2a957c..01bd116c90 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -238,18 +238,14 @@ impl Pallet { SubnetAlphaOut::::insert( netuid_to_register, actual_tao_lock_amount ); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, - coldkey, + &coldkey, netuid_to_register, actual_tao_lock_amount - ) + ); SubnetOwner::::insert(netuid_to_register, coldkey.clone()); SubnetOwnerHotkey::::insert(netuid_to_register, hotkey.clone()); TotalStakeAtDynamic::::insert(netuid_to_register, TotalStake::::get()); - if actual_tao_lock_amount_less_pool_tao > 0 { - Self::burn_tokens(actual_tao_lock_amount_less_pool_tao); - } - // --- 15. Add the identity if it exists if let Some(identity_value) = identity { ensure!( diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 7e30a22514..6d9e4e9260 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3104,7 +3104,7 @@ fn test_childkey_take_drain_validator_take() { // - 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 +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_childkey_multiple_parents_emission --exact --show-output #[test] fn test_childkey_multiple_parents_emission() { new_test_ext(1).execute_with(|| { @@ -3112,7 +3112,8 @@ fn test_childkey_multiple_parents_emission() { 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 + + // Set subnet owner cut to 0 SubtensorModule::set_subnet_owner_cut(0); SubtensorModule::set_tao_weight(0); // No TAO weight @@ -3310,9 +3311,10 @@ fn test_childkey_multiple_parents_emission() { // 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::(); + let owner_cut: u64 = (I96F32::from_num(total_emission) * (I96F32::from_num(1) - SubtensorModule::get_float_subnet_owner_cut())).to_num::(); assert_abs_diff_eq!( total_stake, - initial_total_stake + total_emission, + initial_total_stake + total_emission - owner_cut, epsilon = total_emission / 100, ); }); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index 7939083362..8d3dd2d507 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -551,7 +551,7 @@ fn test_migrate_commit_reveal_2() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --workspace --test migration -- test_migrate_rao --exact --nocapture +// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::migration::test_migrate_rao --exact --show-output #[test] fn test_migrate_rao() { new_test_ext(1).execute_with(|| { @@ -600,7 +600,7 @@ fn test_migrate_rao() { assert_eq!(SubnetTAO::::get(netuid_0), 4 * stake_amount); // Root has everything assert_eq!(SubnetTAO::::get(netuid_1), lock_amount); // Initial Rao amount. assert_eq!(SubnetAlphaIn::::get(netuid_0), 1); // No Alpha in pool on root. - assert_eq!(SubnetAlphaIn::::get(netuid_1), 2 * lock_amount); // Initial Rao amount == num_subnets * lock_amount + assert_eq!(SubnetAlphaIn::::get(netuid_1), lock_amount); // Initial Rao amount == lock_amount assert_eq!(SubnetAlphaOut::::get(netuid_0), 4 * stake_amount); // All stake is outstanding. assert_eq!(SubnetAlphaOut::::get(netuid_1), 0); // No stake outstanding. diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index c0d8decb9e..211c63bd2f 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -670,7 +670,7 @@ pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> u16 { let netuid = SubtensorModule::get_next_netuid(); let lock_cost = SubtensorModule::get_network_lock_cost(); - SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost); + SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost + ExistentialDeposit::get()); assert_ok!(SubtensorModule::register_network( RawOrigin::Signed(*coldkey).into(), diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 61fed23bf1..ba16101495 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -324,8 +324,8 @@ fn test_register_subnet_high_lock_cost() { let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); assert!(SubtensorModule::if_subnet_exist(netuid)); - // Ensure that both Subnet TAO and Subnet Alpha In equal to 100 TAO - assert_eq!(SubnetTAO::::get(netuid), 100_000_000_000,); - assert_eq!(SubnetAlphaIn::::get(netuid), 100_000_000_000,); + // Ensure that both Subnet TAO and Subnet Alpha In equal to lock cost + assert_eq!(SubnetTAO::::get(netuid), lock_cost); + assert_eq!(SubnetAlphaIn::::get(netuid), lock_cost); }) } From 2bfe264bd1728fe17b098fe41c54cf704f73780e Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 3 Feb 2025 17:27:07 -0500 Subject: [PATCH 16/17] Fix all tests --- .../subtensor/src/coinbase/run_coinbase.rs | 79 +++++++++---------- pallets/subtensor/src/macros/dispatches.rs | 2 +- .../subtensor/src/migrations/migrate_rao.rs | 12 ++- pallets/subtensor/src/subnets/subnet.rs | 8 +- pallets/subtensor/src/tests/children.rs | 8 +- pallets/subtensor/src/tests/mock.rs | 9 +++ pallets/subtensor/src/tests/networks.rs | 31 +++++--- pallets/subtensor/src/tests/staking.rs | 1 + 8 files changed, 89 insertions(+), 61 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 63aa9d75a7..aa02d78192 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -19,7 +19,6 @@ pub struct WeightsTlockPayload { } impl Pallet { - pub fn run_coinbase(block_emission: I96F32) { // --- 0. Get current block. let current_block: u64 = Self::get_current_block_as_u64(); @@ -39,9 +38,14 @@ impl Pallet { } // 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; + 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); } log::debug!("Mechanism TAO sums: {:?}", mechanism_tao); @@ -60,7 +64,9 @@ impl Pallet { 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)); + 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, @@ -179,7 +185,7 @@ impl Pallet { }); // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid, |total| { - *total = total.saturating_add( alpha_out_emission ); + *total = total.saturating_add(alpha_out_emission); }); } @@ -204,10 +210,7 @@ impl Pallet { PendingEmission::::insert(netuid, 0); // 5.2.4 Drain pending root divs, alpha emission, and owner cut. - Self::drain_pending_emission( - netuid, - pending_emission, - ); + Self::drain_pending_emission(netuid, pending_emission); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -215,10 +218,7 @@ impl Pallet { } } - pub fn drain_pending_emission( - netuid: u16, - mut emission: u64, - ) { + pub fn drain_pending_emission(netuid: u16, mut emission: u64) { log::debug!( "Draining pending alpha emission for netuid {:?} emission {:?}", netuid, @@ -230,11 +230,13 @@ impl Pallet { if let Ok(owner_coldkey) = SubnetOwner::::try_get(netuid) { if let Ok(owner_hotkey) = SubnetOwnerHotkey::::try_get(netuid) { // Calculate the owner cut. - let owner_cut: u64 = I96F32::saturating_from_num(emission).saturating_mul(Self::get_float_subnet_owner_cut()).saturating_to_num::(); + let owner_cut: u64 = I96F32::saturating_from_num(emission) + .saturating_mul(Self::get_float_subnet_owner_cut()) + .saturating_to_num::(); log::debug!("Owner cut for netuid {:?}: {:?}", netuid, owner_cut); // Subtract the owner cut from the emission - emission = emission.saturating_sub( owner_cut ); + emission = emission.saturating_sub(owner_cut); // Compute and remove the owner cut. // Increase stake for both coldkey and hotkey on the subnet @@ -250,10 +252,7 @@ impl Pallet { // 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, - emission, - ); + let hotkey_emission: Vec<(T::AccountId, u64, u64)> = Self::epoch(netuid, emission); log::debug!( "Hotkey emission for netuid {:?}: {:?}", netuid, @@ -263,7 +262,8 @@ impl Pallet { // Precompute all parent dividends. let mut all_parent_dividends: Vec<(T::AccountId, u64)> = vec![]; for (hotkey, _, dividends) in &hotkey_emission { - all_parent_dividends.extend( Self::get_dividends_distribution( hotkey, netuid, *dividends) ); + all_parent_dividends + .extend(Self::get_dividends_distribution(hotkey, netuid, *dividends)); } // Pay out all mining incentives. @@ -271,7 +271,7 @@ impl Pallet { // First automatically distribute mining emission. Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, - &Owner::::get( hotkey ), + &Owner::::get(hotkey), netuid, *incentive, ); @@ -281,12 +281,12 @@ impl Pallet { let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); for (parent, divs) in all_parent_dividends.iter() { // Set of values. - let mut rem_divs: I96F32 = I96F32::saturating_from_num( *divs ); - let owner: T::AccountId = Owner::::get(parent.clone()); - + let mut rem_divs: I96F32 = I96F32::saturating_from_num(*divs); + let owner: T::AccountId = Owner::::get(parent.clone()); + // Remove the hotkey take straight off the top. - let take: I96F32 = Self::get_hotkey_take_float( parent ).saturating_mul( rem_divs ); - rem_divs = rem_divs.saturating_sub( take ); + let take: I96F32 = Self::get_hotkey_take_float(parent).saturating_mul(rem_divs); + rem_divs = rem_divs.saturating_sub(take); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( parent, &owner, @@ -295,21 +295,21 @@ impl Pallet { ); // Get the hotkey root TAO. - let root_tao: I96F32 = I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( - parent, - Self::get_root_netuid(), - )); + let root_tao: I96F32 = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_on_subnet(parent, Self::get_root_netuid()), + ); let root_alpha: I96F32 = root_tao.saturating_mul(Self::get_tao_weight()); - let local_alpha: I96F32 = I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet( parent, netuid )); - let total_alpha: I96F32 = root_alpha.saturating_add( local_alpha ); + let local_alpha: I96F32 = + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(parent, netuid)); + let total_alpha: I96F32 = root_alpha.saturating_add(local_alpha); // Compute alpha and root proportions. - let local_prop: I96F32 = local_alpha.checked_div( total_alpha ).unwrap_or( zero ); - let root_prop: I96F32 = root_alpha.checked_div( total_alpha ).unwrap_or( zero ); + let local_prop: I96F32 = local_alpha.checked_div(total_alpha).unwrap_or(zero); + let root_prop: I96F32 = root_alpha.checked_div(total_alpha).unwrap_or(zero); // Compute alpha divs - let local_divs: I96F32 = rem_divs.saturating_mul( local_prop ); - let root_divs: I96F32 = rem_divs.saturating_mul( root_prop ); + let local_divs: I96F32 = rem_divs.saturating_mul(local_prop); + let root_divs: I96F32 = rem_divs.saturating_mul(root_prop); // Pay out alpha divs. Self::increase_stake_for_hotkey_on_subnet( @@ -324,11 +324,10 @@ impl Pallet { root_divs.saturating_to_num::(), ); - AlphaDividendsPerSubnet::::mutate( netuid, parent.clone(), |total| { - *total = total.saturating_add( rem_divs.saturating_to_num::() ); + AlphaDividendsPerSubnet::::mutate(netuid, parent.clone(), |total| { + *total = total.saturating_add(rem_divs.saturating_to_num::()); }); } - } /// Returns the self contribution of a hotkey on a subnet. diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 0b540832d8..faf8e397e6 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1874,7 +1874,7 @@ mod dispatches { /// /// # Events /// May emit a `StakeSwapped` event on success. - #[pallet::call_index(91)] + #[pallet::call_index(92)] #[pallet::weight(( Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Operational, diff --git a/pallets/subtensor/src/migrations/migrate_rao.rs b/pallets/subtensor/src/migrations/migrate_rao.rs index d016574cc4..544e9b47d9 100644 --- a/pallets/subtensor/src/migrations/migrate_rao.rs +++ b/pallets/subtensor/src/migrations/migrate_rao.rs @@ -72,7 +72,13 @@ pub fn migrate_rao() -> Weight { continue; } let owner = SubnetOwner::::get(netuid); - let lock = SubnetLocked::::get(netuid); + let mut lock = SubnetLocked::::get(netuid); + + // Subnet 1 doesn't have any lock, so we'll mint 1 TAO for them + if lock == 0 { + lock = 1_000_000_000; + // TODO: Update TotalIssuance properly + } // Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha // The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO. @@ -81,8 +87,8 @@ pub fn migrate_rao() -> Weight { let remaining_lock = lock.saturating_sub(pool_initial_tao); // Refund the owner for the remaining lock. Pallet::::add_balance_to_coldkey_account(&owner, remaining_lock); - SubnetTAO::::insert(netuid, pool_initial_tao ); - SubnetAlphaIn::::insert( netuid, pool_initial_tao ); + SubnetTAO::::insert(netuid, pool_initial_tao); + SubnetAlphaIn::::insert(netuid, pool_initial_tao); SubnetMechanism::::insert(netuid, 1); // Convert to dynamic immediately with initialization. Tempo::::insert(netuid, DefaultTempo::::get()); // Set the token symbol for this subnet using Self instead of Pallet:: diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index 28f1b3983e..7419f5797c 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -235,14 +235,14 @@ impl Pallet { Self::get_symbol_for_subnet(netuid_to_register), ); // Set subnet token symbol. - SubnetTAO::::insert( netuid_to_register, actual_tao_lock_amount); - SubnetAlphaIn::::insert( netuid_to_register, actual_tao_lock_amount ); - SubnetAlphaOut::::insert( netuid_to_register, actual_tao_lock_amount ); + SubnetTAO::::insert(netuid_to_register, actual_tao_lock_amount); + SubnetAlphaIn::::insert(netuid_to_register, actual_tao_lock_amount); + SubnetAlphaOut::::insert(netuid_to_register, actual_tao_lock_amount); Self::increase_stake_for_hotkey_and_coldkey_on_subnet( hotkey, &coldkey, netuid_to_register, - actual_tao_lock_amount + actual_tao_lock_amount, ); SubnetOwner::::insert(netuid_to_register, coldkey.clone()); SubnetOwnerHotkey::::insert(netuid_to_register, hotkey.clone()); diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 6d9e4e9260..053ff65a3c 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3308,14 +3308,16 @@ fn test_childkey_multiple_parents_emission() { 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 + // Allow 0.1% for slippage, etc. let total_stake = parent1_stake + parent2_stake + child_stake + weight_setter_stake; let initial_total_stake: u64 = initial_actual_stakes.iter().sum::(); - let owner_cut: u64 = (I96F32::from_num(total_emission) * (I96F32::from_num(1) - SubtensorModule::get_float_subnet_owner_cut())).to_num::(); + let owner_cut: u64 = (I96F32::from_num(total_emission) + * (I96F32::from_num(1) - SubtensorModule::get_float_subnet_owner_cut())) + .to_num::(); assert_abs_diff_eq!( total_stake, initial_total_stake + total_emission - owner_cut, - epsilon = total_emission / 100, + epsilon = (initial_total_stake + total_emission - owner_cut) / 1000, ); }); } diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 211c63bd2f..489e977ae1 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -669,6 +669,10 @@ pub fn add_network(netuid: u16, tempo: u16, _modality: u16) { #[allow(dead_code)] pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> u16 { let netuid = SubtensorModule::get_next_netuid(); + + // Make lock cost to be zero + SubtensorModule::set_network_last_lock(0); + SubtensorModule::set_network_min_lock(0); let lock_cost = SubtensorModule::get_network_lock_cost(); SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost + ExistentialDeposit::get()); @@ -678,6 +682,11 @@ pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> u16 { )); NetworkRegistrationAllowed::::insert(netuid, true); NetworkPowRegistrationAllowed::::insert(netuid, true); + + // Add some liquidity + SubnetTAO::::insert(netuid, 100_000_000_000); + SubnetAlphaIn::::insert(netuid, 100_000_000_000); + netuid } diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index ba16101495..ae82114191 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -294,18 +294,20 @@ fn test_register_subnet_low_lock_cost() { let subnet_owner_coldkey = U256::from(1); let subnet_owner_hotkey = U256::from(2); - let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let netuid = SubtensorModule::get_next_netuid(); + SubtensorModule::add_balance_to_coldkey_account( + &subnet_owner_coldkey, + lock_cost + ExistentialDeposit::get(), + ); + assert_ok!(SubtensorModule::register_network( + frame_system::RawOrigin::Signed(subnet_owner_coldkey).into(), + subnet_owner_hotkey + )); assert!(SubtensorModule::if_subnet_exist(netuid)); // Ensure that both Subnet TAO and Subnet Alpha In equal to (actual) lock_cost - assert_eq!( - SubnetTAO::::get(netuid), - lock_cost - ExistentialDeposit::get(), - ); - assert_eq!( - SubnetAlphaIn::::get(netuid), - lock_cost - ExistentialDeposit::get(), - ); + assert_eq!(SubnetTAO::::get(netuid), lock_cost,); + assert_eq!(SubnetAlphaIn::::get(netuid), lock_cost,); }) } @@ -321,7 +323,16 @@ fn test_register_subnet_high_lock_cost() { let subnet_owner_coldkey = U256::from(1); let subnet_owner_hotkey = U256::from(2); - let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey); + let netuid = SubtensorModule::get_next_netuid(); + SubtensorModule::add_balance_to_coldkey_account( + &subnet_owner_coldkey, + lock_cost + ExistentialDeposit::get(), + ); + assert_ok!(SubtensorModule::register_network( + frame_system::RawOrigin::Signed(subnet_owner_coldkey).into(), + subnet_owner_hotkey + )); + assert!(SubtensorModule::if_subnet_exist(netuid)); // Ensure that both Subnet TAO and Subnet Alpha In equal to lock cost diff --git a/pallets/subtensor/src/tests/staking.rs b/pallets/subtensor/src/tests/staking.rs index 5282c9941c..78591233ac 100644 --- a/pallets/subtensor/src/tests/staking.rs +++ b/pallets/subtensor/src/tests/staking.rs @@ -3263,6 +3263,7 @@ fn test_max_amount_move_dynamic_dynamic() { }); } +// cargo test --package pallet-subtensor --lib -- tests::staking::test_add_stake_limit_ok --exact --show-output #[test] fn test_add_stake_limit_ok() { new_test_ext(1).execute_with(|| { From 40c4c806b5a383c0fd6861a00b8cbf0a208f7e72 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Mon, 3 Feb 2025 18:36:13 -0500 Subject: [PATCH 17/17] Bump spec version --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c9a5e2237f..4816ab8f14 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,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: 225, + spec_version: 226, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,