Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
76671f6
Add liquid alpha consensus modes
UnArbosSix Aug 11, 2026
777fe85
Align minimum liquid alpha with bond EMA
UnArbosSix Aug 11, 2026
53bace4
clippy
UnArbosSix Aug 12, 2026
f3165a6
retry e2e
UnArbosSix Aug 12, 2026
0bc75c8
Creating release 446
UnArbosFour Aug 12, 2026
ce2d6a6
Correct SubnetAlphaOut and refine conviction term
UnArbosFour Aug 12, 2026
1d31e2c
Fix generation-scoped alpha accounting and safely rebase recycled sub…
UnArbosFour Aug 11, 2026
1fbdd6c
Expose alpha counter cleanup phase in subnet precompile
UnArbosFour Aug 12, 2026
3cf8c4c
Add historical reconstruction of alpha burned
UnArbosFour Aug 12, 2026
8075e5f
Handle registration counters for subnets 116, 92, 40, 16, 58, 99, 90,…
UnArbosFour Aug 12, 2026
2dadde2
Temporarily allow alpha migrations on mainnet clones
UnArbosFour Aug 12, 2026
bd71460
Ignore local alpha accounting scanner data
UnArbosFour Aug 12, 2026
e6567cd
Revert "Temporarily allow alpha migrations on mainnet clones"
UnArbosFour Aug 13, 2026
9628b50
Guard alpha corrections by subnet generation
UnArbosFour Aug 13, 2026
b35a554
fix: keep timelock commitments when reveal fails
unarbos Aug 13, 2026
9bb5a76
fix: quarantine failed and expired timelock reveals
unarbos Aug 13, 2026
5717f90
Merge pull request #3078 from RaoFoundation/fix/alpha-accounting-446
UnArbosFour Aug 13, 2026
a2f6079
TimelockRevealFailedNotAllowed
UnArbosSix Aug 13, 2026
41fda81
add rust timelock commitments e2e
UnArbosSix Aug 13, 2026
959e703
Add consensus by mechanism storage
UnArbosFour Aug 13, 2026
35e8331
spec bump
UnArbosFour Aug 13, 2026
538e2a7
Merge branch 'release-v446' into agent/liquid-alpha-consensus-mode
UnArbosSix Aug 13, 2026
08ccfd9
fix e2e manifest
UnArbosSix Aug 13, 2026
479868e
address review
UnArbosFour Aug 13, 2026
8879c12
Merge branch 'agent/liquid-alpha-consensus-mode' of github.com:RaoFou…
UnArbosFour Aug 13, 2026
e6ed706
Update committed Python SDK metadata
UnArbosFour Aug 13, 2026
80c912c
RoundMismatch
UnArbosSix Aug 13, 2026
57cefe3
Fix tests
UnArbosFour Aug 13, 2026
462e29b
Merge pull request #3070 from RaoFoundation/agent/liquid-alpha-consen…
UnArbosFour Aug 13, 2026
9e58a64
add python e2e
UnArbosSix Aug 13, 2026
ed568d8
Fix GRANDPA set ID handling
UnArbosFive Jul 23, 2026
94dd956
Merge pull request #2971 from RaoFoundation/fix/grandpa-setid
UnArbosFive Aug 13, 2026
9eb28d9
Merge pull request #3080 from RaoFoundation/fix/timelock-reveal-keep-…
UnArbosSix Aug 13, 2026
55cc8cd
docs: complete v446 release documentation
UnArbosFive Aug 13, 2026
64285cc
docs: fix v446 release text spacing
UnArbosFive Aug 13, 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
18 changes: 13 additions & 5 deletions .github/workflows/check-bittensor-e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -448,11 +448,19 @@ jobs:
path: rust-e2e

- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
env:
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
for i in 1 2 3; do
if printf '%s' "$GHCR_TOKEN" \
| docker login ghcr.io --username "$GITHUB_ACTOR" --password-stdin; then
exit 0
fi
echo "GHCR login failed (attempt $i), retrying in $((i * 15))s..."
sleep $((i * 15))
done
printf '%s' "$GHCR_TOKEN" \
| docker login ghcr.io --username "$GITHUB_ACTOR" --password-stdin

