Skip to content
Merged
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
4 changes: 2 additions & 2 deletions pallets/admin-utils/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ parameter_types! {
pub const InitialSubnetLimit: u16 = 10; // Max 10 subnets.
pub const InitialNetworkRateLimit: u64 = 0;
pub const InitialTargetStakesPerInterval: u16 = 1;
pub const InitialHotkeySwapCost: u64 = 1_000_000_000;
pub const InitialKeySwapCost: u64 = 1_000_000_000;
pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default
pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default
pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn
Expand Down Expand Up @@ -166,7 +166,7 @@ impl pallet_subtensor::Config for Test {
type InitialSubnetLimit = InitialSubnetLimit;
type InitialNetworkRateLimit = InitialNetworkRateLimit;
type InitialTargetStakesPerInterval = InitialTargetStakesPerInterval;
type HotkeySwapCost = InitialHotkeySwapCost;
type KeySwapCost = InitialKeySwapCost;
type AlphaHigh = InitialAlphaHigh;
type AlphaLow = InitialAlphaLow;
type LiquidAlphaOn = InitialLiquidAlphaOn;
Expand Down
26 changes: 0 additions & 26 deletions pallets/subtensor/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,30 +429,4 @@ reveal_weights {

}: reveal_weights(RawOrigin::Signed(hotkey.clone()), netuid, uids, weight_values, salt, version_key)

schedule_coldkey_swap {
let seed: u32 = 1;
let old_coldkey: T::AccountId = account("OldColdkey", 0, seed);
let new_coldkey: T::AccountId = account("NewColdkey", 0, seed + 1);
let hotkey: T::AccountId = account("Hotkey", 0, seed);

let netuid = 1u16;
let tempo = 1u16;
let block_number: u64 = Subtensor::<T>::get_current_block_as_u64();
let nonce = 0;

// Initialize the network
Subtensor::<T>::init_new_network(netuid, tempo);
Subtensor::<T>::set_network_registration_allowed(netuid, true);

// Add balance to the old coldkey account
let amount_to_be_staked: u64 = 1000000u32.into();
Subtensor::<T>::add_balance_to_coldkey_account(&old_coldkey.clone(), amount_to_be_staked+1000000000);
// Burned register the hotkey with the old coldkey
assert_ok!(Subtensor::<T>::burned_register(
RawOrigin::Signed(old_coldkey.clone()).into(),
netuid,
hotkey.clone()
));

}: schedule_coldkey_swap(RawOrigin::Signed(old_coldkey.clone()), new_coldkey.clone(), vec![], block_number, nonce)
}
4 changes: 2 additions & 2 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ pub mod pallet {
type InitialTargetStakesPerInterval: Get<u64>;
/// Cost of swapping a hotkey.
#[pallet::constant]
type HotkeySwapCost: Get<u64>;
type KeySwapCost: Get<u64>;
/// The upper bound for the alpha parameter. Used for Liquid Alpha.
#[pallet::constant]
type AlphaHigh: Get<u16>;
Expand Down Expand Up @@ -2089,7 +2089,6 @@ pub mod pallet {
) -> DispatchResultWithPostInfo {
Self::do_swap_coldkey(origin, &new_coldkey)
}

/// Unstakes all tokens associated with a hotkey and transfers them to a new coldkey.
///
/// # Arguments
Expand All @@ -2105,6 +2104,7 @@ pub mod pallet {
/// # Weight
///
/// Weight is calculated based on the number of database reads and writes.
#[cfg(test)]
#[pallet::call_index(72)]
#[pallet::weight((Weight::from_parts(21_000_000, 0)
.saturating_add(T::DbWeight::get().reads(3))
Expand Down
57 changes: 48 additions & 9 deletions pallets/subtensor/src/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ impl<T: Config> Pallet<T> {
new_hotkey: &T::AccountId,
) -> DispatchResultWithPostInfo {
let coldkey = ensure_signed(origin)?;

ensure!(
!Self::coldkey_in_arbitration(&coldkey),
Error::<T>::ColdkeyIsInArbitration
Expand Down Expand Up @@ -60,6 +61,16 @@ impl<T: Config> Pallet<T> {
T::DbWeight::get().reads((TotalNetworks::<T>::get().saturating_add(1u16)) as u64),
);

let swap_cost = Self::get_key_swap_cost();
log::debug!("Swap cost: {:?}", swap_cost);

ensure!(
Self::can_remove_balance_from_coldkey_account(&coldkey, swap_cost),
Error::<T>::NotEnoughBalanceToPaySwapHotKey
);
let actual_burn_amount = Self::remove_balance_from_coldkey_account(&coldkey, swap_cost)?;
Self::burn_tokens(actual_burn_amount);

Self::swap_owner(old_hotkey, new_hotkey, &coldkey, &mut weight);
Self::swap_total_hotkey_stake(old_hotkey, new_hotkey, &mut weight);
Self::swap_delegates(old_hotkey, new_hotkey, &mut weight);
Expand All @@ -74,6 +85,7 @@ impl<T: Config> Pallet<T> {
Self::swap_loaded_emission(old_hotkey, new_hotkey, &netuid_is_member, &mut weight);
Self::swap_uids(old_hotkey, new_hotkey, &netuid_is_member, &mut weight);
Self::swap_prometheus(old_hotkey, new_hotkey, &netuid_is_member, &mut weight);
Self::swap_senate_member(old_hotkey, new_hotkey, &mut weight)?;

Self::swap_total_hotkey_coldkey_stakes_this_interval(old_hotkey, new_hotkey, &mut weight);

Expand Down Expand Up @@ -140,6 +152,20 @@ impl<T: Config> Pallet<T> {
Error::<T>::ColdKeyAlreadyAssociated
);

// Calculate and charge the swap fee
let swap_cost = Self::get_key_swap_cost();
log::debug!("Coldkey swap cost: {:?}", swap_cost);

ensure!(
Self::can_remove_balance_from_coldkey_account(&old_coldkey, swap_cost),
Error::<T>::NotEnoughBalanceToPaySwapColdKey
);
let actual_burn_amount =
Self::remove_balance_from_coldkey_account(&old_coldkey, swap_cost)?;
Self::burn_tokens(actual_burn_amount);

weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));

// Actually do the swap.
weight = weight.saturating_add(
Self::perform_swap_coldkey(&old_coldkey, new_coldkey)
Expand Down Expand Up @@ -189,15 +215,15 @@ impl<T: Config> Pallet<T> {
///
/// This function calculates the remaining arbitration period by subtracting the current block number
/// from the arbitration block number of the coldkey.
// pub fn get_remaining_arbitration_period(coldkey: &T::AccountId) -> u64 {
// let current_block: u64 = Self::get_current_block_as_u64();
// let arbitration_block: u64 = ColdkeyArbitrationBlock::<T>::get(coldkey);
// if arbitration_block > current_block {
// arbitration_block.saturating_sub(current_block)
// } else {
// 0
// }
// }
pub fn get_remaining_arbitration_period(coldkey: &T::AccountId) -> u64 {
let current_block: u64 = Self::get_current_block_as_u64();
let arbitration_block: u64 = ColdkeyArbitrationBlock::<T>::get(coldkey);
if arbitration_block > current_block {
arbitration_block.saturating_sub(current_block)
} else {
0
}
}

pub fn meets_min_allowed_coldkey_balance(coldkey: &T::AccountId) -> bool {
let all_staked_keys: Vec<T::AccountId> = StakingHotkeys::<T>::get(coldkey);
Expand Down Expand Up @@ -948,4 +974,17 @@ impl<T: Config> Pallet<T> {
}
weight.saturating_accrue(T::DbWeight::get().reads(TotalNetworks::<T>::get() as u64));
}

pub fn swap_senate_member(
old_hotkey: &T::AccountId,
new_hotkey: &T::AccountId,
weight: &mut Weight,
) -> DispatchResult {
weight.saturating_accrue(T::DbWeight::get().reads(1));
if T::SenateMembers::is_member(old_hotkey) {
T::SenateMembers::swap_member(old_hotkey, new_hotkey).map_err(|e| e.error)?;
weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 2));
}
Ok(())
}
}
4 changes: 2 additions & 2 deletions pallets/subtensor/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,8 +671,8 @@ impl<T: Config> Pallet<T> {
NominatorMinRequiredStake::<T>::put(min_stake);
}

pub fn get_hotkey_swap_cost() -> u64 {
T::HotkeySwapCost::get()
pub fn get_key_swap_cost() -> u64 {
T::KeySwapCost::get()
}

pub fn get_alpha_values(netuid: u16) -> (u16, u16) {
Expand Down
4 changes: 2 additions & 2 deletions pallets/subtensor/tests/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ parameter_types! {
pub const InitialSubnetLimit: u16 = 10; // Max 10 subnets.
pub const InitialNetworkRateLimit: u64 = 0;
pub const InitialTargetStakesPerInterval: u16 = 2;
pub const InitialHotkeySwapCost: u64 = 1_000_000_000;
pub const InitialKeySwapCost: u64 = 1_000_000_000;
pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default
pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default
pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn
Expand Down Expand Up @@ -369,7 +369,7 @@ impl pallet_subtensor::Config for Test {
type InitialSubnetLimit = InitialSubnetLimit;
type InitialNetworkRateLimit = InitialNetworkRateLimit;
type InitialTargetStakesPerInterval = InitialTargetStakesPerInterval;
type HotkeySwapCost = InitialHotkeySwapCost;
type KeySwapCost = InitialKeySwapCost;
type AlphaHigh = InitialAlphaHigh;
type AlphaLow = InitialAlphaLow;
type LiquidAlphaOn = InitialLiquidAlphaOn;
Expand Down
101 changes: 75 additions & 26 deletions pallets/subtensor/tests/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

use codec::Encode;
use frame_support::weights::Weight;
use frame_support::{assert_err, assert_ok};
use frame_system::Config;
use frame_support::{assert_err, assert_noop, assert_ok};
use frame_system::{Config, RawOrigin};
mod mock;
use mock::*;
use pallet_subtensor::*;
Expand Down Expand Up @@ -167,6 +167,22 @@ fn test_do_swap_hotkey_ok_robust() {
SubtensorModule::add_balance_to_coldkey_account(coldkey, swap_cost);
}

// Add old_hotkeys[0] and old_hotkeys[1] to Senate
assert_ok!(SenateMembers::add_member(
RawOrigin::Root.into(),
old_hotkeys[0]
));
assert_ok!(SenateMembers::add_member(
RawOrigin::Root.into(),
old_hotkeys[1]
));

// Verify initial Senate membership
assert!(Senate::is_member(&old_hotkeys[0]));
assert!(Senate::is_member(&old_hotkeys[1]));
assert!(!Senate::is_member(&new_hotkeys[0]));
assert!(!Senate::is_member(&new_hotkeys[1]));

// Perform the swaps for only two hotkeys
assert_ok!(SubtensorModule::do_swap_hotkey(
<<Test as Config>::RuntimeOrigin>::signed(coldkeys[0]),
Expand Down Expand Up @@ -268,6 +284,10 @@ fn test_do_swap_hotkey_ok_robust() {
assert_eq!(Keys::<Test>::get(netuid, uid), new_hotkeys[i]);
}
}

// Verify Senate membership swap
assert!(!Senate::is_member(&old_hotkeys[i]));
assert!(Senate::is_member(&new_hotkeys[i]));
} else {
// Ensure other hotkeys remain unchanged
assert_eq!(
Expand All @@ -278,6 +298,10 @@ fn test_do_swap_hotkey_ok_robust() {
SubtensorModule::get_owning_coldkey_for_hotkey(&new_hotkeys[i]),
coldkeys[i]
);

// Verify Senate membership remains unchanged for other hotkeys
assert!(!Senate::is_member(&old_hotkeys[i]));
assert!(!Senate::is_member(&new_hotkeys[i]));
}
}
}
Expand Down Expand Up @@ -1059,7 +1083,8 @@ fn test_do_swap_coldkey_success() {
let netuid = 1u16;
let stake_amount1 = 1000u64;
let stake_amount2 = 2000u64;
let free_balance_old = 12345u64 + MIN_BALANCE_TO_PERFORM_COLDKEY_SWAP;
let swap_cost = SubtensorModule::get_key_swap_cost();
let free_balance_old = 12345u64 + swap_cost;

// Setup initial state
add_network(netuid, 13, 0);
Expand Down Expand Up @@ -1158,7 +1183,7 @@ fn test_do_swap_coldkey_success() {
// Verify balance transfer
assert_eq!(
SubtensorModule::get_coldkey_balance(&new_coldkey),
free_balance_old
free_balance_old - swap_cost
);
assert_eq!(SubtensorModule::get_coldkey_balance(&old_coldkey), 0);

Expand Down Expand Up @@ -1332,6 +1357,7 @@ fn test_do_swap_coldkey_with_subnet_ownership() {
let hotkey = U256::from(3);
let netuid = 1u16;
let stake_amount: u64 = 1000u64;
let swap_cost = SubtensorModule::get_key_swap_cost();

// Setup initial state
add_network(netuid, 13, 0);
Expand All @@ -1340,7 +1366,7 @@ fn test_do_swap_coldkey_with_subnet_ownership() {
// Set TotalNetworks because swap relies on it
pallet_subtensor::TotalNetworks::<Test>::set(1);

SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount);
SubtensorModule::add_balance_to_coldkey_account(&old_coldkey, stake_amount + swap_cost);
SubnetOwner::<Test>::insert(netuid, old_coldkey);

// Populate OwnedHotkeys map
Expand Down Expand Up @@ -1666,24 +1692,47 @@ fn test_coldkey_swap_total() {
});
}

// #[test]
// fn test_coldkey_arbitrated_sw() {
// new_test_ext(1).execute_with(|| {
// let coldkey = U256::from(1);
// let hotkey = U256::from(2);
// let netuid = 1u16;

// // Setup initial state
// add_network(netuid, 13, 0);
// register_ok_neuron(netuid, hotkey, coldkey, 0);

// // Check if coldkey has associated hotkeys
// assert!(SubtensorModule::coldkey_has_associated_hotkeys(&coldkey));

// // Check for a coldkey without associated hotkeys
// let unassociated_coldkey = U256::from(3);
// assert!(!SubtensorModule::coldkey_has_associated_hotkeys(
// &unassociated_coldkey
// ));
// });
// }
#[test]
fn test_swap_senate_member() {
new_test_ext(1).execute_with(|| {
let old_hotkey = U256::from(1);
let new_hotkey = U256::from(2);
let non_member_hotkey = U256::from(3);
let mut weight = Weight::zero();

// Setup: Add old_hotkey as a Senate member
assert_ok!(SenateMembers::add_member(
RawOrigin::Root.into(),
old_hotkey
));

// Test 1: Successful swap
assert_ok!(SubtensorModule::swap_senate_member(
&old_hotkey,
&new_hotkey,
&mut weight
));
assert!(Senate::is_member(&new_hotkey));
assert!(!Senate::is_member(&old_hotkey));

// Verify weight update
let expected_weight = <Test as frame_system::Config>::DbWeight::get().reads_writes(2, 2);
assert_eq!(weight, expected_weight);

// Reset weight for next test
weight = Weight::zero();

// Test 2: Swap with non-member (should not change anything)
assert_ok!(SubtensorModule::swap_senate_member(
&non_member_hotkey,
&new_hotkey,
&mut weight
));
assert!(Senate::is_member(&new_hotkey));
assert!(!Senate::is_member(&non_member_hotkey));

// Verify weight update (should only have read operations)
let expected_weight = <Test as frame_system::Config>::DbWeight::get().reads(1);
assert_eq!(weight, expected_weight);
});
}
9 changes: 4 additions & 5 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,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: 194,
spec_version: 162,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 1,
Expand Down Expand Up @@ -312,8 +312,7 @@ impl Contains<RuntimeCall> for SafeModeWhitelistedCalls {
| RuntimeCall::SafeMode(_)
| RuntimeCall::Timestamp(_)
| RuntimeCall::SubtensorModule(
pallet_subtensor::Call::schedule_coldkey_swap { .. }
| pallet_subtensor::Call::set_weights { .. }
pallet_subtensor::Call::set_weights { .. }
| pallet_subtensor::Call::set_root_weights { .. }
| pallet_subtensor::Call::serve_axon { .. }
)
Expand Down Expand Up @@ -877,7 +876,7 @@ parameter_types! {
pub const SubtensorInitialNetworkLockReductionInterval: u64 = 14 * 7200;
pub const SubtensorInitialNetworkRateLimit: u64 = 7200;
pub const SubtensorInitialTargetStakesPerInterval: u16 = 1;
pub const SubtensorInitialHotkeySwapCost: u64 = 1_000_000_000;
pub const SubtensorInitialKeySwapCost: u64 = 1_000_000_000;
pub const InitialAlphaHigh: u16 = 58982; // Represents 0.9 as per the production default
pub const InitialAlphaLow: u16 = 45875; // Represents 0.7 as per the production default
pub const InitialLiquidAlphaOn: bool = false; // Default value for LiquidAlphaOn
Expand Down Expand Up @@ -933,7 +932,7 @@ impl pallet_subtensor::Config for Runtime {
type InitialSubnetLimit = SubtensorInitialSubnetLimit;
type InitialNetworkRateLimit = SubtensorInitialNetworkRateLimit;
type InitialTargetStakesPerInterval = SubtensorInitialTargetStakesPerInterval;
type HotkeySwapCost = SubtensorInitialHotkeySwapCost;
type KeySwapCost = SubtensorInitialKeySwapCost;
type AlphaHigh = InitialAlphaHigh;
type AlphaLow = InitialAlphaLow;
type LiquidAlphaOn = InitialLiquidAlphaOn;
Expand Down