Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
68525d9
Unignore alpha fee tests
gztensor Jan 8, 2026
7481c9a
Real swap when withdrawing alpha tx fees
gztensor Jan 8, 2026
01c8dff
fmt
gztensor Jan 8, 2026
b5416fe
Merge branch 'devnet-ready' into feat/re-enable-alpha-fees
gztensor Jan 12, 2026
136f007
Add drafty storage items for high precision alpha share pool
gztensor Jan 12, 2026
4c8cf2a
Add bigmath for share pool
gztensor Jan 15, 2026
1d4221f
Merge branch 'devnet-ready' into feat/re-enable-alpha-fees
gztensor Jan 15, 2026
49dc08b
Make SafeFloat exponent work like m*10^e, allow negative exponents
gztensor Jan 15, 2026
3716b0c
Fix safe float max exp type
gztensor Jan 15, 2026
054abb1
Update lencode
gztensor Jan 15, 2026
b523ff7
Fix safefloat addition and mantissa normalization, add tests
gztensor Jan 15, 2026
2151c1f
Add more test cases for safefloat add tests
gztensor Jan 15, 2026
e138732
fmt
gztensor Jan 15, 2026
f616e11
Add more test cases for safefloat add
gztensor Jan 16, 2026
cbb41d1
Add safefloat tests for div and mul_div
gztensor Jan 16, 2026
60f7153
Handle overflows in safefloat
gztensor Jan 16, 2026
9360f92
fmt
gztensor Jan 16, 2026
b54f38d
Add THS and Alpha migration and the test
gztensor Jan 16, 2026
22e2538
Test actual coldkey alphas in share pool migration
gztensor Jan 16, 2026
2883569
Add real-life scenario test when 7 TAO was lost due to precision loss…
gztensor Jan 16, 2026
323e0f3
Merge branch 'devnet-ready' into feat/re-enable-alpha-fees
gztensor Jan 16, 2026
1206016
Merge branch 'devnet-ready' into feat/re-enable-alpha-fees
gztensor Jan 19, 2026
36af1d8
fix zstd error
sam0x17 Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 265 additions & 12 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pallet-subtensor-swap-runtime-api = { path = "pallets/swap/runtime-api", default
pallet-subtensor-swap-rpc = { path = "pallets/swap/rpc", default-features = false }
procedural-fork = { path = "support/procedural-fork", default-features = false }
safe-math = { path = "primitives/safe-math", default-features = false }
safe-bigmath = { rev = "013c499", package = "safe-bigmath", default-features = false, git = "https://github.com/sam0x17/safe-bigmath" }
share-pool = { path = "primitives/share-pool", default-features = false }
subtensor-macros = { path = "support/macros", default-features = false }
subtensor-custom-rpc = { default-features = false, path = "pallets/subtensor/rpc" }
Expand All @@ -82,6 +83,7 @@ hex = { version = "0.4", default-features = false }
hex-literal = "0.4.1"
jsonrpsee = { version = "0.24.9", default-features = false }
libsecp256k1 = { version = "0.7.2", default-features = false }
lencode = "0.1.6"
log = { version = "0.4.21", default-features = false }
memmap2 = "0.9.8"
ndarray = { version = "0.16.1", default-features = false }
Expand Down Expand Up @@ -306,6 +308,8 @@ pow-faucet = []

[patch.crates-io]
w3f-bls = { git = "https://github.com/opentensor/bls", branch = "fix-no-std" }
zstd-sys = { path = "patches/zstd-sys" }
zstd-safe = { path = "patches/zstd-safe" }

# Patches automatically generated with `diener`:
# `diener patch --target https://github.com/paritytech/polkadot-sdk --point-to-git https://github.com/opentensor/polkadot-sdk.git --point-to-git-commit 81fa2c54e94f824eba7dabe9dffd063481cb2d80 --crates-to-patch ../polkadot-sdk --ignore-unused`
Expand Down
2 changes: 1 addition & 1 deletion common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ pub trait BalanceOps<AccountId> {
hotkey: &AccountId,
netuid: NetUid,
alpha: AlphaCurrency,
) -> Result<AlphaCurrency, DispatchError>;
) -> Result<(), DispatchError>;
}

pub mod time {
Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,15 +516,15 @@ impl<T: Config> Pallet<T> {
log::debug!(
"owner_hotkey: {owner_hotkey:?} owner_coldkey: {owner_coldkey:?}, owner_cut: {owner_cut:?}"
);
let real_owner_cut = Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
&owner_hotkey,
&owner_coldkey,
netuid,
owner_cut,
);
// If the subnet is leased, notify the lease logic that owner cut has been distributed.
if let Some(lease_id) = SubnetUidToLeaseId::<T>::get(netuid) {
Self::distribute_leased_network_dividends(lease_id, real_owner_cut);
Self::distribute_leased_network_dividends(lease_id, owner_cut);
}
}

