Skip to content

Commit e03b730

Browse files
committed
Do not run epoch if keys have duplicate hotkeys
1 parent 3fde7dc commit e03b730

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

pallets/subtensor/src/coinbase/run_coinbase.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,9 @@ impl<T: Config> Pallet<T> {
256256
log::warn!("Failed to reveal commits for subnet {netuid} due to error: {e:?}");
257257
};
258258
// Pass on subnets that have not reached their tempo.
259-
if Self::should_run_epoch(netuid, current_block) {
259+
if Self::should_run_epoch(netuid, current_block)
260+
&& Self::is_epoch_input_state_consistent(netuid)
261+
{
260262
// Restart counters.
261263
BlocksSinceLastStep::<T>::insert(netuid, 0);
262264
LastMechansimStepBlock::<T>::insert(netuid, current_block);

pallets/subtensor/src/epoch/run_epoch.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::*;
22
use crate::epoch::math::*;
3-
use alloc::collections::BTreeMap;
3+
use alloc::collections::{BTreeMap, BTreeSet};
44
use frame_support::IterableStorageDoubleMap;
55
use safe_math::*;
66
use sp_std::collections::btree_map::IntoIter;
@@ -1617,4 +1617,20 @@ impl<T: Config> Pallet<T> {
16171617

16181618
Ok(())
16191619
}
1620+
1621+
/// This function ensures major assumptions made by epoch function:
1622+
/// 1. Keys map has no duplicate hotkeys
1623+
///
1624+
pub fn is_epoch_input_state_consistent(netuid: NetUid) -> bool {
1625+
// Check if Keys map has duplicate hotkeys or uids
1626+
let mut hotkey_set: BTreeSet<T::AccountId> = BTreeSet::new();
1627+
// `iter_prefix` over a double map yields (uid, value) for the given first key.
1628+
for (_uid, hotkey) in Keys::<T>::iter_prefix(netuid) {
1629+
if !hotkey_set.insert(hotkey) {
1630+
log::error!("Duplicate hotkeys detected for netuid {netuid:?}");
1631+
return false;
1632+
}
1633+
}
1634+
true
1635+
}
16201636
}

pallets/subtensor/src/tests/epoch.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3883,3 +3883,55 @@ fn test_last_update_size_mismatch() {
38833883
assert_eq!(SubtensorModule::get_dividends_for_uid(netuid, uid), 0);
38843884
});
38853885
}
3886+
3887+
#[test]
3888+
fn empty_ok() {
3889+
new_test_ext(1).execute_with(|| {
3890+
let netuid: NetUid = 155.into();
3891+
assert!(Pallet::<Test>::is_epoch_input_state_consistent(netuid));
3892+
});
3893+
}
3894+
3895+
#[test]
3896+
fn unique_hotkeys_and_uids_ok() {
3897+
new_test_ext(1).execute_with(|| {
3898+
let netuid: NetUid = 155.into();
3899+
3900+
// (netuid, uid) -> hotkey (AccountId = U256)
3901+
Keys::<Test>::insert(netuid, 0u16, U256::from(1u64));
3902+
Keys::<Test>::insert(netuid, 1u16, U256::from(2u64));
3903+
Keys::<Test>::insert(netuid, 2u16, U256::from(3u64));
3904+
3905+
assert!(Pallet::<Test>::is_epoch_input_state_consistent(netuid));
3906+
});
3907+
}
3908+
3909+
#[test]
3910+
fn duplicate_hotkey_within_same_netuid_fails() {
3911+
new_test_ext(1).execute_with(|| {
3912+
let netuid: NetUid = 155.into();
3913+
3914+
// Same hotkey mapped from two different UIDs in the SAME netuid
3915+
let hk = U256::from(42u64);
3916+
Keys::<Test>::insert(netuid, 0u16, hk);
3917+
Keys::<Test>::insert(netuid, 1u16, U256::from(42u64)); // duplicate hotkey
3918+
3919+
assert!(!Pallet::<Test>::is_epoch_input_state_consistent(netuid));
3920+
});
3921+
}
3922+
3923+
#[test]
3924+
fn same_hotkey_across_different_netuids_is_ok() {
3925+
new_test_ext(1).execute_with(|| {
3926+
let net_a: NetUid = 10.into();
3927+
let net_b: NetUid = 11.into();
3928+
3929+
// Same hotkey appears once in each netuid — each net checks independently.
3930+
let hk = U256::from(777u64);
3931+
Keys::<Test>::insert(net_a, 0u16, hk);
3932+
Keys::<Test>::insert(net_b, 0u16, hk);
3933+
3934+
assert!(Pallet::<Test>::is_epoch_input_state_consistent(net_a));
3935+
assert!(Pallet::<Test>::is_epoch_input_state_consistent(net_b));
3936+
});
3937+
}

0 commit comments

Comments
 (0)