Skip to content

Repatriate residual stake and locks on lease termination#2709

Open
l0r1s wants to merge 9 commits into
devnet-readyfrom
fix-lease-handoff
Open

Repatriate residual stake and locks on lease termination#2709
l0r1s wants to merge 9 commits into
devnet-readyfrom
fix-lease-handoff

Conversation

@l0r1s

@l0r1s l0r1s commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Repatriate residual stake and locks on lease termination

Closes #2663.

Problem

do_terminate_lease handed subnet ownership to the beneficiary but left the
accumulated owner-cut alpha and lock state on the lease's derived coldkey
(blake2_256("leasing/coldkey", lease_id)). That coldkey has no private key and
the beneficiary proxy can't extract stake, so every fixed-end lease termination
permanently stranded the owner emissions accrued during the lease.

Change

Before tearing down the lease coldkey, termination now:

  • moves the lease coldkey's lock to the beneficiary's hotkey, preserving the
    conviction earned during the lease (do_move_lock(..., preserve_conviction));
  • re-points the subnet owner aggregate buckets to the new owner hotkey
    (reassign_subnet_owner_lock_aggregates, extracted so it's shared with the
    existing owner-change path);
  • repatriates the residual alpha stake to the beneficiary;
  • moves any remaining lock to the beneficiary (transfer_full_lock_to_coldkey);
  • clears the lease coldkey's storage references and SubnetUidToLeaseId;
  • removes the beneficiary proxy before dec_providers, then sweeps the
    unreserved proxy deposit to the beneficiary, so the keyless coldkey is reaped
    cleanly with no dangling account, refcount, or stranded funds.

Post-condition: no alpha, lock, or ownership rows remain under the lease coldkey.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI review — see the sticky summary comment for the verdict and the inline comments below for specific findings.

Comment thread pallets/subtensor/src/subnets/leasing.rs
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🛡️ AI Review — Skeptic (security review)

VERDICT: VULNERABLE

LOW scrutiny: l0r1s has repo write permission, a 2018 account, substantial contribution history, matching commit authors, and no protected instruction/dependency/build-script changes; branch is fix-lease-handoff -> devnet-ready.

