Skip to content

Commit 44e26ff

Browse files
committed
Merge remote-tracking branch 'origin/main' into sam-always-run-e2e-tests
2 parents 1f753d8 + 7d589c4 commit 44e26ff

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
@@ -363,6 +363,9 @@ pub mod pallet {
363363
#[pallet::storage] // --- MAP ( hot ) --> cold | Returns the controlling coldkey for a hotkey.
364364
pub type Owner<T: Config> =
365365
StorageMap<_, Blake2_128Concat, T::AccountId, T::AccountId, ValueQuery, DefaultAccount<T>>;
366+
#[pallet::storage] // --- MAP ( cold ) --> Vec<hot> | Returns the vector of hotkeys controlled by this coldkey.
367+
pub type OwnedHotkeys<T: Config> =
368+
StorageMap<_, Blake2_128Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;
366369
#[pallet::storage] // --- MAP ( hot ) --> take | Returns the hotkey delegation take. And signals that this key is open for delegation.
367370
pub type Delegates<T: Config> =
368371
StorageMap<_, Blake2_128Concat, T::AccountId, u16, ValueQuery, DefaultDefaultTake<T>>;
@@ -377,6 +380,9 @@ pub mod pallet {
377380
ValueQuery,
378381
DefaultAccountTake<T>,
379382
>;
383+
#[pallet::storage] // --- DMAP ( cold ) --> Vec<hot> | Maps coldkey to hotkeys that stake to it
384+
pub type StakingHotkeys<T: Config> =
385+
StorageMap<_, Blake2_128Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;
380386
/// -- ITEM (switches liquid alpha on)
381387
#[pallet::type_value]
382388
pub fn DefaultLiquidAlpha<T: Config>() -> bool {
@@ -1204,6 +1210,13 @@ pub mod pallet {
12041210
// Fill stake information.
12051211
Owner::<T>::insert(hotkey.clone(), coldkey.clone());
12061212

1213+
// Update OwnedHotkeys map
1214+
let mut hotkeys = OwnedHotkeys::<T>::get(coldkey);
1215+
if !hotkeys.contains(hotkey) {
1216+
hotkeys.push(hotkey.clone());
1217+
OwnedHotkeys::<T>::insert(coldkey, hotkeys);
1218+
}
1219+
12071220
TotalHotkeyStake::<T>::insert(hotkey.clone(), stake);
12081221
TotalColdkeyStake::<T>::insert(
12091222
coldkey.clone(),
@@ -1215,6 +1228,13 @@ pub mod pallet {
12151228

12161229
Stake::<T>::insert(hotkey.clone(), coldkey.clone(), stake);
12171230

1231+
// Update StakingHotkeys map
1232+
let mut staking_hotkeys = StakingHotkeys::<T>::get(coldkey);
1233+
if !staking_hotkeys.contains(hotkey) {
1234+
staking_hotkeys.push(hotkey.clone());
1235+
StakingHotkeys::<T>::insert(coldkey, staking_hotkeys);
1236+
}
1237+
12181238
next_uid = next_uid.checked_add(1).expect(
12191239
"should not have total number of hotkey accounts larger than u16::MAX",
12201240
);
@@ -1325,7 +1345,11 @@ pub mod pallet {
13251345
// Storage version v4 -> v5
13261346
.saturating_add(migration::migrate_delete_subnet_3::<T>())
13271347
// Doesn't check storage version. TODO: Remove after upgrade
1328-
.saturating_add(migration::migration5_total_issuance::<T>(false));
1348+
.saturating_add(migration::migration5_total_issuance::<T>(false))
1349+
// Populate OwnedHotkeys map for coldkey swap. Doesn't update storage vesion.
1350+
.saturating_add(migration::migrate_populate_owned::<T>())
1351+
// Populate StakingHotkeys map for coldkey swap. Doesn't update storage vesion.
1352+
.saturating_add(migration::migrate_populate_staking_hotkeys::<T>());
13291353

13301354
weight
13311355
}
@@ -1970,6 +1994,60 @@ pub mod pallet {
19701994
Self::do_swap_hotkey(origin, &hotkey, &new_hotkey)
19711995
}
19721996

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

19752053
// ==================================

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)