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
5 changes: 2 additions & 3 deletions pallets/subtensor/src/coinbase/run_coinbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,8 @@ impl<T: Config> Pallet<T> {
);
epochs_run_this_block = epochs_run_this_block.saturating_add(1);

// Reserved for potential future enhancements.
// Ownership update logic based on conviction is currently inactive by design.
// Self::change_subnet_owner_if_needed(netuid);
// Change subnet owner based on conviction.
Self::change_subnet_owner_if_needed(netuid);
Comment on lines +414 to +415

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] Re-enabled conviction scan can make on_initialize unbounded

run_coinbase is invoked from block_step() in on_initialize, which returns a fixed weight. This call reaches change_subnet_owner_if_needed(), which calls get_total_conviction() and subnet_king(); those iterate HotkeyLock::<T>::iter_prefix(netuid) and DecayingHotkeyLock::<T>::iter_prefix(netuid) and build a BTreeMap over all matching hotkeys. Those maps are not bounded in the fixed hook weight, and the PR now runs the scans on every successful subnet epoch. An attacker can grow lock aggregate entries and force epoch blocks to do storage work proportional to lock-map size, risking overweight/slow block execution. Keep this disabled until the ownership check is made bounded, amortized, or otherwise accurately accounted in block weight.

Suggested change
// Change subnet owner based on conviction.
Self::change_subnet_owner_if_needed(netuid);
// Reserved for potential future enhancements.
// Ownership update logic based on conviction is currently inactive by design.
// Self::change_subnet_owner_if_needed(netuid);

} else {
// Schedule advances below; execution skipped. Pending emissions accumulate
// and will be drained by the next successful epoch.
Expand Down
54 changes: 54 additions & 0 deletions pallets/subtensor/src/tests/locks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2951,6 +2951,60 @@ fn test_change_subnet_owner_if_needed_reassigns_to_subnet_king() {
});
}

#[test]
fn test_run_coinbase_reassigns_subnet_owner_by_conviction_on_epoch() {
new_test_ext(1).execute_with(|| {
let old_owner_coldkey = U256::from(1);
let old_owner_hotkey = U256::from(2);
let netuid = setup_subnet_with_stake(old_owner_coldkey, old_owner_hotkey, 100_000_000_000);
SubnetOwner::<Test>::insert(netuid, old_owner_coldkey);
SubnetOwnerHotkey::<Test>::insert(netuid, old_owner_hotkey);

let new_owner_coldkey = U256::from(5);
let king_hotkey = U256::from(6);
assert_ok!(SubtensorModule::create_account_if_non_existent(
&new_owner_coldkey,
&king_hotkey
));

let now = crate::staking::lock::ONE_YEAR + 1;
System::set_block_number(now);
NetworkRegisteredAt::<Test>::insert(netuid, 1);
SubnetAlphaOut::<Test>::insert(netuid, AlphaBalance::from(10_000u64));
SubtensorModule::set_tempo_unchecked(netuid, 1);
LastEpochBlock::<Test>::insert(netuid, now.saturating_sub(1));
PendingEpochAt::<Test>::insert(netuid, 0);

let locked_mass = AlphaBalance::from(1_000u64);
Lock::<Test>::insert(
(new_owner_coldkey, netuid, king_hotkey),
LockState {
locked_mass,
conviction: U64F64::from_num(1_000),
last_update: now,
},
);
HotkeyLock::<Test>::insert(
netuid,
king_hotkey,
LockState {
locked_mass,
conviction: U64F64::from_num(1_000),
last_update: now,
},
);

assert_eq!(SubnetOwner::<Test>::get(netuid), old_owner_coldkey);
assert_eq!(SubnetOwnerHotkey::<Test>::get(netuid), old_owner_hotkey);

SubtensorModule::run_coinbase(SubtensorModule::mint_tao(0.into()));

assert_eq!(SubnetOwner::<Test>::get(netuid), new_owner_coldkey);
assert_eq!(SubnetOwnerHotkey::<Test>::get(netuid), king_hotkey);
assert_eq!(LastEpochBlock::<Test>::get(netuid), now);
});
}

#[test]
fn test_change_subnet_owner_rebuilds_old_owner_hotkey_by_lock_mode() {
new_test_ext(1).execute_with(|| {
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// `spec_version`, and `authoring_version` are the same between Wasm and native.
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
// the compatible custom types.
spec_version: 423,
spec_version: 424,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down
Loading