- name: Pull Docker Image
# GHCR intermittently returns permission_denied/timeouts under the
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ sdk/python/dist/

# Local dev logs
*.log
.alpha-accounting-scanner/

# Claude Code configuration (skills are checked in; everything else is ignored)
# .claude/skills is a symlink to .agents/skills (single source of truth);
Expand Down
14 changes: 14 additions & 0 deletions pallets/admin-utils/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,20 @@ mod benchmarks {
_(RawOrigin::Root, netuid, 2000u16, 3000u16);
}

#[benchmark]
fn sudo_set_liquid_alpha_consensus_mode() {
let netuid = NetUid::from(1);
pallet_subtensor::Pallet::<T>::set_admin_freeze_window(0);
pallet_subtensor::Pallet::<T>::init_new_network(netuid, 1u16);

#[extrinsic_call]
_(
RawOrigin::Root,
netuid,
pallet_subtensor::ConsensusMode::Previous,
);
}

#[benchmark]
fn sudo_set_coldkey_swap_announcement_delay() {
#[extrinsic_call]
Expand Down
27 changes: 25 additions & 2 deletions pallets/admin-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub mod pallet {
use frame_system::pallet_prelude::*;
use pallet_evm_chain_id::{self, ChainId};
use pallet_subtensor::{
DefaultMaxAllowedUids,
ConsensusMode, DefaultMaxAllowedUids, MAX_BONDS_MOVING_AVERAGE,
utils::rate_limiting::{Hyperparameter, TransactionType},
};
use sp_runtime::{BoundedVec, PerU16};
Expand Down Expand Up @@ -921,7 +921,7 @@ pub mod pallet {
pallet_subtensor::Pallet::<T>::ensure_admin_window_open(netuid)?;
if maybe_owner.is_some() {
ensure!(
bonds_moving_average <= 975000,
bonds_moving_average <= MAX_BONDS_MOVING_AVERAGE,
Error::<T>::BondsMovingAverageMaxReached
)
}
Expand Down Expand Up @@ -1340,6 +1340,29 @@ pub mod pallet {
res
}

/// Sets which consensus values liquid alpha uses for a subnet.
#[pallet::call_index(104)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::sudo_set_alpha_values())]
pub fn sudo_set_liquid_alpha_consensus_mode(
origin: OriginFor<T>,
netuid: NetUid,
mode: ConsensusMode,
) -> DispatchResult {
let maybe_owner = pallet_subtensor::Pallet::<T>::ensure_sn_owner_or_root_with_limits(
origin,
netuid,
&[Hyperparameter::LiquidAlphaConsensusMode.into()],
)?;
pallet_subtensor::Pallet::<T>::ensure_admin_window_open(netuid)?;
pallet_subtensor::Pallet::<T>::set_liquid_alpha_consensus_mode(netuid, mode);
pallet_subtensor::Pallet::<T>::record_owner_rl(
maybe_owner,
netuid,
&[Hyperparameter::LiquidAlphaConsensusMode.into()],
);
Ok(())
}

/// Sets the duration of the dissolve network schedule.
///
/// This extrinsic allows the root account to set the duration for the dissolve network schedule.
Expand Down
80 changes: 76 additions & 4 deletions pallets/admin-utils/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{Error, pallet::PrecompileEnable};
use codec::Encode;
use frame_support::{
assert_err, assert_noop, assert_ok,
dispatch::{DispatchClass, GetDispatchInfo, Pays},
Expand Down Expand Up @@ -1348,6 +1349,77 @@ fn test_sudo_set_liquid_alpha_enabled() {
});
}

#[test]
fn test_sudo_set_liquid_alpha_consensus_mode() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(1);
NetworksAdded::<Test>::insert(netuid, true);
assert_eq!(
SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
ConsensusMode::Auto
);

assert_err!(
AdminUtils::sudo_set_liquid_alpha_consensus_mode(
<<Test as Config>::RuntimeOrigin>::signed(U256::from(1)),
netuid,
ConsensusMode::Previous,
),
DispatchError::BadOrigin
);

for mode in [
ConsensusMode::Current,
ConsensusMode::Previous,
ConsensusMode::Auto,
] {
assert_ok!(AdminUtils::sudo_set_liquid_alpha_consensus_mode(
<<Test as Config>::RuntimeOrigin>::root(),
netuid,
mode,
));
assert_eq!(
SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
mode
);
frame_system::Pallet::<Test>::assert_last_event(RuntimeEvent::SubtensorModule(
Event::LiquidAlphaConsensusModeSet(netuid, mode),
));
}
});
}

