Skip to content

Commit c572ed6

Browse files
committed
Merge remote-tracking branch 'origin/main' into sam-add-freeze-layout
2 parents ae4cdf3 + 7d589c4 commit c572ed6

File tree

11 files changed

+1323
-22
lines changed

11 files changed

+1323
-22
lines changed

justfile

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,11 @@ clippy:
3131

3232
clippy-fix:
3333
@echo "Running cargo clippy with automatic fixes on potentially dirty code..."
34-
cargo +{{RUSTV}} clippy --fix --allow-dirty --workspace --all-targets -- \
35-
-A clippy::todo \
36-
-A clippy::unimplemented \
37-
-A clippy::indexing_slicing
38-
@echo "Running cargo clippy with automatic fixes on potentially dirty code..."
39-
cargo +{{RUSTV}} clippy --fix --allow-dirty --workspace --all-targets -- \
34+
cargo +{{RUSTV}} clippy --fix --allow-dirty --allow-staged --workspace --all-targets -- \
4035
-A clippy::todo \
4136
-A clippy::unimplemented \
4237
-A clippy::indexing_slicing
38+
4339
fix:
4440
@echo "Running cargo fix..."
4541
cargo +{{RUSTV}} fix --workspace

pallets/subtensor/src/errors.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,19 @@ mod errors {
132132
AlphaHighTooLow,
133133
/// Alpha low is out of range: alpha_low > 0 && alpha_low < 0.8
134134
AlphaLowOutOfRange,
135+
/// The coldkey has already been swapped
136+
ColdKeyAlreadyAssociated,
137+
/// The coldkey swap transaction rate limit exceeded
138+
ColdKeySwapTxRateLimitExceeded,
139+
/// The new coldkey is the same as the old coldkey
140+
NewColdKeyIsSameWithOld,
141+
/// The coldkey does not exist
142+
NotExistColdkey,
143+
/// The coldkey balance is not enough to pay for the swap
144+
NotEnoughBalanceToPaySwapColdKey,
145+
/// No balance to transfer
146+
NoBalanceToTransfer,
147+
/// Same coldkey
148+
SameColdkey,
135149
}
136150
}

pallets/subtensor/src/events.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,5 +132,23 @@ mod events {
132132
MinDelegateTakeSet(u16),
133133
/// the target stakes per interval is set by sudo/admin transaction
134134
TargetStakesPerIntervalSet(u64),
135+
/// A coldkey has been swapped
136+
ColdkeySwapped {
137+
/// the account ID of old coldkey
138+
old_coldkey: T::AccountId,
139+
/// the account ID of new coldkey
140+
new_coldkey: T::AccountId,
141+
},
142+
/// All balance of a hotkey has been unstaked and transferred to a new coldkey
143+
AllBalanceUnstakedAndTransferredToNewColdkey {
144+
/// The account ID of the current coldkey
145+
current_coldkey: T::AccountId,
146+
/// The account ID of the new coldkey
147+
new_coldkey: T::AccountId,
148+
/// The total balance of the hotkey
149+
total_balance: <<T as Config>::Currency as fungible::Inspect<
150+
<T as frame_system::Config>::AccountId,
151+
>>::Balance,
152+
},
135153
}
136154
}

