Skip to content

Conversation

@ppolewicz
Copy link
Collaborator

@ppolewicz ppolewicz commented Jan 31, 2026

Description

This PR introduces mechanisms to control which subnets receive TAO emission and how root dividends are distributed based on validator behavior.

New metric: EffectiveRootProp

Each subnet distributes dividends to two groups: alpha stakers (staked directly on subnet) and root stakers (staked on root network). EffectiveRootProp measures what fraction flows to root stakers, scaled by how efficiently root validators are actually earning dividends:

raw_root_prop = root_dividends / (alpha_dividends + root_dividends)
EffectiveRootProp[netuid] = raw_root_prop * utilization

Root stake utilization (dividend efficiency)

Utilization measures how well root validators are earning their expected share of dividends on each subnet. For each root-staked validator registered on the subnet:

expected_share = root_stake_i / total_root_stake_on_subnet
actual_share   = root_dividends_i / total_root_dividends
efficiency_i   = min(actual_share / expected_share, 1.0)
utilization    = sum(root_stake_i * efficiency_i) / total_root_stake_on_subnet

Only root stake of validators with UIDs registered on the subnet counts — TAO which is staked to a validator which is not registered on the subnet as well as unstaked TAO are irrelevant.

Hard cap at 50% utilization

If a subnet's utilization drops below 50%, all root dividends for that subnet are recycled (returned to the subnet's alpha pool) and EffectiveRootProp is set to 0. This is a binary gate: subnets at or above 50% utilization receive full root dividends, subnets below 50% receive none.

Emission filters

Three emission filters are applied sequentially in get_subnet_block_emissions():

  1. EffectiveRootProp scaling (disabled by default): Multiplies each subnet's emission share by min(EffectiveRootProp, RootProp) and re-normalizes. Using the minimum prevents exploitation by disabling alpha validators to artificially inflate EffectiveRootProp above RootProp.
  2. Top subnet proportion: Keeps only the top X% of subnets by share (default 100% = all subnets). Uses ceil() rounding so a single subnet always counts as in top 50%.
  3. Absolute subnet limit: Hard cap on number of subnets receiving emission (default: disabled).

After each filter, shares are re-normalized to sum to 1.0. Subnets that get filtered out receive zero emission.

But why?