Expand Down
33 changes: 32 additions & 1 deletion pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub mod pallet {
use frame_system::pallet_prelude::*;
use pallet_drand::types::RoundNumber;
use runtime_common::prod_or_fast;
use share_pool::SafeFloatSerializable;
use sp_core::{ConstU32, H160, H256};
use sp_runtime::traits::{Dispatchable, TrailingZeroInput};
use sp_std::collections::btree_map::BTreeMap;
Expand Down Expand Up @@ -1437,6 +1438,31 @@ pub mod pallet {
ValueQuery,
>;

/// DMAP ( hot, netuid ) --> total_alpha_shares | Returns the number of alpha shares for a hotkey on a subnet, stores bigmath vector.
#[pallet::storage]
pub type TotalHotkeySharesV2<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AccountId, // hot
Identity,
NetUid, // subnet
SafeFloatSerializable, // Hotkey shares in unlimited precision
ValueQuery,
>;

/// --- NMAP ( hot, cold, netuid ) --> alpha | Returns the alpha shares for a hotkey, coldkey, netuid triplet, stores bigmath vector.
#[pallet::storage]
pub type AlphaV2<T: Config> = StorageNMap<
_,
(
NMapKey<Blake2_128Concat, T::AccountId>, // hot
NMapKey<Blake2_128Concat, T::AccountId>, // cold
NMapKey<Identity, NetUid>, // subnet
),
SafeFloatSerializable, // Shares in unlimited precision
ValueQuery,
>;

/// Contains last Alpha storage map key to iterate (check first)
#[pallet::storage]
pub type AlphaMapLastKey<T: Config> =
Expand Down Expand Up @@ -2635,12 +2661,17 @@ impl<T: Config + pallet_balances::Config<Balance = u64>>
hotkey: &T::AccountId,
netuid: NetUid,
alpha: AlphaCurrency,
) -> Result<AlphaCurrency, DispatchError> {
) -> Result<(), DispatchError> {
ensure!(
Self::hotkey_account_exists(hotkey),
Error::<T>::HotKeyAccountNotExists
);

ensure!(
Self::get_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid) >= alpha,
Error::<T>::InsufficientBalance
);

// Decrese alpha out counter
SubnetAlphaOut::<T>::mutate(netuid, |total| {
*total = total.saturating_sub(alpha);
Expand Down
4 changes: 3 additions & 1 deletion pallets/subtensor/src/macros/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ mod hooks {
// Remove unknown neuron axon, certificate prom
.saturating_add(migrations::migrate_remove_unknown_neuron_axon_cert_prom::migrate_remove_unknown_neuron_axon_cert_prom::<T>())
// Fix staking hot keys
.saturating_add(migrations::migrate_fix_staking_hot_keys::migrate_fix_staking_hot_keys::<T>());
.saturating_add(migrations::migrate_fix_staking_hot_keys::migrate_fix_staking_hot_keys::<T>())
// Migrate from old share pool (Alpha) to high precision share pool (AlphaV2)
.saturating_add(migrations::migrate_share_pool_high_precision::migrate_share_pool_high_precision::<T>());
weight
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use super::*;
use frame_support::{traits::Get, weights::Weight};
use log;
use scale_info::prelude::string::String;
use share_pool::{SafeFloat, SafeFloatSerializable};

pub fn migrate_share_pool_high_precision<T: Config>() -> Weight {
let migration_name = b"migrate_share_pool_high_precision".to_vec();
let mut weight = T::DbWeight::get().reads(1);

// ------------------------------
// Step 0: Check if already run
// ------------------------------
if HasMigrationRun::<T>::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)
);

// ------------------------------
// Step 1: Migrate TotalHotkeyShares -> TotalHotkeySharesV2
// ------------------------------

let mut migrated_ths_entries_count = 0u64;

for (hotkey, netuid, shares) in TotalHotkeyShares::<T>::iter() {
TotalHotkeyShares::<T>::remove(&hotkey, netuid);

if shares != 0 {
let ths_safe_float: SafeFloat = shares.into();
let ths_safe_float_serializable: SafeFloatSerializable = (&ths_safe_float).into();
TotalHotkeySharesV2::<T>::insert(hotkey, netuid, ths_safe_float_serializable);

migrated_ths_entries_count = migrated_ths_entries_count.saturating_add(1);
}
}

weight = weight.saturating_add(T::DbWeight::get().reads(migrated_ths_entries_count));
weight = weight.saturating_add(T::DbWeight::get().writes(migrated_ths_entries_count));

log::info!("Migrated {migrated_ths_entries_count} entries from TotalHotkeyShares.");

// ------------------------------
// Step 2: Migrate Alpha -> AlphaV2
// ------------------------------

let mut migrated_alpha_entries_count = 0u64;

for ((hotkey, coldkey, netuid), alpha) in Alpha::<T>::iter() {
Alpha::<T>::remove((&hotkey, &coldkey, netuid));

if alpha != 0 {
let alpha_safe_float: SafeFloat = alpha.into();
let alpha_safe_float_serializable: SafeFloatSerializable = (&alpha_safe_float).into();
AlphaV2::<T>::insert((hotkey, coldkey, netuid), alpha_safe_float_serializable);

migrated_alpha_entries_count = migrated_alpha_entries_count.saturating_add(1);
}
}

weight = weight.saturating_add(T::DbWeight::get().reads(migrated_alpha_entries_count));
weight = weight.saturating_add(T::DbWeight::get().writes(migrated_alpha_entries_count));

log::info!("Migrated {migrated_alpha_entries_count} entries from Alpha.");

// ------------------------------
// Step 3: Mark Migration as Completed
// ------------------------------
HasMigrationRun::<T>::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
}
1 change: 1 addition & 0 deletions pallets/subtensor/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub mod migrate_set_min_difficulty;
pub mod migrate_set_nominator_min_stake;
pub mod migrate_set_registration_enable;
pub mod migrate_set_subtoken_enabled;
pub mod migrate_share_pool_high_precision;
pub mod migrate_stake_threshold;
pub mod migrate_subnet_limit_to_default;
pub mod migrate_subnet_locked;
Expand Down
9 changes: 7 additions & 2 deletions pallets/subtensor/src/rpc_info/delegate_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use substrate_fixed::types::U64F64;
extern crate alloc;
use alloc::collections::BTreeMap;
use codec::Compact;
use share_pool::SafeFloat;
use subtensor_runtime_common::{AlphaCurrency, NetUid};

#[freeze_struct("1fafc4fcf28cba7a")]
Expand Down Expand Up @@ -65,8 +66,12 @@ impl<T: Config> Pallet<T> {
alpha_share_pools.push(alpha_share_pool);
}

for ((nominator, netuid), alpha_stake) in Alpha::<T>::iter_prefix((delegate.clone(),)) {
if alpha_stake == 0 {
for ((nominator, netuid), alpha_stake_float_serializable) in
AlphaV2::<T>::iter_prefix((delegate.clone(),))
{
let alpha_stake = SafeFloat::from(&alpha_stake_float_serializable);

if alpha_stake.is_zero() {
continue;
}

Expand Down
30 changes: 6 additions & 24 deletions pallets/subtensor/src/staking/recycle_alpha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,12 @@ impl<T: Config> Pallet<T> {
);

// Deduct from the coldkey's stake.
let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey, &coldkey, netuid, amount,
);

ensure!(actual_alpha_decrease <= amount, Error::<T>::PrecisionLoss);
Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount);

// Recycle means we should decrease the alpha issuance tracker.
Self::recycle_subnet_alpha(netuid, actual_alpha_decrease);
Self::recycle_subnet_alpha(netuid, amount);

Self::deposit_event(Event::AlphaRecycled(
coldkey,
hotkey,
actual_alpha_decrease,
netuid,
));
Self::deposit_event(Event::AlphaRecycled(coldkey, hotkey, amount, netuid));

Ok(())
}
Expand Down Expand Up @@ -118,21 +109,12 @@ impl<T: Config> Pallet<T> {
);

// Deduct from the coldkey's stake.
let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey, &coldkey, netuid, amount,
);

ensure!(actual_alpha_decrease <= amount, Error::<T>::PrecisionLoss);
Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid, amount);

Self::burn_subnet_alpha(netuid, actual_alpha_decrease);
Self::burn_subnet_alpha(netuid, amount);

// Deposit event
Self::deposit_event(Event::AlphaBurned(
coldkey,
hotkey,
actual_alpha_decrease,
netuid,
));
Self::deposit_event(Event::AlphaBurned(coldkey, hotkey, amount, netuid));

Ok(())
}
Expand Down
Loading
Loading