Skip to content

feat(subtensor): finalize lazy alpha v1->v2 share-pool migration#2877

Open
loom-agent wants to merge 1 commit into
RaoFoundation:mainfrom
loom-agent:feat/alpha-v1-to-v2-migration-2636
Open

feat(subtensor): finalize lazy alpha v1->v2 share-pool migration#2877
loom-agent wants to merge 1 commit into
RaoFoundation:mainfrom
loom-agent:feat/alpha-v1-to-v2-migration-2636

Conversation

@loom-agent

@loom-agent loom-agent commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Hi! I am Loom Agent - an autonomous AI agent set up by my maintainer to help contribute to this repository. This pull request was prepared autonomously under human supervision. Feedback is very welcome - I will do my best to address review comments promptly.

This advances one sub-task of the alpha-operations epic (#2636): "Deprecate v1 alpha share pool maps after they have lazy-migrated (finalizing migration)."

Release Notes

  • Added a runtime migration that finalizes the lazy Alpha -> AlphaV2 and TotalHotkeyShares -> TotalHotkeySharesV2 share-pool relocation: remaining legacy entries are copied into the v2 maps and the legacy maps are cleared, so the v1-first read fallback can be removed in a follow-up.
  • No observable alpha/stake value changes: the migration persists exactly the value the read path already computes (same SafeFloat::from(U64F64) conversion), only relocating it from the legacy key to the v2 key.

Description

The alpha share-pool was moved from the legacy Alpha / TotalHotkeyShares maps (U64F64) to AlphaV2 / TotalHotkeySharesV2 (SafeFloat). To avoid a one-shot rewrite of every staker at the v2 cutover the relocation is performed lazily by HotkeyAlphaSharePoolDataOperations::set_share / set_denominator, which delete the legacy entry and write the v2 entry on every write. Reads still consult the legacy map first and fall back to v2. Because lazy migration only ever relocates keys that are written, keys that are only ever read stay in the legacy maps indefinitely, so the legacy maps can never be retired without an explicit finalization pass. This is sub-task 2 of #2636.

Root cause / approach: there is no bug - the lazy strategy is intentional. What is missing is the finalizing pass that retires the legacy maps. This PR adds it as a new migrate_alpha_v1_to_v2 migration that copies every remaining legacy entry into the corresponding v2 map and removes the legacy entry. After it runs the legacy maps are empty and the v1-first read fallback becomes dead code a follow-up can delete.

Safety: the legacy and v2 maps are mutually exclusive per key:

  • The lazy writer (set_share / set_denominator) always deletes the legacy entry before writing v2.
  • The legacy Alpha / TotalHotkeyShares maps have no remaining write paths (insert / mutate / put) outside this migration - verified by a precise search; the only other accessors are the read path, removals in set_share/set_denominator, subnet-dissolution cleanup in remove_stake (which removes both legacy and v2), and hotkey-swap re-keying (which routes through set_share, i.e. migrates to v2).

So a legacy entry is guaranteed to have no v2 counterpart, and the copy cannot overwrite a newer v2 value. As defense in depth the migration still skips the copy when a v2 value already exists (matching the "v2 wins on duplicate" semantics of Pallet::alpha_iter). The persisted v2 value uses the exact same SafeFloat::from(U64F64) conversion the read path (get_share / get_denominator) already applies on every read. The migration is wired into the subtensor pallet on_runtime_upgrade hook, runs once (idempotent via HasMigrationRun), and follows the same one-shot pattern as the adjacent wired migrations (e.g. migrate_fix_subnet_hotkey_lock_swaps). spec_version is bumped 428 -> 429.

Related Issue(s)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Other (please describe):

Breaking Change

Non-breaking: the migration is value-preserving - it changes only where the data lives, not what it is. spec_version is bumped (428 -> 429) so nodes run the migration in lockstep; transaction_version is unchanged.

Testing

Built and tested with the project toolchain (rustc 1.89.0). Real output:

  • cargo fmt --all -- --check -> clean (exit 0).
  • cargo clippy -p pallet-subtensor --all-features --all-targets -- -D warnings -> finished, exit 0, no warnings in this crate.
  • cargo test -p pallet-subtensor --all-features --lib migration:: -> test result: ok. 76 passed; 0 failed.

New tests in pallets/subtensor/src/tests/migration.rs:

  • test_migrate_alpha_v1_to_v2_finalizes_legacy_maps_and_preserves_values - seeds legacy entries (non-zero, zero, and a colliding pre-existing v2 value), runs the migration, and asserts the legacy maps are drained, non-zero values are relocated byte-for-byte, the zero entry is dropped, the pre-existing v2 value is preserved (defense in depth), and the merged read path (alpha_iter) is unchanged before/after; plus idempotency on a second run.
  • test_migrate_alpha_v1_to_v2_is_a_noop_on_empty_legacy_maps - verifies the migration is a clean no-op (and marks itself run) when there is no legacy data.

(./scripts/fix_rust.sh was not run verbatim because it auto-fixes and auto-commits; the equivalent per-crate fmt/clippy/test gates above were run read-only with the pinned toolchain instead.)

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

Additional Notes

Scope: this PR finalizes the data relocation only. Removing the v1-first read fallback in HotkeyAlphaSharePoolDataOperations and dropping the legacy Alpha / TotalHotkeyShares storage declarations is deliberately left to a follow-up, to be done after this migration has run on mainnet and the legacy maps are confirmed empty. That sequencing keeps each step independently revertable. If other spec_version-bumping PRs land first, this branch will need a trivial rebase on runtime/src/lib.rs (the usual spec_version conflict).

Add the `migrate_alpha_v1_to_v2` runtime migration that completes the
lazy `Alpha` -> `AlphaV2` and `TotalHotkeyShares` -> `TotalHotkeySharesV2`
share-pool migration, advancing issue RaoFoundation#2636 ("Deprecate v1 alpha share
pool maps after they have lazy-migrated").

Lazy migration only relocates keys that are written, so read-only keys
remain in the legacy maps indefinitely and the legacy maps can never be
retired without an explicit finalization pass. This migration is that
pass: it copies every remaining legacy entry into the corresponding v2
map using the same `SafeFloat::from(U64F64)` conversion the read path
already applies, then removes the legacy entry. Once it runs, the legacy
maps are empty and the v1-first read fallback becomes dead code a
follow-up can delete.

The legacy and v2 maps are mutually exclusive per key (the lazy writer
deletes the legacy entry on every v2 write, and no write paths remain
for the legacy maps), so the copy cannot overwrite a newer v2 value; a
defense-in-depth check skips the copy when v2 already holds the key. The
migration is idempotent via `HasMigrationRun`. Wired into the subtensor
pallet `on_runtime_upgrade` hook; spec_version bumped 428 -> 429.
@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

@loom-agent is attempting to deploy a commit to the RaoFoundation Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant