Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1532,6 +1532,19 @@ pub mod pallet {
OptionQuery,
>;

/// --- NMAP ( netuid, hotkey, coldkey ) --> () | Reverse index for non-zero locks targeting this hotkey on this subnet.
#[pallet::storage]
pub type LockingColdkeys<T: Config> = StorageNMap<
_,
(
NMapKey<Identity, NetUid>, // subnet
NMapKey<Blake2_128Concat, T::AccountId>, // hotkey
NMapKey<Blake2_128Concat, T::AccountId>, // coldkey
),
(),
OptionQuery,
>;

/// --- DMAP ( netuid, hotkey ) --> LockState | Total lock per hotkey per subnet.
#[pallet::storage]
pub type HotkeyLock<T: Config> = StorageDoubleMap<
Expand Down
3 changes: 2 additions & 1 deletion pallets/subtensor/src/macros/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ mod hooks {
// Remove deprecated conviction lock storage.
.saturating_add(migrations::migrate_remove_deprecated_conviction_maps::migrate_remove_deprecated_conviction_maps::<T>())
// Reset testnet conviction lock storage before deploying the current design.
.saturating_add(migrations::migrate_reset_tnet_conviction_locks::migrate_reset_tnet_conviction_locks::<T>());
.saturating_add(migrations::migrate_reset_tnet_conviction_locks::migrate_reset_tnet_conviction_locks::<T>())
.saturating_add(migrations::migrate_populate_locking_coldkeys::migrate_populate_locking_coldkeys::<T>());
weight
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use alloc::string::String;
use frame_support::{traits::Get, weights::Weight};

use crate::{Config, HasMigrationRun, Lock, Pallet as Subtensor};

const MIGRATION_NAME: &[u8] = b"migrate_populate_locking_coldkeys";

pub fn migrate_populate_locking_coldkeys<T: Config>() -> Weight {
let mut weight = T::DbWeight::get().reads(1);

if HasMigrationRun::<T>::get(MIGRATION_NAME) {
log::info!(
"Migration '{}' already executed - skipping",
String::from_utf8_lossy(MIGRATION_NAME)
);
return weight;
}

log::info!(
"Running migration '{}'",
String::from_utf8_lossy(MIGRATION_NAME)
);

let now = Subtensor::<T>::get_current_block_as_u64();
let unlock_rate = crate::UnlockRate::<T>::get();
let maturity_rate = crate::MaturityRate::<T>::get();
let mut scanned_count = 0u64;
let mut indexed_count = 0u64;
let mut removed_count = 0u64;
let lock_keys = Lock::<T>::iter_keys().collect::<sp_std::vec::Vec<_>>();
Comment thread
gztensor marked this conversation as resolved.
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] Unbounded Lock backfill can overweight the runtime upgrade

This still materializes every Lock key and then processes every entry in a single runtime-upgrade call. Lock is user-growable, and returning a larger Weight after the scan does not protect the upgrade from exceeding block execution or memory limits. Bound this migration, prove the current key count is safely below the runtime-upgrade budget, or populate the reverse index lazily/over multiple bounded steps before relying on it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is acceptable, especially at this stage when Lock map is barely populated.


// Note for AI reviewer: Lock has very few records at the time of this migration,
// so iterating it once here is safe and keeps the migration simple.
for (coldkey, netuid, hotkey) in lock_keys {
scanned_count = scanned_count.saturating_add(1);

let mut model =
Subtensor::<T>::read_conviction_model_for_hotkey(&coldkey, netuid, &hotkey, now);
model.roll_forward(now, unlock_rate, maturity_rate);

if model.individual_lock().is_zero() {
removed_count = removed_count.saturating_add(1);
} else {
indexed_count = indexed_count.saturating_add(1);
}

Subtensor::<T>::save_conviction_model(&coldkey, netuid, &hotkey, model);
}

weight = weight.saturating_add(T::DbWeight::get().reads(scanned_count));
weight = weight.saturating_add(
T::DbWeight::get().writes(
indexed_count
.saturating_mul(2)
.saturating_add(removed_count.saturating_mul(3)),
),
);

HasMigrationRun::<T>::insert(MIGRATION_NAME, true);
weight = weight.saturating_add(T::DbWeight::get().writes(1));

log::info!(
"Migration '{}' completed. scanned_entries={}, indexed_entries={}, removed_zero_entries={}",
String::from_utf8_lossy(MIGRATION_NAME),
scanned_count,
indexed_count,
removed_count
);

weight
}
1 change: 1 addition & 0 deletions pallets/subtensor/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod migrate_network_lock_cost_2500;
pub mod migrate_network_lock_reduction_interval;
pub mod migrate_orphaned_storage_items;
pub mod migrate_pending_emissions;
pub mod migrate_populate_locking_coldkeys;
pub mod migrate_populate_owned_hotkeys;
pub mod migrate_rao;
pub mod migrate_rate_limit_keys;
Expand Down
Loading
Loading