Skip to content

Conversation

@l0r1s
Copy link
Collaborator

@l0r1s l0r1s commented Dec 4, 2025

Closes #2230

Summary

This PR refactors the coldkey swap mechanism from a scheduler-based system to an announcement-based system with a configurable delay period. This change improves security, simplifies the codebase, and provides better transparency for coldkey swap operations.

Key Changes

1. New Announcement-Based Flow

  • Added announce_coldkey_swap: Users must first announce their intention to swap coldkeys by providing a hash of the new coldkey
  • Added swap_coldkey_announced: After the announcement delay period, users can execute the swap by providing the actual new coldkey
  • Announcement validation: The system verifies that the new coldkey hash matches the announced hash and that sufficient time has passed

2. Configuration Changes

  • Renamed: InitialColdkeySwapScheduleDurationInitialColdkeySwapAnnouncementDelay
  • Removed: InitialColdkeySwapRescheduleDuration (no longer needed)
  • Storage: ColdkeySwapScheduled map → ColdkeySwapAnnouncements map
  • Default delay: Maintained at 5 days (36,000 blocks)

3. Refactored Core Swap Logic

  • Simplified do_swap_coldkey: Consolidated swap logic with clearer separation of concerns
  • New helper functions:
    • transfer_subnet_ownership
    • transfer_auto_stake_destination
    • transfer_coldkey_stake
    • transfer_staking_hotkeys
    • transfer_hotkeys_ownership
  • Identity preservation: If new coldkey already has an identity, it's preserved instead of overwritten

4. Updated swap_coldkey Extrinsic

  • Now root-only for arbitrary coldkey swaps (doesn't require announcement)
  • Automatically removes any existing announcement when called
  • Returns DispatchResult instead of DispatchResultWithPostInfo

5. Admin Utilities

  • Deprecated: sudo_set_coldkey_swap_schedule_duration (call index 54)
  • Added: sudo_set_coldkey_swap_announcement_delay (call index 84)
  • Added: remove_coldkey_swap_announcement (call index 127) - root-only removal of announcements

6. Events

  • Removed: ColdkeySwapScheduled
  • Added:
    • ColdkeySwapAnnounced - emitted when an announcement is made
    • ColdkeySwapAnnouncementRemoved - emitted when an announcement is removed
  • Updated: ColdkeySwapAnnouncementDelaySet (renamed from ColdkeySwapScheduleDurationSet)

7. Error Handling

  • Removed: ColdkeyIsInArbitration, SwapAlreadyScheduled, FailedToSchedule
  • Added:
    • ColdkeySwapAnnouncementNotFound
    • ColdkeySwapTooEarly
    • ColdkeySwapReannouncedTooEarly
    • AnnouncedColdkeyHashDoesNotMatch
    • Deprecated (for the old schedule_swap_coldkey call)

8. Transaction Extension

  • Updated CustomTransactionError::ColdkeyInSwapScheduleColdkeySwapAnnounced

9. Migration

  • Added: migrate_coldkey_swap_scheduled_to_announcements to migrate existing scheduled swaps to the new announcement system
  • Only migrates future scheduled swaps (past ones are ignored)
  • Adjusts announcement time to be scheduled_time - delay so swaps can execute at the originally scheduled time

Benefits

  1. Improved Security: Announcement hash prevents front-running and ensures the exact coldkey is swapped
  2. Transparency: All announcements are publicly visible on-chain before execution
  3. Simplified Code: Removes complex scheduler integration and reduces storage requirements
  4. Flexibility: Users can reannounce after the delay period expires
  5. Root Override: Admins can still perform emergency swaps via the root-only swap_coldkey call

Breaking Changes

⚠️ BREAKING CHANGES:

  • The schedule_swap_coldkey extrinsic is deprecated and will return an error
  • Configuration parameters renamed (migration handled automatically)
  • Storage layout changed (migration included)

Migration Path

For users with existing scheduled coldkey swaps:

  • The migration will automatically convert scheduled swaps to announcements
  • Your swap can still be executed at the originally scheduled time using swap_coldkey_announced
  • Ensure you have the new coldkey available when the delay period expires

@l0r1s l0r1s force-pushed the rework-coldkey-swap branch from 59d349c to 2f1ad59 Compare December 4, 2025 22:22
@l0r1s l0r1s marked this pull request as ready for review December 5, 2025 16:25
@l0r1s l0r1s requested a review from camfairchild December 16, 2025 16:59
@l0r1s l0r1s requested a review from camfairchild December 22, 2025 14:48
Copy link
Collaborator

@shamil-gadelshin shamil-gadelshin left a comment

Choose a reason for hiding this comment

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

Looks good! I have a general question about enums and public API preservation.

TooManyChildren,
/// Default transaction rate limit exceeded.
TxRateLimitExceeded,
/// Swap already scheduled.
Copy link
Collaborator

@shamil-gadelshin shamil-gadelshin Dec 23, 2025

Choose a reason for hiding this comment

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

Shouldn't we deprecate enum items instead of deleting them?

Copy link
Contributor

Choose a reason for hiding this comment

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

imo this is fine because we should expect clients to use the metadata from the chain to handle decoding, etc.

Copy link
Collaborator Author

@l0r1s l0r1s Dec 29, 2025

Choose a reason for hiding this comment

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

@shamil-gadelshin I was under the impression the enum are safe to add/remove elements when they are not used in long term storage:

  • errors are not in storage and are decoded correctly using the latest stored runtime code.
  • events are only stored in the actual block the event is emitted, this is safe because events storage is never read directly
  • enum stored in any other storage item are potentially unsafe to modify

I realized this after updating the proxy pallet one day and an event was inserted before end and it was fine

Only case it breaks is if clients use hardcoded metadata/index instead of last one as @camfairchild said

Copy link
Collaborator

Choose a reason for hiding this comment

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

The current solution should work, as Cameron said, when clients use the latest metadata from the chain. Polkadot.js seems to reload the metadata on runtime upgrades. I'm not sure how the transition actually works, but the client reload should fix any possible issues.

>>::Balance,
},
/// A coldkey swap has been scheduled
ColdkeySwapScheduled {
Copy link
Collaborator

@shamil-gadelshin shamil-gadelshin Dec 23, 2025

Choose a reason for hiding this comment

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

Shouldn't we deprecate enum items instead of deleting them?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@shamil-gadelshin see my previous response


ensure!(
new_coldkey_hash == T::Hashing::hash_of(&new_coldkey),
Error::<T>::AnnouncedColdkeyHashDoesNotMatch
Copy link
Contributor

Choose a reason for hiding this comment

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

@l0r1s , why do we need to hide the new coldkey behind a hash? Technically, this extrinsic discloses it before transaction is in the block.

Copy link
Collaborator Author

@l0r1s l0r1s Jan 5, 2026

Choose a reason for hiding this comment

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

@l0r1s , why do we need to hide the new coldkey behind a hash? Technically, this extrinsic discloses it before transaction is in the block.

The new coldkey is first announced using its hash (announce_coldkey_swap), and then swapped using swap_coldkey_announced (given the provided new coldkey map the hash provided for the announcement) which go through the MEV shield on the python side to prevent "front running"/blocking the swap. This come after an issue that happened few weeks ago, maybe @camfairchild can explain better than me the exact situation.

Hence, the new coldkey is known only when the swap has been executed.

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.

6 participants