diff --git a/common/src/lib.rs b/common/src/lib.rs index 590eb7fee..7fe062489 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -159,6 +159,7 @@ pub enum ProxyType { SudoUncheckedSetCode, SwapHotkey, SubnetLeaseBeneficiary, // Used to operate the leased subnet + RootClaim, } impl Default for ProxyType { diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 4e534c321..6c7e76b52 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -2663,7 +2663,6 @@ fn test_trim_to_max_allowed_uids() { assert!(!AlphaDividendsPerSubnet::::contains_key( netuid, hotkey )); - assert!(!TaoDividendsPerSubnet::::contains_key(netuid, hotkey)); assert!(!Axons::::contains_key(netuid, hotkey)); assert!(!NeuronCertificates::::contains_key(netuid, hotkey)); assert!(!Prometheus::::contains_key(netuid, hotkey)); diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 5b73ac3f4..d7b9e63ab 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -1577,4 +1577,83 @@ mod pallet_benchmarks { #[extrinsic_call] _(RawOrigin::Signed(coldkey.clone()), netuid, hotkey.clone()); } + #[benchmark] + fn set_root_claim_type() { + let coldkey: T::AccountId = whitelisted_caller(); + + #[extrinsic_call] + _(RawOrigin::Signed(coldkey.clone()), RootClaimTypeEnum::Keep); + } + + #[benchmark] + fn claim_root() { + let coldkey: T::AccountId = whitelisted_caller(); + let hotkey: T::AccountId = account("A", 0, 1); + + let netuid = Subtensor::::get_next_netuid(); + + let lock_cost = Subtensor::::get_network_lock_cost(); + Subtensor::::add_balance_to_coldkey_account(&coldkey, lock_cost.into()); + + assert_ok!(Subtensor::::register_network( + RawOrigin::Signed(coldkey.clone()).into(), + hotkey.clone() + )); + + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_network_pow_registration_allowed(netuid, true); + NetworkRegistrationAllowed::::insert(netuid, true); + FirstEmissionBlockNumber::::insert(netuid, 0); + + SubnetMechanism::::insert(netuid, 1); + SubnetworkN::::insert(netuid, 1); + Subtensor::::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 100_000_000u64; + Subtensor::::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 100_000_000u64; + Subtensor::::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + let pending_root_alpha = 10_000_000u64; + Subtensor::::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + let initial_stake = + Subtensor::::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + + assert_ok!(Subtensor::::set_root_claim_type( + RawOrigin::Signed(coldkey.clone()).into(), + RootClaimTypeEnum::Keep + ),); + + #[extrinsic_call] + _(RawOrigin::Signed(coldkey.clone())); + + // Verification + let new_stake = + Subtensor::::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); + + assert!(new_stake > initial_stake); + } + + #[benchmark] + fn sudo_set_num_root_claims() { + #[extrinsic_call] + _(RawOrigin::Root, 100); + } } diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 6a96090b0..818235a95 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -7,7 +7,8 @@ impl Pallet { /// Executes the necessary operations for each block. 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 = >::parent_hash(); + // --- 1. Adjust difficulties. Self::adjust_registration_terms_for_networks(); // --- 2. Get the current coinbase emission. @@ -21,6 +22,11 @@ impl Pallet { Self::run_coinbase(block_emission); // --- 4. Set pending children on the epoch; but only after the coinbase has been run. Self::try_set_pending_children(block_number); + // --- 5. Run auto-claim root divs. + Self::run_auto_claim_root_divs(last_block_hash); + // --- 6. Populate root coldkey maps. + Self::populate_root_coldkey_staking_maps(); + // Return ok. Ok(()) } diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 6b09c9ed4..9267c75d1 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -366,22 +366,24 @@ impl Pallet { /// * 'NotSubnetOwner': If the caller does not own the specified subnet. /// pub fn do_dissolve_network(netuid: NetUid) -> dispatch::DispatchResult { - // 1. --- The network exists? + // --- The network exists? ensure!( Self::if_subnet_exist(netuid) && netuid != NetUid::ROOT, Error::::SubnetNotExists ); - // 2. --- Perform the cleanup before removing the network. + Self::finalize_all_subnet_root_dividends(netuid); + + // --- Perform the cleanup before removing the network. T::SwapInterface::dissolve_all_liquidity_providers(netuid)?; Self::destroy_alpha_in_out_stakes(netuid)?; T::SwapInterface::clear_protocol_liquidity(netuid)?; T::CommitmentsInterface::purge_netuid(netuid); - // 3. --- Remove the network + // --- Remove the network Self::remove_network(netuid); - // 4. --- Emit the NetworkRemoved event + // --- Emit the NetworkRemoved event log::info!("NetworkRemoved( netuid:{netuid:?} )"); Self::deposit_event(Event::NetworkRemoved(netuid)); @@ -476,8 +478,7 @@ impl Pallet { // --- 15. Mechanism step / emissions bookkeeping. FirstEmissionBlockNumber::::remove(netuid); PendingEmission::::remove(netuid); - PendingRootDivs::::remove(netuid); - PendingAlphaSwapped::::remove(netuid); + PendingRootAlphaDivs::::remove(netuid); PendingOwnerCut::::remove(netuid); BlocksSinceLastStep::::remove(netuid); LastMechansimStepBlock::::remove(netuid); @@ -510,6 +511,7 @@ impl Pallet { RAORecycledForRegistration::::remove(netuid); MaxRegistrationsPerBlock::::remove(netuid); WeightsVersionKey::::remove(netuid); + PendingRootAlphaDivs::::remove(netuid); // --- 17. Subtoken / feature flags. LiquidAlphaOn::::remove(netuid); @@ -528,7 +530,6 @@ impl Pallet { let _ = NeuronCertificates::::clear_prefix(netuid, u32::MAX, None); let _ = Prometheus::::clear_prefix(netuid, u32::MAX, None); let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); - let _ = TaoDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); let _ = PendingChildKeys::::clear_prefix(netuid, u32::MAX, None); let _ = AssociatedEvmAddress::::clear_prefix(netuid, u32::MAX, None); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 9d6d44de8..df7d529fd 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -214,27 +214,14 @@ impl Pallet { // Get pending alpha as original alpha_out - root_alpha. let pending_alpha: U96F32 = alpha_out_i.saturating_sub(root_alpha); log::debug!("pending_alpha: {pending_alpha:?}"); - // Sell root emission through the pool (do not pay fees) + let subsidized: bool = *is_subsidized.get(netuid_i).unwrap_or(&false); if !subsidized { - let swap_result = Self::swap_alpha_for_tao( - *netuid_i, - tou64!(root_alpha).into(), - T::SwapInterface::min_price(), - true, - ); - if let Ok(ok_result) = swap_result { - let root_tao = ok_result.amount_paid_out; - // Accumulate root divs for subnet. - PendingRootDivs::::mutate(*netuid_i, |total| { - *total = total.saturating_add(root_tao); - }); - } + PendingRootAlphaDivs::::mutate(*netuid_i, |total| { + *total = total.saturating_add(tou64!(root_alpha).into()); + }); } - // Accumulate alpha emission in pending. - PendingAlphaSwapped::::mutate(*netuid_i, |total| { - *total = total.saturating_add(tou64!(root_alpha).into()); - }); + // Accumulate alpha emission in pending. PendingEmission::::mutate(*netuid_i, |total| { *total = total.saturating_add(tou64!(pending_alpha).into()); @@ -268,25 +255,15 @@ impl Pallet { PendingEmission::::insert(netuid, AlphaCurrency::ZERO); // Get and drain the subnet pending root divs. - let pending_tao = PendingRootDivs::::get(netuid); - PendingRootDivs::::insert(netuid, TaoCurrency::ZERO); - - // Get this amount as alpha that was swapped for pending root divs. - let pending_swapped = PendingAlphaSwapped::::get(netuid); - PendingAlphaSwapped::::insert(netuid, AlphaCurrency::ZERO); + let pending_root_alpha = PendingRootAlphaDivs::::get(netuid); + PendingRootAlphaDivs::::insert(netuid, AlphaCurrency::ZERO); // Get owner cut and drain. let owner_cut = PendingOwnerCut::::get(netuid); PendingOwnerCut::::insert(netuid, AlphaCurrency::ZERO); // Drain pending root divs, alpha emission, and owner cut. - Self::drain_pending_emission( - netuid, - pending_alpha, - pending_tao, - pending_swapped, - owner_cut, - ); + Self::drain_pending_emission(netuid, pending_alpha, pending_root_alpha, owner_cut); } else { // Increment BlocksSinceLastStep::::mutate(netuid, |total| *total = total.saturating_add(1)); @@ -329,7 +306,7 @@ impl Pallet { pub fn calculate_dividend_distribution( pending_alpha: AlphaCurrency, - pending_tao: TaoCurrency, + pending_root_alpha: AlphaCurrency, tao_weight: U96F32, stake_map: BTreeMap, dividends: BTreeMap, @@ -340,7 +317,7 @@ impl Pallet { log::debug!("dividends: {dividends:?}"); log::debug!("stake_map: {stake_map:?}"); log::debug!("pending_alpha: {pending_alpha:?}"); - log::debug!("pending_tao: {pending_tao:?}"); + log::debug!("pending_root_alpha: {pending_root_alpha:?}"); log::debug!("tao_weight: {tao_weight:?}"); // Setup. @@ -392,22 +369,22 @@ impl Pallet { log::debug!("total_root_divs: {total_root_divs:?}"); log::debug!("total_alpha_divs: {total_alpha_divs:?}"); - // Compute root divs as TAO. Here we take - let mut tao_dividends: BTreeMap = BTreeMap::new(); + // Compute root alpha divs. Here we take + let mut root_alpha_dividends: BTreeMap = BTreeMap::new(); for (hotkey, root_divs) in root_dividends { // Root proportion. let root_share: U96F32 = root_divs.checked_div(total_root_divs).unwrap_or(zero); log::debug!("hotkey: {hotkey:?}, root_share: {root_share:?}"); - // Root proportion in TAO - let root_tao: U96F32 = asfloat!(pending_tao).saturating_mul(root_share); - log::debug!("hotkey: {hotkey:?}, root_tao: {root_tao:?}"); + // Root proportion in alpha + let root_alpha: U96F32 = asfloat!(pending_root_alpha).saturating_mul(root_share); + log::debug!("hotkey: {hotkey:?}, root_alpha: {root_alpha:?}"); // Record root dividends as TAO. - tao_dividends + root_alpha_dividends .entry(hotkey) - .and_modify(|e| *e = root_tao) - .or_insert(root_tao); + .and_modify(|e| *e = root_alpha) + .or_insert(root_alpha); } - log::debug!("tao_dividends: {tao_dividends:?}"); + log::debug!("root_alpha_dividends: {root_alpha_dividends:?}"); // Compute proportional alpha divs using the pending alpha and total alpha divs from the epoch. let mut prop_alpha_dividends: BTreeMap = BTreeMap::new(); @@ -427,7 +404,7 @@ impl Pallet { } log::debug!("prop_alpha_dividends: {prop_alpha_dividends:?}"); - (prop_alpha_dividends, tao_dividends) + (prop_alpha_dividends, root_alpha_dividends) } fn get_owner_hotkeys(netuid: NetUid, coldkey: &T::AccountId) -> Vec { @@ -467,7 +444,7 @@ impl Pallet { owner_cut: AlphaCurrency, incentives: BTreeMap, alpha_dividends: BTreeMap, - tao_dividends: BTreeMap, + root_alpha_dividends: BTreeMap, ) { // Distribute the owner cut. if let Ok(owner_coldkey) = SubnetOwner::::try_get(netuid) { @@ -567,37 +544,31 @@ impl Pallet { TotalHotkeyAlphaLastEpoch::::insert(hotkey, netuid, total_hotkey_alpha); } - // Distribute root tao divs. - let _ = TaoDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); - for (hotkey, mut root_tao) in tao_dividends { + // Distribute root alpha divs. + for (hotkey, mut root_alpha) in root_alpha_dividends { // Get take prop - let tao_take: U96F32 = Self::get_hotkey_take_float(&hotkey).saturating_mul(root_tao); + let alpha_take: U96F32 = + Self::get_hotkey_take_float(&hotkey).saturating_mul(root_alpha); // Remove take prop from root_tao - root_tao = root_tao.saturating_sub(tao_take); + root_alpha = root_alpha.saturating_sub(alpha_take); // Give the validator their take. - log::debug!("hotkey: {hotkey:?} tao_take: {tao_take:?}"); - let validator_stake = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + log::debug!("hotkey: {hotkey:?} alpha_take: {alpha_take:?}"); + let _validator_stake = Self::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &Owner::::get(hotkey.clone()), - NetUid::ROOT, - tou64!(tao_take).into(), + netuid, + tou64!(alpha_take).into(), ); - // Give rest to nominators. - log::debug!("hotkey: {hotkey:?} root_tao: {root_tao:?}"); - Self::increase_stake_for_hotkey_on_subnet( + + Self::increase_root_claimable_for_hotkey_and_subnet( &hotkey, - NetUid::ROOT, - tou64!(root_tao).into(), + netuid, + tou64!(root_alpha).into(), ); + // Record root dividends for this validator on this subnet. - TaoDividendsPerSubnet::::mutate(netuid, hotkey.clone(), |divs| { - *divs = divs.saturating_add(tou64!(root_tao).into()); - }); - // Update the total TAO on the subnet with root tao dividends. - SubnetTAO::::mutate(NetUid::ROOT, |total| { - *total = total - .saturating_add(validator_stake.to_u64().into()) - .saturating_add(tou64!(root_tao).into()); + AlphaDividendsPerSubnet::::mutate(netuid, hotkey.clone(), |divs| { + *divs = divs.saturating_add(tou64!(root_alpha).into()); }); } } @@ -619,7 +590,7 @@ impl Pallet { pub fn calculate_dividend_and_incentive_distribution( netuid: NetUid, - pending_tao: TaoCurrency, + pending_root_alpha: AlphaCurrency, pending_validator_alpha: AlphaCurrency, hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)>, tao_weight: U96F32, @@ -635,33 +606,32 @@ impl Pallet { let stake_map = Self::get_stake_map(netuid, dividends.keys().collect::>()); - let (alpha_dividends, tao_dividends) = Self::calculate_dividend_distribution( + let (alpha_dividends, root_alpha_dividends) = Self::calculate_dividend_distribution( pending_validator_alpha, - pending_tao, + pending_root_alpha, tao_weight, stake_map, dividends, ); - (incentives, (alpha_dividends, tao_dividends)) + (incentives, (alpha_dividends, root_alpha_dividends)) } pub fn drain_pending_emission( netuid: NetUid, pending_alpha: AlphaCurrency, - pending_tao: TaoCurrency, - pending_swapped: AlphaCurrency, + pending_root_alpha: AlphaCurrency, owner_cut: AlphaCurrency, ) { log::debug!( - "Draining pending alpha emission for netuid {netuid:?}, pending_alpha: {pending_alpha:?}, pending_tao: {pending_tao:?}, pending_swapped: {pending_swapped:?}, owner_cut: {owner_cut:?}" + "Draining pending alpha emission for netuid {netuid:?}, pending_alpha: {pending_alpha:?}, pending_root_alpha: {pending_root_alpha:?}, owner_cut: {owner_cut:?}" ); let tao_weight = Self::get_tao_weight(); // Run the epoch. let hotkey_emission: Vec<(T::AccountId, AlphaCurrency, AlphaCurrency)> = - Self::epoch_with_mechanisms(netuid, pending_alpha.saturating_add(pending_swapped)); + Self::epoch_with_mechanisms(netuid, pending_alpha.saturating_add(pending_root_alpha)); log::debug!("hotkey_emission: {hotkey_emission:?}"); // Compute the pending validator alpha. @@ -678,18 +648,18 @@ impl Pallet { let pending_validator_alpha = if !incentive_sum.is_zero() { pending_alpha - .saturating_add(pending_swapped) + .saturating_add(pending_root_alpha) .saturating_div(2.into()) - .saturating_sub(pending_swapped) + .saturating_sub(pending_root_alpha) } else { // If the incentive is 0, then Validators get 100% of the alpha. pending_alpha }; - let (incentives, (alpha_dividends, tao_dividends)) = + let (incentives, (alpha_dividends, root_alpha_dividends)) = Self::calculate_dividend_and_incentive_distribution( netuid, - pending_tao, + pending_root_alpha, pending_validator_alpha, hotkey_emission, tao_weight, @@ -700,7 +670,7 @@ impl Pallet { owner_cut, incentives, alpha_dividends, - tao_dividends, + root_alpha_dividends, ); } diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c52c589ad..87175c43a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -54,6 +54,10 @@ extern crate alloc; pub const MAX_CRV3_COMMIT_SIZE_BYTES: u32 = 5000; +pub const ALPHA_MAP_BATCH_SIZE: usize = 30; + +pub const MAX_NUM_ROOT_CLAIMS: u64 = 50; + #[allow(deprecated)] #[deny(missing_docs)] #[import_section(errors::errors)] @@ -82,6 +86,7 @@ pub mod pallet { use runtime_common::prod_or_fast; use sp_core::{ConstU32, H160, H256}; use sp_runtime::traits::{Dispatchable, TrailingZeroInput}; + use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::vec_deque::VecDeque; use sp_std::vec; use sp_std::vec::Vec; @@ -319,6 +324,53 @@ pub mod pallet { /// ==== Staking + Accounts ==== /// ============================ + #[derive( + Encode, Decode, Default, TypeInfo, Clone, PartialEq, Eq, Debug, DecodeWithMemTracking, + )] + /// Enum for the per-coldkey root claim setting. + pub enum RootClaimTypeEnum { + /// Swap any alpha emission for TAO. + #[default] + 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] + /// 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::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 { + // once per week (+ spare keys for skipped tries) + 5 + } + #[pallet::type_value] /// Default value for zero. pub fn DefaultZeroU64() -> u64 { @@ -862,6 +914,12 @@ pub mod pallet { pub fn DefaultMovingPrice() -> I96F32 { I96F32::saturating_from_num(0.0) } + + #[pallet::type_value] + /// Default subnet root claimable + pub fn DefaultRootClaimable() -> BTreeMap { + Default::default() + } #[pallet::type_value] /// Default value for Share Pool variables pub fn DefaultSharePoolZero() -> U64F64 { @@ -889,6 +947,12 @@ pub mod pallet { 50400 } + /// Default last Alpha map key for iteration + #[pallet::type_value] + pub fn DefaultAlphaIterationLastKey() -> Option> { + None + } + #[pallet::type_value] /// Default number of terminal blocks in a tempo during which admin operations are prohibited pub fn DefaultAdminFreezeWindow() -> u16 { @@ -1056,17 +1120,6 @@ pub mod pallet { ValueQuery, DefaultZeroAlpha, >; - #[pallet::storage] // --- DMAP ( netuid, hotkey ) --> u64 | Last total root dividend paid to this hotkey on this subnet. - pub type TaoDividendsPerSubnet = StorageDoubleMap< - _, - Identity, - NetUid, - Blake2_128Concat, - T::AccountId, - TaoCurrency, - ValueQuery, - DefaultZeroTao, - >; /// ================== /// ==== Coinbase ==== @@ -1221,6 +1274,11 @@ pub mod pallet { U64F64, // Shares ValueQuery, >; + + #[pallet::storage] // Contains last Alpha storage map key to iterate (check first) + pub type AlphaMapLastKey = + StorageValue<_, Option>, ValueQuery, DefaultAlphaIterationLastKey>; + #[pallet::storage] // --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet. pub type TokenSymbol = StorageMap<_, Identity, NetUid, Vec, ValueQuery, DefaultUnicodeVecU8>; @@ -1342,13 +1400,9 @@ pub mod pallet { /// --- MAP ( netuid ) --> pending_emission pub type PendingEmission = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultPendingEmission>; - #[pallet::storage] /// --- MAP ( netuid ) --> pending_root_emission - pub type PendingRootDivs = - StorageMap<_, Identity, NetUid, TaoCurrency, ValueQuery, DefaultZeroTao>; #[pallet::storage] - /// --- MAP ( netuid ) --> pending_alpha_swapped - pub type PendingAlphaSwapped = + pub type PendingRootAlphaDivs = StorageMap<_, Identity, NetUid, AlphaCurrency, ValueQuery, DefaultZeroAlpha>; #[pallet::storage] /// --- MAP ( netuid ) --> pending_owner_cut @@ -1847,6 +1901,49 @@ pub mod pallet { ValueQuery, >; + #[pallet::storage] // --- MAP ( hot ) --> MAP(netuid ) --> claimable_dividends | Root claimable dividends. + pub type RootClaimable = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + BTreeMap, + ValueQuery, + DefaultRootClaimable, + >; + + // Already claimed root alpha. + #[pallet::storage] + pub type RootClaimed = StorageNMap< + _, + ( + NMapKey, // hot + NMapKey, // cold + NMapKey, // subnet + ), + u128, + ValueQuery, + >; + #[pallet::storage] // -- MAP ( cold ) --> root_claim_type enum + pub type RootClaimType = StorageMap< + _, + Blake2_128Concat, + T::AccountId, + RootClaimTypeEnum, + ValueQuery, + DefaultRootClaimType, + >; + #[pallet::storage] // --- MAP ( u64 ) --> coldkey | Maps coldkeys that have stake to an index + pub type StakingColdkeysByIndex = + StorageMap<_, Identity, u64, T::AccountId, OptionQuery>; + + #[pallet::storage] // --- MAP ( coldkey ) --> index | Maps index that have stake to a coldkey + pub type StakingColdkeys = StorageMap<_, Identity, T::AccountId, u64, 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>; + /// ============================= /// ==== EVM related storage ==== /// ============================= diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 78874b9f4..9ebb9420c 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -12,6 +12,8 @@ mod dispatches { use sp_core::ecdsa::Signature; use sp_runtime::{Percent, traits::Saturating}; + use crate::MAX_NUM_ROOT_CLAIMS; + use crate::MAX_CRV3_COMMIT_SIZE_BYTES; /// Dispatchable functions allow users to interact with the pallet and invoke state changes. /// These functions materialize as "extrinsics", which are often compared to transactions. @@ -2399,5 +2401,88 @@ mod dispatches { ensure_root(origin)?; Self::do_dissolve_network(netuid) } + + /// --- 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: + /// + #[pallet::call_index(121)] + #[pallet::weight(( + Weight::from_parts(117_000_000, 7767) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(4_u64)), + DispatchClass::Normal, + Pays::Yes + ))] + pub fn claim_root(origin: OriginFor) -> DispatchResultWithPostInfo { + let coldkey: T::AccountId = ensure_signed(origin)?; + + Self::maybe_add_coldkey_index(&coldkey); + + 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(122)] + #[pallet::weight(( + Weight::from_parts(6_000_000, 0).saturating_add(T::DbWeight::get().writes(1_u64)), + 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::maybe_add_coldkey_index(&coldkey); + + Self::change_root_claim_type(&coldkey, new_root_claim_type); + Ok(()) + } + + /// --- 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(123)] + #[pallet::weight(( + Weight::from_parts(4_000_000, 0).saturating_add(T::DbWeight::get().writes(1_u64)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn sudo_set_num_root_claims(origin: OriginFor, new_value: u64) -> DispatchResult { + ensure_root(origin)?; + + ensure!( + new_value > 0 && new_value <= MAX_NUM_ROOT_CLAIMS, + Error::::InvalidNumRootClaim + ); + + NumRootClaim::::set(new_value); + + Ok(()) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 759e74f6e..bfc5b6837 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -264,5 +264,7 @@ mod errors { TrimmingWouldExceedMaxImmunePercentage, /// Violating the rules of Childkey-Parentkey consistency ChildParentInconsistency, + /// Invalid number of root claims + InvalidNumRootClaim, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index c34219d53..442a363ae 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -456,5 +456,24 @@ mod events { /// The account ID of the hotkey. hotkey: T::AccountId, }, + + /// Root emissions have been claimed for a coldkey on all subnets and hotkeys. + /// Parameters: + /// (coldkey) + RootClaimed { + /// Claim coldkey + coldkey: T::AccountId, + }, + + /// Root claim type for a coldkey has been set. + /// Parameters: + /// (coldkey, u8) + RootClaimTypeSet { + /// Claim coldkey + coldkey: T::AccountId, + + /// Claim type + root_claim_type: RootClaimTypeEnum, + }, } } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 20651f68f..72b517b89 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -153,7 +153,9 @@ mod hooks { // Cleanup child/parent keys .saturating_add(migrations::migrate_fix_childkeys::migrate_fix_childkeys::()) // Migrate AutoStakeDestinationColdkeys - .saturating_add(migrations::migrate_auto_stake_destination::migrate_auto_stake_destination::()); + .saturating_add(migrations::migrate_auto_stake_destination::migrate_auto_stake_destination::()) + // Remove obsolete map entries + .saturating_add(migrations::migrate_remove_tao_dividends::migrate_remove_tao_dividends::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_remove_tao_dividends.rs b/pallets/subtensor/src/migrations/migrate_remove_tao_dividends.rs new file mode 100644 index 000000000..b93df2233 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_remove_tao_dividends.rs @@ -0,0 +1,64 @@ +use crate::{Config, HasMigrationRun}; +use alloc::string::String; +use frame_support::pallet_prelude::Weight; +use frame_support::traits::Get; +use sp_io::KillStorageResult; +use sp_io::hashing::twox_128; +use sp_io::storage::clear_prefix; +use sp_std::vec::Vec; +fn remove_prefix(old_map: &str) -> Weight { + let mut prefix = Vec::new(); + prefix.extend_from_slice(&twox_128("SubtensorModule".as_bytes())); + prefix.extend_from_slice(&twox_128(old_map.as_bytes())); + + let removal_results = clear_prefix(&prefix, Some(u32::MAX)); + + let removed_entries_count = match removal_results { + KillStorageResult::AllRemoved(removed) => removed as u64, + KillStorageResult::SomeRemaining(removed) => { + log::info!("Failed To Remove Some Items During migration"); + removed as u64 + } + }; + + log::info!("Removed {removed_entries_count:?} entries from {old_map:?} map."); + + T::DbWeight::get().writes(removed_entries_count) +} + +pub fn migrate_remove_tao_dividends() -> Weight { + let migration_name = b"migrate_remove_tao_dividends".to_vec(); + let mut weight = T::DbWeight::get().reads(1); + + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + "Running migration '{}'", + String::from_utf8_lossy(&migration_name) + ); + + // Remove obsolete map entries + let weight1 = remove_prefix::("TaoDividendsPerSubnet"); + let weight2 = remove_prefix::("PendingAlphaSwapped"); + let weight3 = remove_prefix::("PendingRootDivs"); + + // Mark Migration as Completed + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight + .saturating_add(weight1) + .saturating_add(weight2) + .saturating_add(weight3) +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index e92b86aa2..c7f75cb03 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -31,6 +31,7 @@ pub mod migrate_rate_limiting_last_blocks; pub mod migrate_remove_commitments_rate_limit; pub mod migrate_remove_network_modality; pub mod migrate_remove_stake_map; +pub mod migrate_remove_tao_dividends; pub mod migrate_remove_total_hotkey_coldkey_stakes_this_interval; pub mod migrate_remove_unused_maps_and_values; pub mod migrate_remove_zero_total_hotkey_alpha; diff --git a/pallets/subtensor/src/rpc_info/dynamic_info.rs b/pallets/subtensor/src/rpc_info/dynamic_info.rs index 28b399008..3bfbda867 100644 --- a/pallets/subtensor/src/rpc_info/dynamic_info.rs +++ b/pallets/subtensor/src/rpc_info/dynamic_info.rs @@ -63,7 +63,7 @@ impl Pallet { alpha_in_emission: SubnetAlphaInEmission::::get(netuid).into(), tao_in_emission: SubnetTaoInEmission::::get(netuid).into(), pending_alpha_emission: PendingEmission::::get(netuid).into(), - pending_root_emission: PendingRootDivs::::get(netuid).into(), + pending_root_emission: TaoCurrency::from(0u64).into(), subnet_volume: SubnetVolume::::get(netuid).into(), network_registered_at: NetworkRegisteredAt::::get(netuid).into(), subnet_identity: SubnetIdentitiesV3::::get(netuid), diff --git a/pallets/subtensor/src/rpc_info/metagraph.rs b/pallets/subtensor/src/rpc_info/metagraph.rs index 2ec772b2c..df0c8023b 100644 --- a/pallets/subtensor/src/rpc_info/metagraph.rs +++ b/pallets/subtensor/src/rpc_info/metagraph.rs @@ -644,7 +644,8 @@ impl Pallet { let mut tao_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; let mut alpha_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; for hotkey in hotkeys.clone() { - let tao_divs = TaoDividendsPerSubnet::::get(netuid, hotkey.clone()); + // Tao dividends were removed + let tao_divs = TaoCurrency::ZERO; let alpha_divs = AlphaDividendsPerSubnet::::get(netuid, hotkey.clone()); tao_dividends_per_hotkey.push((hotkey.clone(), tao_divs.into())); alpha_dividends_per_hotkey.push((hotkey.clone(), alpha_divs.into())); @@ -694,7 +695,7 @@ impl Pallet { alpha_in_emission: SubnetAlphaInEmission::::get(netuid).into(), // amount injected outstanding per block tao_in_emission: SubnetTaoInEmission::::get(netuid).into(), // amount of tao injected per block pending_alpha_emission: PendingEmission::::get(netuid).into(), // pending alpha to be distributed - pending_root_emission: PendingRootDivs::::get(netuid).into(), // panding tao for root divs to be distributed + pending_root_emission: TaoCurrency::from(0u64).into(), // panding tao for root divs to be distributed subnet_volume: subnet_volume.into(), moving_price: SubnetMovingPrice::::get(netuid), @@ -1004,7 +1005,7 @@ impl Pallet { }, Some(SelectiveMetagraphIndex::PendingRootEmission) => SelectiveMetagraph { netuid: netuid.into(), - pending_root_emission: Some(PendingRootDivs::::get(netuid).into()), + pending_root_emission: Some(TaoCurrency::from(0u64).into()), ..Default::default() }, Some(SelectiveMetagraphIndex::SubnetVolume) => SelectiveMetagraph { @@ -1405,7 +1406,8 @@ impl Pallet { let mut tao_dividends_per_hotkey: Vec<(T::AccountId, Compact)> = vec![]; for hotkey in hotkeys.clone() { - let tao_divs = TaoDividendsPerSubnet::::get(netuid, hotkey.clone()); + // Tao dividends were removed + let tao_divs = TaoCurrency::ZERO; tao_dividends_per_hotkey.push((hotkey.clone(), tao_divs.into())); } SelectiveMetagraph { diff --git a/pallets/subtensor/src/rpc_info/stake_info.rs b/pallets/subtensor/src/rpc_info/stake_info.rs index 13789292d..83c0cfed8 100644 --- a/pallets/subtensor/src/rpc_info/stake_info.rs +++ b/pallets/subtensor/src/rpc_info/stake_info.rs @@ -43,7 +43,8 @@ impl Pallet { continue; } let emission = AlphaDividendsPerSubnet::::get(*netuid_i, &hotkey_i); - let tao_emission = TaoDividendsPerSubnet::::get(*netuid_i, &hotkey_i); + // Tao dividends were removed + let tao_emission = TaoCurrency::ZERO; let is_registered: bool = Self::is_hotkey_registered_on_network(*netuid_i, hotkey_i); stake_info_for_coldkey.push(StakeInfo { @@ -101,7 +102,8 @@ impl Pallet { netuid, ); let emission = AlphaDividendsPerSubnet::::get(netuid, &hotkey_account); - let tao_emission = TaoDividendsPerSubnet::::get(netuid, &hotkey_account); + // Tao dividends were removed + let tao_emission = TaoCurrency::ZERO; let is_registered: bool = Self::is_hotkey_registered_on_network(netuid, &hotkey_account); Some(StakeInfo { diff --git a/pallets/subtensor/src/staking/claim_root.rs b/pallets/subtensor/src/staking/claim_root.rs new file mode 100644 index 000000000..1171ae24a --- /dev/null +++ b/pallets/subtensor/src/staking/claim_root.rs @@ -0,0 +1,407 @@ +use super::*; +use frame_support::weights::Weight; +use sp_core::Get; +use sp_std::collections::btree_set::BTreeSet; +use substrate_fixed::types::{I96F32, U64F64}; +use subtensor_swap_interface::SwapHandler; + +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: BTreeSet = BTreeSet::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) + .checked_rem(n) + .unwrap_or(0); + indices.insert(idx); + last_idx = idx; + } + indices.into_iter().collect() + } + + pub fn increase_root_claimable_for_hotkey_and_subnet( + hotkey: &T::AccountId, + netuid: NetUid, + amount: AlphaCurrency, + ) { + // Get total stake on this hotkey on root. + let total: I96F32 = + I96F32::saturating_from_num(Self::get_stake_for_hotkey_on_subnet(hotkey, NetUid::ROOT)); + + // Get increment + let increment: I96F32 = I96F32::saturating_from_num(amount) + .checked_div(total) + .unwrap_or(I96F32::saturating_from_num(0.0)); + + // Unlikely to happen. This is mostly for test environment sanity checks. + if u64::from(amount) > total.saturating_to_num::() { + log::warn!("Not enough root stake. NetUID = {netuid}"); + + let owner = Owner::::get(hotkey); + Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, &owner, netuid, amount); + return; + } + + // Increment claimable for this subnet. + RootClaimable::::mutate(hotkey, |claimable| { + claimable + .entry(netuid) + .and_modify(|claim_total| *claim_total = claim_total.saturating_add(increment)) + .or_insert(increment); + }); + } + + pub fn get_root_claimable_for_hotkey_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: NetUid, + ) -> I96F32 { + // Get this keys stake balance on root. + let root_stake: I96F32 = I96F32::saturating_from_num( + Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, NetUid::ROOT), + ); + + // Get the total claimable_rate for this hotkey and this network + let claimable_rate: I96F32 = *RootClaimable::::get(hotkey) + .get(&netuid) + .unwrap_or(&I96F32::from(0)); + + // Compute the proportion owed to this coldkey via balance. + let claimable: I96F32 = claimable_rate.saturating_mul(root_stake); + + claimable + } + + pub fn get_root_owed_for_hotkey_coldkey_float( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: NetUid, + ) -> I96F32 { + let claimable = Self::get_root_claimable_for_hotkey_coldkey(hotkey, coldkey, netuid); + + // Attain the root claimed to avoid overclaiming. + let root_claimed: I96F32 = + I96F32::saturating_from_num(RootClaimed::::get((hotkey, coldkey, netuid))); + + // Substract the already claimed alpha. + let owed: I96F32 = claimable.saturating_sub(root_claimed); + + owed + } + + pub fn get_root_owed_for_hotkey_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + netuid: NetUid, + ) -> 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: NetUid, + root_claim_type: RootClaimTypeEnum, + ignore_minimum_condition: bool, + ) { + // Substract the root claimed. + let owed: I96F32 = Self::get_root_owed_for_hotkey_coldkey_float(hotkey, coldkey, netuid); + + if !ignore_minimum_condition + && owed < I96F32::saturating_from_num(DefaultMinRootClaimAmount::::get()) + { + log::debug!( + "root claim on subnet {netuid} is skipped: {owed:?} for h={hotkey:?},c={coldkey:?} " + ); + return; // no-op + } + + // 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 { + log::debug!( + "root claim on subnet {netuid} is skipped: {owed:?} for h={hotkey:?},c={coldkey:?}" + ); + return; // no-op + } + + match root_claim_type { + // Increase stake on root + RootClaimTypeEnum::Swap => { + // Swap the alpha owed to TAO + let owed_tao = match Self::swap_alpha_for_tao( + netuid, + owed_u64.into(), + T::SwapInterface::min_price::(), + false, + ) { + Ok(owed_tao) => owed_tao, + Err(err) => { + log::error!("Error swapping alpha for TAO: {err:?}"); + + return; + } + }; + + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + NetUid::ROOT, + owed_tao.amount_paid_out.to_u64().into(), + ); + + Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey( + hotkey, + coldkey, + owed_tao.amount_paid_out.into(), + ); + } + RootClaimTypeEnum::Keep => { + // Increase the stake with the alpha owned + Self::increase_stake_for_hotkey_and_coldkey_on_subnet( + hotkey, + coldkey, + netuid, + owed_u64.into(), + ); + } + }; + + // Increase root claimed by owed amount. + RootClaimed::::mutate((hotkey, coldkey, netuid), |root_claimed| { + *root_claimed = root_claimed.saturating_add(owed_u64.into()); + }); + } + + fn root_claim_on_subnet_weight(_root_claim_type: RootClaimTypeEnum) -> Weight { + Weight::from_parts(60_000_000, 6987) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + pub fn root_claim_all(hotkey: &T::AccountId, coldkey: &T::AccountId) -> Weight { + let mut weight = Weight::default(); + + let root_claim_type = RootClaimType::::get(coldkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + // Iterate over all the subnets this hotkey has claimable for root. + let root_claimable = RootClaimable::::get(hotkey); + weight.saturating_accrue(T::DbWeight::get().reads(1)); + + root_claimable.iter().for_each(|(netuid, _)| { + Self::root_claim_on_subnet(hotkey, coldkey, *netuid, root_claim_type.clone(), false); + weight.saturating_accrue(Self::root_claim_on_subnet_weight(root_claim_type.clone())); + }); + + weight + } + + pub fn add_stake_adjust_root_claimed_for_hotkey_and_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + amount: u64, + ) { + // Iterate over all the subnets this hotkey is staked on for root. + let root_claimable = RootClaimable::::get(hotkey); + for (netuid, claimable_rate) in root_claimable.iter() { + // Get current staker root claimed value. + let root_claimed: u128 = RootClaimed::::get((hotkey, coldkey, netuid)); + + // Increase root claimed based on the claimable rate. + let new_root_claimed = root_claimed.saturating_add( + claimable_rate + .saturating_mul(I96F32::from(u64::from(amount))) + .saturating_to_num(), + ); + + // Set the new root claimed value. + RootClaimed::::insert((hotkey, coldkey, netuid), new_root_claimed); + } + } + + pub fn remove_stake_adjust_root_claimed_for_hotkey_and_coldkey( + hotkey: &T::AccountId, + coldkey: &T::AccountId, + amount: AlphaCurrency, + ) { + // Iterate over all the subnets this hotkey is staked on for root. + let root_claimable = RootClaimable::::get(hotkey); + for (netuid, claimable_rate) in root_claimable.iter() { + if *netuid == NetUid::ROOT.into() { + continue; // Skip the root netuid. + } + + // Get current staker root claimed value. + let root_claimed: u128 = RootClaimed::::get((hotkey, coldkey, netuid)); + + // Decrease root claimed based on the claimable rate. + let new_root_claimed = root_claimed.saturating_sub( + claimable_rate + .saturating_mul(I96F32::from(u64::from(amount))) + .saturating_to_num(), + ); + + // Set the new root_claimed value. + RootClaimed::::insert((hotkey, coldkey, netuid), new_root_claimed); + } + } + + 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)); + }); + + Self::deposit_event(Event::RootClaimed { coldkey }); + + weight + } + + fn block_hash_to_indices_weight(k: u64, _n: u64) -> Weight { + Weight::from_parts(3_000_000, 1517) + .saturating_add(Weight::from_parts(100_412, 0).saturating_mul(k.into())) + } + + pub fn maybe_add_coldkey_index(coldkey: &T::AccountId) { + if !StakingColdkeys::::contains_key(coldkey) { + let n = NumStakingColdkeys::::get(); + StakingColdkeysByIndex::::insert(n, coldkey.clone()); + StakingColdkeys::::insert(coldkey.clone(), n); + NumStakingColdkeys::::mutate(|n| *n = n.saturating_add(1)); + } + } + + 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)); + + let coldkeys_to_claim: Vec = Self::block_hash_to_indices(last_block_hash, k, n); + weight.saturating_accrue(Self::block_hash_to_indices_weight(k, n)); + + for i in coldkeys_to_claim.iter() { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if let Ok(coldkey) = StakingColdkeysByIndex::::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: coldkey.clone(), + root_claim_type: new_type, + }); + } + + pub fn transfer_root_claimed_for_new_keys( + netuid: NetUid, + old_hotkey: &T::AccountId, + new_hotkey: &T::AccountId, + old_coldkey: &T::AccountId, + new_coldkey: &T::AccountId, + ) { + let old_root_claimed = RootClaimed::::get((old_hotkey, old_coldkey, netuid)); + RootClaimed::::remove((old_hotkey, old_coldkey, netuid)); + + RootClaimed::::mutate((new_hotkey, new_coldkey, netuid), |new_root_claimed| { + *new_root_claimed = old_root_claimed.saturating_add(*new_root_claimed); + }); + } + pub fn transfer_root_claimable_for_new_hotkey( + old_hotkey: &T::AccountId, + new_hotkey: &T::AccountId, + ) { + let src_root_claimable = RootClaimable::::get(old_hotkey); + let mut dst_root_claimable = RootClaimable::::get(new_hotkey); + RootClaimable::::remove(old_hotkey); + + for (netuid, claimable_rate) in src_root_claimable.into_iter() { + dst_root_claimable + .entry(netuid) + .and_modify(|total| *total = total.saturating_add(claimable_rate)) + .or_insert(claimable_rate); + } + + RootClaimable::::insert(new_hotkey, dst_root_claimable); + } + + /// Claim all root dividends for subnet and remove all associated data. + pub fn finalize_all_subnet_root_dividends(netuid: NetUid) { + let mut hotkeys_to_clear = BTreeSet::new(); + for (hotkey, root_claimable) in RootClaimable::::iter() { + if root_claimable.contains_key(&netuid) { + let alpha_values: Vec<((T::AccountId, NetUid), U64F64)> = + Alpha::::iter_prefix((&hotkey,)).collect(); + + for ((coldkey, alpha_netuid), _) in alpha_values.into_iter() { + if alpha_netuid == NetUid::ROOT { + Self::root_claim_on_subnet( + &hotkey, + &coldkey, + netuid, + RootClaimTypeEnum::Swap, + true, + ); + + RootClaimed::::remove((hotkey.clone(), coldkey, netuid)); + if !hotkeys_to_clear.contains(&hotkey) { + hotkeys_to_clear.insert(hotkey.clone()); + } + } + } + } + } + + for hotkey in hotkeys_to_clear.into_iter() { + RootClaimable::::mutate(&hotkey, |claimable| { + claimable.remove(&netuid); + }); + } + } +} diff --git a/pallets/subtensor/src/staking/helpers.rs b/pallets/subtensor/src/staking/helpers.rs index cae9579f3..075d5b9d9 100644 --- a/pallets/subtensor/src/staking/helpers.rs +++ b/pallets/subtensor/src/staking/helpers.rs @@ -326,6 +326,56 @@ impl Pallet { }); } + /// The function clears Alpha map in batches. Each run will check ALPHA_MAP_CLEAN_BATCH_SIZE + /// alphas. It keeps the alpha value stored when it's >= than MIN_ALPHA. + /// The function uses AlphaMapCleanLastKey as a storage for key iterator between runs. + pub fn populate_root_coldkey_staking_maps() { + // Get starting key for the batch. Get the first key if we restart the process. + let mut new_starting_raw_key = AlphaMapLastKey::::get(); + let mut starting_key = None; + if new_starting_raw_key.is_none() { + starting_key = Alpha::::iter_keys().next(); + new_starting_raw_key = starting_key.as_ref().map(Alpha::::hashed_key_for); + } + + if let Some(starting_raw_key) = new_starting_raw_key { + // Get the key batch + let mut keys = Alpha::::iter_keys_from(starting_raw_key) + .take(ALPHA_MAP_BATCH_SIZE) + .collect::>(); + + // New iteration: insert the starting key in the batch if it's a new iteration + // iter_keys_from() skips the starting key + if let Some(starting_key) = starting_key { + if keys.len() == ALPHA_MAP_BATCH_SIZE { + keys.remove(keys.len().saturating_sub(1)); + } + keys.insert(0, starting_key); + } + + let mut new_starting_key = None; + let new_iteration = keys.len() < ALPHA_MAP_BATCH_SIZE; + + // Check and remove alphas if necessary + for key in keys { + let (_, coldkey, netuid) = key.clone(); + + if netuid == NetUid::ROOT { + Self::maybe_add_coldkey_index(&coldkey); + } + + new_starting_key = Some(Alpha::::hashed_key_for(key)); + } + + // Restart the process if it's the last batch + if new_iteration { + new_starting_key = None; + } + + AlphaMapLastKey::::put(new_starting_key); + } + } + pub fn burn_subnet_alpha(_netuid: NetUid, _amount: AlphaCurrency) { // Do nothing; TODO: record burned alpha in a tracker } diff --git a/pallets/subtensor/src/staking/mod.rs b/pallets/subtensor/src/staking/mod.rs index 570658631..ad2b66189 100644 --- a/pallets/subtensor/src/staking/mod.rs +++ b/pallets/subtensor/src/staking/mod.rs @@ -1,6 +1,7 @@ use super::*; pub mod account; pub mod add_stake; +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 7910449e2..671632f32 100644 --- a/pallets/subtensor/src/staking/stake_utils.rs +++ b/pallets/subtensor/src/staking/stake_utils.rs @@ -707,6 +707,12 @@ impl Pallet { Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, refund); } + // If this is a root-stake + if netuid == NetUid::ROOT { + // Adjust root claimed value for this hotkey and coldkey. + Self::remove_stake_adjust_root_claimed_for_hotkey_and_coldkey(hotkey, coldkey, alpha); + } + // Step 3: Update StakingHotkeys if the hotkey's total alpha, across all subnets, is zero // TODO const: fix. // if Self::get_stake(hotkey, coldkey) == 0 { @@ -795,6 +801,14 @@ impl Pallet { Self::set_stake_operation_limit(hotkey, coldkey, netuid.into()); } + // If this is a root-stake + if netuid == NetUid::ROOT { + // Adjust root claimed for this hotkey and coldkey. + let alpha = swap_result.amount_paid_out.into(); + Self::add_stake_adjust_root_claimed_for_hotkey_and_coldkey(hotkey, coldkey, alpha); + Self::maybe_add_coldkey_index(coldkey); + } + // Deposit and log the staking event. Self::deposit_event(Event::StakeAdded( coldkey.clone(), diff --git a/pallets/subtensor/src/subnets/uids.rs b/pallets/subtensor/src/subnets/uids.rs index 9d303cc97..669f74bcc 100644 --- a/pallets/subtensor/src/subnets/uids.rs +++ b/pallets/subtensor/src/subnets/uids.rs @@ -200,7 +200,6 @@ impl Pallet { IsNetworkMember::::remove(&hotkey, netuid); LastHotkeyEmissionOnNetuid::::remove(&hotkey, netuid); AlphaDividendsPerSubnet::::remove(netuid, &hotkey); - TaoDividendsPerSubnet::::remove(netuid, &hotkey); Axons::::remove(netuid, &hotkey); NeuronCertificates::::remove(netuid, &hotkey); Prometheus::::remove(netuid, &hotkey); diff --git a/pallets/subtensor/src/swap/swap_coldkey.rs b/pallets/subtensor/src/swap/swap_coldkey.rs index 06f5d77f5..c81138b58 100644 --- a/pallets/subtensor/src/swap/swap_coldkey.rs +++ b/pallets/subtensor/src/swap/swap_coldkey.rs @@ -188,6 +188,21 @@ impl Pallet { ); // Remove the value from the old account. Alpha::::remove((&hotkey, old_coldkey, netuid)); + + if new_alpha.saturating_add(old_alpha) > U64F64::from(0u64) { + Self::transfer_root_claimed_for_new_keys( + netuid, + &hotkey, + &hotkey, + old_coldkey, + new_coldkey, + ); + + if netuid == NetUid::ROOT { + // Register new coldkey with root stake + Self::maybe_add_coldkey_index(new_coldkey); + } + } } // Add the weight for the read and write. weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); diff --git a/pallets/subtensor/src/swap/swap_hotkey.rs b/pallets/subtensor/src/swap/swap_hotkey.rs index ea270c1e1..970fe8abb 100644 --- a/pallets/subtensor/src/swap/swap_hotkey.rs +++ b/pallets/subtensor/src/swap/swap_hotkey.rs @@ -516,15 +516,7 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); // 8.3 Swap TaoDividendsPerSubnet - let old_hotkey_tao_dividends = TaoDividendsPerSubnet::::get(netuid, old_hotkey); - let new_hotkey_tao_dividends = TaoDividendsPerSubnet::::get(netuid, new_hotkey); - TaoDividendsPerSubnet::::remove(netuid, old_hotkey); - TaoDividendsPerSubnet::::insert( - netuid, - new_hotkey, - old_hotkey_tao_dividends.saturating_add(new_hotkey_tao_dividends), - ); - weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + // Tao dividends were removed // 9. Swap Alpha // Alpha( hotkey, coldkey, netuid ) -> alpha @@ -533,9 +525,17 @@ impl Pallet { weight.saturating_accrue(T::DbWeight::get().reads(old_alpha_values.len() as u64)); weight.saturating_accrue(T::DbWeight::get().writes(old_alpha_values.len() as u64)); - // Insert the new alpha values. + // 9.1. Transfer root claimable + + Self::transfer_root_claimable_for_new_hotkey(old_hotkey, new_hotkey); + + // 9.2. Insert the new alpha values. for ((coldkey, netuid_alpha), alpha) in old_alpha_values { if netuid == netuid_alpha { + Self::transfer_root_claimed_for_new_keys( + netuid, old_hotkey, new_hotkey, &coldkey, &coldkey, + ); + let new_alpha = Alpha::::take((new_hotkey, &coldkey, netuid)); Alpha::::remove((old_hotkey, &coldkey, netuid)); Alpha::::insert( diff --git a/pallets/subtensor/src/tests/claim_root.rs b/pallets/subtensor/src/tests/claim_root.rs new file mode 100644 index 000000000..d93419b05 --- /dev/null +++ b/pallets/subtensor/src/tests/claim_root.rs @@ -0,0 +1,1302 @@ +#![allow(clippy::expect_used)] + +use crate::tests::mock::{ + RuntimeOrigin, SubtensorModule, Test, add_dynamic_network, new_test_ext, run_to_block, +}; +use crate::{ + Error, MAX_NUM_ROOT_CLAIMS, NetworksAdded, NumRootClaim, NumStakingColdkeys, + PendingRootAlphaDivs, RootClaimable, StakingColdkeys, StakingColdkeysByIndex, SubnetAlphaIn, + SubnetMechanism, SubnetTAO, SubtokenEnabled, Tempo, pallet, +}; +use crate::{RootClaimType, RootClaimTypeEnum, RootClaimed}; +use approx::assert_abs_diff_eq; +use frame_support::pallet_prelude::Weight; +use frame_support::{assert_noop, assert_ok}; +use sp_core::{H256, U256}; +use sp_runtime::DispatchError; +use substrate_fixed::types::{I96F32, U96F32}; +use subtensor_runtime_common::{AlphaCurrency, Currency, NetUid, TaoCurrency}; +use subtensor_swap_interface::SwapHandler; + +#[test] +fn test_claim_root_set_claim_type() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + }); +} + +#[test] +fn test_claim_root_with_drain_emissions() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 2_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + let old_validator_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + ); + assert_eq!(old_validator_stake, initial_total_hotkey_alpha.into()); + + // Distribute pending root alpha + + let pending_root_alpha = 1_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Check new validator stake + let validator_take_percent = 0.18f64; + + let new_validator_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + ); + let calculated_validator_stake = (pending_root_alpha as f64) * validator_take_percent + + (initial_total_hotkey_alpha as f64); + + assert_abs_diff_eq!( + u64::from(new_validator_stake), + calculated_validator_stake as u64, + epsilon = 100u64, + ); + + // Check claimable + + let claimable = *RootClaimable::::get(hotkey) + .get(&netuid) + .expect("claimable must exist at this point"); + let calculated_rate = + (pending_root_alpha as f64) * (1f64 - validator_take_percent) / (root_stake as f64); + + assert_abs_diff_eq!( + claimable.saturating_to_num::(), + calculated_rate, + epsilon = 0.001f64, + ); + + // Claim root alpha + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + let new_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + + assert_abs_diff_eq!( + new_stake, + (I96F32::from(root_stake) * claimable).saturating_to_num::(), + epsilon = 10u64, + ); + + // Check root claimed value saved + + let claimed = RootClaimed::::get((&hotkey, &coldkey, netuid)); + assert_eq!(u128::from(new_stake), claimed); + + // Distribute pending root alpha (round 2) + + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Check claimable (round 2) + + let claimable2 = *RootClaimable::::get(hotkey) + .get(&netuid) + .expect("claimable must exist at this point"); + let calculated_rate = + (pending_root_alpha as f64) * (1f64 - validator_take_percent) / (root_stake as f64); + + assert_abs_diff_eq!( + claimable2.saturating_to_num::(), + calculated_rate + claimable.saturating_to_num::(), + epsilon = 0.001f64, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + let new_stake2: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + let calculated_new_stake2 = + (I96F32::from(root_stake) * claimable2).saturating_to_num::(); + + assert_abs_diff_eq!( + u64::from(new_stake2), + calculated_new_stake2, + epsilon = 10u64, + ); + + // Check root claimed value saved (round 2) + + let claimed = RootClaimed::::get((&hotkey, &coldkey, netuid)); + assert_eq!(u128::from(u64::from(new_stake2)), claimed); + }); +} + +#[test] +fn test_claim_root_adding_stake_proportionally_for_two_stakers() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let other_coldkey = U256::from(10010); + let hotkey = U256::from(1002); + let alice_coldkey = U256::from(1003); + let bob_coldkey = U256::from(1004); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 1_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + NetUid::ROOT, + root_stake.into(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let root_stake_rate = 0.1f64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &other_coldkey, + NetUid::ROOT, + (8 * root_stake).into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // Claim root alpha + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(alice_coldkey), + RootClaimTypeEnum::Keep + ),); + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(bob_coldkey), + RootClaimTypeEnum::Keep + ),); + + // Distribute pending root alpha + + let pending_root_alpha = 10_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + alice_coldkey + ),)); + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + bob_coldkey + ),)); + + // Check stakes + let validator_take_percent = 0.18f64; + + let alice_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + netuid, + ) + .into(); + + let bob_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + netuid, + ) + .into(); + + let estimated_stake = + (pending_root_alpha as f64) * (1f64 - validator_take_percent) * root_stake_rate; + + assert_eq!(alice_stake, bob_stake); + + assert_abs_diff_eq!(alice_stake, estimated_stake as u64, epsilon = 100u64,); + }); +} + +#[test] +fn test_claim_root_adding_stake_disproportionally_for_two_stakers() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let other_coldkey = U256::from(10010); + let hotkey = U256::from(1002); + let alice_coldkey = U256::from(1003); + let bob_coldkey = U256::from(1004); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let alice_root_stake = 1_000_000u64; + let bob_root_stake = 2_000_000u64; + let other_root_stake = 7_000_000u64; + + let alice_root_stake_rate = 0.1f64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + NetUid::ROOT, + alice_root_stake.into(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + NetUid::ROOT, + bob_root_stake.into(), + ); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &other_coldkey, + NetUid::ROOT, + (other_root_stake).into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // Claim root alpha + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(alice_coldkey), + RootClaimTypeEnum::Keep + ),); + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(bob_coldkey), + RootClaimTypeEnum::Keep + ),); + + // Distribute pending root alpha + + let pending_root_alpha = 10_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + alice_coldkey + ),)); + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + bob_coldkey + ),)); + + // Check stakes + let validator_take_percent = 0.18f64; + + let alice_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + netuid, + ) + .into(); + + let bob_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + netuid, + ) + .into(); + + let alice_estimated_stake = + (pending_root_alpha as f64) * (1f64 - validator_take_percent) * alice_root_stake_rate; + + assert_eq!(2 * alice_stake, bob_stake); + + assert_abs_diff_eq!(alice_stake, alice_estimated_stake as u64, epsilon = 100u64,); + }); +} + +#[test] +fn test_claim_root_with_changed_stake() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let alice_coldkey = U256::from(1003); + let bob_coldkey = U256::from(1004); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + SubtokenEnabled::::insert(NetUid::ROOT, true); + NetworksAdded::::insert(NetUid::ROOT, true); + + let root_stake = 8_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + NetUid::ROOT, + root_stake.into(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // Claim root alpha + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(alice_coldkey), + RootClaimTypeEnum::Keep + ),); + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(bob_coldkey), + RootClaimTypeEnum::Keep + ),); + + // Distribute pending root alpha + + let pending_root_alpha = 10_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + alice_coldkey + ),)); + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + bob_coldkey + ),)); + + // Check stakes + let validator_take_percent = 0.18f64; + + let alice_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + netuid, + ) + .into(); + + let bob_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + netuid, + ) + .into(); + + let estimated_stake = (pending_root_alpha as f64) * (1f64 - validator_take_percent) / 2f64; + + assert_eq!(alice_stake, bob_stake); + + assert_abs_diff_eq!(alice_stake, estimated_stake as u64, epsilon = 100u64,); + + // Remove stake + let stake_decrement = root_stake / 2u64; + + assert_ok!(SubtensorModule::remove_stake( + RuntimeOrigin::signed(bob_coldkey,), + hotkey, + NetUid::ROOT, + stake_decrement.into(), + )); + + // Distribute pending root alpha + + let pending_root_alpha = 10_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + alice_coldkey + ),)); + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + bob_coldkey + ),)); + + // Check new stakes + + let alice_stake2: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + netuid, + ) + .into(); + + let bob_stake2: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + netuid, + ) + .into(); + + let estimated_stake = (pending_root_alpha as f64) * (1f64 - validator_take_percent) / 3f64; + + let alice_stake_diff = alice_stake2 - alice_stake; + let bob_stake_diff = bob_stake2 - bob_stake; + + assert_abs_diff_eq!(alice_stake_diff, 2 * bob_stake_diff, epsilon = 100u64,); + assert_abs_diff_eq!(bob_stake_diff, estimated_stake as u64, epsilon = 100u64,); + + // Add stake + let stake_increment = root_stake / 2u64; + + assert_ok!(SubtensorModule::add_stake( + RuntimeOrigin::signed(bob_coldkey,), + hotkey, + NetUid::ROOT, + stake_increment.into(), + )); + + // Distribute pending root alpha + + let pending_root_alpha = 10_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + alice_coldkey + ),)); + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed( + bob_coldkey + ),)); + + // Check new stakes + + let alice_stake3: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &alice_coldkey, + netuid, + ) + .into(); + + let bob_stake3: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &bob_coldkey, + netuid, + ) + .into(); + + let estimated_stake = (pending_root_alpha as f64) * (1f64 - validator_take_percent) / 2f64; + + let alice_stake_diff2 = alice_stake3 - alice_stake2; + let bob_stake_diff2 = bob_stake3 - bob_stake2; + + assert_abs_diff_eq!(alice_stake_diff2, bob_stake_diff2, epsilon = 100u64,); + assert_abs_diff_eq!(bob_stake_diff2, estimated_stake as u64, epsilon = 100u64,); + }); +} + +#[test] +fn test_claim_root_with_drain_emissions_and_swap_claim_type() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let other_coldkey = U256::from(10010); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + SubnetMechanism::::insert(netuid, 1); + + // let initial_balance = 10_000_000u64; + // SubtensorModule::add_balance_to_coldkey_account(&coldkey, initial_balance.into()); + + let tao_reserve = TaoCurrency::from(50_000_000_000); + let alpha_in = AlphaCurrency::from(100_000_000_000); + SubnetTAO::::insert(netuid, tao_reserve); + SubnetAlphaIn::::insert(netuid, alpha_in); + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + assert_eq!(current_price, 0.5f64); + + let root_stake = 2_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + let root_stake_rate = 0.1f64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &other_coldkey, + NetUid::ROOT, + (9 * root_stake).into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // Distribute pending root alpha + + let pending_root_alpha = 10_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Claim root alpha + + let validator_take_percent = 0.18f64; + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Swap + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Swap); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + // Check new stake + + let new_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + ) + .into(); + + let estimated_stake_increment = (pending_root_alpha as f64) + * (1f64 - validator_take_percent) + * current_price + * root_stake_rate; + + assert_abs_diff_eq!( + new_stake, + root_stake + estimated_stake_increment as u64, + epsilon = 10000u64, + ); + + // Distribute and claim pending root alpha (round 2) + + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + // Check new stake (2) + + let new_stake2: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + ) + .into(); + + // new root stake / new total stake + let root_stake_rate2 = (root_stake as f64 + estimated_stake_increment) + / (root_stake as f64 / root_stake_rate + estimated_stake_increment); + let estimated_stake_increment2 = (pending_root_alpha as f64) + * (1f64 - validator_take_percent) + * current_price + * root_stake_rate2; + + assert_abs_diff_eq!( + new_stake2, + new_stake + estimated_stake_increment2 as u64, + epsilon = 10000u64, + ); + // Distribute and claim pending root alpha (round 3) + + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + // Check new stake (3) + + let new_stake3: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + ) + .into(); + + // new root stake / new total stake + let root_stake_rate3 = + (root_stake as f64 + estimated_stake_increment + estimated_stake_increment2) + / (root_stake as f64 / root_stake_rate + + estimated_stake_increment + + estimated_stake_increment2); + let estimated_stake_increment3 = (pending_root_alpha as f64) + * (1f64 - validator_take_percent) + * current_price + * root_stake_rate3; + + assert_abs_diff_eq!( + new_stake3, + new_stake2 + estimated_stake_increment3 as u64, + epsilon = 10000u64, + ); + }); +} + +#[test] +fn test_claim_root_with_run_coinbase() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + Tempo::::insert(netuid, 1); + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 200_000_000u64; + SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // Distribute pending root alpha + + let initial_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + assert_eq!(initial_stake, 0u64); + + let block_emissions = 1_000_000u64; + SubtensorModule::run_coinbase(U96F32::from(block_emissions)); + + // Claim root alpha + + let initial_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + assert_eq!(initial_stake, 0u64); + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + let new_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + + assert!(new_stake > 0); + }); +} + +#[test] +fn test_claim_root_block_hash_indices() { + new_test_ext(1).execute_with(|| { + let k = 15u64; + let n = 15000u64; + + // 1 + let hash = sp_core::keccak_256(b"some"); + let mut indices = SubtensorModule::block_hash_to_indices(H256(hash), k, n); + indices.sort(); + + assert!(indices.len() <= k as usize); + assert!(!indices.iter().any(|i| *i >= n)); + // precomputed values + let expected_result = vec![ + 265, 630, 1286, 1558, 4496, 4861, 5517, 5789, 6803, 8096, 9092, 11034, 11399, 12055, + 12327, + ]; + assert_eq!(indices, expected_result); + + // 2 + let hash = sp_core::keccak_256(b"some2"); + let mut indices = SubtensorModule::block_hash_to_indices(H256(hash), k, n); + indices.sort(); + + assert!(indices.len() <= k as usize); + assert!(!indices.iter().any(|i| *i >= n)); + // precomputed values + let expected_result = vec![ + 61, 246, 1440, 2855, 3521, 5236, 6130, 6615, 8511, 9405, 9890, 11786, 11971, 13165, + 14580, + ]; + assert_eq!(indices, expected_result); + }); +} + +#[test] +fn test_claim_root_with_block_emissions() { + new_test_ext(0).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + Tempo::::insert(netuid, 1); + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 200_000_000u64; + SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(root_stake)); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + SubtensorModule::maybe_add_coldkey_index(&coldkey); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + // Distribute pending root alpha + + let initial_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + assert_eq!(initial_stake, 0u64); + + run_to_block(2); + + // Check stake after block emissions + + let new_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + + assert!(new_stake > 0); + }); +} +#[test] +fn test_populate_staking_maps() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1000); + let coldkey1 = U256::from(1001); + let coldkey2 = U256::from(1002); + let coldkey3 = U256::from(1003); + let hotkey = U256::from(1004); + let _netuid = add_dynamic_network(&hotkey, &owner_coldkey); + let netuid2 = NetUid::from(2); + + let root_stake = 200_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey1, + NetUid::ROOT, + root_stake.into(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey2, + NetUid::ROOT, + root_stake.into(), + ); + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey3, + netuid2, + root_stake.into(), + ); + + assert_eq!(NumStakingColdkeys::::get(), 0); + + // Populate maps through block step + + run_to_block(2); + + assert_eq!(NumStakingColdkeys::::get(), 2); + + assert!(StakingColdkeysByIndex::::contains_key(0)); + assert!(StakingColdkeysByIndex::::contains_key(1)); + + assert!(StakingColdkeys::::contains_key(coldkey1)); + assert!(StakingColdkeys::::contains_key(coldkey2)); + assert!(!StakingColdkeys::::contains_key(coldkey3)); + }); +} + +#[test] +fn test_claim_root_coinbase_distribution() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + Tempo::::insert(netuid, 1); + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 200_000_000u64; + let initial_tao = 200_000_000u64; + SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(initial_tao)); + + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + let initial_alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); + let alpha_emissions: AlphaCurrency = 1_000_000_000u64.into(); + + // Check total issuance (saved to pending alpha divs) + + run_to_block(2); + + let alpha_issuance = SubtensorModule::get_alpha_issuance(netuid); + assert_eq!(initial_alpha_issuance + alpha_emissions, alpha_issuance); + + let root_prop = initial_tao as f64 / (u64::from(alpha_issuance) + initial_tao) as f64; + let root_validators_share = 0.5f64; + + let expected_pending_root_alpha_divs = + u64::from(alpha_emissions) as f64 * root_prop * root_validators_share; + assert_abs_diff_eq!( + u64::from(PendingRootAlphaDivs::::get(netuid)) as f64, + expected_pending_root_alpha_divs, + epsilon = 100f64 + ); + + // Epoch pending alphas divs is distributed + + run_to_block(3); + + assert_eq!(u64::from(PendingRootAlphaDivs::::get(netuid)), 0u64); + + let claimable = *RootClaimable::::get(hotkey) + .get(&netuid) + .expect("claimable must exist at this point"); + + let validator_take_percent = 0.18f64; + let calculated_rate = (expected_pending_root_alpha_divs * 2f64) + * (1f64 - validator_take_percent) + / (root_stake as f64); + + assert_abs_diff_eq!( + claimable.saturating_to_num::(), + calculated_rate, + epsilon = 0.001f64, + ); + }); +} + +#[test] +fn test_sudo_set_num_root_claims() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1003); + + assert_noop!( + SubtensorModule::sudo_set_num_root_claims(RuntimeOrigin::signed(coldkey), 50u64), + DispatchError::BadOrigin + ); + + assert_noop!( + SubtensorModule::sudo_set_num_root_claims(RuntimeOrigin::root(), 0u64), + Error::::InvalidNumRootClaim + ); + + assert_noop!( + SubtensorModule::sudo_set_num_root_claims( + RuntimeOrigin::root(), + MAX_NUM_ROOT_CLAIMS + 1, + ), + Error::::InvalidNumRootClaim + ); + + let new_value = 27u64; + assert_ok!(SubtensorModule::sudo_set_num_root_claims( + RuntimeOrigin::root(), + new_value, + ),); + + assert_eq!(NumRootClaim::::get(), new_value); + }); +} + +#[test] +fn test_claim_root_with_swap_coldkey() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 2_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + let old_validator_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + ); + assert_eq!(old_validator_stake, initial_total_hotkey_alpha.into()); + + // Distribute pending root alpha + + let pending_root_alpha = 1_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Claim root alpha + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + let new_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + + // Check root claimed value saved + let new_coldkey = U256::from(10030); + + assert_eq!( + u128::from(new_stake), + RootClaimed::::get((&hotkey, &coldkey, netuid)) + ); + assert_eq!( + 0u128, + RootClaimed::::get((&hotkey, &new_coldkey, netuid)) + ); + + // Swap coldkey + let mut weight = Weight::zero(); + + assert_ok!(SubtensorModule::perform_swap_coldkey( + &coldkey, + &new_coldkey, + &mut weight + )); + + // Check swapped keys claimed values + + assert_eq!(0u128, RootClaimed::::get((&hotkey, &coldkey, netuid))); + assert_eq!( + u128::from(new_stake), + RootClaimed::::get((&hotkey, &new_coldkey, netuid)) + ); + }); +} +#[test] +fn test_claim_root_with_swap_hotkey() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + + let root_stake = 2_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + let old_validator_stake = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + ); + assert_eq!(old_validator_stake, initial_total_hotkey_alpha.into()); + + // Distribute pending root alpha + + let pending_root_alpha = 1_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Claim root alpha + + assert_ok!(SubtensorModule::set_root_claim_type( + RuntimeOrigin::signed(coldkey), + RootClaimTypeEnum::Keep + ),); + assert_eq!(RootClaimType::::get(coldkey), RootClaimTypeEnum::Keep); + + assert_ok!(SubtensorModule::claim_root(RuntimeOrigin::signed(coldkey),)); + + let new_stake: u64 = + SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid) + .into(); + + // Check root claimed value saved + let new_hotkey = U256::from(10030); + + assert_eq!( + u128::from(new_stake), + RootClaimed::::get((&hotkey, &coldkey, netuid)) + ); + assert_eq!( + 0u128, + RootClaimed::::get((&new_hotkey, &coldkey, netuid)) + ); + + let _old_claimable = *RootClaimable::::get(hotkey) + .get(&netuid) + .expect("claimable must exist at this point"); + + assert!(!RootClaimable::::get(new_hotkey).contains_key(&netuid)); + + // Swap hotkey + let mut weight = Weight::zero(); + assert_ok!(SubtensorModule::perform_hotkey_swap_on_one_subnet( + &hotkey, + &new_hotkey, + &mut weight, + netuid + )); + + // Check swapped keys claimed values + + assert_eq!(0u128, RootClaimed::::get((&hotkey, &coldkey, netuid))); + assert_eq!( + u128::from(new_stake), + RootClaimed::::get((&new_hotkey, &coldkey, netuid)) + ); + + assert!(!RootClaimable::::get(hotkey).contains_key(&netuid)); + + let _new_claimable = *RootClaimable::::get(new_hotkey) + .get(&netuid) + .expect("claimable must exist at this point"); + }); +} + +#[test] +fn test_claim_root_on_network_deregistration() { + new_test_ext(1).execute_with(|| { + let owner_coldkey = U256::from(1001); + let other_coldkey = U256::from(10010); + let hotkey = U256::from(1002); + let coldkey = U256::from(1003); + let netuid = add_dynamic_network(&hotkey, &owner_coldkey); + + SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + SubnetMechanism::::insert(netuid, 1); + + let tao_reserve = TaoCurrency::from(50_000_000_000); + let alpha_in = AlphaCurrency::from(100_000_000_000); + SubnetTAO::::insert(netuid, tao_reserve); + SubnetAlphaIn::::insert(netuid, alpha_in); + let current_price = + ::SwapInterface::current_alpha_price(netuid.into()) + .saturating_to_num::(); + assert_eq!(current_price, 0.5f64); + + let root_stake = 2_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + root_stake.into(), + ); + let root_stake_rate = 0.1f64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &other_coldkey, + NetUid::ROOT, + (9 * root_stake).into(), + ); + + let initial_total_hotkey_alpha = 10_000_000u64; + SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &owner_coldkey, + netuid, + initial_total_hotkey_alpha.into(), + ); + + // Distribute pending root alpha + + let pending_root_alpha = 10_000_000u64; + SubtensorModule::drain_pending_emission( + netuid, + AlphaCurrency::ZERO, + pending_root_alpha.into(), + AlphaCurrency::ZERO, + ); + + // Claim root via network deregistration + + assert_ok!(SubtensorModule::do_dissolve_network(netuid)); + + // Check new stake + let validator_take_percent = 0.18f64; + + let new_stake: u64 = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet( + &hotkey, + &coldkey, + NetUid::ROOT, + ) + .into(); + + let estimated_stake_increment = (pending_root_alpha as f64) + * (1f64 - validator_take_percent) + * current_price + * root_stake_rate; + + assert_abs_diff_eq!( + new_stake, + root_stake + estimated_stake_increment as u64, + epsilon = 10000u64, + ); + + assert!(!RootClaimed::::contains_key(( + &hotkey, &coldkey, netuid + ))); + assert!(!RootClaimable::::get(&hotkey).contains_key(&netuid)); + }); +} diff --git a/pallets/subtensor/src/tests/coinbase.rs b/pallets/subtensor/src/tests/coinbase.rs index 39a964a06..0d4e1337d 100644 --- a/pallets/subtensor/src/tests/coinbase.rs +++ b/pallets/subtensor/src/tests/coinbase.rs @@ -538,36 +538,25 @@ fn test_owner_cut_base() { // SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_pending_swapped --exact --show-output --nocapture #[test] -fn test_pending_swapped() { +fn test_pending_emission() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); let emission: u64 = 1_000_000; add_network(netuid, 1, 0); mock::setup_reserves(netuid, 1_000_000.into(), 1.into()); SubtensorModule::run_coinbase(U96F32::from_num(0)); - assert_eq!(PendingAlphaSwapped::::get(netuid), 0.into()); // Zero tao weight and no root. SubnetTAO::::insert(NetUid::ROOT, TaoCurrency::from(1_000_000_000)); // Add root weight. SubtensorModule::run_coinbase(U96F32::from_num(0)); - assert_eq!(PendingAlphaSwapped::::get(netuid), 0.into()); // Zero tao weight with 1 root. SubtensorModule::set_tempo(netuid, 10000); // Large number (dont drain) SubtensorModule::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 SubtensorModule::run_coinbase(U96F32::from_num(0)); // 1 TAO / ( 1 + 3 ) = 0.25 * 1 / 2 = 125000000 - assert_abs_diff_eq!( - u64::from(PendingAlphaSwapped::::get(netuid)), - 125000000, - epsilon = 1 - ); + assert_abs_diff_eq!( u64::from(PendingEmission::::get(netuid)), 1_000_000_000 - 125000000, epsilon = 1 ); // 1 - swapped. - assert_abs_diff_eq!( - u64::from(PendingRootDivs::::get(netuid)), - 125000000, - epsilon = 1 - ); // swapped * (price = 1) }); } @@ -578,7 +567,6 @@ fn test_drain_base() { SubtensorModule::drain_pending_emission( 0.into(), AlphaCurrency::ZERO, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ) @@ -594,7 +582,6 @@ fn test_drain_base_with_subnet() { SubtensorModule::drain_pending_emission( netuid, AlphaCurrency::ZERO, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ) @@ -620,7 +607,6 @@ fn test_drain_base_with_subnet_with_single_staker_not_registered() { SubtensorModule::drain_pending_emission( netuid, pending_alpha.into(), - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -650,7 +636,6 @@ fn test_drain_base_with_subnet_with_single_staker_registered() { SubtensorModule::drain_pending_emission( netuid, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -689,14 +674,13 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { netuid, stake_before, ); - let pending_tao = TaoCurrency::from(1_000_000_000); let pending_alpha = AlphaCurrency::from(1_000_000_000); + let pending_root_alpha = AlphaCurrency::from(1_000_000_000); assert_eq!(SubnetTAO::::get(NetUid::ROOT), TaoCurrency::ZERO); SubtensorModule::drain_pending_emission( netuid, pending_alpha, - pending_tao, - AlphaCurrency::ZERO, + pending_root_alpha, AlphaCurrency::ZERO, ); let stake_after = @@ -711,12 +695,7 @@ fn test_drain_base_with_subnet_with_single_staker_registered_root_weight() { stake_after.into(), 10, ); // Registered gets all alpha emission. - close( - stake_before.to_u64() + pending_tao.to_u64(), - root_after.into(), - 10, - ); // Registered gets all tao emission - assert_eq!(SubnetTAO::::get(NetUid::ROOT), pending_tao); + close(stake_before.to_u64(), root_after.into(), 10); // Registered doesn't get tao immediately }); } @@ -748,7 +727,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered() { SubtensorModule::drain_pending_emission( netuid, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -814,7 +792,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { SubtensorModule::drain_pending_emission( netuid, pending_alpha, - pending_tao, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -842,17 +819,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root() { stake_after2.into(), 10, ); // Registered gets 1/2 emission. - close( - stake_before.to_u64() + pending_tao.to_u64() / 2, - root_after1.into(), - 10, - ); // Registered gets 1/2 tao emission - close( - stake_before.to_u64() + pending_tao.to_u64() / 2, - root_after2.into(), - 10, - ); // Registered gets 1/2 tao emission - assert_eq!(SubnetTAO::::get(NetUid::ROOT), pending_tao); }); } @@ -901,8 +867,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am SubtensorModule::drain_pending_emission( netuid, pending_alpha, - pending_tao, - 0.into(), + AlphaCurrency::ZERO, 0.into(), ); let stake_after1 = @@ -933,25 +898,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am stake_after2.into(), epsilon = 10 ); // Registered gets 50% emission - let expected_root1 = I96F32::from_num(2 * u64::from(stake_before)) - + I96F32::from_num(pending_tao.to_u64()) * I96F32::from_num(2.0 / 3.0); - assert_abs_diff_eq!( - expected_root1.to_num::(), - root_after1.into(), - epsilon = 10 - ); // Registered gets 2/3 tao emission - let expected_root2 = I96F32::from_num(u64::from(stake_before)) - + I96F32::from_num(pending_tao.to_u64()) * I96F32::from_num(1.0 / 3.0); - assert_abs_diff_eq!( - expected_root2.to_num::(), - root_after2.into(), - epsilon = 10 - ); // Registered gets 1/3 tao emission - assert_abs_diff_eq!( - SubnetTAO::::get(NetUid::ROOT), - pending_tao, - epsilon = 10.into() - ); }); } @@ -1001,7 +947,7 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am SubtensorModule::drain_pending_emission( netuid, pending_alpha, - pending_tao, + // pending_tao, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1033,27 +979,6 @@ fn test_drain_base_with_subnet_with_two_stakers_registered_and_root_different_am u64::from(stake_after2), epsilon = 10 ); - // hotkey 1 has 2 / 3 root tao - let expected_root1 = I96F32::from_num(2 * u64::from(stake_before)) - + I96F32::from_num(pending_tao) * I96F32::from_num(2.0 / 3.0); - assert_abs_diff_eq!( - expected_root1.to_num::(), - u64::from(root_after1), - epsilon = 10 - ); - // hotkey 1 has 1 / 3 root tao - let expected_root2 = I96F32::from_num(u64::from(stake_before)) - + I96F32::from_num(pending_tao) * I96F32::from_num(1.0 / 3.0); - assert_abs_diff_eq!( - expected_root2.to_num::(), - u64::from(root_after2), - epsilon = 10 - ); - assert_abs_diff_eq!( - SubnetTAO::::get(NetUid::ROOT), - pending_tao, - epsilon = 10.into() - ); }); } @@ -1084,7 +1009,6 @@ fn test_drain_alpha_childkey_parentkey() { SubtensorModule::drain_pending_emission( netuid, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1310,7 +1234,6 @@ fn test_get_root_children_drain() { SubtensorModule::drain_pending_emission( alpha, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1334,7 +1257,7 @@ fn test_get_root_children_drain() { SubtensorModule::drain_pending_emission( alpha, pending_alpha, - pending_root1, + // pending_root1, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1342,16 +1265,13 @@ fn test_get_root_children_drain() { // Alice and Bob both made half of the dividends. assert_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&alice, NetUid::ROOT), - AlphaCurrency::from(alice_root_stake + pending_root1.to_u64() / 2) + AlphaCurrency::from(alice_root_stake) ); assert_eq!( SubtensorModule::get_stake_for_hotkey_on_subnet(&bob, NetUid::ROOT), - AlphaCurrency::from(bob_root_stake + pending_root1.to_u64() / 2) + AlphaCurrency::from(bob_root_stake) ); - // The pending root dividends should be present in root subnet. - assert_eq!(SubnetTAO::::get(NetUid::ROOT), pending_root1); - // Lets change the take value. (Bob is greedy.) ChildkeyTake::::insert(bob, alpha, u16::MAX); @@ -1361,7 +1281,6 @@ fn test_get_root_children_drain() { SubtensorModule::drain_pending_emission( alpha, pending_alpha, - pending_root2, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1371,25 +1290,12 @@ fn test_get_root_children_drain() { AlphaDividendsPerSubnet::::get(alpha, alice), AlphaCurrency::ZERO ); - assert_eq!( - TaoDividendsPerSubnet::::get(alpha, alice), - TaoCurrency::ZERO - ); // Bob makes it all. assert_abs_diff_eq!( AlphaDividendsPerSubnet::::get(alpha, bob), pending_alpha, epsilon = 1.into() ); - assert_eq!( - TaoDividendsPerSubnet::::get(alpha, bob), - pending_root2 - ); - // The pending root dividends should be present in root subnet. - assert_eq!( - SubnetTAO::::get(NetUid::ROOT), - pending_root1 + pending_root2 - ); }); } @@ -1463,7 +1369,6 @@ fn test_get_root_children_drain_half_proportion() { SubtensorModule::drain_pending_emission( alpha, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1550,7 +1455,6 @@ fn test_get_root_children_drain_with_take() { SubtensorModule::drain_pending_emission( alpha, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1638,7 +1542,6 @@ fn test_get_root_children_drain_with_half_take() { SubtensorModule::drain_pending_emission( alpha, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -1940,7 +1843,7 @@ fn test_calculate_dividend_distribution_totals() { let mut dividends: BTreeMap = BTreeMap::new(); let pending_validator_alpha = AlphaCurrency::from(183_123_567_452); - let pending_tao = TaoCurrency::from(837_120_949_872); + let pending_root_alpha = AlphaCurrency::from(837_120_949_872); let tao_weight: U96F32 = U96F32::saturating_from_num(0.18); // 18% let hotkeys = [U256::from(0), U256::from(1)]; @@ -1951,17 +1854,18 @@ fn test_calculate_dividend_distribution_totals() { dividends.insert(hotkeys[0], 77_783_738_u64.into()); dividends.insert(hotkeys[1], 19_283_940_u64.into()); - let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( - pending_validator_alpha, - pending_tao, - tao_weight, - stake_map, - dividends, - ); + let (alpha_dividends, root_alpha_dividends) = + SubtensorModule::calculate_dividend_distribution( + pending_validator_alpha, + pending_root_alpha, + tao_weight, + stake_map, + dividends, + ); // Verify the total of each dividends type is close to the inputs. let total_alpha_dividends = alpha_dividends.values().sum::(); - let total_tao_dividends = tao_dividends.values().sum::(); + let total_root_alpha_dividends = root_alpha_dividends.values().sum::(); assert_abs_diff_eq!( total_alpha_dividends.saturating_to_num::(), @@ -1969,8 +1873,8 @@ fn test_calculate_dividend_distribution_totals() { epsilon = 1_000 ); assert_abs_diff_eq!( - total_tao_dividends.saturating_to_num::(), - pending_tao.to_u64(), + total_root_alpha_dividends.saturating_to_num::(), + pending_root_alpha.to_u64(), epsilon = 1_000 ); }); @@ -1983,7 +1887,7 @@ fn test_calculate_dividend_distribution_total_only_tao() { let mut dividends: BTreeMap = BTreeMap::new(); let pending_validator_alpha = AlphaCurrency::ZERO; - let pending_tao = TaoCurrency::from(837_120_949_872); + let pending_root_alpha = AlphaCurrency::from(837_120_949_872); let tao_weight: U96F32 = U96F32::saturating_from_num(0.18); // 18% let hotkeys = [U256::from(0), U256::from(1)]; @@ -1994,17 +1898,18 @@ fn test_calculate_dividend_distribution_total_only_tao() { dividends.insert(hotkeys[0], 77_783_738_u64.into()); dividends.insert(hotkeys[1], 19_283_940_u64.into()); - let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( - pending_validator_alpha, - pending_tao, - tao_weight, - stake_map, - dividends, - ); + let (alpha_dividends, root_alpha_dividends) = + SubtensorModule::calculate_dividend_distribution( + pending_validator_alpha, + pending_root_alpha, + tao_weight, + stake_map, + dividends, + ); // Verify the total of each dividends type is close to the inputs. let total_alpha_dividends = alpha_dividends.values().sum::(); - let total_tao_dividends = tao_dividends.values().sum::(); + let total_root_alpha_dividends = root_alpha_dividends.values().sum::(); assert_abs_diff_eq!( total_alpha_dividends.saturating_to_num::(), @@ -2012,8 +1917,8 @@ fn test_calculate_dividend_distribution_total_only_tao() { epsilon = 1_000 ); assert_abs_diff_eq!( - total_tao_dividends.saturating_to_num::(), - pending_tao.to_u64(), + total_root_alpha_dividends.saturating_to_num::(), + pending_root_alpha.to_u64(), epsilon = 1_000 ); }); @@ -2039,7 +1944,8 @@ fn test_calculate_dividend_distribution_total_no_tao_weight() { let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( pending_validator_alpha, - pending_tao, + // pending_tao, + AlphaCurrency::ZERO, tao_weight, stake_map, dividends, @@ -2082,7 +1988,8 @@ fn test_calculate_dividend_distribution_total_only_alpha() { let (alpha_dividends, tao_dividends) = SubtensorModule::calculate_dividend_distribution( pending_validator_alpha, - pending_tao, + // pending_tao, + AlphaCurrency::ZERO, tao_weight, stake_map, dividends, @@ -2136,7 +2043,8 @@ fn test_calculate_dividend_and_incentive_distribution() { let (incentives, (alpha_dividends, tao_dividends)) = SubtensorModule::calculate_dividend_and_incentive_distribution( netuid, - pending_tao, + // pending_tao, + AlphaCurrency::ZERO, pending_validator_alpha, hotkey_emission, tao_weight, @@ -2186,7 +2094,8 @@ fn test_calculate_dividend_and_incentive_distribution_all_to_validators() { let (incentives, (alpha_dividends, tao_dividends)) = SubtensorModule::calculate_dividend_and_incentive_distribution( netuid, - pending_tao, + // pending_tao, + AlphaCurrency::ZERO, pending_validator_alpha, hotkey_emission, tao_weight, @@ -2369,7 +2278,6 @@ fn test_drain_pending_emission_no_miners_all_drained() { SubtensorModule::drain_pending_emission( netuid, emission, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -2442,7 +2350,6 @@ fn test_drain_pending_emission_zero_emission() { SubtensorModule::drain_pending_emission( netuid, 0.into(), - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); @@ -2736,7 +2643,6 @@ fn test_drain_alpha_childkey_parentkey_with_burn() { SubtensorModule::drain_pending_emission( netuid, pending_alpha, - TaoCurrency::ZERO, AlphaCurrency::ZERO, AlphaCurrency::ZERO, ); diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index ede32aa06..b3825c147 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -2171,3 +2171,38 @@ fn test_migrate_network_lock_cost_2500_sets_price_and_decay() { ); }); } + +#[test] +fn test_migrate_remove_tao_dividends() { + const MIGRATION_NAME: &str = "migrate_remove_tao_dividends"; + let pallet_name = "SubtensorModule"; + let storage_name = "TaoDividendsPerSubnet"; + let migration = + crate::migrations::migrate_remove_tao_dividends::migrate_remove_tao_dividends::; + + test_remove_storage_item( + MIGRATION_NAME, + pallet_name, + storage_name, + migration, + 200_000, + ); + + let storage_name = "PendingAlphaSwapped"; + test_remove_storage_item( + MIGRATION_NAME, + pallet_name, + storage_name, + migration, + 200_000, + ); + + let storage_name = "PendingRootDivs"; + test_remove_storage_item( + MIGRATION_NAME, + pallet_name, + storage_name, + migration, + 200_000, + ); +} diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index fda2f90b4..019074aca 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -1,6 +1,7 @@ mod auto_stake_hotkey; mod batch_tx; mod children; +mod claim_root; mod coinbase; mod consensus; mod delegate_info; diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index d7b0e15ea..d5d3620bf 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -370,8 +370,7 @@ fn dissolve_clears_all_per_subnet_storages() { NetworkRegistrationAllowed::::insert(net, true); NetworkPowRegistrationAllowed::::insert(net, true); PendingEmission::::insert(net, AlphaCurrency::from(1)); - PendingRootDivs::::insert(net, TaoCurrency::from(1)); - PendingAlphaSwapped::::insert(net, AlphaCurrency::from(1)); + PendingRootAlphaDivs::::insert(net, AlphaCurrency::from(1)); PendingOwnerCut::::insert(net, AlphaCurrency::from(1)); BlocksSinceLastStep::::insert(net, 1u64); LastMechansimStepBlock::::insert(net, 1u64); @@ -419,7 +418,6 @@ fn dissolve_clears_all_per_subnet_storages() { // Per‑subnet dividends AlphaDividendsPerSubnet::::insert(net, owner_hot, AlphaCurrency::from(1)); - TaoDividendsPerSubnet::::insert(net, owner_hot, TaoCurrency::from(1)); // Parent/child topology + takes ChildkeyTake::::insert(owner_hot, net, 1u16); @@ -528,8 +526,7 @@ fn dissolve_clears_all_per_subnet_storages() { assert!(!NetworkRegistrationAllowed::::contains_key(net)); assert!(!NetworkPowRegistrationAllowed::::contains_key(net)); assert!(!PendingEmission::::contains_key(net)); - assert!(!PendingRootDivs::::contains_key(net)); - assert!(!PendingAlphaSwapped::::contains_key(net)); + assert!(!PendingRootAlphaDivs::::contains_key(net)); assert!(!PendingOwnerCut::::contains_key(net)); assert!(!BlocksSinceLastStep::::contains_key(net)); assert!(!LastMechansimStepBlock::::contains_key(net)); @@ -579,7 +576,6 @@ fn dissolve_clears_all_per_subnet_storages() { assert!(!AlphaDividendsPerSubnet::::contains_key( net, owner_hot )); - assert!(!TaoDividendsPerSubnet::::contains_key(net, owner_hot)); // Parent/child topology + takes assert!(!ChildkeyTake::::contains_key(owner_hot, net)); diff --git a/pallets/subtensor/src/tests/swap_hotkey.rs b/pallets/subtensor/src/tests/swap_hotkey.rs index a31c238e8..980b04e6a 100644 --- a/pallets/subtensor/src/tests/swap_hotkey.rs +++ b/pallets/subtensor/src/tests/swap_hotkey.rs @@ -904,7 +904,6 @@ fn test_swap_stake_success() { TotalHotkeyShares::::insert(old_hotkey, netuid, shares); Alpha::::insert((old_hotkey, coldkey, netuid), U64F64::from_num(amount)); AlphaDividendsPerSubnet::::insert(netuid, old_hotkey, AlphaCurrency::from(amount)); - TaoDividendsPerSubnet::::insert(netuid, old_hotkey, TaoCurrency::from(amount)); // Perform the swap SubtensorModule::perform_hotkey_swap_on_all_subnets( @@ -955,14 +954,6 @@ fn test_swap_stake_success() { AlphaDividendsPerSubnet::::get(netuid, new_hotkey), amount.into() ); - assert_eq!( - TaoDividendsPerSubnet::::get(netuid, old_hotkey), - TaoCurrency::ZERO - ); - assert_eq!( - TaoDividendsPerSubnet::::get(netuid, new_hotkey), - amount.into() - ); }); } diff --git a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs index 740f3e97f..8624b97c8 100644 --- a/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs +++ b/pallets/subtensor/src/tests/swap_hotkey_with_subnet.rs @@ -957,7 +957,6 @@ fn test_swap_stake_success() { TotalHotkeyShares::::insert(old_hotkey, netuid, U64F64::from_num(shares)); Alpha::::insert((old_hotkey, coldkey, netuid), U64F64::from_num(amount)); AlphaDividendsPerSubnet::::insert(netuid, old_hotkey, AlphaCurrency::from(amount)); - TaoDividendsPerSubnet::::insert(netuid, old_hotkey, TaoCurrency::from(amount)); // Perform the swap System::set_block_number(System::block_number() + HotkeySwapOnSubnetInterval::get()); @@ -1009,14 +1008,6 @@ fn test_swap_stake_success() { AlphaDividendsPerSubnet::::get(netuid, new_hotkey), AlphaCurrency::from(amount) ); - assert_eq!( - TaoDividendsPerSubnet::::get(netuid, old_hotkey), - TaoCurrency::ZERO - ); - assert_eq!( - TaoDividendsPerSubnet::::get(netuid, new_hotkey), - amount.into() - ); }); } diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 5654def01..6d4b8b1f7 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -881,6 +881,10 @@ impl InstanceFilter for ProxyType { pallet_admin_utils::Call::sudo_set_toggle_transfer { .. } ) ), + ProxyType::RootClaim => matches!( + c, + RuntimeCall::SubtensorModule(pallet_subtensor::Call::claim_root { .. }) + ), } } fn is_superset(&self, o: &Self) -> bool {