diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index d339e0ec83..b4db388621 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -323,6 +323,7 @@ impl Pallet { PendingServerEmission::::remove(netuid); PendingRootAlphaDivs::::remove(netuid); PendingOwnerCut::::remove(netuid); + MinerBurned::::remove(netuid); BlocksSinceLastStep::::remove(netuid); LastMechansimStepBlock::::remove(netuid); LastAdjustmentBlock::::remove(netuid); diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6b2ea6de0b..d7c75964b2 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -669,14 +669,24 @@ impl Pallet { let subnet_owner_coldkey = SubnetOwner::::get(netuid); let owner_hotkeys = Self::get_owner_hotkeys(netuid, &subnet_owner_coldkey); log::debug!("incentives: owner hotkeys: {owner_hotkeys:?}"); + // Track total miner emission vs the portion withheld from miners this tempo + // (directed to an owner/immune hotkey) to record the withheld proportion. + let mut total_incentive: AlphaBalance = AlphaBalance::ZERO; + let mut withheld_incentive: AlphaBalance = AlphaBalance::ZERO; for (hotkey, incentive) in incentives { log::debug!("incentives: hotkey: {incentive:?}"); + total_incentive = total_incentive.saturating_add(incentive); // Skip/burn miner-emission for immune keys if owner_hotkeys.contains(&hotkey) { log::debug!( "incentives: hotkey: {hotkey:?} is SN owner hotkey or associated hotkey, skipping {incentive:?}" ); + // Miner emission directed to an owner (immune) hotkey is withheld from + // miners whether it is recycled or burned. Count both toward the withheld + // proportion so the emission penalty cannot be dodged by choosing Recycle + // and an unset RecycleOrBurn config is not uniquely penalized. + withheld_incentive = withheld_incentive.saturating_add(incentive); // Check if we should recycle or burn the incentive match RecycleOrBurn::::try_get(netuid) { Ok(RecycleOrBurnEnum::Recycle) => { @@ -716,6 +726,13 @@ impl Pallet { ); } + // Record the proportion of this tempo's miner emission that was withheld from + // miners (directed to owner/immune hotkeys, whether recycled or burned). + let withheld_proportion: U96F32 = U96F32::saturating_from_num(withheld_incentive.to_u64()) + .checked_div(U96F32::saturating_from_num(total_incentive.to_u64())) + .unwrap_or_else(|| U96F32::saturating_from_num(0)); + MinerBurned::::insert(netuid, withheld_proportion); + // Distribute alpha divs. let _ = AlphaDividendsPerSubnet::::clear_prefix(netuid, u32::MAX, None); for (hotkey, mut alpha_divs) in alpha_dividends { diff --git a/pallets/subtensor/src/coinbase/subnet_emissions.rs b/pallets/subtensor/src/coinbase/subnet_emissions.rs index caab671ed4..f378e3e930 100644 --- a/pallets/subtensor/src/coinbase/subnet_emissions.rs +++ b/pallets/subtensor/src/coinbase/subnet_emissions.rs @@ -352,7 +352,42 @@ impl Pallet { // redistributed to enabled subnets in `get_subnet_block_emissions`, so the // effective emission is e_i = p_i / sum(p_j) over emit-enabled subnets. pub(crate) fn get_shares(subnets_to_emit_to: &[NetUid]) -> BTreeMap { - Self::get_shares_price_ema(subnets_to_emit_to) + let price_shares = Self::get_shares_price_ema(subnets_to_emit_to); + + // Weight each subnet's price share by root_proportion * (1 - miner_burned), then + // renormalize. The effective emission is therefore proportional to + // root_proportion_i * price_i * (1 - miner_burned_i). + // - root_proportion shrinks as a subnet's alpha issuance grows, so emission is + // reallocated away from older subnets toward newer ones (easier entrance). + // - (1 - miner_burned) reallocates away from subnets that withhold miner emission. + let zero = U64F64::saturating_from_num(0); + let one = U64F64::saturating_from_num(1); + let weighted: BTreeMap = price_shares + .iter() + .map(|(netuid, share)| { + let burned = U64F64::saturating_from_num(MinerBurned::::get(netuid)).min(one); + let root_prop = U64F64::saturating_from_num(Self::root_proportion(*netuid)); + let factor = root_prop.saturating_mul(one.saturating_sub(burned)); + (*netuid, share.saturating_mul(factor)) + }) + .collect(); + + let total_weight = weighted + .values() + .copied() + .fold(zero, |acc, w| acc.saturating_add(w)); + + if total_weight > zero { + weighted + .into_iter() + .map(|(netuid, w)| (netuid, w.safe_div(total_weight))) + .collect() + } else { + // The combined weight zeroes out for every subnet (e.g. no root stake, or + // every subnet burning all of its miner emission); fall back to the + // unweighted price shares so the block's emission is not stranded. + price_shares + } } // Implementation of shares that uses subnet EMA prices (SubnetMovingPrice), diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 39fe375e6b..b1afef3401 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -67,6 +67,9 @@ pub const MAX_SUBNET_CLAIMS: usize = 5; pub const MAX_ROOT_CLAIM_THRESHOLD: u64 = 10_000_000; +/// Account flag bit that opts into receiving locked alpha transfers. +pub const ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA: u128 = 1u128 << 0; + #[allow(deprecated)] #[deny(missing_docs)] #[import_section(errors::errors)] @@ -1190,6 +1193,11 @@ pub mod pallet { pub type Owner = StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount>; + /// MAP ( coldkey ) --> flags | Account-level flags. Defaults to zero. + #[pallet::storage] + pub type AccountFlags = + StorageMap<_, Blake2_128Concat, T::AccountId, u128, ValueQuery>; + /// MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation #[pallet::storage] pub type Delegates = @@ -1926,6 +1934,20 @@ pub mod pallet { pub type PendingOwnerCut = StorageMap<_, Identity, NetUid, AlphaBalance, ValueQuery, DefaultZeroAlpha>; + /// Default miner-burned proportion. + #[pallet::type_value] + pub fn DefaultMinerBurned() -> U96F32 { + U96F32::saturating_from_num(0.0) + } + /// --- MAP ( netuid ) --> miner_burned | Proportion (0..1) of this tempo's miner + /// (incentive) emission that was withheld from miners during emission distribution + /// because the recipient hotkey is owned by the subnet owner (immune key). Counts + /// emission that is either recycled or burned, so the value is independent of the + /// subnet's RecycleOrBurn configuration. + #[pallet::storage] + pub type MinerBurned = + StorageMap<_, Identity, NetUid, U96F32, ValueQuery, DefaultMinerBurned>; + /// --- MAP ( netuid ) --> blocks_since_last_step #[pallet::storage] pub type BlocksSinceLastStep = diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 2a22915ee9..08e5bb8fdf 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -2625,5 +2625,31 @@ mod dispatches { pub fn trigger_epoch(origin: OriginFor, netuid: NetUid) -> DispatchResult { Self::do_trigger_epoch(origin, netuid) } + + /// Sets or clears whether the caller rejects incoming locked alpha. + /// + /// Coldkeys reject locked alpha by default. Passing `false` opts the + /// caller into receiving locked alpha from stake transfers or coldkey + /// swaps. + #[pallet::call_index(142)] + #[pallet::weight(( + ::DbWeight::get().reads_writes(1, 1), + DispatchClass::Normal, + Pays::Yes + ))] + pub fn set_reject_locked_alpha(origin: OriginFor, enabled: bool) -> DispatchResult { + let coldkey = ensure_signed(origin)?; + AccountFlags::::mutate_exists(&coldkey, |maybe_flags| { + let mut flags = maybe_flags.unwrap_or_default(); + if enabled { + flags &= !crate::ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA; + } else { + flags |= crate::ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA; + } + *maybe_flags = if flags == 0 { None } else { Some(flags) }; + }); + Self::deposit_event(Event::RejectLockedAlphaUpdated { coldkey, enabled }); + Ok(()) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 32e0fbcbd6..6401b5846d 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -317,5 +317,7 @@ mod errors { /// an out-of-band epoch would desync the CRv3 reveal window from the wall-clock /// Drand schedule and silently drop committed weights. DynamicTempoBlockedByCommitReveal, + /// The destination coldkey rejects incoming locked alpha. + AccountRejectsLockedAlpha, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index adcd97e0cf..7bc9bf450a 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -669,5 +669,13 @@ mod events { /// Whether this coldkey's locks are now perpetual. enabled: bool, }, + + /// A coldkey's reject locked alpha account flag was updated. + RejectLockedAlphaUpdated { + /// The coldkey whose flag changed. + coldkey: T::AccountId, + /// Whether this coldkey rejects incoming locked alpha. + enabled: bool, + }, } } diff --git a/pallets/subtensor/src/staking/lock.rs b/pallets/subtensor/src/staking/lock.rs index 78e2734c5b..fcf699619d 100644 --- a/pallets/subtensor/src/staking/lock.rs +++ b/pallets/subtensor/src/staking/lock.rs @@ -467,6 +467,29 @@ impl Pallet { LockingColdkeys::::remove((netuid, hotkey, coldkey)); } + pub fn account_rejects_locked_alpha(coldkey: &T::AccountId) -> bool { + AccountFlags::::get(coldkey) & crate::ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA != 1 + } + + pub fn ensure_can_receive_locked_alpha( + coldkey: &T::AccountId, + amount: AlphaBalance, + ) -> DispatchResult { + let rejects_locked_alpha = Self::account_rejects_locked_alpha(coldkey); + Self::ensure_can_receive_locked_alpha_with_flag(rejects_locked_alpha, amount) + } + + fn ensure_can_receive_locked_alpha_with_flag( + rejects_locked_alpha: bool, + amount: AlphaBalance, + ) -> DispatchResult { + if amount.is_zero() { + return Ok(()); + } + ensure!(!rejects_locked_alpha, Error::::AccountRejectsLockedAlpha); + Ok(()) + } + pub fn insert_lock_state( coldkey: &T::AccountId, netuid: NetUid, @@ -1359,6 +1382,10 @@ impl Pallet { Self::ensure_no_active_locks(new_coldkey)?; let mut locks_to_transfer: Vec<(NetUid, T::AccountId, LockState)> = Vec::new(); + let now = Self::get_current_block_as_u64(); + let unlock_rate = UnlockRate::::get(); + let maturity_rate = MaturityRate::::get(); + let new_coldkey_rejects_locked_alpha = Self::account_rejects_locked_alpha(new_coldkey); let decaying_locks_to_transfer: Vec<(NetUid, bool)> = DecayingLock::::iter_prefix(old_coldkey).collect(); @@ -1367,19 +1394,12 @@ impl Pallet { locks_to_transfer.push((netuid, hotkey, lock)); } - for (netuid, decaying) in decaying_locks_to_transfer.iter() { - DecayingLock::::insert(new_coldkey, *netuid, *decaying); - } - - // Remove locks for old coldkey and insert for new + let mut rolled_locks_to_transfer: Vec<(NetUid, T::AccountId, LockState, bool)> = Vec::new(); for (netuid, hotkey, lock) in locks_to_transfer { - let now = Self::get_current_block_as_u64(); - let unlock_rate = UnlockRate::::get(); - let maturity_rate = MaturityRate::::get(); let perpetual_lock = decaying_locks_to_transfer .iter() .any(|(decaying_netuid, decaying)| *decaying_netuid == netuid && !*decaying); - let old_lock = ConvictionModel::roll_forward_lock( + let (old_lock, _) = ConvictionModel::roll_forward_lock( lock, now, unlock_rate, @@ -1387,8 +1407,38 @@ impl Pallet { Self::is_subnet_owner_hotkey(netuid, &hotkey), perpetual_lock, ); + Self::ensure_can_receive_locked_alpha_with_flag( + new_coldkey_rejects_locked_alpha, + old_lock.locked_mass, + )?; + rolled_locks_to_transfer.push((netuid, hotkey, old_lock, perpetual_lock)); + } + + // Remove old locks and reduce old aggregate buckets before moving the + // perpetual-lock flags; aggregate selection depends on the old flag. + for (netuid, hotkey, old_lock, _) in rolled_locks_to_transfer.iter() { + Lock::::remove((old_coldkey.clone(), *netuid, hotkey.clone())); + Self::maybe_remove_locking_coldkey(hotkey, *netuid, old_coldkey); + Self::reduce_aggregate_lock( + old_coldkey, + hotkey, + *netuid, + old_lock.locked_mass, + old_lock.conviction, + ); + } + + for (netuid, _) in decaying_locks_to_transfer { + if let Some(decaying) = DecayingLock::::take(old_coldkey, netuid) { + DecayingLock::::insert(new_coldkey, netuid, decaying); + } + } + + // Insert locks for the new coldkey and add to the destination aggregate + // buckets after the flags have moved. + for (netuid, hotkey, old_lock, perpetual_lock) in rolled_locks_to_transfer { let new_lock = ConvictionModel::roll_forward_lock( - old_lock.0.clone(), + old_lock.clone(), now, unlock_rate, maturity_rate, @@ -1396,23 +1446,10 @@ impl Pallet { perpetual_lock, ) .0; - Lock::::remove((old_coldkey.clone(), netuid, hotkey.clone())); - Self::maybe_remove_locking_coldkey(&hotkey, netuid, old_coldkey); - Self::reduce_aggregate_lock( - old_coldkey, - &hotkey, - netuid, - old_lock.0.locked_mass, - old_lock.0.conviction, - ); Self::insert_lock_state(new_coldkey, netuid, &hotkey, new_lock.clone()); Self::add_aggregate_lock(new_coldkey, &hotkey, netuid, new_lock); } - for (netuid, _) in decaying_locks_to_transfer { - DecayingLock::::remove(old_coldkey, netuid); - } - Ok(()) } @@ -1838,6 +1875,7 @@ impl Pallet { .conviction .saturating_add(conviction_transfer); } + Self::ensure_can_receive_locked_alpha(destination_coldkey, locked_transfer)?; source_lock = ConvictionModel::roll_forward_lock( source_lock, diff --git a/pallets/subtensor/src/tests/locks.rs b/pallets/subtensor/src/tests/locks.rs index 4eaf01668c..91b48b7881 100644 --- a/pallets/subtensor/src/tests/locks.rs +++ b/pallets/subtensor/src/tests/locks.rs @@ -6,6 +6,7 @@ )] use approx::assert_abs_diff_eq; +use frame_support::dispatch::{GetDispatchInfo, Pays}; use frame_support::weights::Weight; use frame_support::{assert_noop, assert_ok}; use safe_math::FixedExt; @@ -96,6 +97,40 @@ fn roll_forward_individual_lock( ) } +#[test] +fn test_account_flags_default_to_zero_and_reject_locked_alpha_setter_pays_fee() { + new_test_ext(1).execute_with(|| { + let coldkey = U256::from(1); + + assert_eq!(AccountFlags::::get(coldkey), 0); + assert!(!AccountFlags::::contains_key(coldkey)); + assert!(SubtensorModule::account_rejects_locked_alpha(&coldkey)); + + let call = + RuntimeCall::SubtensorModule(crate::Call::set_reject_locked_alpha { enabled: true }); + assert_eq!(call.get_dispatch_info().pays_fee, Pays::Yes); + + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(coldkey), + false, + )); + assert_eq!( + AccountFlags::::get(coldkey), + ACCOUNT_FLAGS_ACCEPT_LOCKED_ALPHA + ); + assert!(AccountFlags::::contains_key(coldkey)); + assert!(!SubtensorModule::account_rejects_locked_alpha(&coldkey)); + + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(coldkey), + true, + )); + assert_eq!(AccountFlags::::get(coldkey), 0); + assert!(!AccountFlags::::contains_key(coldkey)); + assert!(SubtensorModule::account_rejects_locked_alpha(&coldkey)); + }); +} + fn roll_forward_hotkey_lock(lock: LockState, now: u64) -> LockState { roll_forward_lock(lock, now, false, true) } @@ -2152,6 +2187,10 @@ fn test_do_transfer_stake_same_subnet_transfers_lock_to_destination_coldkey() { let hotkey = U256::from(2); let netuid = setup_subnet_with_stake(coldkey_sender, hotkey, 100_000_000_000); DecayingLock::::insert(coldkey_receiver, netuid, false); + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(coldkey_receiver), + false, + )); let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_sender, netuid); let lock_half = total / 2.into(); @@ -2247,6 +2286,101 @@ fn test_move_stake_cross_subnet_blocked_by_lock() { }); } +#[test] +fn test_do_transfer_stake_rejects_locked_alpha_to_flagged_destination() { + new_test_ext(1).execute_with(|| { + let coldkey_sender = U256::from(1); + let coldkey_receiver = U256::from(5); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey_sender, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_sender, netuid); + let lock_half = total / 2.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey_sender, + netuid, + &hotkey, + lock_half, + )); + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(coldkey_receiver), + true, + )); + + let sender_lock_before = + Lock::::get((coldkey_sender, netuid, hotkey)).expect("sender lock should exist"); + let sender_alpha_before = + SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_sender, netuid); + let receiver_alpha_before = + SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_receiver, netuid); + + assert_noop!( + SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(coldkey_sender), + coldkey_receiver, + hotkey, + netuid, + netuid, + total, + ), + Error::::AccountRejectsLockedAlpha + ); + + assert_eq!( + Lock::::get((coldkey_sender, netuid, hotkey)), + Some(sender_lock_before) + ); + assert!(Lock::::get((coldkey_receiver, netuid, hotkey)).is_none()); + assert_eq!( + SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_sender, netuid), + sender_alpha_before + ); + assert_eq!( + SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_receiver, netuid), + receiver_alpha_before + ); + }); +} + +#[test] +fn test_do_transfer_stake_allows_unlocked_alpha_to_flagged_destination() { + new_test_ext(1).execute_with(|| { + let coldkey_sender = U256::from(1); + let coldkey_receiver = U256::from(5); + let hotkey = U256::from(2); + let netuid = setup_subnet_with_stake(coldkey_sender, hotkey, 100_000_000_000); + + let total = SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_sender, netuid); + let lock_half = total / 2.into(); + assert_ok!(SubtensorModule::do_lock_stake( + &coldkey_sender, + netuid, + &hotkey, + lock_half, + )); + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(coldkey_receiver), + true, + )); + + let unlocked_transfer = lock_half / 2.into(); + assert_ok!(SubtensorModule::do_transfer_stake( + RuntimeOrigin::signed(coldkey_sender), + coldkey_receiver, + hotkey, + netuid, + netuid, + unlocked_transfer, + )); + + assert!(Lock::::get((coldkey_receiver, netuid, hotkey)).is_none()); + assert_eq!( + SubtensorModule::total_coldkey_alpha_on_subnet(&coldkey_receiver, netuid), + unlocked_transfer + ); + }); +} + #[test] fn test_transfer_stake_cross_coldkey_allowed_partial() { new_test_ext(1).execute_with(|| { @@ -3284,6 +3418,10 @@ fn test_coldkey_swap_swaps_lock() { &hotkey, 5000u64.into(), )); + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(new_coldkey), + false, + )); // Perform coldkey swap assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); @@ -3318,6 +3456,10 @@ fn test_coldkey_swap_lock_blocks_unstake() { &hotkey, total, )); + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(new_coldkey), + false, + )); // Swap coldkey assert_ok!(SubtensorModule::do_swap_coldkey(&old_coldkey, &new_coldkey)); @@ -3362,6 +3504,7 @@ fn test_coldkey_swap_allows_destination_conviction_only_lock() { last_update: SubtensorModule::get_current_block_as_u64(), }, ); + DecayingLock::::insert(old_coldkey, netuid, false); SubtensorModule::insert_lock_state( &new_coldkey, netuid, @@ -3390,6 +3533,8 @@ fn test_coldkey_swap_allows_destination_conviction_only_lock() { assert_eq!(swapped_lock.locked_mass, AlphaBalance::ZERO); assert_eq!(swapped_lock.conviction, old_conviction); assert_eq!(Lock::::iter_prefix((new_coldkey, netuid)).count(), 2); + assert!(DecayingLock::::get(old_coldkey, netuid).is_none()); + assert_eq!(DecayingLock::::get(new_coldkey, netuid), Some(false)); }); } @@ -3452,6 +3597,52 @@ fn test_coldkey_swap_rejects_destination_lock() { }); } +#[test] +fn test_coldkey_swap_rejects_locked_alpha_to_flagged_destination() { + new_test_ext(1).execute_with(|| { + let old_coldkey = U256::from(1); + let new_coldkey = U256::from(10); + let old_hotkey = U256::from(2); + let netuid = subtensor_runtime_common::NetUid::from(1); + + let old_locked = AlphaBalance::from(7_000u64); + let old_conviction = U64F64::from_num(77); + + SubtensorModule::insert_lock_state( + &old_coldkey, + netuid, + &old_hotkey, + LockState { + locked_mass: old_locked, + conviction: old_conviction, + last_update: SubtensorModule::get_current_block_as_u64(), + }, + ); + DecayingLock::::insert(old_coldkey, netuid, false); + assert_ok!(SubtensorModule::set_reject_locked_alpha( + RuntimeOrigin::signed(new_coldkey), + true, + )); + + assert_noop!( + SubtensorModule::swap_coldkey_locks(&old_coldkey, &new_coldkey), + Error::::AccountRejectsLockedAlpha + ); + + let source_lock = Lock::::get((old_coldkey, netuid, old_hotkey)) + .expect("source lock should remain after failed transfer"); + assert_eq!(source_lock.locked_mass, old_locked); + assert_eq!(source_lock.conviction, old_conviction); + assert!( + Lock::::iter_prefix((new_coldkey, netuid)) + .next() + .is_none() + ); + assert_eq!(DecayingLock::::get(old_coldkey, netuid), Some(false)); + assert!(DecayingLock::::get(new_coldkey, netuid).is_none()); + }); +} + #[test] // The public coldkey swap extrinsic runs inside a storage layer, so a late failure rolls back the earlier writes. fn test_failed_coldkey_swap_extrinsic_rolls_back_state_changes() { diff --git a/pallets/subtensor/src/tests/networks.rs b/pallets/subtensor/src/tests/networks.rs index 4696507e2e..a967761ef5 100644 --- a/pallets/subtensor/src/tests/networks.rs +++ b/pallets/subtensor/src/tests/networks.rs @@ -406,6 +406,7 @@ fn dissolve_clears_all_per_subnet_storages() { PendingValidatorEmission::::insert(net, AlphaBalance::from(1)); PendingRootAlphaDivs::::insert(net, AlphaBalance::from(1)); PendingOwnerCut::::insert(net, AlphaBalance::from(1)); + MinerBurned::::insert(net, substrate_fixed::types::U96F32::from_num(1)); BlocksSinceLastStep::::insert(net, 1u64); LastMechansimStepBlock::::insert(net, 1u64); ServingRateLimit::::insert(net, 1u64); @@ -563,6 +564,7 @@ fn dissolve_clears_all_per_subnet_storages() { assert!(!PendingValidatorEmission::::contains_key(net)); assert!(!PendingRootAlphaDivs::::contains_key(net)); assert!(!PendingOwnerCut::::contains_key(net)); + assert!(!MinerBurned::::contains_key(net)); assert!(!BlocksSinceLastStep::::contains_key(net)); assert!(!LastMechansimStepBlock::::contains_key(net)); assert!(!ServingRateLimit::::contains_key(net)); diff --git a/pallets/subtensor/src/tests/subnet_emissions.rs b/pallets/subtensor/src/tests/subnet_emissions.rs index 4bb1aa4c75..9218eabe76 100644 --- a/pallets/subtensor/src/tests/subnet_emissions.rs +++ b/pallets/subtensor/src/tests/subnet_emissions.rs @@ -5,7 +5,7 @@ use alloc::{collections::BTreeMap, vec::Vec}; use approx::assert_abs_diff_eq; use sp_core::U256; use substrate_fixed::types::{I64F64, I96F32, U64F64, U96F32}; -use subtensor_runtime_common::{NetUid, TaoBalance}; +use subtensor_runtime_common::{AlphaBalance, NetUid, TaoBalance}; fn u64f64(x: f64) -> U64F64 { U64F64::from_num(x) diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs index 7de33b4a4c..478c37ad7c 100644 --- a/pallets/subtensor/src/weights.rs +++ b/pallets/subtensor/src/weights.rs @@ -1314,6 +1314,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AccountFlags` (r:1 w:0) + /// Proof: `SubtensorModule::AccountFlags` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:0) @@ -1326,11 +1328,11 @@ impl WeightInfo for SubstrateWeight { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `1988` - // Estimated: `7928` - // Minimum execution time: 263_199_000 picoseconds. - Weight::from_parts(269_320_000, 7928) - .saturating_add(T::DbWeight::get().reads(18_u64)) + // Measured: `2054` + // Estimated: `7994` + // Minimum execution time: 254_636_000 picoseconds. + Weight::from_parts(258_541_000, 7994) + .saturating_add(T::DbWeight::get().reads(19_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) @@ -3800,6 +3802,8 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::StakingHotkeys` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::Lock` (r:1 w:0) /// Proof: `SubtensorModule::Lock` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `SubtensorModule::AccountFlags` (r:1 w:0) + /// Proof: `SubtensorModule::AccountFlags` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetMechanism` (r:1 w:0) /// Proof: `SubtensorModule::SubnetMechanism` (`max_values`: None, `max_size`: None, mode: `Measured`) /// Storage: `SubtensorModule::SubnetAlphaIn` (r:1 w:0) @@ -3812,11 +3816,11 @@ impl WeightInfo for () { /// Proof: `SubtensorModule::LastColdkeyHotkeyStakeBlock` (`max_values`: None, `max_size`: None, mode: `Measured`) fn transfer_stake() -> Weight { // Proof Size summary in bytes: - // Measured: `1988` - // Estimated: `7928` - // Minimum execution time: 263_199_000 picoseconds. - Weight::from_parts(269_320_000, 7928) - .saturating_add(RocksDbWeight::get().reads(18_u64)) + // Measured: `2054` + // Estimated: `7994` + // Minimum execution time: 254_636_000 picoseconds. + Weight::from_parts(258_541_000, 7994) + .saturating_add(RocksDbWeight::get().reads(19_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } /// Storage: `SubtensorModule::Alpha` (r:2 w:0) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8c532c306b..652b83475c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -234,7 +234,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 421, + spec_version: 422, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,