Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 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
d690e6b
Update llvm installation instructions in readme
gztensor Jan 20, 2026
dbbc3a9
Fix test_sets_a_lower_value_clears_small_nominations
gztensor Jan 21, 2026
0ce0a19
Fix test_recycle_precision and test_burn_precision
gztensor Jan 22, 2026
a6a624e
Fix test_share_based_staking_denominator_precision
gztensor Jan 22, 2026
aaeaf16
Fix test_remove_99_9991_per_cent_stake_removes_all
gztensor Jan 22, 2026
d83f8a4
Fix test_coldkey_delegations
gztensor Jan 22, 2026
6154251
Fix clippy
gztensor Jan 22, 2026
4b19e1a
Merge branch 'devnet-ready' into feat/re-enable-alpha-fees
gztensor Jan 22, 2026
282cbf5
Spec version bump
gztensor Jan 22, 2026
919dc1b
Add lazy migration and fix remaining tests
gztensor Jan 22, 2026
d517a2f
Remove fn alpha_iter_keys
gztensor Jan 23, 2026
daf0b54
Fix get_alpha_staked_validators precompile, fix swapping alpha in swa…
gztensor Jan 23, 2026
cba4f41
Add alpha/ths v2 tests for swap hotkey
gztensor Jan 23, 2026
b4ba4a3
Add tests for real (non-mock) sharepool data operations
gztensor Jan 23, 2026
5457035
Fix clippy
gztensor Jan 23, 2026
36b3bc1
Use sfser_from_u64 in tests
gztensor Jan 23, 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
16 changes: 15 additions & 1 deletion docs/rust-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,21 @@ Open the Terminal application and execute the following commands:

# Make sure Homebrew is up-to-date, install protobuf and openssl
brew update
brew install protobuf openssl
brew install protobuf openssl llvm@16
```

Also, add the following lines at the end of your ~/.zshrc:

```
# LLVM 16 from Homebrew
export PATH="/opt/homebrew/opt/llvm@16/bin:$PATH"

export CC="/opt/homebrew/opt/llvm@16/bin/clang"
export CXX="/opt/homebrew/opt/llvm@16/bin/clang++"
export LIBCLANG_PATH="/opt/homebrew/opt/llvm@16/lib/libclang.dylib"