We had a couple of problems since Taoflow emission schedule was deployed:

  • New subnets get very little injection of Tao when they desperately need it
  • Old subnets which have a lot of liquidity get even more
  • Since dtao subnets are generally incentivized to fight root prop (see sn89 using tricks to reduce root prop artificially) to reduce sell pressure
  • sn104 managed to cut off root validators from receiving any dividends at all by using the time gap between CHK application and immediate effectiveness of btcli stake move coupled with randomly chosen "miner" (they are self-mining, the subnet doesn't really do anything). We think they can safely inflow to increase emissions and we want to mitigate this risk.
  • sometimes subnet owners say "oh, the validator code doesn't work for you? Then CHK stake to me, I don't want to debug your environment problems" and it is unacceptable, so it'd be good to incentivize them to care that the validator code actually works

The dividend-efficiency utilization and hard cap directly address these: subnets where root validators can't earn dividends (like sn104) get their root emission recycled, while subnets with healthy validator participation receive full emission.

Type of Change

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have run ./scripts/fix_rust.sh to ensure my code is formatted and linted correctly
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

AI reviewers

  • Claude 4.6 Opus
  • Codex 5.3-high
  • Devin

ppolewicz and others added 9 commits January 31, 2026 08:54
Add EffectiveRootProp StorageMap (NetUid -> U64F64) computed during
distribute_dividends_and_incentives() as:
  sum(RootAlphaDividendsPerSubnet[netuid]) /
  (sum(AlphaDividendsPerSubnet[netuid]) + sum(RootAlphaDividendsPerSubnet[netuid]))

This measures the proportion of dividends on a subnet flowing to root
stakers, stored per-subnet for efficient single-read access during
emission preparation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When enabled, multiplies each subnet's emission share by its stored
EffectiveRootProp value and re-normalizes. This allows root governance
to weight emission toward subnets with higher root staker participation.

- Storage: EffectiveRootPropEmissionScaling (bool, default false)
- Admin extrinsic: sudo_set_effective_root_prop_emission_scaling (call_index 88)
- Helper: normalize_shares(), apply_effective_root_prop_scaling()
- Event: EffectiveRootPropEmissionScalingApplied

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Only the top proportion of subnets (ranked by emission share) receive
emission. Default is 50% (5000 basis points). Remaining subnets have
shares zeroed and redistributed to top subnets.

Uses ceil(count * proportion / 10000) so a single subnet always
qualifies (ceil(1 * 0.5) = 1).

- Storage: EmissionTopSubnetProportion (u16, default 5000)
- Admin extrinsic: sudo_set_emission_top_subnet_proportion (call_index 89)
- Helpers: zero_and_redistribute_bottom_shares(), apply_top_subnet_proportion_filter()
- Event: EmissionTopSubnetFilterApplied

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds absolute cap on the number of subnets that can receive emission.
Default is 0 (disabled). When set to N > 0, only the top N subnets by
share receive emission; the rest are zeroed and redistributed.

Applied after proportion filter so the stricter constraint wins.

- Storage: EmissionTopSubnetAbsoluteLimit (u16, default 0)
- Admin extrinsic: sudo_set_emission_top_subnet_absolute_limit (call_index 90)
- Helper: apply_top_subnet_absolute_limit()
- Event: EmissionAbsoluteLimitApplied
- Fixed 5 existing coinbase tests to disable proportion filter

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace slice indexing `sorted[top_k..]` with `.get(top_k..)`
  to avoid potential panic (clippy::indexing_slicing)
- Replace `(total as u64) * (proportion as u64)` with
  `.saturating_mul()` to avoid arithmetic side effects
  (clippy::arithmetic_side_effects)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tests

Remove workaround EmissionTopSubnetProportion::<Test>::set(10000) from
existing coinbase tests that was needed when default was 50%.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…efault

Remove EffectiveRootPropEmissionScalingApplied, EmissionTopSubnetFilterApplied,
and EmissionAbsoluteLimitApplied events and their test assertions. Update
proportion filter tests to explicitly set 5000 (50%) since default is now 100%.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When subnets tie at the k-th position, all tied subnets are now kept
rather than arbitrarily zeroing some. Uses a threshold-based approach:
find the share value at position top_k-1 and keep all entries >= that
value, avoiding deterministic bias toward lower NetUIDs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@ppolewicz ppolewicz marked this pull request as ready for review February 2, 2026 23:03
@ppolewicz ppolewicz added the skip-cargo-audit This PR fails cargo audit but needs to be merged anyway label Feb 2, 2026
Use min(EffectiveRootProp, RootProp) when scaling emission shares.
This prevents a subnet from inflating its EffectiveRootProp above the
configured RootProp by disabling alpha validators, which would cause
all dividends to flow to root and artificially boost the scaling factor.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Collaborator

@shamil-gadelshin shamil-gadelshin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests don't cover the whole run_coinbase. There are some concerns about the calculation order and other minor issues.

- Remove log::debug! statements from admin extrinsics
- Change EffectiveRootProp storage type from U64F64 to U96F32 to match
  context types and avoid unnecessary conversions in run_coinbase
- Change EmissionTopSubnetProportion from u16 basis points (0-10000)
  to U64F64 fractional range (0.0-1.0)
- Change EmissionTopSubnetAbsoluteLimit from u16 ValueQuery (0=disabled)
  to u16 OptionQuery (None=disabled) for idiomatic representation
- Fix empty array iteration guard in zero_and_redistribute_bottom_shares
- Add detailed comment explaining ERP timing (computed for next round)
  and exploitation mitigation via min(EffectiveRootProp, RootProp)
- Update all tests for new storage types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
ppolewicz and others added 15 commits February 6, 2026 21:39
Three tests covering 2 subnets with 6 neurons each (owner, major/minor
root validators, major/minor subnet validators, miner):

1. test_basic_all_validators_set_weights_to_miners (price=0.6)
   - root_sell_flag=true, root validators earn dividends
   - Verifies miner incentive, validator dividend proportionality,
     root validator earnings, owner cut (18%), incentive vector

2. test_no_root_sell_all_validators_set_weights_to_miners (price=0.5)
   - root_sell_flag=false, root validators earn 0
   - Same structure but verifies root channel is unfunded

3. test_basic_major_root_no_weights (price=0.6)
   - Major root validator (5.55M TAO) does NOT set weights
   - Verifies major root earns 0 while minor root still earns

Test structure: 5 blocks total (setup at block 1, epochs at blocks 3+5),
SN1 only with tempo=1, TaoWeight=0.18, SubnetOwnerCut=18%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* wide-scope-dividend-test: (44 commits)
  Add wide_scope_dividend test suite for emission distribution
  Fix sudo_set_max_allowed_uids benchmark
  raise fee on `set_pending_childkey_cooldown`
  less sleep
  catch error with wrong nonce
  bump version
  add transaction replacement test case
  Fix try-runtime build
  bump version
  bump psdk/frontier with patch to prevent node panic
  Spec bump
  Spec bump
  auto-update benchmark weights
  bump frontier hash 2 by removing comment
  bump spec version
  bump Cargo toml frontier deps
  Bump spec version
  Add SubnetBuyback event
  format code
  test is ok
  ...
EffectiveRootProp now multiplies the raw dividend ratio by a utilization
factor: active_root_stake / total_root_stake. This ensures subnets where
most root stake is idle (validators not setting weights) get a much lower
EffectiveRootProp than subnets where all root stake is active.

Also enables EffectiveRootPropEmissionScaling in wide_scope_dividend tests
and updates all assertions accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change compute_and_store_effective_root_prop to use dividend-efficiency
  metric instead of binary active/inactive. Returns U96F32 utilization.
- Move ERP computation from distribute_dividends_and_incentives to
  distribute_emission; add utilization scaling (< 1.0) and hard cap
  (< 0.5 recycles all root dividends, sets ERP to 0).
- Add 555k unstaked TAO to test setup_test().
- Add 3 new tests: unstaked_tao_does_not_affect_utilization,
  half_weights_to_validator, half_weights_no_minor_root.
- Update existing test assertions for hard cap behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Skip hard cap and scaling when root_alpha_dividends is empty (e.g.
root_sell_flag=false). Fixes tests where validators with root stake
but no root dividend emission were incorrectly having their alpha
dividends recycled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sudo_set_effective_root_prop_emission_scaling was assigned the same
call_index(88) as sudo_set_max_mechanism_count. Reassign to 91.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verifies that when root validators stop setting weights on a subnet,
the hard cap triggers (ERP=0, root dividends recycled), but the subnet
recovers once root validators resume validating (ERP>0, dividends flow).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refactors the inline utilization scaling logic from distribute_emission
into two testable functions:
- get_root_dividend_fraction: computes root stake fraction of dividends
- apply_utilization_scaling: applies hard cap / scaling to dividend maps

Adds 10 unit tests covering: no root stake, no alpha stake, mixed stake,
high tao_weight, full utilization, no root dividends, partial scaling,
hard cap, boundary (0.5), and just-below-boundary (0.4999).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reads on-chain state to compute dividend-efficiency utilization per
subnet and simulate hard cap / scaling. Supports --debug mode for
per-hotkey breakdown of a single subnet.

Filters root dividends to registered-only hotkeys (pre-delegation)
to match the Rust utilization computation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Subnets with utilization >= 50% now receive full root dividends
instead of being scaled proportionally. Only subnets below 50%
have their root dividends withheld (hard cap).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix u64 as i64 wrapping cast in record_tao_inflow/outflow (use try_from)
- Update stale doc comment on apply_utilization_scaling (binary, not 3-tier)
- Add docstrings distinguishing RootProp from EffectiveRootProp
- Document tie-inclusion behavior in filter functions and storage items
- Fix eps() helper to guarantee minimum tolerance of 1
- Uncomment 8 get_shares flow tests disabled during refactor
- Add 3 full filter chain composition tests
- Add 11 tie-inclusion tests for all filter functions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sudo_set_emission_top_subnet_proportion now accepts u64 ppm
(1_000_000 = 100%) instead of u16 percentage (100 = 100%),
matching the U64F64 storage precision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- M1: Clean up EffectiveRootProp, RootProp, RootClaimableThreshold in
  remove_network to prevent stale data on subnet reuse
- M2: Bound BTreeSet in set_root_claim_type to MAX_SUBNET_CLAIMS
- L1: Remove unnecessary I96F32 conversion in threshold validation
- L2: Replace `as usize` with try_from for u64->usize conversion
- L3: Remove redundant .into() on NetUid comparison
- N1: Remove dead `continue` at end of for loop
- N2: Replace raw `%` with checked_rem for clippy compliance

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ppolewicz and others added 4 commits February 10, 2026 03:07
Tests cover: single validator utilization, root-only/alpha-only stakers,
boundary conditions (utilization at exactly 0.5), zero incentive handling,
filter chain composition, EMA flow idempotency, storage cleanup on network
removal, finalize_all_subnet_root_dividends cleanup, and root dividend
fraction edge cases (no stake, tao_weight=0).

Fixes MechId import, NetUid type mismatches, fixed-point precision in
assertions, and RootClaimableThreshold default value handling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-cargo-audit This PR fails cargo audit but needs to be merged anyway

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants