fix: clean up orphaned storage on subnet deregistration#2579
fix: clean up orphaned storage on subnet deregistration#2579plind-junior wants to merge 14 commits intoopentensor:devnet-readyfrom
Conversation
f2cbbd4 to
a4261ae
Compare
5f14e48 to
462b882
Compare
RootAlphaDividendsPerSubnet is not cleaned up when a subnet is dissolved. Since netuids are recycled (subnet.rs:178-186), stale root dividend entries from the old subnet persist and become visible to the new subnet, potentially affecting run_coinbase dividend calculations. Add clear_prefix for RootAlphaDividendsPerSubnet alongside the existing AlphaDividendsPerSubnet cleanup in remove_network. Closes opentensor#1867
462b882 to
7074c43
Compare
|
pls fix the fmt in CI. and the generation make the solution complicated. subnet deregistration and registration not happens frequently. we can just remove the storage after subnet is deregistered. |
Add PrecompileCleanupInterface trait to allow the pallet to invoke cross-crate storage cleanup when a subnet is removed. The precompiles crate iterates and removes all AllowancesStorage entries matching the dissolved netuid, preventing stale EVM approvals from carrying over when the netuid is recycled.
7074c43 to
0010253
Compare
Add PrecompileCleanupInterface no-op to all test mocks (chain-extensions, eco-tests, admin-utils, transaction-fee). Clean up additional orphaned storage items on subnet deregistration: MinAllowedUids, MaxWeightsLimit, AdjustmentAlpha, AdjustmentInterval, MinNonImmuneUids, RootProp, RecycleOrBurn, RootClaimableThreshold, VotingPower, VotingPowerTrackingEnabled, VotingPowerDisableAtBlock, VotingPowerEmaAlpha. Move PrecompileCleanup struct into the precompiles crate for cleaner wiring.
Alpha storage was migrated to AlphaV2 (share-based) in January. The test's before hook was still calling api.query.SubtensorModule.Alpha.getValue, which now returns 0 for new stake and caused the stakeAfter > stakeBefore assertion to fail. Use contract.getStake on both sides instead, since that's what the rest of the suite does and it exercises the precompile directly.
|
The PR#2592 is merged. please pull devnet-ready and fix the conflict. |
…-root-alpha-dividends-on-network-removal # Conflicts: # contract-tests/test/staking.precompile.approval.test.ts # pallets/subtensor/src/tests/networks.rs
RootClaimable is keyed by hotkey but stores BTreeMap<NetUid, _> as its value, so a plain StorageMap walk misses the per-subnet state hiding inside each entry. After dereg the stripped netuid could linger (and empty BTreeMap entries could pile up) because finalize_all_subnet_root_ dividends uses mutate, which keeps zero-value entries under ValueQuery. Add a cleanup step in remove_network that removes the dissolved netuid from every hotkey's BTreeMap and drops the outer entry entirely when the map becomes empty, regardless of whether this pass or the earlier finalize call was the one to strip the netuid. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Introduces test_dissolve_network_no_storage_leak which automatically detects per-subnet storage that is not cleaned up when a subnet is dissolved. The test: 1. Snapshots ALL raw storage keys before subnet creation 2. Creates a subnet, registers neurons, stakes, serves axon/prometheus, sets childkeys, sets weights, and runs 2 epochs 3. Dissolves the subnet via root 4. Snapshots ALL storage keys after dissolution 5. Diffs the snapshots, filtering to SubtensorModule and Swap pallets, excluding known global storage items This is future-proof: when a developer adds a new per-netuid StorageMap but forgets cleanup in remove_network, this test fails automatically with a clear error message naming the leaked storage item. Also fixes Swap::ScrapReservoirTao and Swap::ScrapReservoirAlpha not being cleaned up in do_clear_protocol_liquidity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend subnet dissolution cleanup to drop per-hotkey global bookkeeping (Owner, Delegates, OwnedHotkeys, StakingHotkeys, LastColdkeyHotkeyStakeBlock) for hotkeys that lose their last IsNetworkMember entry when the subnet goes away. Without this, a hotkey registered only on a dissolved subnet leaves orphan entries forever. Tighten the leak-detection allowlist in the dissolution test to match: drop Owner/Delegates/OwnedHotkeys/StakingHotkeys/RootClaimable/ LastColdkeyHotkeyStakeBlock/UsedWork/AlphaMapLastKey, and add RegisteredSubnetCounter (the one intentional per-subnet counter). Seed RootClaimable with a per-netuid entry in the test lifecycle so the BTreeMap-hidden per-subnet state is exercised. Deduplicate VotingPower::clear_prefix (was in both 18b and 19) and RootProp/RootClaimableThreshold removals (already handled in section 12). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
please fix the CI |
ppolewicz
left a comment
There was a problem hiding this comment.
it looks like a good superset of my PR
Storage key was extended to (H160, u16, u64) in eea8d41 but purge_netuid_allowances and its unit test still used the old 2-tuple shape, breaking every compile-dependent CI check. Rewire both to the 3-tuple key. Drop stray blank line in networks.rs flagged by rustfmt. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
merge conflicts |
…-root-alpha-dividends-on-network-removal # Conflicts: # precompiles/src/staking.rs
|
|
||
| /// Remove all AllowancesStorage entries whose key contains the given netuid. | ||
| pub fn purge_netuid_allowances(netuid: u16) { | ||
| let to_remove: Vec<(H160, (H160, u16, u64))> = AllowancesStorage::iter() |
There was a problem hiding this comment.
The purpose for us to introduce the counter is to keep the allowance even the subnet is de-registered. I will involve the allowance implementer to review this part.
645638f to
6c58eba
Compare
…t_dividends finalize_all_subnet_root_dividends (called early in remove_network) already strips this netuid from every RootClaimable map and clears the RootClaimed prefix. Section 18c was re-stripping the netuid (no-op) and keeping a dead else-if branch, and section 19 was re-clearing RootClaimed. Simplify 18c to just garbage-collect entries whose BTreeMap is now empty, and drop the redundant RootClaimed clear_prefix.
6c58eba to
734419e
Compare
…-root-alpha-dividends-on-network-removal # Conflicts: # pallets/subtensor/src/macros/config.rs
| // longer reachable via any subnet, so drop its per-hotkey global | ||
| // bookkeeping (Owner, Delegates, OwnedHotkeys, StakingHotkeys, | ||
| // LastColdkeyHotkeyStakeBlock) to stop state piling up forever. | ||
| let mut orphans: sp_std::collections::btree_set::BTreeSet<T::AccountId> = |
There was a problem hiding this comment.
hot from the second key of storage Keys. it can not be duplicated. use Vec is ok.
|
In general, the PR looks good to me. For these gc code, I am not sure if we really need it. It can identify some unneeded data like LastColdkeyHotkeyStakeBlock, but some operation need to scan a lot of data. It is risky to consume too much weights. |
subnet_hotkeys is built from Keys::iter_prefix(netuid) where uid is the unique second key, so it cannot contain duplicates. BTreeSet was unnecessary — Vec is sufficient and slightly cheaper for this small bounded list.
Description
When a subnet is dissolved, its netuid gets recycled for future registrations. Several storage items were not being cleared during
remove_network, which means a newly registered subnet could inherit stale state from the previous occupant of that netuid.This PR ensures all per-subnet storage is properly cleaned up on deregistration:
AllowancesStorageembeds netuid in its second key(spender, netuid), so it can't be cleared withclear_prefix. We introduce aPrecompileCleanupInterfacetrait (mirroring the existingCommitmentsInterfacepattern) to let the pallet invoke cleanup across the crate boundary without creating a circular dependency.RootAlphaDividendsPerSubnet,VotingPower,RootClaimed): cleared viaclear_prefixsince netuid is the first key.MinAllowedUids,MaxWeightsLimit,AdjustmentAlpha,AdjustmentInterval,MinNonImmuneUids,RootProp,RecycleOrBurn,RootClaimableThreshold,VotingPowerTrackingEnabled,VotingPowerDisableAtBlock,VotingPowerEmaAlpha.Related Issue(s)
Type of Change
Breaking Change
N/A — only adds cleanup that was previously missing. No behavioral change for live subnets.
Checklist
./scripts/fix_rust.shto ensure my code is formatted and linted correctlyAdditional Notes
Subnet deregistration is infrequent, so iterating
AllowancesStorageto filter by netuid is acceptable. The simple map removals are single-key deletes with negligible cost.