export LDFLAGS="-L/opt/homebrew/opt/llvm@16/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm@16/include"
```

### Windows
Expand Down
1 change: 1 addition & 0 deletions pallets/subtensor/src/coinbase/block_step.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ impl<T: Config + pallet_drand::Config> Pallet<T> {
Self::run_auto_claim_root_divs(last_block_hash);
// --- 9. Populate root coldkey maps.
Self::populate_root_coldkey_staking_maps();
Self::populate_root_coldkey_staking_maps_v2();

// Return ok.
Ok(())
Expand Down
6 changes: 3 additions & 3 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 Expand Up @@ -618,7 +618,7 @@ impl<T: Config> Pallet<T> {
root_alpha = root_alpha.saturating_sub(alpha_take);
// Give the validator their take.
log::debug!("hotkey: {hotkey:?} alpha_take: {alpha_take:?}");
let _validator_stake = Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
&hotkey,
&Owner::<T>::get(hotkey.clone()),
netuid,
Expand Down
44 changes: 40 additions & 4 deletions 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,11 +1438,41 @@ 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> =
StorageValue<_, Option<Vec<u8>>, ValueQuery, DefaultAlphaIterationLastKey<T>>;

/// Contains last AlphaV2 storage map key to iterate (check first)
#[pallet::storage]
pub type AlphaV2MapLastKey<T: Config> =
StorageValue<_, Option<Vec<u8>>, ValueQuery, DefaultAlphaIterationLastKey<T>>;

/// --- MAP ( netuid ) --> token_symbol | Returns the token symbol for a subnet.
#[pallet::storage]
pub type TokenSymbol<T: Config> =
Expand Down Expand Up @@ -2635,20 +2666,25 @@ 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);
});

Ok(Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(
hotkey, coldkey, netuid, alpha,
))
Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha);

Ok(())
}
}

Expand Down
2 changes: 0 additions & 2 deletions pallets/subtensor/src/macros/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,5 @@ mod errors {
InvalidRootClaimThreshold,
/// Exceeded subnet limit number or zero.
InvalidSubnetNumber,
/// Unintended precision loss when unstaking alpha
PrecisionLoss,
}
}
9 changes: 5 additions & 4 deletions pallets/subtensor/src/macros/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use frame_support::pallet_macros::pallet_section;
/// This can later be imported into the pallet using [`import_section`].
#[pallet_section]
mod genesis {
use share_pool::SafeFloat;
use sp_core::crypto::Pair;
use sp_core::sr25519::Pair as Sr25519Pair;

Expand Down Expand Up @@ -91,20 +92,20 @@ mod genesis {
SubnetOwner::<T>::insert(netuid, hotkey.clone());
SubnetLocked::<T>::insert(netuid, TaoCurrency::from(1));
LargestLocked::<T>::insert(netuid, 1);
Alpha::<T>::insert(
AlphaV2::<T>::insert(
// Lock the initial funds making this key the owner.
(hotkey.clone(), hotkey.clone(), netuid),
U64F64::saturating_from_num(1_000_000_000),
SafeFloatSerializable::from(&SafeFloat::from(1_000_000_000)),
);
TotalHotkeyAlpha::<T>::insert(
hotkey.clone(),
netuid,
AlphaCurrency::from(1_000_000_000),
);
TotalHotkeyShares::<T>::insert(
TotalHotkeySharesV2::<T>::insert(
hotkey.clone(),
netuid,
U64F64::saturating_from_num(1_000_000_000),
SafeFloatSerializable::from(&SafeFloat::from(1_000_000_000)),
);
// TotalColdkeyAlpha::<T>::insert(hotkey.clone(), netuid, 1_000_000_000);
SubnetAlphaOut::<T>::insert(netuid, AlphaCurrency::from(1_000_000_000));
Expand Down
6 changes: 3 additions & 3 deletions pallets/subtensor/src/rpc_info/delegate_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ 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) in Self::alpha_iter_single_prefix(&delegate) {
if alpha_stake.is_zero() {
continue;
}

Expand Down Expand Up @@ -166,7 +166,7 @@ impl<T: Config> Pallet<T> {
)> = Vec::new();
for delegate in <Delegates<T> as IterableStorageMap<T::AccountId, u16>>::iter_keys() {
// Staked to this delegate, so add to list
for (netuid, _) in Alpha::<T>::iter_prefix((delegate.clone(), delegatee.clone())) {
for (netuid, _) in Self::alpha_iter_prefix((&delegate, &delegatee)) {
let delegate_info = Self::get_delegate_by_existing_account(delegate.clone(), true);
delegates.push((
delegate_info,
Expand Down
117 changes: 114 additions & 3 deletions pallets/subtensor/src/staking/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use frame_support::traits::{
},
};
use safe_math::*;
use share_pool::SafeFloat;
use substrate_fixed::types::U96F32;
use subtensor_runtime_common::{NetUid, TaoCurrency};
use subtensor_swap_interface::{Order, SwapHandler};
Expand Down Expand Up @@ -68,7 +69,7 @@ impl<T: Config> Pallet<T> {
hotkeys
.iter()
.map(|hotkey| {
Alpha::<T>::iter_prefix((hotkey, coldkey))
Self::alpha_iter_prefix((hotkey, coldkey))
.map(|(netuid, _)| {
let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet(
hotkey, coldkey, netuid,
Expand Down Expand Up @@ -101,7 +102,7 @@ impl<T: Config> Pallet<T> {
hotkeys
.iter()
.map(|hotkey| {
Alpha::<T>::iter_prefix((hotkey, coldkey))
Self::alpha_iter_prefix((hotkey, coldkey))
.map(|(netuid_on_storage, _)| {
if netuid_on_storage == netuid {
let alpha_stake = Self::get_stake_for_hotkey_and_coldkey_on_subnet(
Expand Down Expand Up @@ -261,7 +262,7 @@ impl<T: Config> Pallet<T> {
/// used with caution.
pub fn clear_small_nominations() {
// Loop through all staking accounts to identify and clear nominations below the minimum stake.
for ((hotkey, coldkey, netuid), _) in Alpha::<T>::iter() {
for ((hotkey, coldkey, netuid), _) in Self::alpha_iter() {
Self::clear_small_nomination_if_required(&hotkey, &coldkey, netuid);
}
}
Expand Down Expand Up @@ -413,7 +414,117 @@ impl<T: Config> Pallet<T> {
}
}

// Same thing as populate_root_coldkey_staking_maps, but for AlphaV2
pub fn populate_root_coldkey_staking_maps_v2() {
// Get starting key for the batch. Get the first key if we restart the process.
let mut new_starting_raw_key = AlphaV2MapLastKey::<T>::get();
let mut starting_key = None;
if new_starting_raw_key.is_none() {
starting_key = AlphaV2::<T>::iter_keys().next();
new_starting_raw_key = starting_key.as_ref().map(AlphaV2::<T>::hashed_key_for);
}

if let Some(starting_raw_key) = new_starting_raw_key {
// Get the key batch
let mut keys = AlphaV2::<T>::iter_keys_from(starting_raw_key)
.take(ALPHA_MAP_BATCH_SIZE)
.collect::<Vec<_>>();

// 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(AlphaV2::<T>::hashed_key_for(key));
}

// Restart the process if it's the last batch
if new_iteration {
new_starting_key = None;
}

AlphaV2MapLastKey::<T>::put(new_starting_key);
}
}

pub fn burn_subnet_alpha(_netuid: NetUid, _amount: AlphaCurrency) {
// Do nothing; TODO: record burned alpha in a tracker
}

/// Several alpha iteration helpers that merge key space from Alpha and AlphaV2 maps
pub fn alpha_iter() -> impl Iterator<Item = ((T::AccountId, T::AccountId, NetUid), SafeFloat)> {
// Old Alpha shares format: U64F64 -> SafeFloat
let legacy = Alpha::<T>::iter().map(|(key, val_u64f64)| {
let sf: SafeFloat = val_u64f64.into();
(key, sf)
});

// New Alpha shares format: SafeFloatSerializable -> SafeFloat
let v2 = AlphaV2::<T>::iter().map(|(key, val_sf_ser)| {
let sf: SafeFloat = SafeFloat::from(&val_sf_ser);
(key, sf)
});

legacy.chain(v2)
}

pub fn alpha_iter_prefix(
prefix: (&T::AccountId, &T::AccountId),
) -> impl Iterator<Item = (NetUid, SafeFloat)>
where
T::AccountId: Clone,
{
// Old Alpha shares format: U64F64 -> SafeFloat
let legacy = Alpha::<T>::iter_prefix(prefix).map(|(netuid, val_u64f64)| {
let sf: SafeFloat = val_u64f64.into();
(netuid, sf)
});

// New Alpha shares format: SafeFloatSerializable -> SafeFloat
let v2 = AlphaV2::<T>::iter_prefix(prefix).map(|(netuid, val_sf_ser)| {
let sf: SafeFloat = SafeFloat::from(&val_sf_ser);
(netuid, sf)
});

legacy
.chain(v2)
.filter(|(_, alpha_share)| !alpha_share.is_zero())
}

pub fn alpha_iter_single_prefix(
prefix: &T::AccountId,
) -> impl Iterator<Item = (T::AccountId, NetUid, SafeFloat)>
where
T::AccountId: Clone,
{
// Old Alpha shares format: U64F64 -> SafeFloat
let legacy =
Alpha::<T>::iter_prefix((prefix.clone(),)).map(|((coldkey, netuid), val_u64f64)| {
let sf: SafeFloat = val_u64f64.into();
(coldkey, netuid, sf)
});

// New Alpha shares format: SafeFloatSerializable -> SafeFloat
let v2 = AlphaV2::<T>::iter_prefix((prefix,)).map(|((coldkey, netuid), val_sf_ser)| {
let sf: SafeFloat = SafeFloat::from(&val_sf_ser);
(coldkey, netuid, sf)
});

legacy.chain(v2)
}
}
Loading
Loading