pallets/subtensor/src/lib.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,9 @@ pub mod pallet {
365365
#[pallet::storage] // --- MAP ( hot ) --> cold | Returns the controlling coldkey for a hotkey.
366366
pub type Owner<T: Config> =
367367
StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount<T>>;
368+
#[pallet::storage] // --- MAP ( cold ) --> Vec<hot> | Returns the vector of hotkeys controlled by this coldkey.
369+
pub type OwnedHotkeys<T: Config> =
370+
StorageMap<_, Blake2_128Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;
368371
#[pallet::storage] // --- MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation.
369372
pub type Delegates<T: Config> =
370373
StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDefaultTake<T>>;
@@ -379,6 +382,9 @@ pub mod pallet {
379382
ValueQuery,
380383
DefaultAccountTake<T>,
381384
>;
385+
#[pallet::storage] // --- DMAP ( cold ) --> Vec<hot> | Maps coldkey to hotkeys that stake to it
386+
pub type StakingHotkeys<T: Config> =
387+
StorageMap<_, Blake2_128Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;
382388
/// -- ITEM (switches liquid alpha on)
383389
#[pallet::type_value]
384390
pub fn DefaultLiquidAlpha<T: Config>() -> bool {
@@ -1208,6 +1214,13 @@ pub mod pallet {
12081214
// Fill stake information.
12091215
Owner::<T>::insert(hotkey.clone(), coldkey.clone());
12101216

1217+
// Update OwnedHotkeys map
1218+
let mut hotkeys = OwnedHotkeys::<T>::get(coldkey);
1219+
if !hotkeys.contains(hotkey) {
1220+
hotkeys.push(hotkey.clone());
1221+
OwnedHotkeys::<T>::insert(coldkey, hotkeys);
1222+
}
1223+
12111224
TotalHotkeyStake::<T>::insert(hotkey.clone(), stake);
12121225
TotalColdkeyStake::<T>::insert(
12131226
coldkey.clone(),
@@ -1219,6 +1232,13 @@ pub mod pallet {
12191232

12201233
Stake::<T>::insert(hotkey.clone(), coldkey.clone(), stake);
12211234

1235+
// Update StakingHotkeys map
1236+
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
1237+
if !staking_hotkeys.contains(hotkey) {
1238+
staking_hotkeys.push(hotkey.clone());
1239+
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys);
1240+
}
1241+
12221242
next_uid = next_uid.checked_add(1).expect(
12231243
"should not have total number of hotkey accounts larger than u16::MAX",
12241244
);
@@ -1329,7 +1349,11 @@ pub mod pallet {
13291349
// Storage version v4 -> v5
13301350
.saturating_add(migration::migrate_delete_subnet_3::<T>())
13311351
// Doesn't check storage version. TODO: Remove after upgrade
1332-
.saturating_add(migration::migration5_total_issuance::<T>(false));
1352+
.saturating_add(migration::migration5_total_issuance::<T>(false))
1353+
// Populate OwnedHotkeys map for coldkey swap. Doesn't update storage vesion.
1354+
.saturating_add(migration::migrate_populate_owned::<T>())
1355+
// Populate StakingHotkeys map for coldkey swap. Doesn't update storage vesion.
1356+
.saturating_add(migration::migrate_populate_staking_hotkeys::<T>());
13331357

13341358
weight
13351359
}
@@ -1974,6 +1998,60 @@ pub mod pallet {
19741998
Self::do_swap_hotkey(origin, &hotkey, &new_hotkey)
19751999
}
19762000

2001+
/// The extrinsic for user to change the coldkey associated with their account.
2002+
///
2003+
/// # Arguments
2004+
///
2005+
/// * `origin` - The origin of the call, must be signed by the old coldkey.
2006+
/// * `old_coldkey` - The current coldkey associated with the account.
2007+
/// * `new_coldkey` - The new coldkey to be associated with the account.
2008+
///
2009+
/// # Returns
2010+
///
2011+
/// Returns a `DispatchResultWithPostInfo` indicating success or failure of the operation.
2012+
///
2013+
/// # Weight
2014+
///
2015+
/// Weight is calculated based on the number of database reads and writes.
2016+
#[pallet::call_index(71)]
2017+
#[pallet::weight((Weight::from_parts(1_940_000_000, 0)
2018+
.saturating_add(T::DbWeight::get().reads(272))
2019+
.saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))]
2020+
pub fn swap_coldkey(
2021+
origin: OriginFor<T>,
2022+
old_coldkey: T::AccountId,
2023+
new_coldkey: T::AccountId,
2024+
) -> DispatchResultWithPostInfo {
2025+
Self::do_swap_coldkey(origin, &old_coldkey, &new_coldkey)
2026+
}
2027+
2028+
/// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey.
2029+
///
2030+
/// # Arguments
2031+
///
2032+
/// * `origin` - The origin of the call, must be signed by the current coldkey.
2033+
/// * `hotkey` - The hotkey associated with the stakes to be unstaked.
2034+
/// * `new_coldkey` - The new coldkey to receive the unstaked tokens.
2035+
///
2036+
/// # Returns
2037+
///
2038+
/// Returns a `DispatchResult` indicating success or failure of the operation.
2039+
///
2040+
/// # Weight
2041+
///
2042+
/// Weight is calculated based on the number of database reads and writes.
2043+
#[pallet::call_index(72)]
2044+
#[pallet::weight((Weight::from_parts(1_940_000_000, 0)
2045+
.saturating_add(T::DbWeight::get().reads(272))
2046+
.saturating_add(T::DbWeight::get().writes(527)), DispatchClass::Operational, Pays::No))]
2047+
pub fn unstake_all_and_transfer_to_new_coldkey(
2048+
origin: OriginFor<T>,
2049+
new_coldkey: T::AccountId,
2050+
) -> DispatchResult {
2051+
let current_coldkey = ensure_signed(origin)?;
2052+
Self::do_unstake_all_and_transfer_to_new_coldkey(current_coldkey, new_coldkey)
2053+
}
2054+
19772055
// ---- SUDO ONLY FUNCTIONS ------------------------------------------------------------
19782056

19792057
// ==================================

