Skip to content

Commit 07e46e3

Browse files
constconst
authored andcommitted
initial
1 parent a2b8d70 commit 07e46e3

File tree

2 files changed

+281
-1
lines changed

2 files changed

+281
-1
lines changed

pallets/subtensor/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ pub mod pallet {
367367
pub type TotalColdkeyStake<T: Config> =
368368
StorageMap<_, Identity, T::AccountId, u64, ValueQuery, DefaultAccountTake<T>>;
369369
#[pallet::storage]
370-
/// MAP (hot, cold) --> stake | Returns a tuple (u64: stakes, u64: block_number)
370+
/// MAP (hot, cold) --> u64, u64) | Returns a tuple (u64: stakes, u64: block_number)
371371
pub type TotalHotkeyColdkeyStakesThisInterval<T: Config> = StorageDoubleMap<
372372
_,
373373
Identity,
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
use super::*;
2+
use crate::MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP;
3+
use frame_support::traits::fungible::Mutate;
4+
use frame_support::traits::tokens::Preservation;
5+
use frame_support::{storage::IterableStorageDoubleMap, weights::Weight};
6+
use sp_core::{Get, U256};
7+
8+
impl<T: Config> Pallet<T> {
9+
/// Swaps the hotkey of a coldkey account.
10+
///
11+
/// # Arguments
12+
///
13+
/// * `origin` - The origin of the transaction, and also the coldkey account.
14+
/// * `old_hotkey` - The old hotkey to be swapped.
15+
/// * `new_hotkey` - The new hotkey to replace the old one.
16+
///
17+
/// # Returns
18+
///
19+
/// * `DispatchResultWithPostInfo` - The result of the dispatch.
20+
///
21+
/// # Errors
22+
///
23+
/// * `NonAssociatedColdKey` - If the coldkey does not own the old hotkey.
24+
/// * `HotKeySetTxRateLimitExceeded` - If the transaction rate limit is exceeded.
25+
/// * `NewHotKeyIsSameWithOld` - If the new hotkey is the same as the old hotkey.
26+
/// * `HotKeyAlreadyRegisteredInSubNet` - If the new hotkey is already registered in the subnet.
27+
/// * `NotEnoughBalanceToPaySwapHotKey` - If there is not enough balance to pay for the swap.
28+
pub fn do_swap_hotkey(
29+
origin: T::RuntimeOrigin,
30+
old_hotkey: &T::AccountId,
31+
new_hotkey: &T::AccountId,
32+
) -> DispatchResultWithPostInfo {
33+
// Ensure the origin is signed and get the coldkey
34+
let coldkey = ensure_signed(origin)?;
35+
36+
// Check if the coldkey is in arbitration
37+
ensure!(
38+
!Self::coldkey_in_arbitration(&coldkey),
39+
Error::<T>::ColdkeyIsInArbitration
40+
);
41+
42+
// Initialize the weight for this operation
43+
let mut weight = T::DbWeight::get().reads(2);
44+
45+
// Ensure the new hotkey is different from the old one
46+
ensure!(old_hotkey != new_hotkey, Error::<T>::NewHotKeyIsSameWithOld);
47+
// Ensure the new hotkey is not already registered on any network
48+
ensure!(
49+
!Self::is_hotkey_registered_on_any_network(new_hotkey),
50+
Error::<T>::HotKeyAlreadyRegisteredInSubNet
51+
);
52+
53+
// Update the weight for the checks above
54+
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 0));
55+
// Ensure the coldkey owns the old hotkey
56+
ensure!(
57+
Self::coldkey_owns_hotkey(&coldkey, old_hotkey),
58+
Error::<T>::NonAssociatedColdKey
59+
);
60+
61+
// Get the current block number
62+
let block: u64 = Self::get_current_block_as_u64();
63+
// Ensure the transaction rate limit is not exceeded
64+
ensure!(
65+
!Self::exceeds_tx_rate_limit(Self::get_last_tx_block(&coldkey), block),
66+
Error::<T>::HotKeySetTxRateLimitExceeded
67+
);
68+
69+
// Update the weight for reading the total networks
70+
weight.saturating_accrue(
71+
T::DbWeight::get().reads((TotalNetworks::<T>::get().saturating_add(1u16)) as u64),
72+
);
73+
74+
// Get the cost for swapping the key
75+
let swap_cost = Self::get_key_swap_cost();
76+
log::debug!("Swap cost: {:?}", swap_cost);
77+
78+
// Ensure the coldkey has enough balance to pay for the swap
79+
ensure!(
80+
Self::can_remove_balance_from_coldkey_account(&coldkey, swap_cost),
81+
Error::<T>::NotEnoughBalanceToPaySwapHotKey
82+
);
83+
// Remove the swap cost from the coldkey's account
84+
let actual_burn_amount = Self::remove_balance_from_coldkey_account(&coldkey, swap_cost)?;
85+
// Burn the tokens
86+
Self::burn_tokens(actual_burn_amount);
87+
88+
// Perform the hotkey swap
89+
Self::perform_hotkey_swap(old_hotkey, new_hotkey, &coldkey, &mut weight);
90+
91+
// Update the last transaction block for the coldkey
92+
Self::set_last_tx_block(&coldkey, block);
93+
weight.saturating_accrue(T::DbWeight::get().writes(1));
94+
95+
// Emit an event for the hotkey swap
96+
Self::deposit_event(Event::HotkeySwapped {
97+
coldkey,
98+
old_hotkey: old_hotkey.clone(),
99+
new_hotkey: new_hotkey.clone(),
100+
});
101+
102+
// Return the weight of the operation
103+
Ok(Some(weight).into())
104+
}
105+
106+
pub fn perform_hotkey_swap( old_hotkey: &T::AccountId, new_hotkey: &T::AccountId, coldkey: &T::AccountId, weight: &mut Weight ) {
107+
108+
// 1. Swap owner.
109+
// Owner( hotkey ) -> coldkey -- the coldkey that owns the hotkey.
110+
Owner::<T>::remove(old_hotkey);
111+
Owner::<T>::insert(new_hotkey, coldkey.clone());
112+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
113+
114+
// 2. Swap OwnedHotkeys.
115+
// OwnedHotkeys( coldkey ) -> Vec<hotkey> -- the hotkeys that the coldkey owns.
116+
let mut hotkeys = OwnedHotkeys::<T>::get(coldkey);
117+
// Add the new key if needed.
118+
if !hotkeys.contains(new_hotkey) {
119+
hotkeys.push(new_hotkey.clone());
120+
}
121+
// Remove the old key.
122+
hotkeys.retain(|hk| *hk != *old_hotkey);
123+
OwnedHotkeys::<T>::insert(coldkey, hotkeys);
124+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
125+
126+
// 3. Swap total hotkey stake.
127+
// TotalHotkeyStake( hotkey ) -> stake -- the total stake that the hotkey has across all delegates.
128+
let old_total_hotkey_stake = TotalHotkeyStake::<T>::get( old_hotkey ); // Get the old total hotkey stake.
129+
let new_total_hotkey_stake = TotalHotkeyStake::<T>::get( new_hotkey ); // Get the new total hotkey stake.
130+
TotalHotkeyStake::<T>::remove( old_hotkey ); // Remove the old total hotkey stake.
131+
TotalHotkeyStake::<T>::insert( new_hotkey, old_total_hotkey_stake.saturating_add( new_total_hotkey_stake ) ); // Insert the new total hotkey stake via the addition.
132+
weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2));
133+
134+
// Swap total hotkey stakes.
135+
// TotalHotkeyColdkeyStakesThisInterval( hotkey ) --> (u64: stakes, u64: block_number)
136+
let stake_tuples: Vec<(T::AccountId, (u64, u64))> = TotalHotkeyColdkeyStakesThisInterval::<T>::iter_prefix(old_hotkey).collect();
137+
for (coldkey, stake_tup) in stake_tuples {
138+
// NOTE: You could use this to increase your allowed stake operations but this would cost.
139+
TotalHotkeyColdkeyStakesThisInterval::<T>::insert(new_hotkey, &coldkey, stake_tup);
140+
TotalHotkeyColdkeyStakesThisInterval::<T>::remove(old_hotkey, &coldkey);
141+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
142+
}
143+
144+
// Swap LastTxBlock
145+
// LastTxBlock( hotkey ) --> u64 -- the last transaction block for the hotkey.
146+
let old_last_tx_block: u64 = LastTxBlock::<T>::get( old_hotkey );
147+
LastTxBlock::<T>::remove( old_hotkey );
148+
LastTxBlock::<T>::insert( new_hotkey, Self::get_current_block_as_u64() );
149+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
150+
151+
// Swap LastTxBlockDelegateTake
152+
// LastTxBlockDelegateTake( hotkey ) --> u64 -- the last transaction block for the hotkey delegate take.
153+
LastTxBlockDelegateTake::<T>::remove( old_hotkey );
154+
LastTxBlockDelegateTake::<T>::insert( new_hotkey, Self::get_current_block_as_u64() );
155+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
156+
157+
// Swap Senate members.
158+
// Senate( hotkey ) --> ?
159+
if T::SenateMembers::is_member(old_hotkey) {
160+
T::SenateMembers::swap_member(old_hotkey, new_hotkey).map_err(|e| e.error)?;
161+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
162+
}
163+
164+
// 4. Swap delegates.
165+
// Delegates( hotkey ) -> take value -- the hotkey delegate take value.
166+
let old_delegate_take = Delegates::<T>::get( old_hotkey );
167+
Delegates::<T>::remove( old_hotkey ); // Remove the old delegate take.
168+
Delegates::<T>::insert( new_hotkey, old_delegate_take ); // Insert the new delegate take.
169+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
170+
171+
// Swap all subnet specific info.
172+
let all_netuid: Vec<u16> = Self::get_all_subnet_netuids();
173+
for netuid in all_netuids {
174+
// 7.1 Remove the previous hotkey and insert the new hotkey from membership.
175+
// IsNetworkMember( hotkey, netuid ) -> bool -- is the hotkey a subnet member.
176+
let is_network_member: bool = IsNetworkMember::<T>::get( old_hotkey, netuid );
177+
IsNetworkMember::<T>::remove( old_hotkey, netuid );
178+
IsNetworkMember::<T>::insert( new_hotkey, netuid, is_network_member );
179+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
180+
181+
// 7.2 Swap Uids + Keys.
182+
// Keys( netuid, hotkey ) -> uid -- the uid the hotkey has in the network if it is a member.
183+
// Uids( netuid, hotkey ) -> uid -- the uids that the hotkey has.
184+
if is_network_member {
185+
// 7.2.1 Swap the UIDS
186+
let old_uid: u16 = Uids::<T>::get(netuid, old_hotkey);
187+
Uids::<T>::remove(netuid, old_hotkey);
188+
Uids::<T>::insert(netuid, new_hotkey, old_uid);
189+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
190+
191+
// 7.2.2 Swap the keys.
192+
Keys::<T>::insert(netuid, old_uid, new_hotkey.clone());
193+
weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 1));
194+
}
195+
196+
// 7.3 Swap Prometheus.
197+
// Prometheus( netuid, hotkey ) -> prometheus -- the prometheus data that a hotkey has in the network.
198+
if is_network_member {
199+
let old_prometheus_info: PrometheusInfo = Prometheus::<T>::get(netuid, old_hotkey);
200+
Prometheus::<T>::remove(netuid, old_hotkey);
201+
Prometheus::<T>::insert(netuid, new_hotkey, old_prometheus_info);
202+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
203+
}
204+
205+
// 7.4. Swap axons.
206+
// Axons( netuid, hotkey ) -> axon -- the axon that the hotkey has.
207+
if is_network_member {
208+
let old_axon_info: AxonInfo = Axons::<T>::get(netuid, old_hotkey);
209+
Axons::<T>::remove(netuid, old_hotkey);
210+
Axons::<T>::insert(netuid, new_hotkey, old_axon_info);
211+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
212+
}
213+
214+
// 7.5 Swap WeightCommits
215+
// WeightCommits( hotkey ) --> Vec<u64> -- the weight commits for the hotkey.
216+
if is_network_member {
217+
if let Ok(old_weight_commits) = WeightCommits::<T>::try_get(old_hotkey) {
218+
WeightCommits::<T>::remove(old_hotkey);
219+
WeightCommits::<T>::insert(new_hotkey, old_weight_commits);
220+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
221+
}
222+
}
223+
224+
// 7.5. Swap the subnet loaded emission.
225+
// LoadedEmission( netuid ) --> Vec<(hotkey, u64)> -- the loaded emission for the subnet.
226+
if is_network_member {
227+
if let Some(mut old_loaded_emission) = LoadedEmission::<T>::get(netuid) {
228+
for emission in old_loaded_emission.iter_mut() {
229+
if emission.0 == *old_hotkey {
230+
emission.0 = new_hotkey.clone();
231+
}
232+
}
233+
LoadedEmission::<T>::remove(netuid);
234+
LoadedEmission::<T>::insert(netuid, old_loaded_emission);
235+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
236+
}
237+
}
238+
239+
}
240+
241+
// Swap Stake.
242+
// Stake( hotkey, coldkey ) -> stake -- the stake that the hotkey controls on behalf of the coldkey.
243+
let stakes: Vec<(T::AccountId, u64)> = Stake::<T>::iter_prefix(old_hotkey).collect();
244+
// Clear the entire old prefix here.
245+
let _ = Stake::<T>::clear_prefix( old_hotkey, stakes.len() as u32, None );
246+
// Iterate over all the staking rows and insert them into the new hotkey.
247+
for (coldkey, old_stake_amount) in stakes {
248+
weight.saturating_accrue(T::DbWeight::get().reads(1));
249+
250+
// Swap Stake value
251+
// Stake( hotkey, coldkey ) -> stake -- the stake that the hotkey controls on behalf of the coldkey.
252+
// Get the new stake value.
253+
let new_stake_value: u64 = Stake::<T>::get(new_hotkey, &coldkey);
254+
// Insert the new stake value.
255+
Stake::<T>::insert(new_hotkey, &coldkey, new_stake_value.saturating_add(old_stake_amount));
256+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
257+
258+
// Swap StakingHotkeys.
259+
// StakingHotkeys( coldkey ) --> Vec<hotkey> -- the hotkeys that the coldkey stakes.
260+
let mut staking_hotkeys = StakingHotkeys::<T>::get(&coldkey);
261+
staking_hotkeys.retain(|hk| *hk != *old_hotkey && *hk != *new_hotkey);
262+
staking_hotkeys.push(new_hotkey.clone());
263+
StakingHotkeys::<T>::insert(coldkey.clone(), staking_hotkeys);
264+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
265+
}
266+
}
267+
268+
pub fn swap_senate_member(
269+
old_hotkey: &T::AccountId,
270+
new_hotkey: &T::AccountId,
271+
weight: &mut Weight,
272+
) -> DispatchResult {
273+
weight.saturating_accrue(T::DbWeight::get().reads(1));
274+
if T::SenateMembers::is_member(old_hotkey) {
275+
T::SenateMembers::swap_member(old_hotkey, new_hotkey).map_err(|e| e.error)?;
276+
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
277+
}
278+
Ok(())
279+
}
280+
}

0 commit comments

Comments
 (0)