Skip to content

Emission suppression via root validator voting#2420

Open
ppolewicz wants to merge 15 commits intodevnet-readyfrom
emission_suppression
Open

Emission suppression via root validator voting#2420
ppolewicz wants to merge 15 commits intodevnet-readyfrom
emission_suppression

Conversation

@ppolewicz
Copy link
Collaborator

Summary

Adds a mechanism for root validators to vote to suppress TAO emissions from subnets they believe are not operating in the network's interest. When more than half of the stake-weighted root validators vote to suppress a subnet, its emission share is zeroed and redistributed proportionally to the remaining subnets.

Key features

  • Stake-weighted voting: Each root validator's vote is weighted by their root stake. Suppression activates when >50% of total registered root stake votes to suppress.
  • Root override: A sudo extrinsic (sudo_set_emission_suppression_override) allows root to force-suppress or force-unsuppress any subnet, bypassing the vote outcome.
  • Root sell pressure control: A global flag (KeepRootSellPressureOnSuppressedSubnets, default true) controls whether root validators still receive alpha dividends from suppressed subnets. When disabled, all alpha goes to subnet validators instead — removing root sell pressure on the suppressed subnet's token.
  • Coldkey-keyed votes: Votes are keyed by coldkey (not hotkey) so they can be migrated during coldkey swaps. Swaps fail if the destination coldkey already has votes to prevent silent overwrites.
  • Per-epoch collection: Votes are collected and suppression ratios are recalculated once per epoch per subnet, keeping on-chain computation bounded.

New extrinsics

Call index Name Access
133 sudo_set_emission_suppression_override Root
134 vote_emission_suppression Signed (coldkey)
135 sudo_set_keep_root_sell_pressure_on_suppressed_subnets Root

New storage items

  • EmissionSuppression<NetUid → U64F64> — current suppression ratio per subnet
  • EmissionSuppressionOverride<NetUid → Option<bool>> — root override per subnet
  • EmissionSuppressionVote<(NetUid, AccountId) → Option<bool>> — per-coldkey vote per subnet
  • KeepRootSellPressureOnSuppressedSubnets<bool> — global flag (default true)

Tests

24 test scenarios covering:

  • Share zeroing and renormalization with majority/minority votes
  • Override force-suppress and force-unsuppress
  • Vote eligibility (root registration, minimum stake)
  • Vote clearing and epoch-only collection
  • Coldkey swap migration and conflict detection
  • Subnet dissolution cleanup
  • Root sell pressure flag behavior
  • Root subnet vote rejection
  • Multi-hotkey vote weight aggregation
  • Sudo event emission

Test plan

  • All 24 emission suppression tests pass (cargo test -p pallet-subtensor --lib)
  • Full pallet test suite passes
  • cargo clippy --workspace --all-features --all-targets clean
  • scripts/fix_rust.sh clean

🤖 Generated with Claude Code

ppolewicz and others added 7 commits February 11, 2026 23:14
- Add EmissionSuppression<T> storage map (NetUid -> U64F64) for
  stake-weighted suppression fraction per subnet
- Add normalize_shares() helper to re-normalize shares to sum to 1.0
- Add apply_emission_suppression() to zero shares of suppressed subnets
  (suppression > 0.5) and re-normalize remaining shares
- Call apply_emission_suppression in get_subnet_block_emissions after
  get_shares
- Clean up EmissionSuppression in remove_network

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add EmissionSuppressionOverride<T> storage map (NetUid -> Option<bool>)
  for root override of suppression per subnet
- Modify apply_emission_suppression to check override first:
  Some(true) forces suppression, Some(false) forces unsuppression,
  None falls back to vote-based EmissionSuppression value
- Add sudo_set_emission_suppression_override extrinsic (call_index 133)
- Clean up EmissionSuppressionOverride in remove_network

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add EmissionSuppressionVote<T> storage double map (NetUid, AccountId -> bool)
  for per-coldkey votes on subnet emission suppression
- Add vote_emission_suppression extrinsic (call_index 134):
  requires signed coldkey owning root-registered hotkey with stake >= threshold
- Add collect_emission_suppression_votes() called per-subnet on epoch:
  iterates root validators, accumulates stake-weighted votes, updates
  EmissionSuppression fraction
- Add transfer_emission_suppression_votes in do_swap_coldkey for
  coldkey swap migration