pallets/subtensor/src/migration.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,3 +477,129 @@ pub fn migrate_to_v2_fixed_total_stake<T: Config>() -> Weight {
477477
Weight::zero()
478478
}
479479
}
480+
481+
/// Migrate the OwnedHotkeys map to the new storage format
482+
pub fn migrate_populate_owned<T: Config>() -> Weight {
483+
// Setup migration weight
484+
let mut weight = T::DbWeight::get().reads(1);
485+
let migration_name = "Populate OwnedHotkeys map";
486+
487+
// Check if this migration is needed (if OwnedHotkeys map is empty)
488+
let migrate = OwnedHotkeys::<T>::iter().next().is_none();
489+
490+
// Only runs if the migration is needed
491+
if migrate {
492+
info!(target: LOG_TARGET_1, ">>> Starting Migration: {}", migration_name);
493+
494+
let mut longest_hotkey_vector: usize = 0;
495+
let mut longest_coldkey: Option<T::AccountId> = None;
496+
let mut keys_touched: u64 = 0;
497+
let mut storage_reads: u64 = 0;
498+
let mut storage_writes: u64 = 0;
499+
500+
// Iterate through all Owner entries
501+
Owner::<T>::iter().for_each(|(hotkey, coldkey)| {
502+
storage_reads = storage_reads.saturating_add(1); // Read from Owner storage
503+
let mut hotkeys = OwnedHotkeys::<T>::get(&coldkey);
504+
storage_reads = storage_reads.saturating_add(1); // Read from OwnedHotkeys storage
505+
506+
// Add the hotkey if it's not already in the vector
507+
if !hotkeys.contains(&hotkey) {
508+
hotkeys.push(hotkey);
509+
keys_touched = keys_touched.saturating_add(1);
510+
511+
// Update longest hotkey vector info
512+
if longest_hotkey_vector < hotkeys.len() {
513+
longest_hotkey_vector = hotkeys.len();
514+
longest_coldkey = Some(coldkey.clone());
515+
}
516+
517+
// Update the OwnedHotkeys storage
518+
OwnedHotkeys::<T>::insert(&coldkey, hotkeys);
519+
storage_writes = storage_writes.saturating_add(1); // Write to OwnedHotkeys storage
520+
}
521+
522+
// Accrue weight for reads and writes
523+
weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 1));
524+
});
525+
526+
// Log migration results
527+
info!(
528+
target: LOG_TARGET_1,
529+
"Migration {} finished. Keys touched: {}, Longest hotkey vector: {}, Storage reads: {}, Storage writes: {}",
530+
migration_name, keys_touched, longest_hotkey_vector, storage_reads, storage_writes
531+
);
532+
if let Some(c) = longest_coldkey {
533+
info!(target: LOG_TARGET_1, "Longest hotkey vector is controlled by: {:?}", c);
534+
}
535+
536+
weight
537+
} else {
538+
info!(target: LOG_TARGET_1, "Migration {} already done!", migration_name);
539+
Weight::zero()
540+
}
541+
}
542+
543+
/// Populate the StakingHotkeys map from Stake map
544+
pub fn migrate_populate_staking_hotkeys<T: Config>() -> Weight {
545+
// Setup migration weight
546+
let mut weight = T::DbWeight::get().reads(1);
547+
let migration_name = "Populate StakingHotkeys map";
548+
549+
// Check if this migration is needed (if StakingHotkeys map is empty)
550+
let migrate = StakingHotkeys::<T>::iter().next().is_none();
551+
552+
// Only runs if the migration is needed
553+
if migrate {
554+
info!(target: LOG_TARGET_1, ">>> Starting Migration: {}", migration_name);
555+
556+
let mut longest_hotkey_vector: usize = 0;
557+
let mut longest_coldkey: Option<T::AccountId> = None;
558+
let mut keys_touched: u64 = 0;
559+
let mut storage_reads: u64 = 0;
560+
let mut storage_writes: u64 = 0;
561+
562+
// Iterate through all Owner entries
563+
Stake::<T>::iter().for_each(|(hotkey, coldkey, stake)| {
564+
storage_reads = storage_reads.saturating_add(1); // Read from Owner storage
565+
if stake > 0 {
566+
let mut hotkeys = StakingHotkeys::<T>::get(&coldkey);
567+
storage_reads = storage_reads.saturating_add(1); // Read from StakingHotkeys storage
568+
569+
// Add the hotkey if it's not already in the vector
570+
if !hotkeys.contains(&hotkey) {
571+
hotkeys.push(hotkey);
572+
keys_touched = keys_touched.saturating_add(1);
573+
574+
// Update longest hotkey vector info
575+
if longest_hotkey_vector < hotkeys.len() {
576+
longest_hotkey_vector = hotkeys.len();
577+
longest_coldkey = Some(coldkey.clone());
578+
}
579+
580+
// Update the StakingHotkeys storage
581+
StakingHotkeys::<T>::insert(&coldkey, hotkeys);
582+
storage_writes = storage_writes.saturating_add(1); // Write to StakingHotkeys storage
583+
}
584+
585+
// Accrue weight for reads and writes
586+
weight = weight.saturating_add(T::DbWeight::get().reads_writes(2, 1));
587+
}
588+
});
589+
590+
// Log migration results
591+
info!(
592+
target: LOG_TARGET_1,
593+
"Migration {} finished. Keys touched: {}, Longest hotkey vector: {}, Storage reads: {}, Storage writes: {}",
594+
migration_name, keys_touched, longest_hotkey_vector, storage_reads, storage_writes
595+
);
596+
if let Some(c) = longest_coldkey {
597+
info!(target: LOG_TARGET_1, "Longest hotkey vector is controlled by: {:?}", c);
598+
}
599+
600+
weight
601+
} else {
602+
info!(target: LOG_TARGET_1, "Migration {} already done!", migration_name);
603+
Weight::zero()
604+
}
605+
}

0 commit comments

Comments
 (0)