Skip to content

Hotkey splitting #1559

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 58 commits into
base: devnet-ready
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
c6a68e9
init solution
open-junius Apr 22, 2025
3ef08ce
Merge branch 'devnet-ready' into hotkey-splitting
open-junius Apr 22, 2025
e7ce313
Merge branch 'devnet-ready' into hotkey-splitting
open-junius Apr 24, 2025
d01d1c5
keep progress
open-junius Apr 25, 2025
fac488d
Merge branch 'devnet-ready' into hotkey-splitting
open-junius Apr 28, 2025
3b2fbce
most of coding done
open-junius Apr 28, 2025
5240483
commit Cargo.lock
open-junius Apr 28, 2025
3a99183
commit Cargo.lock
open-junius Apr 28, 2025
8438f12
fix compilation
open-junius Apr 28, 2025
e302557
commit Cargo.lock
open-junius Apr 28, 2025
8b0be6d
commit Cargo.lock
open-junius Apr 28, 2025
f61af52
Merge branch 'devnet-ready' into hotkey-splitting
open-junius Apr 29, 2025
ea252dd
Merge branch 'devnet-ready' into hotkey-splitting
open-junius May 1, 2025
f79aef9
fix test case
open-junius May 1, 2025
b29e18d
cargo clippy
open-junius May 1, 2025
d937209
fix test cases
open-junius May 1, 2025
c1903a2
fix compile error
open-junius May 1, 2025
ee036f9
cargo clippy
open-junius May 1, 2025
9baf4a0
commit Cargo.lock
open-junius May 1, 2025
3ab6fc1
add new test file
open-junius May 2, 2025
e10c37c
fix an old test
open-junius May 2, 2025
41dd313
fix more test case
open-junius May 2, 2025
a07982a
merge target branch
open-junius May 2, 2025
8b335e8
commit Cargo.lock
open-junius May 2, 2025
70f768f
cargo clippy
open-junius May 2, 2025
51360f8
cargo fmt
open-junius May 2, 2025
fa7c9df
commit Cargo.lock
open-junius May 2, 2025
a37245b
fix test case
open-junius May 3, 2025
18e4bc4
all test case done
open-junius May 3, 2025
6e37a4c
clean up code
open-junius May 3, 2025
5fe01d8
fix failed test cases
open-junius May 3, 2025
197b68b
fix test cases
open-junius May 4, 2025
75f56ec
update weights
open-junius May 4, 2025
c0bd9ec
commit Cargo.lock
open-junius May 4, 2025
4da3b4e
cargo clippy
open-junius May 4, 2025
24d9c55
add swap records
open-junius May 5, 2025
bbf8337
add hook for clean up records
open-junius May 5, 2025
3cbe465
commit Cargo.lock
open-junius May 5, 2025
1055f85
fix mock, missed parameter
open-junius May 5, 2025
f63424e
fix clippy
open-junius May 5, 2025
566b24b
fix clippy
open-junius May 5, 2025
0b0856b
fix test case
open-junius May 5, 2025
054222d
test case for records
open-junius May 5, 2025
57f1097
update execution index
open-junius May 6, 2025
c7330ae
merge with devnet ready
open-junius May 6, 2025
04a2b12
Merge branch 'devnet-ready' into hotkey-splitting
open-junius May 8, 2025
c6ee5d6
fix comments in PR
open-junius May 8, 2025
70b1ec0
fix clippy
open-junius May 8, 2025
52a0656
merge target branch
open-junius May 9, 2025
896a139
bump version
open-junius May 13, 2025
04d07f8
bump runtime version
open-junius May 13, 2025
30dedd0
merge with devnet-ready
open-junius May 13, 2025
a87fd6a
merge target branch
open-junius May 17, 2025
7c982a8
fix conflict
open-junius May 22, 2025
2e7b1ad
Merge branch 'devnet-ready' into hotkey-splitting
open-junius May 29, 2025
0d18c16
remove duplicated doc
open-junius May 30, 2025
d49fbb2
Merge branch 'devnet-ready' into hotkey-splitting
open-junius May 30, 2025
a0c3321
Merge branch 'devnet-ready' into hotkey-splitting
open-junius May 30, 2025
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
4 changes: 4 additions & 0 deletions pallets/admin-utils/src/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ parameter_types! {
pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight.
pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks
pub const DurationOfStartCall: u64 = 7 * 24 * 60 * 60 / 12; // 7 days
pub const InitialKeySwapOnSubnetCost: u64 = 10_000_000;
pub const HotkeySwapOnSubnetInterval: u64 = 7 * 24 * 60 * 60 / 12; // 7 days
Copy link
Collaborator

@l0r1s l0r1s May 30, 2025

Choose a reason for hiding this comment

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

Why is 7 days divided by 12 here? The comment should be 7 days / 12 then

Copy link
Contributor Author

Choose a reason for hiding this comment

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

one block each 12 seconds. like RootEnterDuration: BlockNumber = 5 * 60 * 24; // 24 hours

}

