Skip to content

Commit 819e900

Browse files
committed
fix: alpha accounting precision and add repair mechanism
1 parent de708ba commit 819e900

File tree

5 files changed

+59
-15
lines changed

5 files changed

+59
-15
lines changed

pallets/subtensor/src/lib.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2612,13 +2612,16 @@ impl<T: Config + pallet_balances::Config<Balance = u64>>
26122612
Error::<T>::HotKeyAccountNotExists
26132613
);
26142614

2615+
// Apply share-pool math first because the actual credited amount can differ due to
2616+
// rounding/precision.
2617+
let actual_alpha =
2618+
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha);
2619+
26152620
// Increse alpha out counter
26162621
SubnetAlphaOut::<T>::mutate(netuid, |total| {
2617-
*total = total.saturating_add(alpha);
2622+
*total = total.saturating_add(actual_alpha);
26182623
});
26192624

2620-
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha);
2621-
26222625
Ok(())
26232626
}
26242627

@@ -2633,14 +2636,17 @@ impl<T: Config + pallet_balances::Config<Balance = u64>>
26332636
Error::<T>::HotKeyAccountNotExists
26342637
);
26352638

2639+
// Apply share-pool math first because the actual debited amount can differ due to
2640+
// rounding/precision.
2641+
let actual_alpha =
2642+
Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(hotkey, coldkey, netuid, alpha);
2643+
26362644
// Decrese alpha out counter
26372645
SubnetAlphaOut::<T>::mutate(netuid, |total| {
2638-
*total = total.saturating_sub(alpha);
2646+
*total = total.saturating_sub(actual_alpha);
26392647
});
26402648

2641-
Ok(Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(
2642-
hotkey, coldkey, netuid, alpha,
2643-
))
2649+
Ok(actual_alpha)
26442650
}
26452651
}
26462652

pallets/subtensor/src/macros/dispatches.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,5 +2431,26 @@ mod dispatches {
24312431

24322432
Ok(())
24332433
}
2434+
2435+
/// Set SubnetAlphaOut for a subnet (root only).
2436+
///
2437+
/// This is an administrative escape hatch to repair alpha issuance / burn accounting
2438+
/// when historical drift is detected.
2439+
#[pallet::call_index(125)]
2440+
#[pallet::weight((\n Weight::from_parts(6_000, 0)\n .saturating_add(T::DbWeight::get().reads(1_u64))\n .saturating_add(T::DbWeight::get().writes(1_u64)),\n DispatchClass::Operational,\n Pays::Yes\n ))]
2441+
pub fn sudo_set_subnet_alpha_out(
2442+
origin: OriginFor<T>,
2443+
netuid: NetUid,
2444+
alpha_out: AlphaCurrency,
2445+
) -> DispatchResult {
2446+
ensure_root(origin)?;
2447+
2448+
ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);
2449+
2450+
SubnetAlphaOut::<T>::insert(netuid, alpha_out);
2451+
Self::deposit_event(Event::SubnetAlphaOutSet(netuid, alpha_out));
2452+
2453+
Ok(())
2454+
}
24342455
}
24352456
}

pallets/subtensor/src/macros/events.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,12 @@ mod events {
322322
/// (coldkey, hotkey, amount, subnet_id)
323323
AlphaBurned(T::AccountId, T::AccountId, AlphaCurrency, NetUid),
324324

325+
/// Subnet alpha-out tracker has been updated via sudo.
326+
///
327+
/// Parameters:
328+
/// (netuid, alpha_out)
329+
SubnetAlphaOutSet(NetUid, AlphaCurrency),
330+
325331
/// An EVM key has been associated with a hotkey.
326332
EvmKeyAssociated {
327333
/// The subnet that the hotkey belongs to.

pallets/subtensor/src/staking/recycle_alpha.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,18 @@ impl<T: Config> Pallet<T> {
4545
Self::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid);
4646
let amount = amount.min(alpha_available);
4747

48-
ensure!(
49-
SubnetAlphaOut::<T>::get(netuid) >= amount,
50-
Error::<T>::InsufficientLiquidity
51-
);
52-
5348
// Deduct from the coldkey's stake.
5449
let actual_alpha_decrease = Self::decrease_stake_for_hotkey_and_coldkey_on_subnet(
5550
&hotkey, &coldkey, netuid, amount,
5651
);
5752

53+
ensure!(
54+
SubnetAlphaOut::<T>::get(netuid) >= actual_alpha_decrease,
55+
Error::<T>::NotEnoughAlphaOutToRecycle
56+
);
57+
5858
// Recycle means we should decrease the alpha issuance tracker.
59-
Self::recycle_subnet_alpha(netuid, amount);
59+
Self::recycle_subnet_alpha(netuid, actual_alpha_decrease);
6060

6161
Self::deposit_event(Event::AlphaRecycled(
6262
coldkey,

pallets/subtensor/src/tests/recycle_alpha.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,19 @@ fn test_recycle_success() {
4545
netuid
4646
));
4747

48-
assert!(TotalHotkeyAlpha::<Test>::get(hotkey, netuid) < initial_alpha);
49-
assert!(SubnetAlphaOut::<Test>::get(netuid) < initial_net_alpha);
48+
let new_alpha = TotalHotkeyAlpha::<Test>::get(hotkey, netuid);
49+
let new_net_alpha = SubnetAlphaOut::<Test>::get(netuid);
50+
51+
assert!(new_alpha < initial_alpha);
52+
assert!(new_net_alpha < initial_net_alpha);
53+
54+
// Accounting invariant: recycle should reduce SubnetAlphaOut by the *same* amount of alpha
55+
// that was actually removed from the hotkey's total.
56+
assert_eq!(
57+
initial_net_alpha.saturating_sub(new_net_alpha),
58+
initial_alpha.saturating_sub(new_alpha)
59+
);
60+
5061
assert!(
5162
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid)
5263
< initial_alpha

0 commit comments

Comments
 (0)