No .github/ai-review/*, copilot-instruction, dependency, build-script, or lockfile changes were present. Static review only; no build or tests were run per Skeptic policy.

The latest commits address part of the prior review by adding swap_hotkey() to the declared dispatch weight, but the successful post-dispatch weight still refunds that work. The partial-handoff issue also remains because the fallible lock handoff is still reached after earlier storage mutations without a transaction wrapper around terminate_lease.

Findings

Sev File Finding
HIGH pallets/subtensor/src/subnets/leasing.rs:282 Hotkey-swap work is still refunded from terminate_lease post weight inline
MEDIUM pallets/subtensor/src/subnets/leasing.rs:238 Lease termination can still commit a partial handoff on lock conflict inline

Prior-comment reconciliation

  • 92d73963: not addressed — The declared call weight now includes swap_hotkey(), but the successful post-dispatch weight still returns only terminate_lease(clear_result.unique), so the swap component is still refunded/under-accounted.
  • 9d8fc284: not addressed — The latest diff adds a rollback test but no transaction wrapper or preflight validation before the fallible transfer_full_lock_to_coldkey call; the same partial-mutation path remains.
  • 5069e74c: not addressed — Same carried-forward issue: terminate_lease still performs lock/owner/aggregate/stake mutations before a later beneficiary-controlled LockHotkeyMismatch can be returned, with no storage transaction wrapper visible in the call path.

Conclusion

The PR still under-accounts the hotkey-swap work in successful terminate_lease calls and can still leave a partial lease handoff committed on a beneficiary-controlled lock conflict. These are security-relevant runtime safety issues, so the verdict remains vulnerable.


📜 Previous run (superseded)
Sev File Finding Status
HIGH pallets/subtensor/src/subnets/leasing.rs:343 Hotkey-swap work is discarded from terminate_lease weight ➡️ Carried forward to current findings
The declared call weight now includes swap_hotkey(), but the successful post-dispatch weight still returns only terminate_lease(clear_result.unique), so the swap component is still refunded/under-accounted.
MEDIUM pallets/subtensor/src/subnets/leasing.rs:237 Lease termination can still commit a partial handoff on lock conflict ➡️ Carried forward to current findings
The latest diff adds a rollback test but no transaction wrapper or preflight validation before the fallible transfer_full_lock_to_coldkey call; the same partial-mutation path remains.

# 🔍 AI Review — Auditor (domain review) has not yet run on this PR.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🔄 AI review updated — Skeptic: VULNERABLE

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI review — see the sticky summary comment for the verdict and the inline comments below for specific findings.


// Move any lease coldkey lock to the beneficiary-controlled hotkey without
// assigning ownership of the generated lease hotkey to the beneficiary.
Self::move_lease_lock_to_beneficiary_hotkey(&lease, &hotkey)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Lease termination can still commit a partial handoff on lock conflict

terminate_lease is not wrapped in #[transactional] or with_transaction, but this call mutates lock rows via do_move_lock before later fallible calls. In the conflict case covered by the new test, repatriate_lease_coldkey_alpha can still return LockHotkeyMismatch after this mutation and after owner/aggregate updates, leaving the lease in storage while the subnet owner and lock state have already changed. Make the whole termination path storage-transactional, or preflight every fallible lock/proxy/currency operation before the first mutation.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🔄 AI review updated — Skeptic: VULNERABLE

gztensor
gztensor previously approved these changes Jun 2, 2026
Comment thread pallets/subtensor/src/subnets/leasing.rs

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI review — see the sticky summary comment for the verdict and the inline comments below for specific findings.

Comment thread pallets/subtensor/src/subnets/leasing.rs
Comment on lines +342 to +343
let mut weight = Weight::zero();
Self::perform_hotkey_swap_on_one_subnet(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[HIGH] Hotkey-swap work is discarded from terminate_lease weight

This creates a local Weight::zero() and passes it into perform_hotkey_swap_on_one_subnet, but the accumulated weight is never returned from terminate_lease or otherwise charged. That helper walks and rewrites dynamic storage such as PendingChildKeys::<T>::iter_prefix(netuid), Alpha::<T>::iter_prefix(old_hotkey), and AlphaV2::<T>::iter_prefix(old_hotkey). The dispatch weight for terminate_lease is still only keyed by contributor count, so a lease termination can execute hotkey-swap work that is not reflected in the block weight. Account for the helper's accumulated weight in the post-dispatch result and regenerate/update the lease termination weights with bounds for the dynamic hotkey-swap state, or avoid this helper in favor of a bounded lease-specific migration.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🔄 AI review updated — Skeptic: VULNERABLE

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AI review — see the sticky summary comment for the verdict and the inline comments below for specific findings.

Comment thread pallets/subtensor/src/subnets/leasing.rs
Comment on lines +237 to +238
Self::repatriate_lease_coldkey_alpha(&lease, &hotkey)?;
Self::transfer_full_lock_to_coldkey(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[MEDIUM] Lease termination can still commit a partial handoff on lock conflict

This fallible handoff still runs after earlier storage mutations: move_lease_lock_to_beneficiary_hotkey, SubnetOwner::insert, set_subnet_owner_hotkey, reassign_subnet_owner_lock_aggregates, and repatriate_lease_coldkey_alpha have already modified state before transfer_full_lock_to_coldkey can return LockHotkeyMismatch for a beneficiary that already has a lock on the same subnet under a different hotkey. I do not see a with_storage_layer / with_transaction wrapper around do_terminate_lease, so that error path can leave ownership, lock aggregates, and residual stake partially moved while the lease remains live. Add a preflight check for the beneficiary's existing lock hotkey before any mutation, or wrap the whole termination mutation sequence in a storage transaction that rolls back on any later error.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Error rollbacks storage

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

🔄 AI review updated — Skeptic: VULNERABLE

@l0r1s l0r1s added the skip-cargo-audit This PR fails cargo audit but needs to be merged anyway label Jun 8, 2026
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.

3 participants