impl pallet_subtensor::Config for Test {
Expand Down Expand Up @@ -209,6 +211,8 @@ impl pallet_subtensor::Config for Test {
type InitialTaoWeight = InitialTaoWeight;
type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod;
type DurationOfStartCall = DurationOfStartCall;
type KeySwapOnSubnetCost = InitialKeySwapOnSubnetCost;
type HotkeySwapOnSubnetInterval = HotkeySwapOnSubnetInterval;
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
Expand Down
7 changes: 6 additions & 1 deletion pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1454,7 +1454,12 @@ mod pallet_benchmarks {
Subtensor::<T>::add_balance_to_coldkey_account(&coldkey, cost);

#[extrinsic_call]
_(RawOrigin::Signed(coldkey.clone()), old.clone(), new.clone());
_(
RawOrigin::Signed(coldkey.clone()),
old.clone(),
new.clone(),
None,
);
}

#[benchmark]
Expand Down
13 changes: 13 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,19 @@ pub mod pallet {
OptionQuery,
>;

#[pallet::storage]
/// --- DMap ( netuid, coldkey ) --> blocknumber | last hotkey swap on network.
pub type LastHotkeySwapOnNetuid<T: Config> = StorageDoubleMap<
_,
Identity,
u16,
Blake2_128Concat,
T::AccountId,
u64,
ValueQuery,
DefaultZeroU64<T>,
>;

#[pallet::storage]
/// Ensures unique IDs for StakeJobs storage map
pub type NextStakeJobId<T> = StorageValue<_, u64, ValueQuery, DefaultZeroU64<T>>;
Expand Down
6 changes: 6 additions & 0 deletions pallets/subtensor/src/macros/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,5 +224,11 @@ mod config {
/// Block number after a new subnet accept the start call extrinsic.
#[pallet::constant]
type DurationOfStartCall: Get<u64>;
/// Cost of swapping a hotkey in a subnet.
#[pallet::constant]
type KeySwapOnSubnetCost: Get<u64>;
/// Block number for a coldkey swap the hotkey in specific subnet.
#[pallet::constant]
type HotkeySwapOnSubnetInterval: Get<u64>;
}
}
5 changes: 3 additions & 2 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ mod dispatches {
Self::do_burned_registration(origin, netuid, hotkey)
}

/// The extrinsic for user to change its hotkey
/// The extrinsic for user to change its hotkey in subnet or all subnets.
#[pallet::call_index(70)]
#[pallet::weight((Weight::from_parts(240_600_000, 0)
.saturating_add(T::DbWeight::get().reads(31))
Expand All @@ -944,8 +944,9 @@ mod dispatches {
origin: OriginFor<T>,
hotkey: T::AccountId,
new_hotkey: T::AccountId,
netuid: Option<u16>,
) -> DispatchResultWithPostInfo {
Self::do_swap_hotkey(origin, &hotkey, &new_hotkey)
Self::do_swap_hotkey(origin, &hotkey, &new_hotkey, netuid)
}

/// The extrinsic for user to change the coldkey associated with their account.
Expand Down
2 changes: 2 additions & 0 deletions pallets/subtensor/src/macros/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ mod errors {
InvalidRecoveredPublicKey,
/// SubToken disabled now
SubtokenDisabled,
/// Too frequent hotkey swap on subnet
HotKeySwapOnSubnetIntervalNotPassed,
/// Zero max stake amount
ZeroMaxStakeAmount,
/// Invalid netuid duplication
Expand Down
12 changes: 12 additions & 0 deletions pallets/subtensor/src/macros/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,5 +351,17 @@ mod events {
/// - **netuid**: The network identifier.
/// - **Enabled**: Is Commit-Reveal enabled.
CommitRevealEnabled(u16, bool),

/// the hotkey is swapped
HotkeySwappedOnSubnet {
/// the account ID of coldkey
coldkey: T::AccountId,
/// the account ID of old hotkey
old_hotkey: T::AccountId,
/// the account ID of new hotkey
new_hotkey: T::AccountId,
/// the subnet ID
netuid: u16,
},
}
}
44 changes: 43 additions & 1 deletion pallets/subtensor/src/macros/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ mod hooks {
// # Args:
// * 'n': (BlockNumberFor<T>):
// - The number of the block we are initializing.
fn on_initialize(_block_number: BlockNumberFor<T>) -> Weight {
fn on_initialize(block_number: BlockNumberFor<T>) -> Weight {
let hotkey_swap_clean_up_weight = Self::clean_up_hotkey_swap_records(block_number);

let block_step_result = Self::block_step();
match block_step_result {
Ok(_) => {
Expand All @@ -23,13 +25,15 @@ mod hooks {
Weight::from_parts(110_634_229_000_u64, 0)
.saturating_add(T::DbWeight::get().reads(8304_u64))
.saturating_add(T::DbWeight::get().writes(110_u64))
.saturating_add(hotkey_swap_clean_up_weight)
}
Err(e) => {
// --- If the block step was unsuccessful, return the weight anyway.
log::error!("Error while stepping block: {:?}", e);
Weight::from_parts(110_634_229_000_u64, 0)
.saturating_add(T::DbWeight::get().reads(8304_u64))
.saturating_add(T::DbWeight::get().writes(110_u64))
.saturating_add(hotkey_swap_clean_up_weight)
}
}
}
Expand Down Expand Up @@ -127,4 +131,42 @@ mod hooks {
Ok(())
}
}

impl<T: Config> Pallet<T> {
// This function is to clean up the old hotkey swap records
// It just clean up for one subnet at a time, according to the block number
fn clean_up_hotkey_swap_records(block_number: BlockNumberFor<T>) -> Weight {
let mut weight = Weight::from_parts(0, 0);
let hotkey_swap_on_subnet_interval = T::HotkeySwapOnSubnetInterval::get();
let block_number: u64 = TryInto::try_into(block_number)
.ok()
.expect("blockchain will not exceed 2^64 blocks; QED.");
weight.saturating_accrue(T::DbWeight::get().reads(2_u64));

let netuids = Self::get_all_subnet_netuids();
weight.saturating_accrue(T::DbWeight::get().reads(netuids.len() as u64));

if let Some(slot) = block_number.checked_rem(hotkey_swap_on_subnet_interval) {
// only handle the subnet with the same residue as current block number by HotkeySwapOnSubnetInterval
for netuid in netuids.iter().filter(|netuid| {
(**netuid as u64).checked_rem(hotkey_swap_on_subnet_interval) == Some(slot)
}) {
// Iterate over all the coldkeys in the subnet
for (coldkey, swap_block_number) in
LastHotkeySwapOnNetuid::<T>::iter_prefix(netuid)
{
// Clean up out of date swap records
if swap_block_number.saturating_add(hotkey_swap_on_subnet_interval)
< block_number
{
LastHotkeySwapOnNetuid::<T>::remove(netuid, coldkey);
weight.saturating_accrue(T::DbWeight::get().writes(1_u64));
}
weight.saturating_accrue(T::DbWeight::get().reads(1_u64));
}
}
}
weight
}
}
}
6 changes: 6 additions & 0 deletions pallets/subtensor/src/subnets/uids.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,10 @@ impl<T: Config> Pallet<T> {
}
false
}

/// Return true if a hotkey is registered on specific network.
///
pub fn is_hotkey_registered_on_specific_network(hotkey: &T::AccountId, netuid: u16) -> bool {
IsNetworkMember::<T>::contains_key(hotkey, netuid)
}
}
Loading
Loading