- Clean up EmissionSuppressionVote in remove_network
- New errors: CannotVoteOnRootSubnet, NotEnoughStakeToVote
- New event: EmissionSuppressionVoteCast

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 test scenarios covering:
- Share zeroing and renormalization with majority suppression
- No effect when suppression is below 50%
- Root override force suppress/unsuppress
- Override=None falls back to votes
- Vote requires root registration and minimum stake
- Vote clearing removes suppression
- Votes collected only on epoch
- Coldkey swap migrates votes
- Network dissolution clears all suppression state
- Share renormalization across 3 subnets
- Unstaked TAO not counted in suppression denominator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add KeepRootSellPressureOnSuppressedSubnets<T> storage (default true):
  controls whether root validators receive alpha dividends from suppressed
  subnets
- Add sudo_set_keep_root_sell_pressure_on_suppressed_subnets extrinsic
  (call_index 135)
- Modify emit_to_subnets: when flag is false and subnet is suppressed,
  root_alpha is zeroed and all validator alpha goes to subnet validators
- Add is_subnet_emission_suppressed() helper to deduplicate suppression
  check logic
- Refactor apply_emission_suppression to use the new helper
- Add 3 tests (14-16): root alpha on suppressed subnet with flag on/off,
  and unsuppressed subnet unaffected by flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add events for sudo_set_emission_suppression_override and
  sudo_set_keep_root_sell_pressure_on_suppressed_subnets
- Fix missing read in sudo_set_emission_suppression_override weight
- Add early return guard in collect_emission_suppression_votes for root
- Fail coldkey swap if destination already has emission suppression votes
  (new error: DestinationColdkeyHasExistingVotes)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- test_vote_on_root_subnet_rejected: CannotVoteOnRootSubnet error
- test_vote_explicit_false: Some(false) stored, produces 0 suppression
- test_all_subnets_suppressed: all shares zeroed, zero total emission
- test_coldkey_swap_blocked_by_existing_votes: swap fails with error
- test_multi_hotkey_coldkey_vote_weight: 3 hotkeys, weight = sum stakes
- test_sudo_override_emits_event: EmissionSuppressionOverrideSet event
- test_sudo_sell_pressure_emits_event: KeepRootSellPressureSet event
- test_collect_votes_skips_root: ROOT no-op guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ppolewicz ppolewicz added the skip-cargo-audit This PR fails cargo audit but needs to be merged anyway label Feb 12, 2026
@ppolewicz ppolewicz force-pushed the emission_suppression branch from 2dc0894 to 752f5c3 Compare February 12, 2026 14:48
@ppolewicz ppolewicz force-pushed the emission_suppression branch from c6158be to 11b0d5f Compare February 12, 2026 19:50
ppolewicz and others added 8 commits February 12, 2026 20:14
…hip workflow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nedHotkeys, R-9 root guard, R-11 cache netuids, R-12 call_index order, R-13 saturating fold, R-14 narrow lints, R-16 allow vote cleanup

- R-1: Move DestinationColdkeyHasExistingVotes check to top of do_swap_coldkey
  before any mutations; make transfer_emission_suppression_votes infallible
- R-2: Increase vote_emission_suppression weight from reads(5) to reads(131)
  to account for up to 64 hotkeys at 2 reads each plus 3 base reads
- R-5: Use OwnedHotkeys instead of StakingHotkeys for vote eligibility so
  only hotkeys owned by the coldkey qualify
- R-9: Add root subnet guard to sudo_set_emission_suppression_override
- R-11: Cache get_all_subnet_netuids in transfer_emission_suppression_votes
- R-12: Reorder extrinsics so call indices appear in ascending order (132-135)
- R-13: Replace .sum() with saturating_add fold in test
- R-14: Remove blanket #![allow(unused)] from test file
- R-16: Skip stake threshold check when clearing a vote (suppress=None)
  so deregistered coldkeys can clean up stale entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix compiler warnings for unused variables by prefixing them with
underscores. All tests pass successfully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bnetsMode enum

Introduce a 3-mode enum (Disable/Enable/Recycle) to control how root alpha
dividends are handled on emission-suppressed subnets. Recycle mode (new
default) swaps root alpha to TAO via AMM and burns it, creating sell
pressure without benefiting root validators.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…king

- Use add_dynamic_network to properly initialize AMM for swap tests
- Add record_tao_outflow call after swap_alpha_for_tao in recycle path
- Check SubnetTAO decrease instead of SubnetMovingPrice (needs epoch)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…dec indices, R-10 doc fix

- R-1: Add else fallback to recycle alpha when swap_alpha_for_tao fails
  in Recycle mode, preventing orphaned tokens
- R-3: Cache is_subnet_emission_suppressed result to avoid double
  storage read per subnet per block
- R-8: Add explicit #[codec(index)] annotations to enum variants for
  migration safety
- R-10: Fix misleading doc comment on Disable variant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sam0x17 sam0x17 requested a review from a team February 13, 2026 16:46
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.

1 participant