feat: implement on-chain invariant enforcement with recovery mechanism #2313
+401
−4
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Subtensor Invariant Enforcement: Problem, Analysis & Fix
1. The Problem: Silent Economic Corruption
In a complex Delegated Proof of Stake (DPoS) system like Subtensor, accounting bugs can be catastrophic. If the total stake stored in
SubnetAlphaOutdrifts from the actual sum of individual neuron stakes (TotalHotkeyAlpha), or if emission injection exceeds the protocol's defined schedule (BlockEmission), the network suffers from silent economic corruption.Prior to this fix, such inconsistencies could persist undetected, potentially allowing malicious actors to exploit infinite money glitches or causing catastrophic inflation.
2. Thought Process & Design Decisions
Q: Where should we check for consistency?
Decision: on_finalize hook.
Rationale: Invariants must hold true at the end of every block after all extrinsics and internal logic (like run_coinbase) have executed. Checking at on_initialize is too late (corruption happened in previous block), and checking inside every extrinsic is too heavyweight.
Q: How do we balance safety vs. liveness?
Decision: Pause, Don't Panic (in Production).
Rationale:
cfg(test), we want immediate failure to catch bugs during development.Q: How do we handle "dust" and non-injected sinks?
Decision: Inequality Check (
<=).Rationale: Strict equality (
sum == total) is dangerous for emissions because some TAO might be burned or added to total issuance directly (excess) without being injected into a subnet pool. The invarianttotal_injected <= block_emissionensures we never create more money than allowed, which is the critical safety property.3. The Solution Implementation
We implemented a robust invariant enforcement system with the following components:
A. Dedicated Invariant Module (src/invariants.rs)
A new module specifically for consistency checks, separating audit logic from business logic.
check_stake_invariant():
TotalHotkeyAlpha.SubnetAlphaOut.BlocksSinceLastStep == 0(epoch boundary) to minimize computational weight.check_emission_invariant():
SubnetTaoInEmissionacross all active subnets.sum(injected) <= BlockEmission.B. Safety Mechanisms (src/macros/hooks.rs & events.rs)
CriticalInvariantViolation(NetUid, Bytes)alerts the world.SubnetEmissionPausedmap prevents broken subnets from receiving further funds.C. Recovery Path (src/macros/dispatches.rs)
D. Comprehensive Testing (src/tests/invariants.rs)
4. Verification
The changes have been pushed to the
fix/invariant-enforcementbranch.https://github.com/Dairus01/subtensorfix/invariant-enforcementYou can verify the fix by running:
cargo test --package pallet-subtensor --lib -- tests::invariants