diff --git a/pallets/admin-utils/src/lib.rs b/pallets/admin-utils/src/lib.rs index 445642d02f..1ae994e039 100644 --- a/pallets/admin-utils/src/lib.rs +++ b/pallets/admin-utils/src/lib.rs @@ -90,6 +90,20 @@ pub mod pallet { /// Indicates if the Bonds Reset was enabled or disabled. enabled: bool, }, + /// Event emitted when the burn half-life parameter is set for a subnet. + BurnHalfLifeSet { + /// The network identifier. + netuid: NetUid, + /// The new burn half-life value. + burn_half_life: u16, + }, + /// Event emitted when the burn increase multiplier is set for a subnet. + BurnIncreaseMultSet { + /// The network identifier. + netuid: NetUid, + /// The new burn increase multiplier. + burn_increase_mult: u64, + }, } // Errors inform users that something went wrong. @@ -117,6 +131,8 @@ pub mod pallet { MaxAllowedUidsGreaterThanDefaultMaxAllowedUids, /// Bad parameter value InvalidValue, + /// Operation is not permitted on the root network. + NotPermittedOnRootSubnet, } /// Enum for specifying the type of precompile operation. #[derive( @@ -2259,6 +2275,67 @@ pub mod pallet { log::trace!("ColdkeySwapReannouncementDelaySet( duration: {duration:?} )"); Ok(()) } + /// Set BurnHalfLife for a subnet. + #[pallet::call_index(88)] + #[pallet::weight(( + Weight::from_parts(25_000_000, 0) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::Yes, + ))] + pub fn sudo_set_burn_half_life( + origin: OriginFor, + netuid: NetUid, + burn_half_life: u16, + ) -> DispatchResult { + ensure_root(origin)?; + + ensure!( + pallet_subtensor::Pallet::::if_subnet_exist(netuid), + Error::::SubnetDoesNotExist + ); + ensure!(!netuid.is_root(), Error::::NotPermittedOnRootSubnet); + ensure!(burn_half_life > 0, Error::::InvalidValue); + + pallet_subtensor::BurnHalfLife::::insert(netuid, burn_half_life); + Self::deposit_event(Event::BurnHalfLifeSet { + netuid, + burn_half_life, + }); + Ok(()) + } + + /// Set BurnIncreaseMult for a subnet. + #[pallet::call_index(89)] + #[pallet::weight(( + Weight::from_parts(25_000_000, 0) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)), + DispatchClass::Operational, + Pays::Yes, + ))] + pub fn sudo_set_burn_increase_mult( + origin: OriginFor, + netuid: NetUid, + burn_increase_mult: u64, + ) -> DispatchResult { + ensure_root(origin)?; + + ensure!( + pallet_subtensor::Pallet::::if_subnet_exist(netuid), + Error::::SubnetDoesNotExist + ); + ensure!(!netuid.is_root(), Error::::NotPermittedOnRootSubnet); + ensure!(burn_increase_mult >= 1, Error::::InvalidValue); + + pallet_subtensor::BurnIncreaseMult::::insert(netuid, burn_increase_mult); + Self::deposit_event(Event::BurnIncreaseMultSet { + netuid, + burn_increase_mult, + }); + Ok(()) + } } } diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index f97e678034..bc15d3d7b8 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -502,27 +502,29 @@ pub fn register_ok_neuron( netuid: NetUid, hotkey_account_id: U256, coldkey_account_id: U256, - start_nonce: u64, + _start_nonce: u64, ) { - let block_number: u64 = SubtensorModule::get_current_block_as_u64(); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - start_nonce, - &hotkey_account_id, - ); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + // Ensure reserves exist for swap/burn path. + let reserve: u64 = 1_000_000_000_000; + setup_reserves(netuid, reserve.into(), reserve.into()); + + // Ensure coldkey has enough to pay the current burn. + let burn: TaoCurrency = SubtensorModule::get_burn(netuid); + let burn_u64: u64 = burn.into(); + let bal = SubtensorModule::get_coldkey_balance(&coldkey_account_id); + + if bal < burn_u64 { + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_u64 - bal + 10); + } + + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), netuid, - block_number, - nonce, - work, hotkey_account_id, - coldkey_account_id, ); assert_ok!(result); log::info!( - "Register ok neuron: netuid: {netuid:?}, coldkey: {hotkey_account_id:?}, hotkey: {coldkey_account_id:?}" + "Register ok neuron: netuid: {netuid:?}, coldkey: {coldkey_account_id:?}, hotkey: {hotkey_account_id:?}" ); } @@ -530,5 +532,27 @@ pub fn register_ok_neuron( pub fn add_network(netuid: NetUid, tempo: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); - SubtensorModule::set_network_pow_registration_allowed(netuid, true); + + pallet_subtensor::FirstEmissionBlockNumber::::insert(netuid, 1); + pallet_subtensor::SubtokenEnabled::::insert(netuid, true); + + // make interval 1 block so tests can register by stepping 1 block. + pallet_subtensor::BurnHalfLife::::insert(netuid, 1); + pallet_subtensor::BurnIncreaseMult::::insert(netuid, 1); + pallet_subtensor::BurnLastHalvingBlock::::insert( + netuid, + SubtensorModule::get_current_block_as_u64(), + ); +} + +use subtensor_runtime_common::AlphaCurrency; +pub(crate) fn setup_reserves(netuid: NetUid, tao: TaoCurrency, alpha: AlphaCurrency) { + pallet_subtensor::SubnetTAO::::set(netuid, tao); + pallet_subtensor::SubnetAlphaIn::::set(netuid, alpha); +} + +/// Convenience wrapper for tests that need to advance blocks incrementally. +pub fn step_block(n: u64) { + let current: u64 = frame_system::Pallet::::block_number().into(); + run_to_block(current + n); } diff --git a/pallets/admin-utils/src/tests/mod.rs b/pallets/admin-utils/src/tests/mod.rs index 00ce3d19f4..648148425b 100644 --- a/pallets/admin-utils/src/tests/mod.rs +++ b/pallets/admin-utils/src/tests/mod.rs @@ -1,26 +1,22 @@ -use frame_support::sp_runtime::DispatchError; +use crate::{Error, pallet::PrecompileEnable}; use frame_support::{ assert_err, assert_noop, assert_ok, dispatch::{DispatchClass, GetDispatchInfo, Pays}, - traits::Hooks, + sp_runtime::DispatchError, + traits::{Currency as _, Hooks}, }; use frame_system::Config; use pallet_subtensor::{ - Error as SubtensorError, MaxRegistrationsPerBlock, Rank, SubnetOwner, - TargetRegistrationsPerInterval, Tempo, WeightsVersionKeyRateLimit, *, + Error as SubtensorError, Event, MaxRegistrationsPerBlock, Rank, SubnetOwner, + TargetRegistrationsPerInterval, Tempo, WeightsVersionKeyRateLimit, + utils::rate_limiting::TransactionType, *, }; -// use pallet_subtensor::{migrations, Event}; -use pallet_subtensor::{Event, utils::rate_limiting::TransactionType}; use sp_consensus_grandpa::AuthorityId as GrandpaId; use sp_core::{Get, Pair, U256, ed25519}; use substrate_fixed::types::I96F32; use subtensor_runtime_common::{Currency, MechId, NetUid, TaoCurrency}; - -use crate::Error; -use crate::pallet::PrecompileEnable; -use mock::*; - mod mock; +use mock::*; #[test] fn test_sudo_set_default_take() { @@ -485,9 +481,16 @@ fn test_sudo_set_max_allowed_uids() { MaxRegistrationsPerBlock::::insert(netuid, 256); TargetRegistrationsPerInterval::::insert(netuid, 256); - // Register some neurons for i in 0..=8 { - register_ok_neuron(netuid, U256::from(i * 1000), U256::from(i * 1000 + i), 0); + let hotkey = U256::from(i * 1000); + let coldkey = U256::from(i * 1000 + i); + + let funds: u64 = 1_000_000_000_000_000; // 1,000,000 TAO (in RAO) + let _ = Balances::deposit_creating(&coldkey, Balance::from(funds)); + let _ = Balances::deposit_creating(&hotkey, Balance::from(funds)); // defensive + + register_ok_neuron(netuid, hotkey, coldkey, 0); + step_block(1); } // Bad origin that is not root or subnet owner @@ -1682,6 +1685,12 @@ fn test_sets_a_lower_value_clears_small_nominations() { assert!(to_stake > nominator_min_required_stake_0); // Should stay when set assert!(to_stake < nominator_min_required_stake_1); // Should be removed when set + // ---- FIX: fund accounts so burn-based registration + staking doesn't fail. + let funds: u64 = 1_000_000_000_000_000; // 1,000,000 TAO (in RAO) + let _ = Balances::deposit_creating(&owner_coldkey, Balance::from(funds)); + let _ = Balances::deposit_creating(&staker_coldkey, Balance::from(funds)); + let _ = Balances::deposit_creating(&hotkey, Balance::from(funds)); // defensive + // Create network let netuid = NetUid::from(2); add_network(netuid, 10); @@ -1747,49 +1756,6 @@ fn test_sets_a_lower_value_clears_small_nominations() { }); } -// #[test] -// fn test_sudo_set_subnet_owner_hotkey() { -// new_test_ext().execute_with(|| { -// let netuid = NetUid::from(1); - -// let coldkey: U256 = U256::from(1); -// let hotkey: U256 = U256::from(2); -// let new_hotkey: U256 = U256::from(3); - -// let coldkey_origin = <::RuntimeOrigin>::signed(coldkey); -// let root = RuntimeOrigin::root(); -// let random_account = RuntimeOrigin::signed(U256::from(123456)); - -// pallet_subtensor::SubnetOwner::::insert(netuid, coldkey); -// pallet_subtensor::SubnetOwnerHotkey::::insert(netuid, hotkey); -// assert_eq!( -// pallet_subtensor::SubnetOwnerHotkey::::get(netuid), -// hotkey -// ); - -// assert_ok!(AdminUtils::sudo_set_subnet_owner_hotkey( -// coldkey_origin, -// netuid, -// new_hotkey -// )); - -// assert_eq!( -// pallet_subtensor::SubnetOwnerHotkey::::get(netuid), -// new_hotkey -// ); - -// assert_noop!( -// AdminUtils::sudo_set_subnet_owner_hotkey(random_account, netuid, new_hotkey), -// DispatchError::BadOrigin -// ); - -// assert_noop!( -// AdminUtils::sudo_set_subnet_owner_hotkey(root, netuid, new_hotkey), -// DispatchError::BadOrigin -// ); -// }); -// } - // cargo test --package pallet-admin-utils --lib -- tests::test_sudo_set_ema_halving --exact --show-output #[test] fn test_sudo_set_ema_halving() { @@ -2471,10 +2437,11 @@ fn test_trim_to_max_allowed_uids() { let netuid = NetUid::from(1); let sn_owner = U256::from(1); let sn_owner_hotkey1 = U256::from(2); - let sn_owner_hotkey2 = U256::from(3); + add_network(netuid, 10); SubnetOwner::::insert(netuid, sn_owner); SubnetOwnerHotkey::::insert(netuid, sn_owner_hotkey1); + MaxRegistrationsPerBlock::::insert(netuid, 256); TargetRegistrationsPerInterval::::insert(netuid, 256); ImmuneOwnerUidsLimit::::insert(netuid, 2); @@ -2484,38 +2451,42 @@ fn test_trim_to_max_allowed_uids() { let mechanism_count = MechId::from(4); MechanismCountCurrent::::insert(netuid, mechanism_count); - // Add some neurons - let max_n = 16; + // Add some neurons (fund accounts + step blocks between regs). + let max_n: u16 = 16; for i in 1..=max_n { - let n = i * 1000; - register_ok_neuron(netuid, U256::from(n), U256::from(n + i), 0); + let n: u64 = (i as u64) * 1000; + let hotkey = U256::from(n); + let coldkey = U256::from(n + i as u64); + + let funds: u64 = 1_000_000_000_000_000; // 1,000,000 TAO (in RAO) + let _ = Balances::deposit_creating(&coldkey, Balance::from(funds)); + let _ = Balances::deposit_creating(&hotkey, Balance::from(funds)); // defensive + + register_ok_neuron(netuid, hotkey, coldkey, 0); + step_block(1); } // Run some blocks to ensure stake weights are set and that we are past the immunity period // for all neurons - run_to_block((ImmunityPeriod::::get(netuid) + 1).into()); + let immunity_period: u64 = ImmunityPeriod::::get(netuid).into(); + let current_block: u64 = frame_system::Pallet::::block_number().into(); + run_to_block(current_block + immunity_period + 1); // Set some randomized values that we can keep track of let values = vec![ - 17u16, 42u16, 8u16, 56u16, 23u16, 91u16, 34u16, // owner owned - 77u16, // temporally immune - 12u16, 65u16, 3u16, 88u16, // owner owned - 29u16, 51u16, 74u16, // temporally immune - 39u16, + 17u16, 42u16, 8u16, 56u16, 23u16, 91u16, + 34u16, // uid 6 (34) will be forced-immune below + 77u16, 12u16, 65u16, 3u16, 88u16, 29u16, 51u16, 74u16, 39u16, ]; let bool_values = vec![ - false, false, false, true, false, true, true, // owner owned - true, // temporally immune - false, true, false, true, // owner owned - false, true, true, // temporally immune - false, + false, false, false, true, false, true, true, true, false, true, false, true, false, + true, true, false, ]; let alpha_values = values.iter().map(|&v| (v as u64).into()).collect(); let u64_values: Vec = values.iter().map(|&v| v as u64).collect(); Emission::::set(netuid, alpha_values); - // NOTE: `Rank`, `Trust`, and `PruningScores` are *not* trimmed anymore, - // but we can still populate them without asserting on them. + // NOTE: Rank/Trust/PruningScores are *not* trimmed anymore, but we can populate them. Rank::::insert(netuid, values.clone()); Trust::::insert(netuid, values.clone()); Consensus::::insert(netuid, values.clone()); @@ -2533,18 +2504,11 @@ fn test_trim_to_max_allowed_uids() { LastUpdate::::insert(netuid_index, u64_values.clone()); } - // We set some owner immune uids + // Make UID 6 temporally immune so it cannot be trimmed even though it's not a top-8 emitter. let now = frame_system::Pallet::::block_number(); BlockAtRegistration::::set(netuid, 6, now); - BlockAtRegistration::::set(netuid, 11, now); - // And some temporally immune uids - Keys::::insert(netuid, 7, sn_owner_hotkey1); - Uids::::insert(netuid, sn_owner_hotkey1, 7); - Keys::::insert(netuid, 14, sn_owner_hotkey2); - Uids::::insert(netuid, sn_owner_hotkey2, 14); - - // Set some evm addresses + // Set some evm addresses (include both kept + trimmed uids) AssociatedEvmAddress::::insert( netuid, 6, @@ -2567,7 +2531,6 @@ fn test_trim_to_max_allowed_uids() { ); // Populate Weights and Bonds storage items to test trimming - // Create weights and bonds that span across the range that will be trimmed for uid in 0..max_n { let mut weights = Vec::new(); let mut bonds = Vec::new(); @@ -2575,7 +2538,6 @@ fn test_trim_to_max_allowed_uids() { // Add connections to all other uids, including those that will be trimmed for target_uid in 0..max_n { if target_uid != uid { - // Use some non-zero values to make the test more meaningful let weight_value = (uid + target_uid) % 1000; let bond_value = (uid * target_uid) % 1000; weights.push((target_uid, weight_value)); @@ -2602,8 +2564,7 @@ fn test_trim_to_max_allowed_uids() { // Ensure the max allowed uids has been set correctly assert_eq!(MaxAllowedUids::::get(netuid), new_max_n); - // Ensure the emission has been trimmed correctly, keeping the highest emitters - // (after respecting immunity/owner exclusions) and compressed to the left + // Ensure the emission has been trimmed correctly and compressed to the left assert_eq!( Emission::::get(netuid), vec![ @@ -2760,11 +2721,19 @@ fn test_trim_to_max_allowed_uids_too_many_immune() { ImmuneOwnerUidsLimit::::insert(netuid, 2); MinAllowedUids::::set(netuid, 2); - // Add 5 neurons + // Add 5 neurons (fund + step blocks between regs) let max_n = 5; for i in 1..=max_n { let n = i * 1000; - register_ok_neuron(netuid, U256::from(n), U256::from(n + i), 0); + let hotkey = U256::from(n); + let coldkey = U256::from(n + i); + + let funds: u64 = 1_000_000_000_000_000; // 1,000,000 TAO (in RAO) + let _ = Balances::deposit_creating(&coldkey, Balance::from(funds)); + let _ = Balances::deposit_creating(&hotkey, Balance::from(funds)); // defensive + + register_ok_neuron(netuid, hotkey, coldkey, 0); + step_block(1); } // Run some blocks to ensure stake weights are set @@ -2848,9 +2817,16 @@ fn test_sudo_set_min_allowed_uids() { MaxRegistrationsPerBlock::::insert(netuid, 256); TargetRegistrationsPerInterval::::insert(netuid, 256); - // Register some neurons for i in 0..=16 { - register_ok_neuron(netuid, U256::from(i * 1000), U256::from(i * 1000 + i), 0); + let hotkey = U256::from(i * 1000); + let coldkey = U256::from(i * 1000 + i); + + let funds: u64 = 1_000_000_000_000_000; // 1,000,000 TAO (in RAO) + let _ = Balances::deposit_creating(&coldkey, Balance::from(funds)); + let _ = Balances::deposit_creating(&hotkey, Balance::from(funds)); // defensive + + register_ok_neuron(netuid, hotkey, coldkey, 0); + step_block(1); } // Normal case @@ -2946,6 +2922,11 @@ fn test_sudo_set_start_call_delay_permissions_and_zero_delay() { // Test 2: Create a subnet add_network(netuid, tempo); + + if pallet_subtensor::FirstEmissionBlockNumber::::get(netuid).is_some() { + pallet_subtensor::FirstEmissionBlockNumber::::remove(netuid); + } + assert_eq!( pallet_subtensor::FirstEmissionBlockNumber::::get(netuid), None, @@ -2986,7 +2967,6 @@ fn test_sudo_set_start_call_delay_permissions_and_zero_delay() { )); // Test 5: Try to start the subnet again - should be FAILED (first emission block already set) - let current_block = frame_system::Pallet::::block_number(); assert_err!( pallet_subtensor::Pallet::::start_call( <::RuntimeOrigin>::signed(coldkey_account_id), @@ -2997,7 +2977,7 @@ fn test_sudo_set_start_call_delay_permissions_and_zero_delay() { assert_eq!( pallet_subtensor::FirstEmissionBlockNumber::::get(netuid), - Some(current_block + 1), + Some(frame_system::Pallet::::block_number() + 1), "Emission should start at next block" ); diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index ce3238e2e6..32a05e969b 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -26,6 +26,20 @@ use subtensor_runtime_common::{AlphaCurrency, NetUid, TaoCurrency}; mod pallet_benchmarks { use super::*; + /// This helper funds an account with: + /// - 2x burn fee + /// - 100x DefaultMinStake + fn fund_for_registration(netuid: NetUid, who: &T::AccountId) { + let burn = Subtensor::::get_burn(netuid); + let min_stake = DefaultMinStake::::get(); + + let deposit = burn + .saturating_mul(2.into()) + .saturating_add(min_stake.saturating_mul(100.into())); + + Subtensor::::add_balance_to_coldkey_account(who, deposit.into()); + } + #[benchmark] fn register() { let netuid = NetUid::from(1); @@ -34,16 +48,26 @@ mod pallet_benchmarks { let coldkey: T::AccountId = account("Test", 0, 2); Subtensor::::init_new_network(netuid, tempo); + Subtensor::::set_max_allowed_uids(netuid, 4096); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_network_pow_registration_allowed(netuid, true); + // Ensure burn fee is non-zero for realistic funding. + Subtensor::::set_burn(netuid, 1.into()); + + // Fund BOTH to be robust regardless of which account is charged internally. + fund_for_registration::(netuid, &coldkey); + fund_for_registration::(netuid, &hotkey); + let block_number: u64 = Subtensor::::get_current_block_as_u64(); let (nonce, work): (u64, Vec) = Subtensor::::create_work_for_block_number(netuid, block_number, 3, &hotkey); #[extrinsic_call] _( - RawOrigin::Signed(hotkey.clone()), + RawOrigin::Signed(coldkey.clone()), netuid, block_number, nonce, @@ -78,14 +102,18 @@ mod pallet_benchmarks { seed += 1; Subtensor::::set_burn(netuid, 1.into()); - let amount_to_be_staked: u64 = 1_000_000; - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount_to_be_staked); - assert_ok!(Subtensor::::do_burned_registration( + // Ensure enough for registration + minimum stake. + fund_for_registration::(netuid, &coldkey); + + RegistrationsThisInterval::::insert(netuid, 0); + + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() )); + let uid = Subtensor::::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); Subtensor::::set_validator_permit_for_uid(netuid, uid, true); @@ -121,7 +149,8 @@ mod pallet_benchmarks { let amount = TaoCurrency::from(60_000_000); Subtensor::::add_balance_to_coldkey_account(&coldkey, total_stake.into()); - assert_ok!(Subtensor::::do_burned_registration( + + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -150,17 +179,18 @@ mod pallet_benchmarks { Subtensor::::init_new_network(netuid, 1); SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_max_allowed_uids(netuid, 4096); - let reg_fee = Subtensor::::get_burn(netuid); - let deposit = reg_fee.saturating_mul(2.into()); - Subtensor::::add_balance_to_coldkey_account(&caller, deposit.into()); + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &caller); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(caller.clone()).into(), netuid, caller.clone() )); + Subtensor::::set_serving_rate_limit(netuid, 0); #[extrinsic_call] @@ -188,17 +218,18 @@ mod pallet_benchmarks { Subtensor::::init_new_network(netuid, 1); SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_max_allowed_uids(netuid, 4096); - let reg_fee = Subtensor::::get_burn(netuid); - let deposit = reg_fee.saturating_mul(2.into()); - Subtensor::::add_balance_to_coldkey_account(&caller, deposit.into()); + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &caller); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(caller.clone()).into(), netuid, caller.clone() )); + Subtensor::::set_serving_rate_limit(netuid, 0); #[extrinsic_call] @@ -212,24 +243,6 @@ mod pallet_benchmarks { ); } - #[benchmark] - fn burned_register() { - let netuid = NetUid::from(1); - let seed: u32 = 1; - let hotkey: T::AccountId = account("Alice", 0, seed); - let coldkey: T::AccountId = account("Test", 0, seed); - - Subtensor::::init_new_network(netuid, 1); - SubtokenEnabled::::insert(netuid, true); - Subtensor::::set_burn(netuid, 1.into()); - - let amount: u64 = 1_000_000; - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount); - - #[extrinsic_call] - _(RawOrigin::Signed(coldkey.clone()), netuid, hotkey.clone()); - } - #[benchmark] fn root_register() { let netuid = NetUid::from(1); @@ -247,7 +260,7 @@ mod pallet_benchmarks { let amount: u64 = 100_000_000_000_000; Subtensor::::add_balance_to_coldkey_account(&coldkey, amount); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -280,7 +293,6 @@ mod pallet_benchmarks { let weight_values: Vec = vec![10]; let hotkey: T::AccountId = account("hot", 0, 1); let coldkey: T::AccountId = account("cold", 0, 2); - let start_nonce: u64 = 300_000; let commit_hash: H256 = BlakeTwo256::hash_of(&( hotkey.clone(), @@ -291,24 +303,18 @@ mod pallet_benchmarks { )); Subtensor::::init_new_network(netuid, tempo); - Subtensor::::set_network_pow_registration_allowed(netuid, true); + Subtensor::::set_network_registration_allowed(netuid, true); + SubtokenEnabled::::insert(netuid, true); - let block_number: u64 = Subtensor::::get_current_block_as_u64(); - let (nonce, work) = Subtensor::::create_work_for_block_number( - netuid, - block_number, - start_nonce, - &hotkey, - ); - assert_ok!(Subtensor::::register( - RawOrigin::Signed(hotkey.clone()).into(), + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &coldkey); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), netuid, - block_number, - nonce, - work, - hotkey.clone(), - coldkey.clone() + hotkey.clone() )); + Subtensor::::set_validator_permit_for_uid(netuid, 0, true); Subtensor::::set_commit_reveal_weights_enabled(netuid, true); @@ -329,21 +335,16 @@ mod pallet_benchmarks { Subtensor::::init_new_network(netuid, tempo); Subtensor::::set_network_registration_allowed(netuid, true); - Subtensor::::set_network_pow_registration_allowed(netuid, true); + SubtokenEnabled::::insert(netuid, true); - let block_number: u64 = Subtensor::::get_current_block_as_u64(); - let (nonce, work) = - Subtensor::::create_work_for_block_number(netuid, block_number, 3, &hotkey); + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &coldkey); - let _ = Subtensor::::register( - RawOrigin::Signed(hotkey.clone()).into(), + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), netuid, - block_number, - nonce, - work.clone(), - hotkey.clone(), - coldkey.clone(), - ); + hotkey.clone() + )); Subtensor::::set_validator_permit_for_uid(netuid, 0, true); Subtensor::::set_commit_reveal_weights_enabled(netuid, true); @@ -356,11 +357,12 @@ mod pallet_benchmarks { salt.clone(), version_key, )); - let _ = Subtensor::::commit_weights( + + assert_ok!(Subtensor::::commit_weights( RawOrigin::Signed(hotkey.clone()).into(), netuid, - commit_hash, - ); + commit_hash + )); #[extrinsic_call] _( @@ -392,11 +394,10 @@ mod pallet_benchmarks { Subtensor::::set_network_registration_allowed(netuid, true); SubtokenEnabled::::insert(netuid, true); - let reg_fee = Subtensor::::get_burn(netuid); - let deposit = reg_fee.saturating_mul(2.into()); - Subtensor::::add_balance_to_coldkey_account(&coldkey, deposit.into()); + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &coldkey); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -438,62 +439,37 @@ mod pallet_benchmarks { frame_system::Pallet::::set_block_number(now + delay + 1u32.into()); let netuid = NetUid::from(1); - Subtensor::::init_new_network(netuid, 1); - Subtensor::::set_network_registration_allowed(netuid, true); - Subtensor::::set_network_pow_registration_allowed(netuid, true); - - let block_number = Subtensor::::get_current_block_as_u64(); - let (nonce, work) = - Subtensor::::create_work_for_block_number(netuid, block_number, 3, &hotkey1); - let _ = Subtensor::::register( - RawOrigin::Signed(old_coldkey.clone()).into(), - netuid, - block_number, - nonce, - work.clone(), - hotkey1.clone(), - old_coldkey.clone(), - ); - - #[extrinsic_call] - _(RawOrigin::Signed(old_coldkey), new_coldkey); - } - - #[benchmark] - fn swap_coldkey() { - let old_coldkey: T::AccountId = account("old_coldkey", 0, 0); - let new_coldkey: T::AccountId = account("new_coldkey", 0, 0); - let hotkey1: T::AccountId = account("hotkey1", 0, 0); - - let ed = ::ExistentialDeposit::get(); let swap_cost = Subtensor::::get_key_swap_cost(); - Subtensor::::add_balance_to_coldkey_account(&old_coldkey, swap_cost.to_u64() + ed); + let free_balance_old = swap_cost + 12345.into(); - let netuid = NetUid::from(1); Subtensor::::init_new_network(netuid, 1); Subtensor::::set_network_registration_allowed(netuid, true); - Subtensor::::set_network_pow_registration_allowed(netuid, true); + SubtokenEnabled::::insert(netuid, true); + Subtensor::::set_burn(netuid, 1.into()); - let block_number = Subtensor::::get_current_block_as_u64(); - let (nonce, work) = - Subtensor::::create_work_for_block_number(netuid, block_number, 3, &hotkey1); - let _ = Subtensor::::register( + Subtensor::::add_balance_to_coldkey_account(&old_coldkey, free_balance_old.into()); + + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(old_coldkey.clone()).into(), netuid, - block_number, - nonce, - work.clone(), hotkey1.clone(), - old_coldkey.clone(), - ); + )); + + Subtensor::::add_balance_to_coldkey_account(&old_coldkey, free_balance_old.into()); + let name: Vec = b"The fourth Coolest Identity".to_vec(); + let identity = ChainIdentityV2 { + name, + url: vec![], + github_repo: vec![], + image: vec![], + discord: vec![], + description: vec![], + additional: vec![], + }; + IdentitiesV2::::insert(&old_coldkey, identity); #[extrinsic_call] - _( - RawOrigin::Root, - old_coldkey.clone(), - new_coldkey.clone(), - swap_cost, - ); + _(RawOrigin::Signed(old_coldkey.clone()), new_coldkey.clone()); } #[benchmark] @@ -532,23 +508,19 @@ mod pallet_benchmarks { Subtensor::::init_new_network(netuid, tempo); Subtensor::::set_network_registration_allowed(netuid, true); - Subtensor::::set_network_pow_registration_allowed(netuid, true); Subtensor::::set_commit_reveal_weights_enabled(netuid, true); Subtensor::::set_weights_set_rate_limit(netuid, 0); + SubtokenEnabled::::insert(netuid, true); - let block_number: u64 = Subtensor::::get_current_block_as_u64(); - let (nonce, work) = - Subtensor::::create_work_for_block_number(netuid, block_number, 3, &hotkey); - let origin = T::RuntimeOrigin::from(RawOrigin::Signed(hotkey.clone())); - assert_ok!(Subtensor::::register( - origin.clone(), + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &coldkey); + + assert_ok!(Subtensor::::burned_register( + RawOrigin::Signed(coldkey.clone()).into(), netuid, - block_number, - nonce, - work.clone(), - hotkey.clone(), - coldkey.clone() + hotkey.clone() )); + Subtensor::::set_validator_permit_for_uid(netuid, 0, true); let mut uids_list = Vec::new(); @@ -606,9 +578,9 @@ mod pallet_benchmarks { Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_burn(netuid, 1.into()); - let amount_to_be_staked = 1_000_000_000; - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount_to_be_staked); - assert_ok!(Subtensor::::do_burned_registration( + fund_for_registration::(netuid, &coldkey); + + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -649,9 +621,9 @@ mod pallet_benchmarks { Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_burn(netuid, 1.into()); - let amount_to_be_staked: u64 = 1_000_000_000; - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount_to_be_staked); - assert_ok!(Subtensor::::do_burned_registration( + fund_for_registration::(netuid, &coldkey); + + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -665,6 +637,7 @@ mod pallet_benchmarks { netuid, alpha_amount.into(), ); + assert_eq!( TotalHotkeyAlpha::::get(&hotkey, netuid), alpha_amount.into() @@ -690,15 +663,15 @@ mod pallet_benchmarks { Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_burn(netuid, 1.into()); - let amount_to_be_staked = 1_000_000; - Subtensor::::add_balance_to_coldkey_account(&coldkey, amount_to_be_staked); + fund_for_registration::(netuid, &coldkey); SubnetOwner::::set(netuid, coldkey.clone()); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() )); + assert_eq!(SubnetOwner::::get(netuid), coldkey.clone()); assert_eq!(FirstEmissionBlockNumber::::get(netuid), None); @@ -739,7 +712,7 @@ mod pallet_benchmarks { SubnetTAO::::insert(netuid, tao_reserve); SubnetAlphaIn::::insert(netuid, alpha_in); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -765,6 +738,7 @@ mod pallet_benchmarks { SubtokenEnabled::::insert(netuid, true); Subtensor::::init_new_network(netuid, 1); + Subtensor::::set_network_registration_allowed(netuid, true); let burn_fee = Subtensor::::get_burn(netuid); let stake_tao = DefaultMinStake::::get().saturating_mul(10.into()); @@ -795,7 +769,6 @@ mod pallet_benchmarks { Subtensor::::create_account_if_non_existent(&coldkey, &destination); - // Remove stake limit for benchmark StakingOperationRateLimiter::::remove((origin.clone(), coldkey.clone(), netuid)); #[extrinsic_call] @@ -815,7 +788,6 @@ mod pallet_benchmarks { let tempo: u16 = 1; let seed: u32 = 1; - // Set our total stake to 1000 TAO Subtensor::::increase_total_stake(1_000_000_000_000.into()); Subtensor::::init_new_network(netuid, tempo); @@ -838,7 +810,7 @@ mod pallet_benchmarks { let wallet_bal = 1000000u32.into(); Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), wallet_bal); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -856,7 +828,6 @@ mod pallet_benchmarks { let amount_unstaked = AlphaCurrency::from(30_000_000_000); - // Remove stake limit for benchmark StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); #[extrinsic_call] @@ -880,8 +851,11 @@ mod pallet_benchmarks { SubtokenEnabled::::insert(netuid1, true); Subtensor::::init_new_network(netuid1, 1); + Subtensor::::set_network_registration_allowed(netuid1, true); + SubtokenEnabled::::insert(netuid2, true); Subtensor::::init_new_network(netuid2, 1); + Subtensor::::set_network_registration_allowed(netuid2, true); let tao_reserve = TaoCurrency::from(150_000_000_000); let alpha_in = AlphaCurrency::from(100_000_000_000); @@ -903,7 +877,6 @@ mod pallet_benchmarks { netuid1, hot.clone() )); - assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid2, @@ -919,7 +892,6 @@ mod pallet_benchmarks { allow )); - // Remove stake limit for benchmark StakingOperationRateLimiter::::remove((hot.clone(), coldkey.clone(), netuid1)); #[extrinsic_call] @@ -943,6 +915,7 @@ mod pallet_benchmarks { SubtokenEnabled::::insert(netuid, true); Subtensor::::init_new_network(netuid, 1); + Subtensor::::set_network_registration_allowed(netuid, true); let reg_fee = Subtensor::::get_burn(netuid); let stake_tao = DefaultMinStake::::get().saturating_mul(10.into()); @@ -973,7 +946,6 @@ mod pallet_benchmarks { Subtensor::::create_account_if_non_existent(&dest, &hot); - // Remove stake limit for benchmark StakingOperationRateLimiter::::remove((hot.clone(), coldkey.clone(), netuid)); #[extrinsic_call] @@ -996,8 +968,11 @@ mod pallet_benchmarks { SubtokenEnabled::::insert(netuid1, true); Subtensor::::init_new_network(netuid1, 1); + Subtensor::::set_network_registration_allowed(netuid1, true); + SubtokenEnabled::::insert(netuid2, true); Subtensor::::init_new_network(netuid2, 1); + Subtensor::::set_network_registration_allowed(netuid2, true); let reg_fee = Subtensor::::get_burn(netuid1); let stake_tao = DefaultMinStake::::get().saturating_mul(10.into()); @@ -1028,7 +1003,6 @@ mod pallet_benchmarks { let alpha_to_swap = Subtensor::::get_stake_for_hotkey_and_coldkey_on_subnet(&hot, &coldkey, netuid1); - // Remove stake limit for benchmark StakingOperationRateLimiter::::remove((hot.clone(), coldkey.clone(), netuid1)); #[extrinsic_call] @@ -1050,11 +1024,11 @@ mod pallet_benchmarks { let mut hashes: Vec = Vec::new(); Subtensor::::init_new_network(netuid, 1); - Subtensor::::set_network_pow_registration_allowed(netuid, true); + Subtensor::::set_network_registration_allowed(netuid, true); SubtokenEnabled::::insert(netuid, true); - let reg_fee = Subtensor::::get_burn(netuid); - Subtensor::::add_balance_to_coldkey_account(&hotkey, reg_fee.to_u64().saturating_mul(2)); + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &hotkey); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(hotkey.clone()).into(), @@ -1093,8 +1067,11 @@ mod pallet_benchmarks { SubtokenEnabled::::insert(netuid, true); Subtensor::::set_commit_reveal_weights_enabled(netuid, false); - let reg_fee = Subtensor::::get_burn(netuid); - Subtensor::::add_balance_to_coldkey_account(&hotkey, reg_fee.to_u64().saturating_mul(2)); + // Avoid any weights set rate-limit edge cases during benchmark setup. + Subtensor::::set_weights_set_rate_limit(netuid, 0); + + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &hotkey); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(hotkey.clone()).into(), @@ -1102,6 +1079,9 @@ mod pallet_benchmarks { hotkey.clone() )); + // Batch set weights generally requires validator permit. + Subtensor::::set_validator_permit_for_uid(netuid, 0, true); + #[extrinsic_call] _( RawOrigin::Signed(hotkey.clone()), @@ -1173,9 +1153,8 @@ mod pallet_benchmarks { Subtensor::::set_network_registration_allowed(netuid, true); SubtokenEnabled::::insert(netuid, true); - let reg_fee = Subtensor::::get_burn(netuid); - let deposit = reg_fee.saturating_mul(2.into()); - Subtensor::::add_balance_to_coldkey_account(&caller, deposit.into()); + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &caller); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(caller.clone()).into(), @@ -1212,9 +1191,12 @@ mod pallet_benchmarks { Subtensor::::create_account_if_non_existent(&coldkey, &hotkey); Subtensor::::init_new_network(1.into(), 1); + Subtensor::::set_network_registration_allowed(1.into(), true); + SubtokenEnabled::::insert(NetUid::from(1), true); + Subtensor::::set_burn(1.into(), 1.into()); + let deposit: u64 = 1_000_000_000u64.saturating_mul(2); Subtensor::::add_balance_to_coldkey_account(&coldkey, deposit); - SubtokenEnabled::::insert(NetUid::from(1), true); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), @@ -1225,13 +1207,13 @@ mod pallet_benchmarks { #[extrinsic_call] _( RawOrigin::Signed(coldkey.clone()), - name.clone(), - url.clone(), - repo.clone(), - img.clone(), - disc.clone(), - descr.clone(), - add.clone(), + name, + url, + repo, + img, + disc, + descr, + add, ); } @@ -1255,14 +1237,14 @@ mod pallet_benchmarks { _( RawOrigin::Signed(coldkey.clone()), netuid, - name.clone(), - repo.clone(), - contact.clone(), - url.clone(), - disc.clone(), - descr.clone(), - logo_url.clone(), - add.clone(), + name, + repo, + contact, + url, + disc, + descr, + logo_url, + add, ); } @@ -1276,12 +1258,7 @@ mod pallet_benchmarks { Subtensor::::add_balance_to_coldkey_account(&coldkey, cost.into()); #[extrinsic_call] - _( - RawOrigin::Signed(coldkey.clone()), - old.clone(), - new.clone(), - None, - ); + _(RawOrigin::Signed(coldkey.clone()), old, new, None); } #[benchmark] @@ -1290,7 +1267,7 @@ mod pallet_benchmarks { let hot: T::AccountId = account("A", 0, 1); #[extrinsic_call] - _(RawOrigin::Signed(coldkey.clone()), hot.clone()); + _(RawOrigin::Signed(coldkey.clone()), hot); } #[benchmark] @@ -1300,7 +1277,7 @@ mod pallet_benchmarks { Subtensor::::create_account_if_non_existent(&coldkey, &hotkey); #[extrinsic_call] - _(RawOrigin::Signed(coldkey.clone()), hotkey.clone()); + _(RawOrigin::Signed(coldkey.clone()), hotkey); } #[benchmark] @@ -1325,7 +1302,7 @@ mod pallet_benchmarks { Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), 1000000u32.into()); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -1341,7 +1318,6 @@ mod pallet_benchmarks { staked_amt.into() )); - // Remove stake limit for benchmark StakingOperationRateLimiter::::remove((hotkey.clone(), coldkey.clone(), netuid)); #[extrinsic_call] @@ -1354,7 +1330,6 @@ mod pallet_benchmarks { let tempo: u16 = 1; let seed: u32 = 1; - // Set our total stake to 1000 TAO Subtensor::::increase_total_stake(1_000_000_000_000.into()); Subtensor::::init_new_network(netuid, tempo); @@ -1377,7 +1352,7 @@ mod pallet_benchmarks { let wallet_bal = 1000000u32.into(); Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), wallet_bal); - assert_ok!(Subtensor::::do_burned_registration( + assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone() @@ -1434,13 +1409,10 @@ mod pallet_benchmarks { }, ); - // Set the block to the end of the crowdloan frame_system::Pallet::::set_block_number(end); - // Simulate deposit pallet_crowdloan::Contributions::::insert(crowdloan_id, &beneficiary, deposit); - // Simulate k - 1 contributions, the deposit is already taken into account let contributors = k - 1; let amount = (cap - deposit) / contributors as u64; for i in 0..contributors { @@ -1448,7 +1420,6 @@ mod pallet_benchmarks { pallet_crowdloan::Contributions::::insert(crowdloan_id, contributor, amount); } - // Mark the crowdloan as finalizing pallet_crowdloan::CurrentCrowdloanId::::set(Some(0)); let emissions_share = Percent::from_percent(30); @@ -1459,24 +1430,21 @@ mod pallet_benchmarks { None, ); - // Ensure the lease was created let lease_id = 0; let lease = SubnetLeases::::get(lease_id).unwrap(); assert_eq!(lease.beneficiary, beneficiary); assert_eq!(lease.emissions_share, emissions_share); assert_eq!(lease.end_block, None); - // Ensure the subnet exists assert!(SubnetMechanism::::contains_key(lease.netuid)); } #[benchmark(extra)] fn terminate_lease(k: Linear<2, { T::MaxContributors::get() }>) { - // Setup a crowdloan let crowdloan_id = 0; let beneficiary: T::AccountId = whitelisted_caller(); let deposit = 20_000_000_000; // 20 TAO - let now = frame_system::Pallet::::block_number(); // not really important here + let now = frame_system::Pallet::::block_number(); let crowdloan_end = now + T::MaximumBlockDuration::get(); let cap = 2_000_000_000_000; // 2000 TAO @@ -1500,13 +1468,10 @@ mod pallet_benchmarks { }, ); - // Set the block to the end of the crowdloan frame_system::Pallet::::set_block_number(crowdloan_end); - // Simulate deposit pallet_crowdloan::Contributions::::insert(crowdloan_id, &beneficiary, deposit); - // Simulate k - 1 contributions, the deposit is already taken into account let contributors = k - 1; let amount = (cap - deposit) / contributors as u64; for i in 0..contributors { @@ -1514,10 +1479,8 @@ mod pallet_benchmarks { pallet_crowdloan::Contributions::::insert(crowdloan_id, contributor, amount); } - // Mark the crowdloan as finalizing pallet_crowdloan::CurrentCrowdloanId::::set(Some(0)); - // Register the leased network let emissions_share = Percent::from_percent(30); let lease_end = crowdloan_end + 1000u32.into(); assert_ok!(Subtensor::::register_leased_network( @@ -1526,13 +1489,13 @@ mod pallet_benchmarks { Some(lease_end), )); - // Set the block to the end of the lease frame_system::Pallet::::set_block_number(lease_end); let lease_id = 0; let lease = SubnetLeases::::get(0).unwrap(); let hotkey = account::("beneficiary_hotkey", 0, 0); Subtensor::::create_account_if_non_existent(&beneficiary, &hotkey); + #[extrinsic_call] _( RawOrigin::Signed(beneficiary.clone()), @@ -1540,11 +1503,9 @@ mod pallet_benchmarks { hotkey.clone(), ); - // Ensure the beneficiary is now the owner of the subnet assert_eq!(SubnetOwner::::get(lease.netuid), beneficiary); assert_eq!(SubnetOwnerHotkey::::get(lease.netuid), hotkey); - // Ensure everything has been cleaned up assert_eq!(SubnetLeases::::get(lease_id), None); assert!(!SubnetLeaseShares::::contains_prefix(lease_id)); assert!(!AccumulatedLeaseDividends::::contains_key(lease_id)); @@ -1575,14 +1536,11 @@ mod pallet_benchmarks { let round: u64 = 0; Subtensor::::init_new_network(netuid, 1); - Subtensor::::set_network_pow_registration_allowed(netuid, true); + Subtensor::::set_network_registration_allowed(netuid, true); SubtokenEnabled::::insert(netuid, true); - let reg_fee = Subtensor::::get_burn(netuid); - Subtensor::::add_balance_to_coldkey_account( - &hotkey, - reg_fee.saturating_mul(2.into()).into(), - ); + Subtensor::::set_burn(netuid, 1.into()); + fund_for_registration::(netuid, &hotkey); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(hotkey.clone()).into(), @@ -1590,6 +1548,9 @@ mod pallet_benchmarks { hotkey.clone() )); + // Ensure caller is allowed to commit (common requirement for weights ops). + Subtensor::::set_validator_permit_for_uid(netuid, 0, true); + Subtensor::::set_commit_reveal_weights_enabled(netuid, true); #[extrinsic_call] @@ -1607,10 +1568,12 @@ mod pallet_benchmarks { let coldkey: T::AccountId = whitelisted_caller(); let netuid = NetUid::from(1); let hotkey: T::AccountId = account("A", 0, 1); + SubtokenEnabled::::insert(netuid, true); Subtensor::::init_new_network(netuid, 1); - let amount = 900_000_000_000; + Subtensor::::set_network_registration_allowed(netuid, true); + let amount = 900_000_000_000; Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), amount); assert_ok!(Subtensor::::burned_register( @@ -1622,6 +1585,7 @@ mod pallet_benchmarks { #[extrinsic_call] _(RawOrigin::Signed(coldkey.clone()), netuid, hotkey.clone()); } + #[benchmark] fn set_root_claim_type() { let coldkey: T::AccountId = whitelisted_caller(); @@ -1646,13 +1610,15 @@ mod pallet_benchmarks { )); SubtokenEnabled::::insert(netuid, true); - Subtensor::::set_network_pow_registration_allowed(netuid, true); + + Subtensor::::set_network_registration_allowed(netuid, true); + NetworkRegistrationAllowed::::insert(netuid, true); FirstEmissionBlockNumber::::insert(netuid, 0); SubnetMechanism::::insert(netuid, 1); SubnetworkN::::insert(netuid, 1); - Subtensor::::set_tao_weight(u64::MAX); // Set TAO weight to 1.0 + Subtensor::::set_tao_weight(u64::MAX); let root_stake = 100_000_000u64; Subtensor::::increase_stake_for_hotkey_and_coldkey_on_subnet( @@ -1685,12 +1651,11 @@ mod pallet_benchmarks { assert_ok!(Subtensor::::set_root_claim_type( RawOrigin::Signed(coldkey.clone()).into(), RootClaimTypeEnum::Keep - ),); + )); #[extrinsic_call] _(RawOrigin::Signed(coldkey.clone()), BTreeSet::from([netuid])); - // Verification let new_stake = Subtensor::::get_stake_for_hotkey_and_coldkey_on_subnet(&hotkey, &coldkey, netuid); diff --git a/pallets/subtensor/src/coinbase/block_step.rs b/pallets/subtensor/src/coinbase/block_step.rs index 6081edad19..27038bda39 100644 --- a/pallets/subtensor/src/coinbase/block_step.rs +++ b/pallets/subtensor/src/coinbase/block_step.rs @@ -9,8 +9,9 @@ impl Pallet { let block_number: u64 = Self::get_current_block_as_u64(); let last_block_hash: T::Hash = >::parent_hash(); - // --- 1. Adjust difficulties. - Self::adjust_registration_terms_for_networks(); + // --- 1. Update registration burn prices. + Self::update_registration_prices_for_networks(); + // --- 2. Get the current coinbase emission. let block_emission: U96F32 = U96F32::saturating_from_num( Self::get_block_emission() @@ -47,225 +48,72 @@ impl Pallet { } } - /// Adjusts the network difficulties/burns of every active network. Resetting state parameters. + /// Updates burn price and resets per-block counters. /// - pub fn adjust_registration_terms_for_networks() { - log::debug!("adjust_registration_terms_for_networks"); + /// Behavior: + /// - Every BurnHalfLife blocks: burn is halved and RegistrationsThisInterval is reset. + /// - Each block: if there were registrations in the previous block, burn is multiplied by BurnIncreaseMult^regs_prev. + /// - Each block: RegistrationsThisBlock is reset to 0 (for the new block). + pub fn update_registration_prices_for_networks() { + let current_block: u64 = Self::get_current_block_as_u64(); - // --- 1. Iterate through each network. for (netuid, _) in NetworksAdded::::iter() { - // --- 2. Pull counters for network difficulty. - let last_adjustment_block: u64 = Self::get_last_adjustment_block(netuid); - let adjustment_interval: u16 = Self::get_adjustment_interval(netuid); - let current_block: u64 = Self::get_current_block_as_u64(); - log::debug!( - "netuid: {netuid:?} last_adjustment_block: {last_adjustment_block:?} adjustment_interval: {adjustment_interval:?} current_block: {current_block:?}" - ); + // --- 1) Apply halving + interval reset when BurnHalfLife elapses. + let half_life: u16 = BurnHalfLife::::get(netuid); + if half_life > 0 { + let last_halving: u64 = BurnLastHalvingBlock::::get(netuid); + let delta: u64 = current_block.saturating_sub(last_halving); + + let intervals_passed: u64 = delta / half_life as u64; + if intervals_passed > 0 { + // burn halves once per interval passed: burn /= 2^intervals_passed + let burn_u64: u64 = Self::get_burn(netuid).into(); + let shift: u32 = core::cmp::min(intervals_passed, 64) as u32; + let mut new_burn_u64: u64 = if shift >= 64 { 0 } else { burn_u64 >> shift }; + + // Prevent stuck-at-zero behavior. + if new_burn_u64 == 0 { + new_burn_u64 = 1; + } - // --- 3. Check if we are at the adjustment interval for this network. - // If so, we need to adjust the registration difficulty based on target and actual registrations. - if current_block.saturating_sub(last_adjustment_block) >= adjustment_interval as u64 { - log::debug!("interval reached."); + Self::set_burn(netuid, TaoCurrency::from(new_burn_u64)); - // --- 4. Get the current counters for this network w.r.t burn and difficulty values. - let current_burn = Self::get_burn(netuid); - let current_difficulty: u64 = Self::get_difficulty_as_u64(netuid); - let registrations_this_interval: u16 = - Self::get_registrations_this_interval(netuid); - let pow_registrations_this_interval: u16 = - Self::get_pow_registrations_this_interval(netuid); - let burn_registrations_this_interval: u16 = - Self::get_burn_registrations_this_interval(netuid); - let target_registrations_this_interval: u16 = - Self::get_target_registrations_per_interval(netuid); - // --- 5. Adjust burn + pow - // There are six cases to consider. A, B, C, D, E, F - if registrations_this_interval > target_registrations_this_interval { - #[allow(clippy::comparison_chain)] - if pow_registrations_this_interval > burn_registrations_this_interval { - // A. There are too many registrations this interval and most of them are pow registrations - // this triggers an increase in the pow difficulty. - // pow_difficulty ++ - Self::set_difficulty( - netuid, - Self::upgraded_difficulty( - netuid, - current_difficulty, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - } else if pow_registrations_this_interval < burn_registrations_this_interval { - // B. There are too many registrations this interval and most of them are burn registrations - // this triggers an increase in the burn cost. - // burn_cost ++ - Self::set_burn( - netuid, - Self::upgraded_burn( - netuid, - current_burn, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - } else { - // F. There are too many registrations this interval and the pow and burn registrations are equal - // this triggers an increase in the burn cost and pow difficulty - // burn_cost ++ - Self::set_burn( - netuid, - Self::upgraded_burn( - netuid, - current_burn, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - // pow_difficulty ++ - Self::set_difficulty( - netuid, - Self::upgraded_difficulty( - netuid, - current_difficulty, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - } - } else { - // Not enough registrations this interval. - #[allow(clippy::comparison_chain)] - if pow_registrations_this_interval > burn_registrations_this_interval { - // C. There are not enough registrations this interval and most of them are pow registrations - // this triggers a decrease in the burn cost - // burn_cost -- - Self::set_burn( - netuid, - Self::upgraded_burn( - netuid, - current_burn, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - } else if pow_registrations_this_interval < burn_registrations_this_interval { - // D. There are not enough registrations this interval and most of them are burn registrations - // this triggers a decrease in the pow difficulty - // pow_difficulty -- - Self::set_difficulty( - netuid, - Self::upgraded_difficulty( - netuid, - current_difficulty, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - } else { - // E. There are not enough registrations this interval and the pow and burn registrations are equal - // this triggers a decrease in the burn cost and pow difficulty - // burn_cost -- - Self::set_burn( - netuid, - Self::upgraded_burn( - netuid, - current_burn, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - // pow_difficulty -- - Self::set_difficulty( - netuid, - Self::upgraded_difficulty( - netuid, - current_difficulty, - registrations_this_interval, - target_registrations_this_interval, - ), - ); - } - } + // Advance the halving anchor forward by whole intervals. + BurnLastHalvingBlock::::insert( + netuid, + last_halving + .saturating_add(intervals_passed.saturating_mul(half_life as u64)), + ); - // --- 6. Drain all counters for this network for this interval. - Self::set_last_adjustment_block(netuid, current_block); - Self::set_registrations_this_interval(netuid, 0); - Self::set_pow_registrations_this_interval(netuid, 0); - Self::set_burn_registrations_this_interval(netuid, 0); - } else { - log::debug!("interval not reached."); + // Reset interval counter (MaxRegistrationsPerInterval = 1 per half-life interval). + RegistrationsThisInterval::::insert(netuid, 0); + } } - // --- 7. Drain block registrations for each network. Needed for registration rate limits. - Self::set_registrations_this_block(netuid, 0); - } - } + // --- 2) Apply post-registration bump. + // + // At the start of block N, RegistrationsThisBlock contains the count from block N-1. + // We skip bumping on root because root_register does not use burn-based pricing. + if !netuid.is_root() { + let regs_prev_block: u16 = RegistrationsThisBlock::::get(netuid); + if regs_prev_block > 0 { + let mult: u64 = BurnIncreaseMult::::get(netuid).max(1); + let bump: u64 = Self::saturating_pow_u64(mult, regs_prev_block); + + let burn_u64: u64 = Self::get_burn(netuid).into(); + let mut new_burn_u64: u64 = burn_u64.saturating_mul(bump); + + // Prevent stuck-at-zero behavior. + if new_burn_u64 == 0 { + new_burn_u64 = 1; + } - /// Calculates the upgraded difficulty by multiplying the current difficulty by the ratio ( reg_actual + reg_target / reg_target + reg_target ) - /// We use U110F18 to avoid any overflows on u64. Also min_difficulty and max_difficulty bound the range. - /// - pub fn upgraded_difficulty( - netuid: NetUid, - current_difficulty: u64, - registrations_this_interval: u16, - target_registrations_per_interval: u16, - ) -> u64 { - let updated_difficulty: U110F18 = U110F18::saturating_from_num(current_difficulty) - .saturating_mul(U110F18::saturating_from_num( - registrations_this_interval.saturating_add(target_registrations_per_interval), - )) - .safe_div(U110F18::saturating_from_num( - target_registrations_per_interval.saturating_add(target_registrations_per_interval), - )); - let alpha: U110F18 = U110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) - .safe_div(U110F18::saturating_from_num(u64::MAX)); - let next_value: U110F18 = alpha - .saturating_mul(U110F18::saturating_from_num(current_difficulty)) - .saturating_add( - U110F18::saturating_from_num(1.0) - .saturating_sub(alpha) - .saturating_mul(updated_difficulty), - ); - if next_value >= U110F18::saturating_from_num(Self::get_max_difficulty(netuid)) { - Self::get_max_difficulty(netuid) - } else if next_value <= U110F18::saturating_from_num(Self::get_min_difficulty(netuid)) { - Self::get_min_difficulty(netuid) - } else { - next_value.saturating_to_num::() - } - } + Self::set_burn(netuid, TaoCurrency::from(new_burn_u64)); + } + } - /// Calculates the upgraded burn by multiplying the current burn by the ratio ( reg_actual + reg_target / reg_target + reg_target ) - /// We use U110F18 to avoid any overflows on u64. Also min_burn and max_burn bound the range. - /// - pub fn upgraded_burn( - netuid: NetUid, - current_burn: TaoCurrency, - registrations_this_interval: u16, - target_registrations_per_interval: u16, - ) -> TaoCurrency { - let updated_burn: U110F18 = U110F18::saturating_from_num(current_burn) - .saturating_mul(U110F18::saturating_from_num( - registrations_this_interval.saturating_add(target_registrations_per_interval), - )) - .safe_div(U110F18::saturating_from_num( - target_registrations_per_interval.saturating_add(target_registrations_per_interval), - )); - let alpha: U110F18 = U110F18::saturating_from_num(Self::get_adjustment_alpha(netuid)) - .safe_div(U110F18::saturating_from_num(u64::MAX)); - let next_value: U110F18 = alpha - .saturating_mul(U110F18::saturating_from_num(current_burn)) - .saturating_add( - U110F18::saturating_from_num(1.0) - .saturating_sub(alpha) - .saturating_mul(updated_burn), - ); - if next_value >= U110F18::saturating_from_num(Self::get_max_burn(netuid)) { - Self::get_max_burn(netuid) - } else if next_value <= U110F18::saturating_from_num(Self::get_min_burn(netuid)) { - Self::get_min_burn(netuid) - } else { - next_value.saturating_to_num::().into() + // --- 3) Reset per-block registrations counter for the new block. + Self::set_registrations_this_block(netuid, 0); } } diff --git a/pallets/subtensor/src/coinbase/root.rs b/pallets/subtensor/src/coinbase/root.rs index 83567b6f57..0ce6502134 100644 --- a/pallets/subtensor/src/coinbase/root.rs +++ b/pallets/subtensor/src/coinbase/root.rs @@ -330,7 +330,6 @@ impl Pallet { AlphaSigmoidSteepness::::remove(netuid); MaxAllowedValidators::::remove(netuid); - AdjustmentInterval::::remove(netuid); BondsMovingAverage::::remove(netuid); BondsPenalty::::remove(netuid); BondsResetOn::::remove(netuid); @@ -338,9 +337,12 @@ impl Pallet { ValidatorPruneLen::::remove(netuid); ScalingLawPower::::remove(netuid); TargetRegistrationsPerInterval::::remove(netuid); - AdjustmentAlpha::::remove(netuid); CommitRevealWeightsEnabled::::remove(netuid); + BurnHalfLife::::remove(netuid); + BurnIncreaseMult::::remove(netuid); + BurnLastHalvingBlock::::remove(netuid); + Burn::::remove(netuid); MinBurn::::remove(netuid); MaxBurn::::remove(netuid); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index c956c517de..4590d75379 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -344,6 +344,24 @@ pub mod pallet { }, } + /// Default burn half-life (in blocks) for subnet registration price decay. + #[pallet::type_value] + pub fn DefaultBurnHalfLife() -> u16 { + 360 + } + + /// Default multiplier applied to the burn price after a successful registration. + #[pallet::type_value] + pub fn DefaultBurnIncreaseMult() -> u64 { + 2 + } + + /// Default block number used as the initial burn halving anchor. + #[pallet::type_value] + pub fn DefaultBurnLastHalvingBlock() -> u64 { + 0 + } + /// Default minimum root claim amount. /// This is the minimum amount of root claim that can be made. /// Any amount less than this will not be claimed. @@ -2395,6 +2413,21 @@ pub mod pallet { pub type MechanismEmissionSplit = StorageMap<_, Twox64Concat, NetUid, Vec, OptionQuery>; + /// --- MAP ( netuid ) --> BurnHalfLife (blocks) + #[pallet::storage] + pub type BurnHalfLife = + StorageMap<_, Identity, NetUid, u16, ValueQuery, DefaultBurnHalfLife>; + + /// --- MAP ( netuid ) --> BurnIncreaseMult + #[pallet::storage] + pub type BurnIncreaseMult = + StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultBurnIncreaseMult>; + + /// --- MAP ( netuid ) --> last block at which we applied halving + interval reset + #[pallet::storage] + pub type BurnLastHalvingBlock = + StorageMap<_, Identity, NetUid, u64, ValueQuery, DefaultBurnLastHalvingBlock>; + /// ================== /// ==== Genesis ===== /// ================== diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 93de861a58..febaaf0302 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1020,13 +1020,13 @@ mod dispatches { pub fn register( origin: OriginFor, netuid: NetUid, - block_number: u64, - nonce: u64, - work: Vec, + _block_number: u64, + _nonce: u64, + _work: Vec, hotkey: T::AccountId, - coldkey: T::AccountId, + _coldkey: T::AccountId, ) -> DispatchResult { - Self::do_registration(origin, netuid, block_number, nonce, work, hotkey, coldkey) + Self::do_register(origin, netuid, hotkey) } /// Register the hotkey to root network @@ -1048,7 +1048,7 @@ mod dispatches { netuid: NetUid, hotkey: T::AccountId, ) -> DispatchResult { - Self::do_burned_registration(origin, netuid, hotkey) + Self::do_register(origin, netuid, hotkey) } /// The extrinsic for user to change its hotkey in subnet or all subnets. diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 107bbf975c..921e7a9ea4 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -516,5 +516,10 @@ mod events { /// The amount of alpha distributed alpha: AlphaCurrency, }, + /// Burn Half Life Set for Neuron Registration. + BurnHalfLifeSet(NetUid, u16), + + /// Burn Increase Multiplier Set for Neuron Registration. + BurnIncreaseMultSet(NetUid, u64), } } diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 899e8d32f2..135ea97c6b 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -166,7 +166,9 @@ mod hooks { // Fix staking hot keys .saturating_add(migrations::migrate_fix_staking_hot_keys::migrate_fix_staking_hot_keys::()) // Migrate coldkey swap scheduled to announcements - .saturating_add(migrations::migrate_coldkey_swap_scheduled_to_announcements::migrate_coldkey_swap_scheduled_to_announcements::()); + .saturating_add(migrations::migrate_coldkey_swap_scheduled_to_announcements::migrate_coldkey_swap_scheduled_to_announcements::()) + // Migration for new Neuron Registration + .saturating_add(migrations::migrate_clear_deprecated_registration_maps::migrate_clear_deprecated_registration_maps::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_clear_deprecated_registration_maps.rs b/pallets/subtensor/src/migrations/migrate_clear_deprecated_registration_maps.rs new file mode 100644 index 0000000000..ea9705c2bb --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_clear_deprecated_registration_maps.rs @@ -0,0 +1,160 @@ +use super::*; +use crate::AccountIdOf; +use frame_support::{ + IterableStorageMap, + pallet_prelude::{Blake2_128Concat, OptionQuery}, + storage_alias, + traits::Get, + weights::Weight, +}; +use scale_info::prelude::string::String; + +/// Clears deprecated registration-related storage items after moving to the new +/// BurnHalfLife/BurnIncreaseMult model. +/// +/// This migration is **idempotent** via `HasMigrationRun`. +/// +/// Why it “takes into account root_register”: +/// - `root_register()` still relies on `RegistrationsThisInterval`/`RegistrationsThisBlock`. +/// - We **do not reset** those counters here. +/// - We set `BurnLastHalvingBlock(NetUid::ROOT)` to “now” so your new per-interval reset logic +/// does **not** retroactively wipe root’s interval counter (which could temporarily allow extra +/// root registrations). +/// - We migrate the old `AdjustmentInterval` → `BurnHalfLife` for **all** networks (including ROOT), +/// preserving the prior interval length semantics. +/// +/// Deprecated maps cleared: +/// - PoW path: `UsedWork`, `Difficulty`, `MinDifficulty`, `MaxDifficulty`, `NetworkPowRegistrationAllowed` +/// - Old reg accounting: `POWRegistrationsThisInterval`, `BurnRegistrationsThisInterval` +/// - Old adjustment system: `AdjustmentAlpha`, `AdjustmentInterval`, `LastAdjustmentBlock` +pub fn migrate_clear_deprecated_registration_maps() -> Weight { + const RAO_PER_TAO: u64 = 1_000_000_000; + const ONE_TAO_RAO: u64 = 1 * RAO_PER_TAO; + const DEFAULT_BURN_INCREASE_MULT: u64 = 2; + + let migration_name = b"migrate_clear_deprecated_registration_maps_v1".to_vec(); + let mut weight: Weight = T::DbWeight::get().reads(1); + + // --- 0) Skip if already executed + if HasMigrationRun::::get(&migration_name) { + log::info!( + target: "runtime", + "Migration '{}' already run - skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + // Use the current block, but ensure it’s non-zero + let current_block = Pallet::::get_current_block_as_u64(); + let block_to_set = if current_block == 0 { 1 } else { current_block }; + + // --- 1) Initialize new pricing params for *all* networks (including ROOT) + // - BurnHalfLife replaces AdjustmentInterval; migrate old value. + // - BurnIncreaseMult defaults to 2. + // - BurnLastHalvingBlock set to "now" to prevent retroactive halving/interval resets. + // + // We do NOT touch RegistrationsThisInterval/RegistrationsThisBlock here. + let mut networks_seen: u64 = 0; + + for (netuid, added) in NetworksAdded::::iter() { + if !added { + continue; + } + networks_seen = networks_seen.saturating_add(1); + + // 1.a) Migrate old AdjustmentInterval -> BurnHalfLife (guard against 0). + let old_interval: u16 = AdjustmentInterval::::get(netuid); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + + let new_half_life: u16 = old_interval.max(1); + BurnHalfLife::::insert(netuid, new_half_life); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + // 1.b) Set BurnIncreaseMult default. + BurnIncreaseMult::::insert(netuid, DEFAULT_BURN_INCREASE_MULT.max(1)); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + // 1.c) Start halving schedule "now". + BurnLastHalvingBlock::::insert(netuid, block_to_set); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + // 1.d) Ensure burn is non-zero on non-root nets so multiplier logic works. + if netuid != NetUid::ROOT { + let burn_u64: u64 = Pallet::::get_burn(netuid).into(); + weight = weight.saturating_add(T::DbWeight::get().reads(1)); + + if burn_u64 == 0 { + Pallet::::set_burn(netuid, TaoCurrency::from(ONE_TAO_RAO)); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + } + } + } + + // Account for the NetworksAdded iteration itself. + weight = weight.saturating_add(T::DbWeight::get().reads(networks_seen)); + + // --- 2) Clear deprecated/unused maps + + macro_rules! clear_map_and_log { + ($map:ident, $label:expr) => {{ + let res = $map::::clear(u32::MAX, None); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + if res.maybe_cursor.is_some() { + log::warn!( + target: "runtime", + "Migration '{}' - '{}' not fully cleared (cursor present).", + String::from_utf8_lossy(&migration_name), + $label + ); + } else { + log::info!( + target: "runtime", + "Migration '{}' - cleared '{}'.", + String::from_utf8_lossy(&migration_name), + $label + ); + } + }}; + } + + // PoW path (deprecated) + clear_map_and_log!(UsedWork, "UsedWork"); + clear_map_and_log!(Difficulty, "Difficulty"); + clear_map_and_log!(MinDifficulty, "MinDifficulty"); + clear_map_and_log!(MaxDifficulty, "MaxDifficulty"); + clear_map_and_log!( + NetworkPowRegistrationAllowed, + "NetworkPowRegistrationAllowed" + ); + + // Old per-interval tracking (deprecated) + clear_map_and_log!(POWRegistrationsThisInterval, "POWRegistrationsThisInterval"); + clear_map_and_log!( + BurnRegistrationsThisInterval, + "BurnRegistrationsThisInterval" + ); + + // Old adjustment mechanism (deprecated) + clear_map_and_log!(AdjustmentAlpha, "AdjustmentAlpha"); + clear_map_and_log!(AdjustmentInterval, "AdjustmentInterval"); + clear_map_and_log!(LastAdjustmentBlock, "LastAdjustmentBlock"); + + // Burn bounds (deprecated, NOT part of new spec) + clear_map_and_log!(MinBurn, "MinBurn"); + clear_map_and_log!(MaxBurn, "MaxBurn"); + + // --- 3) Mark migration done + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().writes(1)); + + log::info!( + target: "runtime", + "Migration '{}' completed at block {}. Initialized BurnHalfLife/BurnIncreaseMult/BurnLastHalvingBlock for {} networks and cleared deprecated maps (root_register preserved).", + String::from_utf8_lossy(&migration_name), + block_to_set, + networks_seen + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 23a2899b94..9af1945c69 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -5,6 +5,7 @@ use sp_io::KillStorageResult; use sp_io::hashing::twox_128; use sp_io::storage::clear_prefix; pub mod migrate_auto_stake_destination; +pub mod migrate_clear_deprecated_registration_maps; pub mod migrate_clear_rank_trust_pruning_maps; pub mod migrate_coldkey_swap_scheduled; pub mod migrate_coldkey_swap_scheduled_to_announcements; diff --git a/pallets/subtensor/src/subnets/registration.rs b/pallets/subtensor/src/subnets/registration.rs index a7771857bb..5cd0cd4733 100644 --- a/pallets/subtensor/src/subnets/registration.rs +++ b/pallets/subtensor/src/subnets/registration.rs @@ -34,98 +34,69 @@ impl Pallet { } } - /// ---- The implementation for the extrinsic do_burned_registration: registering by burning TAO. - /// - /// # Args: - /// * 'origin': (RuntimeOrigin): - /// - The signature of the calling coldkey. - /// Burned registers can only be created by the coldkey. - /// - /// * 'netuid' (u16): - /// - The u16 network identifier. - /// - /// * 'hotkey' ( T::AccountId ): - /// - Hotkey to be registered to the network. - /// - /// # Event: - /// * NeuronRegistered; - /// - On successfully registereing a uid to a neuron slot on a subnetwork. - /// - /// # Raises: - /// * 'MechanismDoesNotExist': - /// - Attempting to registed to a non existent network. - /// - /// * 'TooManyRegistrationsThisBlock': - /// - This registration exceeds the total allowed on this network this block. - /// - /// * 'HotKeyAlreadyRegisteredInSubNet': - /// - The hotkey is already registered on this network. - /// - pub fn do_burned_registration( + pub fn do_register( origin: T::RuntimeOrigin, netuid: NetUid, hotkey: T::AccountId, ) -> DispatchResult { - // --- 1. Check that the caller has signed the transaction. (the coldkey of the pairing) + // 1) coldkey pays let coldkey = ensure_signed(origin)?; - log::debug!("do_registration( coldkey:{coldkey:?} netuid:{netuid:?} hotkey:{hotkey:?} )"); + log::debug!("do_register( coldkey:{coldkey:?} netuid:{netuid:?} hotkey:{hotkey:?} )"); - // --- 2. Ensure the passed network is valid. + // 2) network validity ensure!( !netuid.is_root(), Error::::RegistrationNotPermittedOnRootSubnet ); ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); - // --- 3. Ensure the passed network allows registrations. + // 3) registrations allowed ensure!( Self::get_network_registration_allowed(netuid), Error::::SubNetRegistrationDisabled ); - // --- 4. Ensure we are not exceeding the max allowed registrations per block. + // 4) per-block cap ensure!( Self::get_registrations_this_block(netuid) < Self::get_max_registrations_per_block(netuid), Error::::TooManyRegistrationsThisBlock ); - // --- 5. Ensure we are not exceeding the max allowed registrations per interval. + // 5) per-interval cap: MaxRegistrationsPerInterval == 1 + // Interval length is BurnHalfLife (we reset RegistrationsThisInterval when halving happens). ensure!( - Self::get_registrations_this_interval(netuid) - < Self::get_target_registrations_per_interval(netuid).saturating_mul(3), + Self::get_registrations_this_interval(netuid) < 1, Error::::TooManyRegistrationsThisInterval ); - // --- 6. Ensure that the key is not already registered. + // 6) hotkey not already registered ensure!( !Uids::::contains_key(netuid, &hotkey), Error::::HotKeyAlreadyRegisteredInSubNet ); - // --- 7. Ensure the callers coldkey has enough stake to perform the transaction. - let registration_cost = Self::get_burn(netuid); + // 7) compute current burn price (already updated in on_initialize for this block) + let registration_cost: TaoCurrency = Self::get_burn(netuid); + ensure!( Self::can_remove_balance_from_coldkey_account(&coldkey, registration_cost.into()), Error::::NotEnoughBalanceToStake ); - // If the network account does not exist we will create it here. + // 8) ensure pairing exists and is correct Self::create_account_if_non_existent(&coldkey, &hotkey); - - // --- 8. Ensure that the pairing is correct. ensure!( Self::coldkey_owns_hotkey(&coldkey, &hotkey), Error::::NonAssociatedColdKey ); - // --- 9. Possibly there are no neuron slots at all. + // 9) capacity check + prune candidate if full ensure!( Self::get_max_allowed_uids(netuid) != 0, Error::::NoNeuronIdAvailable ); - // --- 10. If replacement is needed, ensure a safe prune candidate exists. let current_n = Self::get_subnetwork_n(netuid); let max_n = Self::get_max_allowed_uids(netuid); if current_n >= max_n { @@ -135,11 +106,10 @@ impl Pallet { ); } - // --- 11. Ensure the remove operation from the coldkey is a success. + // 10) burn payment (same mechanics as old burned_register) let actual_burn_amount = Self::remove_balance_from_coldkey_account(&coldkey, registration_cost.into())?; - // Tokens are swapped and then burned. let burned_alpha = Self::swap_tao_for_alpha( netuid, actual_burn_amount, @@ -147,190 +117,23 @@ impl Pallet { false, )? .amount_paid_out; + SubnetAlphaOut::::mutate(netuid, |total| { *total = total.saturating_sub(burned_alpha.into()) }); - // Actually perform the registration. - let neuron_uid: u16 = Self::register_neuron(netuid, &hotkey)?; - - // --- 12. Record the registration and increment block and interval counters. - BurnRegistrationsThisInterval::::mutate(netuid, |val| val.saturating_inc()); - RegistrationsThisInterval::::mutate(netuid, |val| val.saturating_inc()); - RegistrationsThisBlock::::mutate(netuid, |val| val.saturating_inc()); - Self::increase_rao_recycled(netuid, Self::get_burn(netuid).into()); - - // --- 13. Deposit successful event. - log::debug!("NeuronRegistered( netuid:{netuid:?} uid:{neuron_uid:?} hotkey:{hotkey:?} ) "); - Self::deposit_event(Event::NeuronRegistered(netuid, neuron_uid, hotkey)); - - // --- 14. Ok and done. - Ok(()) - } - - /// ---- The implementation for the extrinsic do_registration. - /// - /// # Args: - /// *'origin': (RuntimeOrigin): - /// - The signature of the calling hotkey. - /// - /// *'netuid' (u16): - /// - The u16 network identifier. - /// - /// *'block_number' ( u64 ): - /// - Block hash used to prove work done. - /// - /// *'nonce' ( u64 ): - /// - Positive integer nonce used in POW. - /// - /// *'work' ( Vec ): - /// - Vector encoded bytes representing work done. - /// - /// *'hotkey' ( T::AccountId ): - /// - Hotkey to be registered to the network. - /// - /// *'coldkey' ( T::AccountId ): - /// - Associated coldkey account. - /// - /// # Event: - /// *NeuronRegistered; - /// - On successfully registereing a uid to a neuron slot on a subnetwork. - /// - /// # Raises: - /// *'MechanismDoesNotExist': - /// - Attempting to registed to a non existent network. - /// - /// *'TooManyRegistrationsThisBlock': - /// - This registration exceeds the total allowed on this network this block. - /// - /// *'HotKeyAlreadyRegisteredInSubNet': - /// - The hotkey is already registered on this network. - /// - /// *'InvalidWorkBlock': - /// - The work has been performed on a stale, future, or non existent block. - /// - /// *'InvalidDifficulty': - /// - The work does not match the difficutly. - /// - /// *'InvalidSeal': - /// - The seal is incorrect. - /// - pub fn do_registration( - origin: T::RuntimeOrigin, - netuid: NetUid, - block_number: u64, - nonce: u64, - work: Vec, - hotkey: T::AccountId, - coldkey: T::AccountId, - ) -> DispatchResult { - // --- 1. Check that the caller has signed the transaction. - let signing_origin = ensure_signed(origin)?; - log::debug!( - "do_registration( origin:{signing_origin:?} netuid:{netuid:?} hotkey:{hotkey:?}, coldkey:{coldkey:?} )" - ); - - ensure!( - signing_origin == hotkey, - Error::::TransactorAccountShouldBeHotKey - ); - - // --- 2. Ensure the passed network is valid. - ensure!( - !netuid.is_root(), - Error::::RegistrationNotPermittedOnRootSubnet - ); - ensure!(Self::if_subnet_exist(netuid), Error::::SubnetNotExists); - - // --- 3. Ensure the passed network allows registrations. - ensure!( - Self::get_network_pow_registration_allowed(netuid), - Error::::SubNetRegistrationDisabled - ); - - // --- 4. Ensure we are not exceeding the max allowed registrations per block. - ensure!( - Self::get_registrations_this_block(netuid) - < Self::get_max_registrations_per_block(netuid), - Error::::TooManyRegistrationsThisBlock - ); - - // --- 5. Ensure we are not exceeding the max allowed registrations per interval. - ensure!( - Self::get_registrations_this_interval(netuid) - < Self::get_target_registrations_per_interval(netuid).saturating_mul(3), - Error::::TooManyRegistrationsThisInterval - ); - - // --- 6. Ensure that the key is not already registered. - ensure!( - !Uids::::contains_key(netuid, &hotkey), - Error::::HotKeyAlreadyRegisteredInSubNet - ); - - // --- 7. Ensure the passed block number is valid, not in the future or too old. - // Work must have been done within 3 blocks (stops long range attacks). - let current_block_number: u64 = Self::get_current_block_as_u64(); - ensure!( - block_number <= current_block_number, - Error::::InvalidWorkBlock - ); - ensure!( - current_block_number.saturating_sub(block_number) < 3, - Error::::InvalidWorkBlock - ); - - // --- 8. Ensure the supplied work passes the difficulty. - let difficulty: U256 = Self::get_difficulty(netuid); - let work_hash: H256 = Self::vec_to_hash(work.clone()); - ensure!( - Self::hash_meets_difficulty(&work_hash, difficulty), - Error::::InvalidDifficulty - ); // Check that the work meets difficulty. - - // --- 9. Check Work is the product of the nonce, the block number, and hotkey. Add this as used work. - let seal: H256 = Self::create_seal_hash(block_number, nonce, &hotkey); - ensure!(seal == work_hash, Error::::InvalidSeal); - UsedWork::::insert(work.clone(), current_block_number); - - // --- 10. If the network account does not exist we will create it here. - Self::create_account_if_non_existent(&coldkey, &hotkey); - - // --- 11. Ensure that the pairing is correct. - ensure!( - Self::coldkey_owns_hotkey(&coldkey, &hotkey), - Error::::NonAssociatedColdKey - ); - - // --- 12. Possibly there is no neuron slots at all. - ensure!( - Self::get_max_allowed_uids(netuid) != 0, - Error::::NoNeuronIdAvailable - ); - - // --- 13. If replacement is needed, ensure a safe prune candidate exists. - let current_n = Self::get_subnetwork_n(netuid); - let max_n = Self::get_max_allowed_uids(netuid); - if current_n >= max_n { - ensure!( - Self::get_neuron_to_prune(netuid).is_some(), - Error::::NoNeuronIdAvailable - ); - } - - // Actually perform the registration. + // 11) register neuron let neuron_uid: u16 = Self::register_neuron(netuid, &hotkey)?; - // --- 14. Record the registration and increment block and interval counters. - POWRegistrationsThisInterval::::mutate(netuid, |val| val.saturating_inc()); + // 12) counters RegistrationsThisInterval::::mutate(netuid, |val| val.saturating_inc()); RegistrationsThisBlock::::mutate(netuid, |val| val.saturating_inc()); + Self::increase_rao_recycled(netuid, registration_cost.into()); - // --- 15. Deposit successful event. - log::debug!("NeuronRegistered( netuid:{netuid:?} uid:{neuron_uid:?} hotkey:{hotkey:?} ) "); + // 13) event + log::debug!("NeuronRegistered( netuid:{netuid:?} uid:{neuron_uid:?} hotkey:{hotkey:?} )"); Self::deposit_event(Event::NeuronRegistered(netuid, neuron_uid, hotkey)); - // --- 16. Ok and done. Ok(()) } diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index ecb5ce0452..0cfa5e56ba 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -273,13 +273,15 @@ impl Pallet { Self::set_max_allowed_uids(netuid, 256); Self::set_max_allowed_validators(netuid, 64); Self::set_min_allowed_weights(netuid, 1); - Self::set_adjustment_interval(netuid, 360); Self::set_target_registrations_per_interval(netuid, 1); - Self::set_adjustment_alpha(netuid, 17_893_341_751_498_265_066); // 18_446_744_073_709_551_615 * 0.97 = 17_893_341_751_498_265_066 Self::set_immunity_period(netuid, 5000); Self::set_min_difficulty(netuid, u64::MAX); Self::set_max_difficulty(netuid, u64::MAX); + Self::set_burn(netuid, TaoCurrency::from(1_000_000_000)); + let current_block = Self::get_current_block_as_u64(); + BurnLastHalvingBlock::::insert(netuid, current_block); + // Make network parameters explicit. if !Tempo::::contains_key(netuid) { Tempo::::insert(netuid, Tempo::::get(netuid)); diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index f0dfd89bdf..0bafdcc1b0 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -555,6 +555,9 @@ where } } +pub const RAO_PER_TAO: u64 = 1_000_000_000; +pub const DEFAULT_RESERVE: u64 = 1_000_000_000_000; + #[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig)] impl pallet_timestamp::Config for Test { type MinimumPeriod = ConstU64<0>; @@ -750,27 +753,31 @@ pub fn register_ok_neuron( netuid: NetUid, hotkey_account_id: U256, coldkey_account_id: U256, - start_nonce: u64, + _start_nonce: u64, ) { - let block_number: u64 = SubtensorModule::get_current_block_as_u64(); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - start_nonce, - &hotkey_account_id, - ); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + // Ensure reserves exist for swap/burn path. + let reserve: u64 = 1_000_000_000_000; + setup_reserves(netuid, reserve.into(), reserve.into()); + + RegistrationsThisInterval::::insert(netuid, RegistrationsThisInterval::::get(netuid) + 1); + + // Ensure coldkey has enough to pay the current burn. + let burn: TaoCurrency = SubtensorModule::get_burn(netuid); + let burn_u64: u64 = burn.into(); + let bal = SubtensorModule::get_coldkey_balance(&coldkey_account_id); + + if bal < burn_u64 { + SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_u64 - bal + 10); + } + + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey_account_id), netuid, - block_number, - nonce, - work, hotkey_account_id, - coldkey_account_id, ); assert_ok!(result); log::info!( - "Register ok neuron: netuid: {netuid:?}, coldkey: {hotkey_account_id:?}, hotkey: {coldkey_account_id:?}" + "Register ok neuron: netuid: {netuid:?}, coldkey: {coldkey_account_id:?}, hotkey: {hotkey_account_id:?}" ); } @@ -778,24 +785,35 @@ pub fn register_ok_neuron( pub fn add_network(netuid: NetUid, tempo: u16, _modality: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); - SubtensorModule::set_network_pow_registration_allowed(netuid, true); FirstEmissionBlockNumber::::insert(netuid, 1); SubtokenEnabled::::insert(netuid, true); + + // make interval 1 block so tests can register by stepping 1 block. + BurnHalfLife::::insert(netuid, 1); + BurnIncreaseMult::::insert(netuid, 1); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); } #[allow(dead_code)] pub fn add_network_without_emission_block(netuid: NetUid, tempo: u16, _modality: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); - SubtensorModule::set_network_pow_registration_allowed(netuid, true); + + BurnHalfLife::::insert(netuid, 1); + BurnIncreaseMult::::insert(netuid, 1); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); } #[allow(dead_code)] pub fn add_network_disable_subtoken(netuid: NetUid, tempo: u16, _modality: u16) { SubtensorModule::init_new_network(netuid, tempo); SubtensorModule::set_network_registration_allowed(netuid, true); - SubtensorModule::set_network_pow_registration_allowed(netuid, true); + SubtokenEnabled::::insert(netuid, false); + + BurnHalfLife::::insert(netuid, 1); + BurnIncreaseMult::::insert(netuid, 1); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); } #[allow(dead_code)] @@ -812,9 +830,14 @@ pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> NetUid { *hotkey )); NetworkRegistrationAllowed::::insert(netuid, true); - NetworkPowRegistrationAllowed::::insert(netuid, true); FirstEmissionBlockNumber::::insert(netuid, 0); SubtokenEnabled::::insert(netuid, true); + + // make interval 1 block so tests can register by stepping 1 block. + BurnHalfLife::::insert(netuid, 1); + BurnIncreaseMult::::insert(netuid, 1); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); + netuid } @@ -831,8 +854,12 @@ pub fn add_dynamic_network_without_emission_block(hotkey: &U256, coldkey: &U256) RawOrigin::Signed(*coldkey).into(), *hotkey )); + NetworkRegistrationAllowed::::insert(netuid, true); - NetworkPowRegistrationAllowed::::insert(netuid, true); + BurnHalfLife::::insert(netuid, 1); + BurnIncreaseMult::::insert(netuid, 1); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); + netuid } diff --git a/pallets/subtensor/src/tests/registration.rs b/pallets/subtensor/src/tests/registration.rs index 635b996cea..70271040ad 100644 --- a/pallets/subtensor/src/tests/registration.rs +++ b/pallets/subtensor/src/tests/registration.rs @@ -21,1276 +21,465 @@ use crate::{AxonInfoOf, CustomTransactionError, Error}; *********************************************/ #[test] -fn test_registration_difficulty() { +fn test_init_new_network_registration_defaults() { new_test_ext(1).execute_with(|| { - assert_eq!(SubtensorModule::get_difficulty(1.into()).as_u64(), 10000); - }); -} - -#[test] -fn test_registration_invalid_seal_hotkey() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; let netuid = NetUid::from(1); let tempo: u16 = 13; - let hotkey_account_id_1: U256 = U256::from(1); - let hotkey_account_id_2: U256 = U256::from(2); - let coldkey_account_id: U256 = U256::from(667); // Neighbour of the beast, har har - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id_1, - ); - let (nonce2, work2): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id_1, - ); - //add network - add_network(netuid, tempo, 0); + SubtensorModule::init_new_network(netuid, tempo); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id_1), - netuid, - block_number, - nonce, - work.clone(), - hotkey_account_id_1, - coldkey_account_id - )); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id_2), - netuid, - block_number, - nonce2, - work2.clone(), - hotkey_account_id_2, - coldkey_account_id, + assert_eq!(BurnHalfLife::::get(netuid), 360); + assert_eq!(BurnIncreaseMult::::get(netuid), 2); + + assert_eq!( + SubtensorModule::get_burn(netuid), + TaoCurrency::from(RAO_PER_TAO) + ); + + assert_eq!( + BurnLastHalvingBlock::::get(netuid), + SubtensorModule::get_current_block_as_u64() ); - assert_eq!(result, Err(Error::::InvalidSeal.into())); }); } #[test] fn test_registration_ok() { new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; let netuid = NetUid::from(1); let tempo: u16 = 13; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - //add network add_network(netuid, tempo, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); - // Subscribe and check extrinsic output - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + // Make burn small and stable for this test. + SubtensorModule::set_burn(netuid, 1_000u64.into()); + + let hotkey = U256::from(1); + let coldkey = U256::from(667); + + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 50_000); + + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id + hotkey )); - // Check if neuron has added to the specified network(netuid) + // neuron inserted assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 1); - //check if hotkey is added to the Hotkeys + // ownership set assert_eq!( - SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey_account_id), - coldkey_account_id + SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey), + coldkey ); - // Check if the neuron has added to the Keys - let neuron_uid = - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).unwrap(); - - assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_ok()); - // Check if neuron has added to Uids - let neuro_uid = - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).unwrap(); - assert_eq!(neuro_uid, neuron_uid); + // uid exists + let uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); + assert_eq!(uid, 0); - // Check if the balance of this hotkey account for this subnetwork == 0 + // no stake by default assert_eq!( - SubtensorModule::get_stake_for_uid_and_subnetwork(netuid, neuron_uid), + SubtensorModule::get_stake_for_uid_and_subnetwork(netuid, uid), AlphaCurrency::ZERO ); }); } #[test] -fn test_registration_without_neuron_slot() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; - let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - - //add network - add_network(netuid, tempo, 0); - SubtensorModule::set_max_allowed_uids(netuid, 0); - - assert_noop!( - SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id - ), - Error::::NoNeuronIdAvailable - ); - }); -} - -#[test] -fn test_registration_under_limit() { +fn test_registration_failed_no_signature() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let block_number: u64 = 0; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(667); - let who: ::AccountId = hotkey_account_id; + add_network(netuid, 13, 0); - let max_registrants = 2; - SubtensorModule::set_target_registrations_per_interval(netuid, max_registrants); + let hotkey = U256::from(1); - let (nonce, work) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - let work_clone = work.clone(); - let call = crate::Call::register { + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::none(), netuid, - block_number, - nonce, - work: work_clone, - hotkey: hotkey_account_id, - coldkey: coldkey_account_id, - }; - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - //does not actually call register - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.into(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, + hotkey, ); - assert_ok!(result); - - //actually call register - add_network(netuid, 13, 0); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id - )); - let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); - let target_registrants = SubtensorModule::get_target_registrations_per_interval(netuid); - assert!(current_registrants <= target_registrants); + assert_eq!(result, Err(sp_runtime::DispatchError::BadOrigin)); }); } #[test] -fn test_registration_rate_limit_exceeded() { +fn test_registration_disabled() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let block_number: u64 = 0; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(667); - let who: ::AccountId = hotkey_account_id; + add_network(netuid, 13, 0); - let target_registrants = 1; - let max_registrants = target_registrants * 3; - SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); - SubtensorModule::set_registrations_this_interval(netuid, max_registrants); + SubtensorModule::set_network_registration_allowed(netuid, false); - let (nonce, work) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 129123813, - &hotkey_account_id, - ); - let call = crate::Call::register { + let hotkey = U256::from(1); + let coldkey = U256::from(667); + + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce, - work, - hotkey: hotkey_account_id, - coldkey: coldkey_account_id, - }; - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - let result = extension.validate( - RawOrigin::Signed(who).into(), - &call.into(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, + hotkey, ); - // Expectation: The transaction should be rejected assert_eq!( - result.unwrap_err(), - CustomTransactionError::RateLimitExceeded.into() + result, + Err(Error::::SubNetRegistrationDisabled.into()) ); - - let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); - assert!(current_registrants <= max_registrants); }); } -/******************************************** - registration::do_burned_registration tests -*********************************************/ - #[test] -fn test_burned_registration_under_limit() { +fn test_registration_root_not_permitted() { new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(667); - let who: ::AccountId = coldkey_account_id; - let burn_cost = 1000; - // Set the burn cost - SubtensorModule::set_burn(netuid, burn_cost.into()); - - let reserve = 1_000_000_000_000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - add_network(netuid, 13, 0); // Add the network - // Give it some TAO to the coldkey balance; more than the burn cost - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost + 10_000); + let tempo: u16 = 13; + // Ensure root exists in this test env. + SubtensorModule::init_new_network(NetUid::ROOT, tempo); - let target_registrants = 2; - let max_registrants = target_registrants * 3; // Maximum is 3 times the target - SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); + let hotkey = U256::from(1); + let coldkey = U256::from(2); - let call_burned_register: crate::Call = crate::Call::burned_register { - netuid, - hotkey: hotkey_account_id, - }; - - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - //does not actually call register - let burned_register_result = extension.validate( - RawOrigin::Signed(who).into(), - &call_burned_register.into(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), + NetUid::ROOT, + hotkey, ); - assert_ok!(burned_register_result); - //actually call register - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid, - hotkey_account_id, - )); - - let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); - assert!(current_registrants <= max_registrants); + assert_eq!( + result, + Err(Error::::RegistrationNotPermittedOnRootSubnet.into()) + ); }); } #[test] -fn test_burned_registration_rate_limit_exceeded() { +fn test_registration_not_enough_balance() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(667); - let who: ::AccountId = coldkey_account_id; + add_network(netuid, 13, 0); - let target_registrants = 1; - let max_registrants = target_registrants * 3; // Maximum is 3 times the target + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); - SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); - // Set the current registrations to the maximum; should not be able to register more - SubtensorModule::set_registrations_this_interval(netuid, max_registrants); + // burn cost is 10_000, but coldkey only has 9_999. + SubtensorModule::set_burn(netuid, 10_000u64.into()); - let call_burned_register: crate::Call = crate::Call::burned_register { - netuid, - hotkey: hotkey_account_id, - }; - - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - let burned_register_result = extension.validate( - RawOrigin::Signed(who).into(), - &call_burned_register.into(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, - ); + let hotkey = U256::from(1); + let coldkey = U256::from(667); - // Expectation: The transaction should be rejected - assert_eq!( - burned_register_result.unwrap_err(), - CustomTransactionError::RateLimitExceeded.into() + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 9_999); + + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), + netuid, + hotkey, ); - let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); - assert!(current_registrants <= max_registrants); + assert_eq!(result, Err(Error::::NotEnoughBalanceToStake.into())); + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); }); } #[test] -fn test_burned_registration_rate_allows_burn_adjustment() { - // We need to be able to register more than the *target* registrations per interval +fn test_registration_non_associated_coldkey() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id = U256::from(667); - let who: ::AccountId = coldkey_account_id; + add_network(netuid, 13, 0); - let burn_cost = 1000; - // Set the burn cost - SubtensorModule::set_burn(netuid, burn_cost.into()); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); - let reserve = 1_000_000_000_000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); + let hotkey = U256::from(1); + let true_owner = U256::from(111); + let attacker = U256::from(222); - add_network(netuid, 13, 0); // Add the network - // Give it some TAO to the coldkey balance; more than the burn cost - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, burn_cost + 10_000); + // Pre-own the hotkey by a different coldkey. + Owner::::insert(hotkey, true_owner); - let target_registrants = 1; // Target is 1, but we can register more than that, up to some maximum. - SubtensorModule::set_target_registrations_per_interval(netuid, target_registrants); - // Set the current registrations to above the target; we should be able to register at least 1 more - SubtensorModule::set_registrations_this_interval(netuid, target_registrants); + // Attacker has enough funds, but doesn't own the hotkey. + SubtensorModule::add_balance_to_coldkey_account(&attacker, 50_000); - // Register one more, so the current registrations are above the target - let call_burned_register: crate::Call = crate::Call::burned_register { + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(attacker), netuid, - hotkey: hotkey_account_id, - }; - - let info: DispatchInfo = - DispatchInfoOf::<::RuntimeCall>::default(); - let extension = SubtensorTransactionExtension::::new(); - //does not actually call register - let burned_register_result = extension.validate( - RawOrigin::Signed(who).into(), - &call_burned_register.into(), - &info, - 10, - (), - &TxBaseImplication(()), - TransactionSource::External, + hotkey, ); - assert_ok!(burned_register_result); - - //actually call register - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid, - hotkey_account_id - )); - let current_registrants = SubtensorModule::get_registrations_this_interval(netuid); - assert!(current_registrants > target_registrants); // Should be able to register more than the target + assert_eq!(result, Err(Error::::NonAssociatedColdKey.into())); + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); }); } #[test] -fn test_burned_registration_ok() { +fn test_registration_without_neuron_slot_doesnt_burn() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let burn_cost = 1000; - let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har - //add network - SubtensorModule::set_burn(netuid, burn_cost.into()); - add_network(netuid, tempo, 0); + add_network(netuid, 13, 0); - let reserve = 1_000_000_000_000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); - // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10000); - // Subscribe and check extrinsic output - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid, - hotkey_account_id - )); - // Check if balance has decreased to pay for the burn. - assert_eq!( - SubtensorModule::get_coldkey_balance(&coldkey_account_id), - 10000 - burn_cost - ); // funds drained on reg. - // Check if neuron has added to the specified network(netuid) - assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 1); - //check if hotkey is added to the Hotkeys - assert_eq!( - SubtensorModule::get_owning_coldkey_for_hotkey(&hotkey_account_id), - coldkey_account_id - ); - // Check if the neuron has added to the Keys - let neuron_uid = - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).unwrap(); - assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_ok()); - // Check if neuron has added to Uids - let neuro_uid = - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).unwrap(); - assert_eq!(neuro_uid, neuron_uid); - // Check if the balance of this hotkey account for this subnetwork == 0 - assert_eq!( - SubtensorModule::get_stake_for_uid_and_subnetwork(netuid, neuron_uid), - AlphaCurrency::ZERO - ); - }); -} + let hotkey = U256::from(1); + let coldkey = U256::from(667); -#[test] -fn test_burn_registration_without_neuron_slot() { - new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let burn_cost = 1000; - let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har - //add network - SubtensorModule::set_burn(netuid, burn_cost.into()); - add_network(netuid, tempo, 0); - // Give it some $$$ in his coldkey balance - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10000); - SubtensorModule::set_max_allowed_uids(netuid, 0); - - assert_noop!( - SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid, - hotkey_account_id - ), - Error::::NoNeuronIdAvailable - ); - }); -} + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 10_000); + let before = SubtensorModule::get_coldkey_balance(&coldkey); -#[test] -fn test_burn_registration_doesnt_write_on_failure() { - new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let burn_cost = 1000; - let initial_balance = burn_cost * 10; - let coldkey_account_id = U256::from(987); - - // Add network and set burn cost - add_network(netuid, tempo, 0); - SubtensorModule::set_burn(netuid, burn_cost.into()); - // Give coldkey balance to pay for registration - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, initial_balance); - // Set max allowed uids to 0 so registration will fail, but only on last check. + // No slots => should fail before burning. SubtensorModule::set_max_allowed_uids(netuid, 0); - // We expect this to fail at the last ensure check. - assert_err!( + assert_noop!( SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), + <::RuntimeOrigin>::signed(coldkey), netuid, - hotkey_account_id + hotkey ), Error::::NoNeuronIdAvailable ); - // Make sure the coldkey balance is unchanged. - assert_eq!( - SubtensorModule::get_coldkey_balance(&coldkey_account_id), - initial_balance - ); - // Make sure the neuron is not registered. + assert_eq!(SubtensorModule::get_coldkey_balance(&coldkey), before); assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); - // Make sure the hotkey is not registered. - assert!(SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id).is_err()); }); } #[test] -fn test_burn_adjustment() { +fn test_registration_too_many_registrations_this_interval() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let tempo: u16 = 13; - let init_burn_cost: u64 = InitialMinBurn::get() + 10_000; - let adjustment_interval = 1; - let target_registrations_per_interval = 1; - add_network(netuid, tempo, 0); - SubtensorModule::set_burn(netuid, init_burn_cost.into()); - SubtensorModule::set_adjustment_interval(netuid, adjustment_interval); - SubtensorModule::set_adjustment_alpha(netuid, 58000); // Set to old value. - SubtensorModule::set_target_registrations_per_interval( - netuid, - target_registrations_per_interval, - ); + add_network(netuid, 13, 0); + + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); - let reserve = 1_000_000_000_000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); + let coldkey = U256::from(667); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000); - // Register key 1. - let hotkey_account_id_1 = U256::from(1); - let coldkey_account_id_1 = U256::from(1); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id_1, init_burn_cost); + let hotkey1 = U256::from(1); + let hotkey2 = U256::from(2); + + // First ok assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(hotkey_account_id_1), + <::RuntimeOrigin>::signed(coldkey), netuid, - hotkey_account_id_1 + hotkey1 )); - // Register key 2. - let hotkey_account_id_2 = U256::from(2); - let coldkey_account_id_2 = U256::from(2); - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id_2, init_burn_cost); - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(hotkey_account_id_2), + // Same interval (same block) => reject + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - hotkey_account_id_2 - )); + hotkey2, + ); + assert_eq!( + result, + Err(Error::::TooManyRegistrationsThisInterval.into()) + ); - // We are over the number of regs allowed this interval. - // Step the block and trigger the adjustment. + // Advance 1 block: add_network sets BurnHalfLife=1 for tests => interval resets each block. step_block(1); - // Check the adjusted burn is above the initial min burn. - assert!(SubtensorModule::get_burn(netuid) > init_burn_cost.into()); - assert_abs_diff_eq!( - SubtensorModule::get_burn(netuid), - (init_burn_cost.saturating_mul(3).saturating_div(2)).into(), // 1.5x - epsilon = 1000.into() - ); + // Now allowed + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), + netuid, + hotkey2 + )); }); } -#[allow(clippy::indexing_slicing)] #[test] -fn test_burn_registration_pruning_scenarios() { +fn test_registration_already_active_hotkey_error() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let tempo: u16 = 13; - let burn_cost = 1000; - let coldkey_account_id = U256::from(667); - let max_allowed_uids = 6; - let immunity_period = 5000; - - const IS_IMMUNE: bool = true; - const NOT_IMMUNE: bool = false; - - // --- Neutralize the safety floor for this test. - SubtensorModule::set_min_non_immune_uids(netuid, 0); - - // Initial setup - SubtensorModule::set_burn(netuid, burn_cost.into()); - SubtensorModule::set_max_allowed_uids(netuid, max_allowed_uids); - SubtensorModule::set_target_registrations_per_interval(netuid, max_allowed_uids); - SubtensorModule::set_immunity_period(netuid, immunity_period); - - let reserve = 1_000_000_000_000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - add_network(netuid, tempo, 0); - - let mint_balance = burn_cost * max_allowed_uids as u64 + 1_000_000_000; - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, mint_balance); - - // Register first half of neurons (uids: 0,1,2); all will be immune initially. - for i in 0..3 { - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid, - U256::from(i) - )); - step_block(1); - } - - // 1) All immune neurons - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 0), IS_IMMUNE); - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 1), IS_IMMUNE); - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 2), IS_IMMUNE); - - // Drive selection with emissions: lowest emission is pruned (among immune if all immune). - // Set: uid0=100, uid1=75, uid2=50 -> expect uid2 - Emission::::mutate(netuid, |v| { - v[0] = 100u64.into(); - v[1] = 75u64.into(); - v[2] = 50u64.into(); - }); - assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(2)); - - // 2) Tie-breaking for immune neurons: uid1=50, uid2=50 -> earliest registration among {1,2} is uid1 - Emission::::mutate(netuid, |v| { - v[1] = 50u64.into(); - v[2] = 50u64.into(); - }); - assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(1)); - - // 3) Make all three non-immune - step_block(immunity_period); - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 0), NOT_IMMUNE); - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 1), NOT_IMMUNE); - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 2), NOT_IMMUNE); - - // Among non-immune, choose lowest emission: set uid0=100, uid1=50, uid2=75 -> expect uid1 - Emission::::mutate(netuid, |v| { - v[0] = 100u64.into(); - v[1] = 50u64.into(); - v[2] = 75u64.into(); - }); - assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(1)); - - // 4) Non-immune tie-breaking: uid1=50, uid2=50 -> earliest registration among {1,2} is uid1 - Emission::::mutate(netuid, |v| { - v[1] = 50u64.into(); - v[2] = 50u64.into(); - }); - assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(1)); - - // 5) Mixed immunity: register another 3 immune neurons (uids: 3,4,5) - for i in 3..6 { - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid, - U256::from(i) - )); - step_block(1); - } - - // Ensure new neurons are immune - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 3), IS_IMMUNE); - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 4), IS_IMMUNE); - assert_eq!(SubtensorModule::get_neuron_is_immune(netuid, 5), IS_IMMUNE); - - // Set emissions: - // non-immune (0..2): [75, 50, 60] -> lowest among non-immune is uid1 - // immune (3..5): [40, 55, 45] -> ignored while a non-immune exists - Emission::::mutate(netuid, |v| { - v[0] = 75u64.into(); - v[1] = 50u64.into(); - v[2] = 60u64.into(); - v[3] = 40u64.into(); - v[4] = 55u64.into(); - v[5] = 45u64.into(); - }); - assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(1)); - - // Remove lowest non-immune by making uid1 emission very high -> next lowest non-immune is uid2 - Emission::::mutate(netuid, |v| { - v[1] = 10_000u64.into(); - }); - assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(2)); - - // If all non-immune are equally high, choose the oldest non-immune -> uid0 - Emission::::mutate(netuid, |v| { - v[0] = 10_000u64.into(); - v[1] = 10_000u64.into(); - v[2] = 10_000u64.into(); - }); - assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(0)); - }); -} + add_network(netuid, 13, 0); -#[test] -fn test_registration_too_many_registrations_per_block() { - new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); - let tempo: u16 = 13; - add_network(netuid, tempo, 0); - SubtensorModule::set_max_registrations_per_block(netuid, 10); - SubtensorModule::set_target_registrations_per_interval(netuid, 10); - assert_eq!(SubtensorModule::get_max_registrations_per_block(netuid), 10); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); - let block_number: u64 = 0; - let (nonce0, work0): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 3942084, - &U256::from(0), - ); - let (nonce1, work1): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 11231312312, - &U256::from(1), - ); - let (nonce2, work2): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 212312414, - &U256::from(2), - ); - let (nonce3, work3): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 21813123, - &U256::from(3), - ); - let (nonce4, work4): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 148141209, - &U256::from(4), - ); - let (nonce5, work5): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 1245235534, - &U256::from(5), - ); - let (nonce6, work6): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 256234, - &U256::from(6), - ); - let (nonce7, work7): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 6923424, - &U256::from(7), - ); - let (nonce8, work8): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 124242, - &U256::from(8), - ); - let (nonce9, work9): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 153453, - &U256::from(9), - ); - let (nonce10, work10): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 345923888, - &U256::from(10), - ); - assert_eq!(SubtensorModule::get_difficulty_as_u64(netuid), 10000); + let coldkey = U256::from(667); + let hotkey = U256::from(1); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000); - // Subscribe and check extrinsic output - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(0)), - netuid, - block_number, - nonce0, - work0, - U256::from(0), - U256::from(0) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 1); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(1)), - netuid, - block_number, - nonce1, - work1, - U256::from(1), - U256::from(1) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 2); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(2)), - netuid, - block_number, - nonce2, - work2, - U256::from(2), - U256::from(2) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 3); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(3)), - netuid, - block_number, - nonce3, - work3, - U256::from(3), - U256::from(3) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 4); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(4)), - netuid, - block_number, - nonce4, - work4, - U256::from(4), - U256::from(4) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 5); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(5)), - netuid, - block_number, - nonce5, - work5, - U256::from(5), - U256::from(5) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 6); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(6)), - netuid, - block_number, - nonce6, - work6, - U256::from(6), - U256::from(6) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 7); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(7)), - netuid, - block_number, - nonce7, - work7, - U256::from(7), - U256::from(7) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 8); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(8)), - netuid, - block_number, - nonce8, - work8, - U256::from(8), - U256::from(8) - )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 9); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(9)), + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce9, - work9, - U256::from(9), - U256::from(9) + hotkey )); - assert_eq!(SubtensorModule::get_registrations_this_block(netuid), 10); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(10)), + + // Step to a new interval so we don't hit TooManyRegistrationsThisInterval first. + step_block(1); + + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce10, - work10, - U256::from(10), - U256::from(10), + hotkey, ); assert_eq!( result, - Err(Error::::TooManyRegistrationsThisBlock.into()) + Err(Error::::HotKeyAlreadyRegisteredInSubNet.into()) ); }); } #[test] -fn test_registration_too_many_registrations_per_interval() { +fn test_registration_too_many_registrations_this_block_when_block_cap_zero() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let tempo: u16 = 13; - add_network(netuid, tempo, 0); - SubtensorModule::set_max_registrations_per_block(netuid, 11); - assert_eq!(SubtensorModule::get_max_registrations_per_block(netuid), 11); - SubtensorModule::set_target_registrations_per_interval(netuid, 3); - assert_eq!( - SubtensorModule::get_target_registrations_per_interval(netuid), - 3 - ); - // Then the max is 3 * 3 = 9 + add_network(netuid, 13, 0); - let block_number: u64 = 0; - let (nonce0, work0): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 3942084, - &U256::from(0), - ); - let (nonce1, work1): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 11231312312, - &U256::from(1), - ); - let (nonce2, work2): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 212312414, - &U256::from(2), - ); - let (nonce3, work3): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 21813123, - &U256::from(3), - ); - let (nonce4, work4): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 148141209, - &U256::from(4), - ); - let (nonce5, work5): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 1245235534, - &U256::from(5), - ); - let (nonce6, work6): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 256234, - &U256::from(6), - ); - let (nonce7, work7): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 6923424, - &U256::from(7), - ); - let (nonce8, work8): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 124242, - &U256::from(8), - ); - let (nonce9, work9): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 153453, - &U256::from(9), - ); - assert_eq!(SubtensorModule::get_difficulty_as_u64(netuid), 10000); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); - // Subscribe and check extrinsic output - // Try 10 registrations, this is less than the max per block, but more than the max per interval - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(0)), - netuid, - block_number, - nonce0, - work0, - U256::from(0), - U256::from(0) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 1); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(1)), - netuid, - block_number, - nonce1, - work1, - U256::from(1), - U256::from(1) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 2); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(2)), - netuid, - block_number, - nonce2, - work2, - U256::from(2), - U256::from(2) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 3); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(3)), - netuid, - block_number, - nonce3, - work3, - U256::from(3), - U256::from(3) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 4); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(4)), - netuid, - block_number, - nonce4, - work4, - U256::from(4), - U256::from(4) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 5); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(5)), - netuid, - block_number, - nonce5, - work5, - U256::from(5), - U256::from(5) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 6); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(6)), - netuid, - block_number, - nonce6, - work6, - U256::from(6), - U256::from(6) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 7); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(7)), - netuid, - block_number, - nonce7, - work7, - U256::from(7), - U256::from(7) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 8); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(8)), - netuid, - block_number, - nonce8, - work8, - U256::from(8), - U256::from(8) - )); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 9); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(U256::from(9)), + // Block cap at 0 => first reg should fail with TooManyRegistrationsThisBlock + SubtensorModule::set_max_registrations_per_block(netuid, 0); + + let coldkey = U256::from(667); + let hotkey = U256::from(1); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000); + + let result = SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce9, - work9, - U256::from(9), - U256::from(9), + hotkey, ); + assert_eq!( result, - Err(Error::::TooManyRegistrationsThisInterval.into()) + Err(Error::::TooManyRegistrationsThisBlock.into()) ); }); } -#[test] -fn test_registration_immunity_period() { //impl this test when epoch impl and calculating pruning score is done - // TODO: Implement this test -} +// ----------------------------- +// Burn price dynamics tests +// ----------------------------- #[test] -fn test_registration_already_active_hotkey() { +fn test_burn_increases_next_block_after_registration() { new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id, - ); + add_network(netuid, 13, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); - //add network - add_network(netuid, tempo, 0); + // Override to make behavior deterministic: + BurnHalfLife::::insert(netuid, 1_000); // no halving during this test + BurnIncreaseMult::::insert(netuid, 2); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); + + SubtensorModule::set_burn(netuid, 1_000u64.into()); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + let coldkey = U256::from(100); + let hotkey = U256::from(200); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000); + + // Register in this block. Burn doesn't change until next block. + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id + hotkey )); + assert_eq!(SubtensorModule::get_burn(netuid), 1_000u64.into()); - let block_number: u64 = 0; - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id, - ); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id, - ); - assert_eq!( - result, - Err(Error::::HotKeyAlreadyRegisteredInSubNet.into()) - ); + // Next block => bump applies from previous block's registration + step_block(1); + + assert_eq!(SubtensorModule::get_burn(netuid), 2_000u64.into()); }); } #[test] -fn test_registration_invalid_seal() { +fn test_burn_halves_every_half_life() { new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); - let (nonce, work): (u64, Vec) = - SubtensorModule::create_work_for_block_number(netuid, 1, 0, &hotkey_account_id); + add_network(netuid, 13, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); - //add network - add_network(netuid, tempo, 0); + BurnHalfLife::::insert(netuid, 2); + BurnIncreaseMult::::insert(netuid, 1); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id, - ); - assert_eq!(result, Err(Error::::InvalidSeal.into())); + SubtensorModule::set_burn(netuid, 1024u64.into()); + + step_block(2); + assert_eq!(SubtensorModule::get_burn(netuid), 512u64.into()); + + step_block(2); + assert_eq!(SubtensorModule::get_burn(netuid), 256u64.into()); }); } #[test] -fn test_registration_invalid_block_number() { +fn test_burn_floor_prevents_zero_stuck_and_allows_bump() { new_test_ext(1).execute_with(|| { - System::set_block_number(0); - let block_number: u64 = 1; let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id, - ); + add_network(netuid, 13, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); - //add network - add_network(netuid, tempo, 0); + // Half-life every block; multiplier 2. + BurnHalfLife::::insert(netuid, 1); + BurnIncreaseMult::::insert(netuid, 2); + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + // Start at 1 => halving would go to 0, but floor keeps it at 1. + SubtensorModule::set_burn(netuid, 1u64.into()); + + // Step one block => halving applies, but floor => burn stays 1. + step_block(1); + assert_eq!(SubtensorModule::get_burn(netuid), 1u64.into()); + + // Register now; bump should apply next block and not be stuck at 0. + let coldkey = U256::from(1); + let hotkey = U256::from(2); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 10_000); + + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id, - ); - assert_eq!(result, Err(Error::::InvalidWorkBlock.into())); + hotkey + )); + + step_block(1); + + // burn should become 2 (after halving floor then bump) + assert_eq!(SubtensorModule::get_burn(netuid), 2u64.into()); }); } #[test] -fn test_registration_invalid_difficulty() { +fn test_registration_increases_recycled_rao_per_subnet() { new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id, - ); + add_network(netuid, 13, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); - //add network - add_network(netuid, tempo, 0); + BurnHalfLife::::insert(netuid, 1); // allow 1 reg / block + BurnIncreaseMult::::insert(netuid, 1); // keep burn stable aside from halving + BurnLastHalvingBlock::::insert(netuid, SubtensorModule::get_current_block_as_u64()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); - SubtensorModule::set_difficulty(netuid, 18_446_744_073_709_551_615u64); + let coldkey = U256::from(667); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 1_000_000); - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + // First registration + let burn1 = SubtensorModule::get_burn(netuid); + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id, - ); - assert_eq!(result, Err(Error::::InvalidDifficulty.into())); - }); -} + U256::from(1) + )); + assert_eq!(SubtensorModule::get_rao_recycled(netuid), burn1); -#[test] -fn test_registration_failed_no_signature() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 1; - let netuid = NetUid::from(1); - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id, - ); + // Next interval + step_block(1); - // Subscribe and check extrinsic output - let result = SubtensorModule::register( - <::RuntimeOrigin>::none(), + // Second registration (burn may have changed; read it) + let burn2 = SubtensorModule::get_burn(netuid); + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce, - work, - hotkey_account_id, - coldkey_account_id, - ); - assert_eq!(result, Err(DispatchError::BadOrigin)); + U256::from(2) + )); + + assert_eq!(SubtensorModule::get_rao_recycled(netuid), burn1 + burn2); }); } @@ -1301,23 +490,23 @@ fn test_registration_get_uid_to_prune_all_in_immunity_period() { System::set_block_number(0); let netuid = NetUid::from(1); add_network(netuid, 1, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); // Neutralize safety floor and owner-immortality for deterministic selection SubtensorModule::set_min_non_immune_uids(netuid, 0); ImmuneOwnerUidsLimit::::insert(netuid, 0); - // Make sure the subnet owner is not one of the test coldkeys SubnetOwner::::insert(netuid, U256::from(999_999u64)); - register_ok_neuron(netuid, U256::from(0), U256::from(0), 39420842); - register_ok_neuron(netuid, U256::from(1), U256::from(1), 12412392); + // Register uid0 @ block 0 + register_ok_neuron(netuid, U256::from(0), U256::from(0), 0); + // Move to next block so interval resets; register uid1 @ block 1 + step_block(1); + register_ok_neuron(netuid, U256::from(1), U256::from(1), 0); + + // Both immune with immunity_period=2 at current block=1. SubtensorModule::set_immunity_period(netuid, 2); - assert_eq!(SubtensorModule::get_immunity_period(netuid), 2); - assert_eq!(SubtensorModule::get_current_block_as_u64(), 0); - assert_eq!( - SubtensorModule::get_neuron_block_at_registration(netuid, 0), - 0 - ); + assert_eq!(SubtensorModule::get_current_block_as_u64(), 1); // Both immune; prune lowest emission among immune (uid0=100, uid1=110 => uid0) Emission::::mutate(netuid, |v| { @@ -1336,28 +525,26 @@ fn test_registration_get_uid_to_prune_none_in_immunity_period() { System::set_block_number(0); let netuid = NetUid::from(1); add_network(netuid, 1, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); // Neutralize safety floor and owner-immortality for deterministic selection SubtensorModule::set_min_non_immune_uids(netuid, 0); ImmuneOwnerUidsLimit::::insert(netuid, 0); SubnetOwner::::insert(netuid, U256::from(999_999u64)); - register_ok_neuron(netuid, U256::from(0), U256::from(0), 39420842); - register_ok_neuron(netuid, U256::from(1), U256::from(1), 12412392); + // Register uid0 @ block 0 + register_ok_neuron(netuid, U256::from(0), U256::from(0), 0); + + // Register uid1 @ block 1 + step_block(1); + register_ok_neuron(netuid, U256::from(1), U256::from(1), 0); SubtensorModule::set_immunity_period(netuid, 2); - assert_eq!(SubtensorModule::get_immunity_period(netuid), 2); - assert_eq!(SubtensorModule::get_current_block_as_u64(), 0); - assert_eq!( - SubtensorModule::get_neuron_block_at_registration(netuid, 0), - 0 - ); - // Advance beyond immunity -> both non-immune + // Advance beyond immunity -> both non-immune (current=1, step 3 => 4) step_block(3); - assert_eq!(SubtensorModule::get_current_block_as_u64(), 3); + assert_eq!(SubtensorModule::get_current_block_as_u64(), 4); - // Among non-immune, lowest emission pruned: uid0=100, uid1=110 -> expect uid0 Emission::::mutate(netuid, |v| { v[0] = 100u64.into(); v[1] = 110u64.into(); @@ -1367,31 +554,28 @@ fn test_registration_get_uid_to_prune_none_in_immunity_period() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::registration::test_registration_get_uid_to_prune_owner_immortality --exact --show-output --nocapture +// These owner-immortality tests do not depend on registration paths; keep as-is. + #[test] fn test_registration_get_uid_to_prune_owner_immortality() { new_test_ext(1).execute_with(|| { [ - // Limit = 1: only the earliest owner hotkey is immortal -> prune the other owner hotkey (uid 1) - (1, 1), - // Limit = 2: both owner hotkeys are immortal -> prune the non-owner (uid 2) - (2, 2), + (1, 1), // limit=1 => prune other owner hk (uid1) + (2, 2), // limit=2 => both owner hks immortal => prune non-owner (uid2) ] .iter() .for_each(|(limit, uid_to_prune)| { let subnet_owner_ck = U256::from(0); let subnet_owner_hk = U256::from(1); - // Other hk owned by owner let other_owner_hk = U256::from(2); Owner::::insert(other_owner_hk, subnet_owner_ck); OwnedHotkeys::::insert(subnet_owner_ck, vec![subnet_owner_hk, other_owner_hk]); - // Another hk not owned by owner let non_owner_hk = U256::from(3); let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); - // Make sure registration blocks are set + BlockAtRegistration::::insert(netuid, 1, 1); BlockAtRegistration::::insert(netuid, 2, 2); Uids::::insert(netuid, other_owner_hk, 1); @@ -1401,16 +585,12 @@ fn test_registration_get_uid_to_prune_owner_immortality() { ImmunityPeriod::::insert(netuid, 1); SubnetworkN::::insert(netuid, 3); - // Neutralize safety floor for this test SubtensorModule::set_min_non_immune_uids(netuid, 0); step_block(10); // all non-immune - // Configure the number of immortal owner UIDs ImmuneOwnerUidsLimit::::insert(netuid, *limit); - // Drive selection by emissions (lowest first) - // uid0=0, uid1=0, uid2=1 Emission::::insert( netuid, vec![AlphaCurrency::from(0), 0u64.into(), 1u64.into()], @@ -1424,7 +604,6 @@ fn test_registration_get_uid_to_prune_owner_immortality() { }); } -// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::registration::test_registration_get_uid_to_prune_owner_immortality_all_immune --exact --show-output --nocapture #[test] fn test_registration_get_uid_to_prune_owner_immortality_all_immune() { new_test_ext(1).execute_with(|| { @@ -1433,15 +612,13 @@ fn test_registration_get_uid_to_prune_owner_immortality_all_immune() { let subnet_owner_ck = U256::from(0); let subnet_owner_hk = U256::from(1); - // Other hk owned by owner let other_owner_hk = U256::from(2); Owner::::insert(other_owner_hk, subnet_owner_ck); OwnedHotkeys::::insert(subnet_owner_ck, vec![subnet_owner_hk, other_owner_hk]); - // Another hk not owned by owner let non_owner_hk = U256::from(3); - let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck); + BlockAtRegistration::::insert(netuid, 0, 12); BlockAtRegistration::::insert(netuid, 1, 11); BlockAtRegistration::::insert(netuid, 2, 10); @@ -1452,14 +629,12 @@ fn test_registration_get_uid_to_prune_owner_immortality_all_immune() { ImmunityPeriod::::insert(netuid, 100); SubnetworkN::::insert(netuid, 3); - // Neutralize safety floor for this test SubtensorModule::set_min_non_immune_uids(netuid, 0); step_block(20); // all still immune ImmuneOwnerUidsLimit::::insert(netuid, limit); - // Lowest emission among non-immortal candidates -> uid2 Emission::::insert( netuid, vec![AlphaCurrency::from(0), 0u64.into(), 1u64.into()], @@ -1476,32 +651,21 @@ fn test_registration_get_uid_to_prune_owner_immortality_all_immune() { fn test_registration_get_neuron_metadata() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let block_number: u64 = 0; - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); - let (nonce0, work0): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 3942084, - &hotkey_account_id, - ); + add_network(netuid, 13, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); - add_network(netuid, tempo, 0); + let hotkey = U256::from(1); + let coldkey = U256::from(667); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 100_000); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce0, - work0, - hotkey_account_id, - coldkey_account_id + hotkey )); - // - //let neuron_id = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey_account_id); - // let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey( netuid, &hotkey_account_id ).unwrap(); - let neuron: AxonInfoOf = SubtensorModule::get_axon_info(netuid, &hotkey_account_id); + + let neuron: AxonInfoOf = SubtensorModule::get_axon_info(netuid, &hotkey); assert_eq!(neuron.ip, 0); assert_eq!(neuron.version, 0); assert_eq!(neuron.port, 0); @@ -1509,361 +673,116 @@ fn test_registration_get_neuron_metadata() { } #[test] -fn test_registration_add_network_size() { +fn test_last_update_correctness() { new_test_ext(1).execute_with(|| { let netuid = NetUid::from(1); - let netuid2 = NetUid::from(2); - let block_number: u64 = 0; - let hotkey_account_id = U256::from(1); - let hotkey_account_id1 = U256::from(2); - let hotkey_account_id2 = U256::from(3); - let (nonce0, work0): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 3942084, - &hotkey_account_id, - ); - let (nonce1, work1): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid2, - block_number, - 11231312312, - &hotkey_account_id1, - ); - let (nonce2, work2): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid2, - block_number, - 21813123, - &hotkey_account_id2, - ); - let coldkey_account_id = U256::from(667); - add_network(netuid, 13, 0); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); - add_network(netuid2, 13, 0); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid2), 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + SubtensorModule::set_burn(netuid, 1_000u64.into()); + + // Simulate existing neurons + let existing_neurons: u16 = 3; + SubnetworkN::::insert(netuid, existing_neurons); + + // Simulate no LastUpdate so far (can happen on mechanisms) + LastUpdate::::remove(NetUidStorageIndex::from(netuid)); + + let coldkey = U256::from(667); + let hotkey = U256::from(1); + SubtensorModule::add_balance_to_coldkey_account(&coldkey, 100_000); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), + assert_ok!(SubtensorModule::burned_register( + <::RuntimeOrigin>::signed(coldkey), netuid, - block_number, - nonce0, - work0, - hotkey_account_id, - coldkey_account_id - )); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 1); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid), 1); - - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id1), - netuid2, - block_number, - nonce1, - work1, - hotkey_account_id1, - coldkey_account_id - )); - assert_ok!(SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id2), - netuid2, - block_number, - nonce2, - work2, - hotkey_account_id2, - coldkey_account_id + hotkey )); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid2), 2); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid2), 2); + + assert_eq!( + LastUpdate::::get(NetUidStorageIndex::from(netuid)).len(), + (existing_neurons + 1) as usize + ); }); } -#[test] -fn test_burn_registration_increase_recycled_rao() { - new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); - let netuid2 = NetUid::from(2); +#[allow(clippy::indexing_slicing)] +#[test] +fn test_registration_pruning() { + new_test_ext(1).execute_with(|| { + let netuid = NetUid::from(5); + add_network(netuid, 10_000, 0); + mock::setup_reserves(netuid, DEFAULT_RESERVE.into(), DEFAULT_RESERVE.into()); + + ImmuneOwnerUidsLimit::::insert(netuid, 0); + + MaxRegistrationsPerBlock::::insert(netuid, 1024); + SubtensorModule::set_max_allowed_uids(netuid, 3); + + let coldkeys = [U256::from(20_001), U256::from(20_002), U256::from(20_003)]; + let hotkeys = [U256::from(30_001), U256::from(30_002), U256::from(30_003)]; + + for i in 0..3 { + register_ok_neuron(netuid, hotkeys[i], coldkeys[i], 0); + step_block(1); + } + + let uid0 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[0]).unwrap(); + let uid1 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[1]).unwrap(); + let uid2 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[2]).unwrap(); - let hotkey_account_id = U256::from(1); - let coldkey_account_id = U256::from(667); + assert_eq!(uid0, 0); + assert_eq!(uid1, 1); + assert_eq!(uid2, 2); + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 3); - // Give funds for burn. 1000 TAO - let _ = - Balances::deposit_creating(&coldkey_account_id, Balance::from(1_000_000_000_000_u64)); + let now: u64 = 1_000; + frame_system::Pallet::::set_block_number(now); - let reserve = 1_000_000_000_000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - mock::setup_reserves(netuid2, reserve.into(), reserve.into()); + SubtensorModule::set_immunity_period(netuid, 100); - add_network(netuid, 13, 0); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 0); + BlockAtRegistration::::insert(netuid, uid0, now - 150); + BlockAtRegistration::::insert(netuid, uid1, now - 200); + BlockAtRegistration::::insert(netuid, uid2, now - 10); - add_network(netuid2, 13, 0); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid2), 0); + assert!(!SubtensorModule::get_neuron_is_immune(netuid, uid0)); + assert!(!SubtensorModule::get_neuron_is_immune(netuid, uid1)); + assert!(SubtensorModule::get_neuron_is_immune(netuid, uid2)); - run_to_block(1); + Emission::::mutate(netuid, |v| { + v[uid0 as usize] = 10u64.into(); + v[uid1 as usize] = 10u64.into(); + v[uid2 as usize] = 1u64.into(); + }); - let burn_amount = SubtensorModule::get_burn(netuid); - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - hotkey_account_id - )); - assert_eq!(SubtensorModule::get_rao_recycled(netuid), burn_amount); + SubtensorModule::set_min_non_immune_uids(netuid, 0); - run_to_block(2); + assert_eq!(SubtensorModule::get_neuron_to_prune(netuid), Some(uid1)); - let burn_amount2 = SubtensorModule::get_burn(netuid2); - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid2, - hotkey_account_id - )); - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(U256::from(2)), - netuid2, - U256::from(2) - )); - assert_eq!( - SubtensorModule::get_rao_recycled(netuid2), - burn_amount2 * 2.into() - ); - // Validate netuid is not affected. - assert_eq!(SubtensorModule::get_rao_recycled(netuid), burn_amount); - }); -} + let new_hotkey = U256::from(40_000); + let new_coldkey = U256::from(50_000); -#[test] -fn test_full_pass_through() { - new_test_ext(1).execute_with(|| { - // Create 3 networks. - let netuid0 = NetUid::from(1); - let netuid1 = NetUid::from(2); - let netuid2 = NetUid::from(3); - - // With 3 tempos - let tempo0: u16 = 2; - let tempo1: u16 = 2; - let tempo2: u16 = 2; - - // Create 3 keys. - let hotkey0 = U256::from(0); - let hotkey1 = U256::from(1); - let hotkey2 = U256::from(2); + // Ensure interval allows another registration + step_block(1); - // With 3 different coldkeys. - let coldkey0 = U256::from(0); - let coldkey1 = U256::from(1); - let coldkey2 = U256::from(2); - - // Add the 3 networks. - add_network(netuid0, tempo0, 0); - add_network(netuid1, tempo1, 0); - add_network(netuid2, tempo2, 0); - - // owners are not deregisterd - let dummy_owner = U256::from(99999); - crate::SubnetOwner::::insert(netuid0, dummy_owner); - crate::SubnetOwner::::insert(netuid1, dummy_owner); - crate::SubnetOwner::::insert(netuid2, dummy_owner); - - // Check their tempo. - assert_eq!(SubtensorModule::get_tempo(netuid0), tempo0); - assert_eq!(SubtensorModule::get_tempo(netuid1), tempo1); - assert_eq!(SubtensorModule::get_tempo(netuid2), tempo2); - - // Set their max allowed uids. - SubtensorModule::set_max_allowed_uids(netuid0, 2); - SubtensorModule::set_max_allowed_uids(netuid1, 2); - SubtensorModule::set_max_allowed_uids(netuid2, 2); - - // Check their max allowed. - assert_eq!(SubtensorModule::get_max_allowed_uids(netuid0), 2); - assert_eq!(SubtensorModule::get_max_allowed_uids(netuid0), 2); - assert_eq!(SubtensorModule::get_max_allowed_uids(netuid0), 2); - - // Set the max registration per block. - SubtensorModule::set_max_registrations_per_block(netuid0, 3); - SubtensorModule::set_max_registrations_per_block(netuid1, 3); - SubtensorModule::set_max_registrations_per_block(netuid2, 3); - assert_eq!(SubtensorModule::get_max_registrations_per_block(netuid0), 3); - assert_eq!(SubtensorModule::get_max_registrations_per_block(netuid1), 3); - assert_eq!(SubtensorModule::get_max_registrations_per_block(netuid2), 3); - - // Check that no one has registered yet. - assert_eq!(SubtensorModule::get_subnetwork_n(netuid0), 0); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid1), 0); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid2), 0); - - // Registered the keys to all networks. - register_ok_neuron(netuid0, hotkey0, coldkey0, 39420842); - register_ok_neuron(netuid0, hotkey1, coldkey1, 12412392); - register_ok_neuron(netuid1, hotkey0, coldkey0, 21813123); - register_ok_neuron(netuid1, hotkey1, coldkey1, 25755207); - register_ok_neuron(netuid2, hotkey0, coldkey0, 251232207); - register_ok_neuron(netuid2, hotkey1, coldkey1, 159184122); - - // Check uids. - // n0 [ h0, h1 ] - // n1 [ h0, h1 ] - // n2 [ h0, h1 ] - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid0, 0).unwrap(), - hotkey0 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid1, 0).unwrap(), - hotkey0 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid2, 0).unwrap(), - hotkey0 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid0, 1).unwrap(), - hotkey1 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid1, 1).unwrap(), - hotkey1 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid2, 1).unwrap(), - hotkey1 - ); + register_ok_neuron(netuid, new_hotkey, new_coldkey, 0); - // Check registered networks. - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey0 ).contains( &netuid0 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey0 ).contains( &netuid1 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey0 ).contains( &netuid2 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey1 ).contains( &netuid0 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey1 ).contains( &netuid1 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey1 ).contains( &netuid2 ) ); - // assert!( !SubtensorModule::get_registered_networks_for_hotkey( &hotkey2 ).contains( &netuid0 ) ); - // assert!( !SubtensorModule::get_registered_networks_for_hotkey( &hotkey2 ).contains( &netuid1 ) ); - // assert!( !SubtensorModule::get_registered_networks_for_hotkey( &hotkey2 ).contains( &netuid2 ) ); - - // Check the number of registrations. - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid0), 2); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid1), 2); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid2), 2); - - // Get the number of uids in each network. - assert_eq!(SubtensorModule::get_subnetwork_n(netuid0), 2); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid1), 2); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid2), 2); - - // Check the uids exist. - assert!(SubtensorModule::is_uid_exist_on_network(netuid0, 0)); - assert!(SubtensorModule::is_uid_exist_on_network(netuid1, 0)); - assert!(SubtensorModule::is_uid_exist_on_network(netuid2, 0)); - - // Check the other exists. - assert!(SubtensorModule::is_uid_exist_on_network(netuid0, 1)); - assert!(SubtensorModule::is_uid_exist_on_network(netuid1, 1)); - assert!(SubtensorModule::is_uid_exist_on_network(netuid2, 1)); - - // Get the hotkey under each uid. - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid0, 0).unwrap(), - hotkey0 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid1, 0).unwrap(), - hotkey0 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid2, 0).unwrap(), - hotkey0 - ); + assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 3); - // Get the hotkey under the other uid. - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid0, 1).unwrap(), - hotkey1 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid1, 1).unwrap(), - hotkey1 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid2, 1).unwrap(), - hotkey1 + assert!( + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[1]).is_err(), + "Hotkey for pruned UID should no longer be registered" ); - // Check for replacement. - assert_eq!(SubtensorModule::get_subnetwork_n(netuid0), 2); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid1), 2); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid2), 2); - - // Register the 3rd hotkey. - register_ok_neuron(netuid0, hotkey2, coldkey2, 59420842); - register_ok_neuron(netuid1, hotkey2, coldkey2, 31813123); - register_ok_neuron(netuid2, hotkey2, coldkey2, 451232207); - - // Check for replacement. - assert_eq!(SubtensorModule::get_subnetwork_n(netuid0), 2); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid1), 2); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid2), 2); - - // Check uids. - // n0 [ h0, h1 ] - // n1 [ h0, h1 ] - // n2 [ h0, h1 ] - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid0, 0).unwrap(), - hotkey2 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid1, 0).unwrap(), - hotkey2 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid2, 0).unwrap(), - hotkey2 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid0, 1).unwrap(), - hotkey1 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid1, 1).unwrap(), - hotkey1 - ); - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid2, 1).unwrap(), - hotkey1 - ); + let new_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &new_hotkey).unwrap(); + assert_eq!(new_uid, uid1); - // Check registered networks. - // hotkey0 has been deregistered. - // assert!( !SubtensorModule::get_registered_networks_for_hotkey( &hotkey0 ).contains( &netuid0 ) ); - // assert!( !SubtensorModule::get_registered_networks_for_hotkey( &hotkey0 ).contains( &netuid1 ) ); - // assert!( !SubtensorModule::get_registered_networks_for_hotkey( &hotkey0 ).contains( &netuid2 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey1 ).contains( &netuid0 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey1 ).contains( &netuid1 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey1 ).contains( &netuid2 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey2 ).contains( &netuid0 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey2 ).contains( &netuid1 ) ); - // assert!( SubtensorModule::get_registered_networks_for_hotkey( &hotkey2 ).contains( &netuid2 ) ); - - // Check the registration counters. - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid0), 3); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid1), 3); - assert_eq!(SubtensorModule::get_registrations_this_interval(netuid2), 3); - - // Check the hotkeys are expected. - assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid0, 0).unwrap(), - hotkey2 - ); assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid1, 0).unwrap(), - hotkey2 + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[0]).unwrap(), + uid0 ); assert_eq!( - SubtensorModule::get_hotkey_for_net_and_uid(netuid2, 0).unwrap(), - hotkey2 + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[2]).unwrap(), + uid2 ); }); } @@ -2052,240 +971,6 @@ fn test_full_pass_through() { // }); // } -#[test] -fn test_registration_origin_hotkey_mismatch() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; - let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id_1: U256 = U256::from(1); - let hotkey_account_id_2: U256 = U256::from(2); - let coldkey_account_id: U256 = U256::from(668); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id_1, - ); - - //add network - add_network(netuid, tempo, 0); - - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id_1), - netuid, - block_number, - nonce, - work.clone(), - hotkey_account_id_2, // Not the same as the origin. - coldkey_account_id, - ); - assert_eq!( - result, - Err(Error::::TransactorAccountShouldBeHotKey.into()) - ); - }); -} - -#[test] -fn test_registration_disabled() { - new_test_ext(1).execute_with(|| { - let block_number: u64 = 0; - let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id: U256 = U256::from(1); - let coldkey_account_id: U256 = U256::from(668); - let (nonce, work): (u64, Vec) = SubtensorModule::create_work_for_block_number( - netuid, - block_number, - 0, - &hotkey_account_id, - ); - - //add network - add_network(netuid, tempo, 0); - SubtensorModule::set_network_registration_allowed(netuid, false); - SubtensorModule::set_network_pow_registration_allowed(netuid, false); - - let result = SubtensorModule::register( - <::RuntimeOrigin>::signed(hotkey_account_id), - netuid, - block_number, - nonce, - work.clone(), - hotkey_account_id, - coldkey_account_id, - ); - assert_eq!( - result, - Err(Error::::SubNetRegistrationDisabled.into()) - ); - }); -} - -#[test] -fn test_last_update_correctness() { - new_test_ext(1).execute_with(|| { - let netuid = NetUid::from(1); - let tempo: u16 = 13; - let hotkey_account_id = U256::from(1); - let burn_cost = 1000; - let coldkey_account_id = U256::from(667); // Neighbour of the beast, har har - //add network - SubtensorModule::set_burn(netuid, burn_cost.into()); - add_network(netuid, tempo, 0); - - let reserve = 1_000_000_000_000; - mock::setup_reserves(netuid, reserve.into(), reserve.into()); - - // Simulate existing neurons - let existing_neurons = 3; - SubnetworkN::::insert(netuid, existing_neurons); - - // Simulate no LastUpdate so far (can happen on mechanisms) - LastUpdate::::remove(NetUidStorageIndex::from(netuid)); - - // Give some $$$ to coldkey - SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, 10000); - // Subscribe and check extrinsic output - assert_ok!(SubtensorModule::burned_register( - <::RuntimeOrigin>::signed(coldkey_account_id), - netuid, - hotkey_account_id - )); - - // Check that LastUpdate has existing_neurons + 1 elements now - assert_eq!( - LastUpdate::::get(NetUidStorageIndex::from(netuid)).len(), - (existing_neurons + 1) as usize - ); - }); -} - -#[allow(clippy::indexing_slicing)] -#[test] -fn test_registration_pruning() { - new_test_ext(1).execute_with(|| { - // --- Setup a simple non-root subnet. - let netuid = NetUid::from(5); - add_network(netuid, 10_000, 0); - - // No owner-based immortality: we want to test time-based immunity only. - ImmuneOwnerUidsLimit::::insert(netuid, 0); - - // Allow registrations freely. - MaxRegistrationsPerBlock::::insert(netuid, 1024); - SubtensorModule::set_target_registrations_per_interval(netuid, u16::MAX); - - // Cap the subnet at 3 UIDs so the 4th registration *must* prune. - SubtensorModule::set_max_allowed_uids(netuid, 3); - - // --- Register three neurons (uids 0, 1, 2). - let coldkeys = [U256::from(20_001), U256::from(20_002), U256::from(20_003)]; - let hotkeys = [U256::from(30_001), U256::from(30_002), U256::from(30_003)]; - - for i in 0..3 { - register_ok_neuron(netuid, hotkeys[i], coldkeys[i], 0); - } - - // Sanity: ensure we got sequential UIDs. - let uid0 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[0]).unwrap(); - let uid1 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[1]).unwrap(); - let uid2 = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[2]).unwrap(); - - assert_eq!(uid0, 0); - assert_eq!(uid1, 1); - assert_eq!(uid2, 2); - assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 3); - - // --- Craft immunity and tie‑breaking conditions. - - // Fixed "current" block. - let now: u64 = 1_000; - frame_system::Pallet::::set_block_number(now); - - // Immunity lasts 100 blocks. - SubtensorModule::set_immunity_period(netuid, 100); - - // Registration blocks: - // - uid0: now - 150 -> non‑immune - // - uid1: now - 200 -> non‑immune (older than uid0) - // - uid2: now - 10 -> immune - BlockAtRegistration::::insert(netuid, uid0, now - 150); - BlockAtRegistration::::insert(netuid, uid1, now - 200); - BlockAtRegistration::::insert(netuid, uid2, now - 10); - - // Check immunity flags: the 3rd neuron is immune, the first two are not. - assert!(!SubtensorModule::get_neuron_is_immune(netuid, uid0)); - assert!(!SubtensorModule::get_neuron_is_immune(netuid, uid1)); - assert!(SubtensorModule::get_neuron_is_immune(netuid, uid2)); - - // Emissions: - // - uid0: 10 - // - uid1: 10 (same emission as uid0) - // - uid2: 1 (better emission, but immune) - // - // Among *non‑immune* neurons, emission ties -> break on reg_block: - // uid1 registered earlier (now-200 < now-150), so uid1 should be pruned. - // The immune uid2 should **not** be chosen even though it has lower emission. - Emission::::mutate(netuid, |v| { - v[uid0 as usize] = 10u64.into(); - v[uid1 as usize] = 10u64.into(); - v[uid2 as usize] = 1u64.into(); - }); - - // Allow pruning of any non‑immune UID (no safety floor). - SubtensorModule::set_min_non_immune_uids(netuid, 0); - - // Check that pruning decision respects: - // 1. Prefer non‑immune over immune. - // 2. Then lowest emission. - // 3. Then earliest registration block. - // 4. Then uid (not needed here). - assert_eq!( - SubtensorModule::get_neuron_to_prune(netuid), - Some(uid1), - "Expected pruning to choose the oldest non‑immune neuron \ - when emissions tie, even if an immune neuron has lower emission" - ); - - // --- Now actually perform a registration that forces pruning. - - let new_hotkey = U256::from(40_000); - let new_coldkey = U256::from(50_000); - - // This should internally call do_burned_registration -> register_neuron, - // which must reuse the UID returned by get_neuron_to_prune (uid1). - register_ok_neuron(netuid, new_hotkey, new_coldkey, 0); - - // Still capped at 3 UIDs. - assert_eq!(SubtensorModule::get_subnetwork_n(netuid), 3); - - // Old uid1 hotkey should be gone. - assert!( - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[1]).is_err(), - "Hotkey for pruned UID should no longer be registered" - ); - - // New hotkey should reuse uid1 (the pruned slot). - let new_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &new_hotkey).unwrap(); - assert_eq!( - new_uid, uid1, - "New registration should reuse the UID selected by get_neuron_to_prune" - ); - - // The other two original neurons (uid0 and uid2) must remain registered. - assert_eq!( - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[0]).unwrap(), - uid0 - ); - assert_eq!( - SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkeys[2]).unwrap(), - uid2 - ); - }); -} - // #[ignore] // #[test] // fn test_hotkey_swap_ok() { diff --git a/pallets/subtensor/src/utils/misc.rs b/pallets/subtensor/src/utils/misc.rs index 609e43cf63..390457c608 100644 --- a/pallets/subtensor/src/utils/misc.rs +++ b/pallets/subtensor/src/utils/misc.rs @@ -911,4 +911,13 @@ impl Pallet { pub fn set_tao_flow_smoothing_factor(smoothing_factor: u64) { FlowEmaSmoothingFactor::::set(smoothing_factor); } + + pub fn saturating_pow_u64(base: u64, mut exp: u16) -> u64 { + let mut acc: u64 = 1; + while exp > 0 { + acc = acc.saturating_mul(base); + exp -= 1; + } + acc + } }