#[test]
fn test_subnet_owner_can_set_liquid_alpha_consensus_mode() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(1);
let owner = U256::from(10);
add_network(netuid, 10);
SubnetOwner::<Test>::insert(netuid, owner);
SubtensorModule::set_admin_freeze_window(0);

assert_ok!(AdminUtils::sudo_set_liquid_alpha_consensus_mode(
<<Test as Config>::RuntimeOrigin>::signed(owner),
netuid,
ConsensusMode::Previous,
));
assert_eq!(
SubtensorModule::get_liquid_alpha_consensus_mode(netuid),
ConsensusMode::Previous
);
});
}

#[test]
fn regression_liquid_alpha_consensus_mode_call_index() {
let call = crate::Call::<Test>::sudo_set_liquid_alpha_consensus_mode {
netuid: NetUid::from(1),
mode: ConsensusMode::Auto,
};

assert_eq!(call.encode().first(), Some(&104));
}

#[test]
fn test_sudo_set_alpha_sigmoid_steepness() {
new_test_ext().execute_with(|| {
Expand Down Expand Up @@ -1411,7 +1483,7 @@ fn test_sudo_set_alpha_sigmoid_steepness() {
fn test_set_alpha_values_dispatch_info_ok() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(1);
let alpha_low: u16 = 1638_u16;
let alpha_low = MIN_ALPHA_LOW;
let alpha_high: u16 = u16::MAX - 10;
let call = RuntimeCall::AdminUtils(crate::Call::sudo_set_alpha_values {
netuid,
Expand All @@ -1430,7 +1502,7 @@ fn test_set_alpha_values_dispatch_info_ok() {
fn test_sudo_get_set_alpha() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(1);
let alpha_low: u16 = 1638_u16;
let alpha_low = MIN_ALPHA_LOW;
let alpha_high: u16 = u16::MAX - 10;

let hotkey: U256 = U256::from(1);
Expand Down Expand Up @@ -1509,7 +1581,7 @@ fn test_sudo_get_set_alpha() {
));

// 2. Alpha high too low
let alpha_high_too_low = (u16::MAX as u32 / 40) as u16 - 1; // One less than the minimum acceptable value
let alpha_high_too_low = MIN_ALPHA_LOW - 1;
assert_err!(
AdminUtils::sudo_set_alpha_values(
signer.clone(),
Expand All @@ -1528,7 +1600,7 @@ fn test_sudo_get_set_alpha() {
));

// 3. Alpha low too low or too high
let alpha_low_too_low = 0_u16;
let alpha_low_too_low = MIN_ALPHA_LOW - 1;
assert_err!(
AdminUtils::sudo_set_alpha_values(
signer.clone(),
Expand Down
71 changes: 71 additions & 0 deletions pallets/alpha-assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,17 @@ impl Imbalance<AlphaBalance> for NegativeAlphaImbalance {
pub trait AlphaAssetsInterface {
fn total_alpha_issuance(netuid: NetUid) -> AlphaBalance;

fn alpha_burned(netuid: NetUid) -> AlphaBalance;

fn clear_alpha_counters(netuid: NetUid);

fn rebase_alpha_counters(
netuid: NetUid,
issuance_offset: AlphaBalance,
burned_offset: AlphaBalance,
recycled_offset: AlphaBalance,
);

fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance;

fn burn_alpha(netuid: NetUid, amount: AlphaBalance) -> AlphaBalance;
Expand All @@ -257,6 +268,20 @@ impl AlphaAssetsInterface for () {
AlphaBalance::ZERO
}

fn alpha_burned(_netuid: NetUid) -> AlphaBalance {
AlphaBalance::ZERO
}

fn clear_alpha_counters(_netuid: NetUid) {}

fn rebase_alpha_counters(
_netuid: NetUid,
_issuance_offset: AlphaBalance,
_burned_offset: AlphaBalance,
_recycled_offset: AlphaBalance,
) {
}

fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance {
PositiveAlphaImbalance::new(netuid, amount)
}
Expand Down Expand Up @@ -300,6 +325,35 @@ pub mod pallet {
}

impl<T: pallet::Config> Pallet<T> {
pub fn clear_alpha_counters(netuid: NetUid) {
TotalAlphaIssuance::<T>::remove(netuid);
AlphaBurned::<T>::remove(netuid);
AlphaRecycled::<T>::remove(netuid);
}

pub fn rebase_alpha_counters(
netuid: NetUid,
issuance_offset: AlphaBalance,
burned_offset: AlphaBalance,
recycled_offset: AlphaBalance,
) {
if !issuance_offset.is_zero() {
TotalAlphaIssuance::<T>::mutate(netuid, |issuance| {
*issuance = issuance.saturating_sub(issuance_offset);
});
}
if !burned_offset.is_zero() {
AlphaBurned::<T>::mutate(netuid, |burned| {
*burned = burned.saturating_sub(burned_offset);
});
}
if !recycled_offset.is_zero() {
AlphaRecycled::<T>::mutate(netuid, |recycled| {
*recycled = recycled.saturating_sub(recycled_offset);
});
}
}

pub fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance {
if !amount.is_zero() {
TotalAlphaIssuance::<T>::mutate(netuid, |issuance| {
Expand Down Expand Up @@ -339,6 +393,23 @@ impl<T: pallet::Config> AlphaAssetsInterface for Pallet<T> {
TotalAlphaIssuance::<T>::get(netuid)
}

fn alpha_burned(netuid: NetUid) -> AlphaBalance {
AlphaBurned::<T>::get(netuid)
}

fn clear_alpha_counters(netuid: NetUid) {
Self::clear_alpha_counters(netuid)
}

fn rebase_alpha_counters(
netuid: NetUid,
issuance_offset: AlphaBalance,
burned_offset: AlphaBalance,
recycled_offset: AlphaBalance,
) {
Self::rebase_alpha_counters(netuid, issuance_offset, burned_offset, recycled_offset)
}

fn mint_alpha(netuid: NetUid, amount: AlphaBalance) -> PositiveAlphaImbalance {
Self::mint_alpha(netuid, amount)
}
Expand Down
33 changes: 33 additions & 0 deletions pallets/alpha-assets/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,39 @@ fn recycle_alpha_reduces_total_issuance_saturating_at_zero() {
});
}

#[test]
fn clear_alpha_counters_removes_previous_generation_state() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(6u16);

AlphaAssets::mint_alpha(netuid, 100u64.into());
AlphaAssets::burn_alpha(netuid, 30u64.into());
AlphaAssets::recycle_alpha(netuid, 20u64.into());
AlphaAssets::clear_alpha_counters(netuid);

assert!(!TotalAlphaIssuance::<Test>::contains_key(netuid));
assert!(!AlphaBurned::<Test>::contains_key(netuid));
assert!(!AlphaRecycled::<Test>::contains_key(netuid));
});
}

#[test]
fn rebase_alpha_counters_preserves_current_generation_deltas() {
new_test_ext().execute_with(|| {
let netuid = NetUid::from(7u16);

TotalAlphaIssuance::<Test>::insert(netuid, AlphaBalance::from(125u64));
AlphaBurned::<Test>::insert(netuid, AlphaBalance::from(47u64));
AlphaRecycled::<Test>::insert(netuid, AlphaBalance::from(31u64));

AlphaAssets::rebase_alpha_counters(netuid, 100u64.into(), 40u64.into(), 30u64.into());

assert_eq!(TotalAlphaIssuance::<Test>::get(netuid), 25u64.into());
assert_eq!(AlphaBurned::<Test>::get(netuid), 7u64.into());
assert_eq!(AlphaRecycled::<Test>::get(netuid), 1u64.into());
});
}

#[test]
fn positive_imbalance_only_merges_with_same_netuid() {
new_test_ext().execute_with(|| {
Expand Down
Loading
Loading