diff --git a/pallets/admin-utils/src/tests/mock.rs b/pallets/admin-utils/src/tests/mock.rs index fc0d016198..43a40ec32e 100644 --- a/pallets/admin-utils/src/tests/mock.rs +++ b/pallets/admin-utils/src/tests/mock.rs @@ -135,6 +135,7 @@ parameter_types! { pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // 5 days pub const InitialTaoWeight: u64 = u64::MAX/10; // 10% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks + pub const DurationOfStartCall: u64 = 7 * 24 * 60 * 60 / 12; // 7 days } impl pallet_subtensor::Config for Test { @@ -199,6 +200,7 @@ impl pallet_subtensor::Config for Test { type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; + type DurationOfStartCall = DurationOfStartCall; } #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] diff --git a/pallets/subtensor/src/benchmarks.rs b/pallets/subtensor/src/benchmarks.rs index 47dd7a5bc5..8d4457b0c9 100644 --- a/pallets/subtensor/src/benchmarks.rs +++ b/pallets/subtensor/src/benchmarks.rs @@ -6,7 +6,7 @@ use crate::Pallet as Subtensor; use crate::*; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_support::assert_ok; -use frame_system::RawOrigin; +use frame_system::{RawOrigin, pallet_prelude::BlockNumberFor}; pub use pallet::*; use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, Hash}; @@ -658,4 +658,35 @@ benchmark_burn_alpha { }: burn_alpha(RawOrigin::Signed(coldkey), hotkey, alpha_amount, netuid) + +benchmark_start_call { + let caller: T::AccountId = whitelisted_caller::>(); + let caller_origin = ::RuntimeOrigin::from(RawOrigin::Signed(caller.clone())); + let netuid: u16 = 1; + let tempo: u16 = 1; + let seed: u32 = 1; + + // Set up coldkey and hotkey + let coldkey: T::AccountId = account("Test", 0, seed); + let hotkey: T::AccountId = account("Alice", 0, seed); + + // Initialize network + Subtensor::::init_new_network(netuid, tempo); + Subtensor::::set_network_registration_allowed(netuid, true); + + // Register the neuron + Subtensor::::set_burn(netuid, 1); + let amount_to_be_staked = 1000000u32.into(); + Subtensor::::add_balance_to_coldkey_account(&coldkey.clone(), amount_to_be_staked); + + assert_ok!(Subtensor::::do_burned_registration(RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone())); + assert_eq!(SubnetOwner::::get(netuid), coldkey.clone()); + assert_eq!(FirstEmissionBlockNumber::::get(netuid), None); + let current_block: u64 = Subtensor::::get_current_block_as_u64(); + let duration = ::DurationOfStartCall::get(); + let block: BlockNumberFor = (current_block + duration).try_into().ok().expect("can't convert to block number"); + frame_system::Pallet::::set_block_number(block); + +}: start_call(RawOrigin::Signed(coldkey), netuid) + } diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 7836423868..7a29d4959a 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -37,10 +37,11 @@ impl Pallet { let current_block: u64 = Self::get_current_block_as_u64(); log::debug!("Current block: {:?}", current_block); - // --- 1. Get all netuids (filter out root.) + // --- 1. Get all netuids (filter out root and new subnet without first emission block) let subnets: Vec = Self::get_all_subnet_netuids() .into_iter() .filter(|netuid| *netuid != 0) + .filter(|netuid| FirstEmissionBlockNumber::::get(*netuid).is_some()) .collect(); log::debug!("All subnet netuids: {:?}", subnets); diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 03438aa637..1ec9cadb0a 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -1125,7 +1125,11 @@ pub mod pallet { /// ============================ /// ==== Subnet Parameters ===== /// ============================ - #[pallet::storage] // --- MAP ( netuid ) --> subnet mechanism + /// --- MAP ( netuid ) --> block number of first emission + #[pallet::storage] + pub type FirstEmissionBlockNumber = StorageMap<_, Identity, u16, u64, OptionQuery>; + /// --- MAP ( netuid ) --> subnet mechanism + #[pallet::storage] pub type SubnetMechanism = StorageMap<_, Identity, u16, u16, ValueQuery, DefaultZeroU16>; #[pallet::storage] diff --git a/pallets/subtensor/src/macros/config.rs b/pallets/subtensor/src/macros/config.rs index af448c8771..cf4d97b65b 100644 --- a/pallets/subtensor/src/macros/config.rs +++ b/pallets/subtensor/src/macros/config.rs @@ -210,5 +210,8 @@ mod config { /// Initial EMA price halving period #[pallet::constant] type InitialEmaPriceHalvingPeriod: Get; + /// Block number after a new subnet accept the start call extrinsic. + #[pallet::constant] + type DurationOfStartCall: Get; } } diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index 9f61aca60c..9158073e17 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1910,6 +1910,25 @@ mod dispatches { Ok(()) } + /// Initiates a call on a subnet. + /// + /// # Arguments + /// * `origin` - The origin of the call, which must be signed by the subnet owner. + /// * `netuid` - The unique identifier of the subnet on which the call is being initiated. + /// + /// # Events + /// Emits a `FirstEmissionBlockNumberSet` event on success. + #[pallet::call_index(92)] + #[pallet::weight(( + Weight::from_parts(3_000_000, 0).saturating_add(T::DbWeight::get().reads_writes(6, 1)), + DispatchClass::Operational, + Pays::Yes + ))] + pub fn start_call(origin: T::RuntimeOrigin, netuid: u16) -> DispatchResult { + Self::do_start_call(origin, netuid)?; + Ok(()) + } + /// Recycles alpha from a cold/hot key pair, reducing AlphaOut on a subnet /// /// # Arguments diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 609f87b641..ef965bf169 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -195,6 +195,10 @@ mod errors { ActivityCutoffTooLow, /// Call is disabled CallDisabled, + /// FirstEmissionBlockNumber is already set. + FirstEmissionBlockNumberAlreadySet, + /// need wait for more blocks to accept the start call extrinsic. + NeedWaitingMoreBlocksToStarCall, /// Not enough AlphaOut on the subnet to recycle NotEnoughAlphaOutToRecycle, } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 1c87d37ed1..04a2093abf 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -275,6 +275,12 @@ mod events { /// Parameters: /// (netuid, new_hotkey) SubnetOwnerHotkeySet(u16, T::AccountId), + /// FirstEmissionBlockNumber is set via start call extrinsic + /// + /// Parameters: + /// netuid + /// block number + FirstEmissionBlockNumberSet(u16, u64), /// Alpha has been recycled, reducing AlphaOut on a subnet. /// diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index df9dffabca..3203ae312c 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -81,7 +81,9 @@ mod hooks { // Remove Stake map entries .saturating_add(migrations::migrate_remove_stake_map::migrate_remove_stake_map::()) // Remove unused maps entries - .saturating_add(migrations::migrate_remove_unused_maps_and_values::migrate_remove_unused_maps_and_values::()); + .saturating_add(migrations::migrate_remove_unused_maps_and_values::migrate_remove_unused_maps_and_values::()) + // Set last emission block number for all existed subnets before start call feature applied + .saturating_add(migrations::migrate_set_first_emission_block_number::migrate_set_first_emission_block_number::()); weight } diff --git a/pallets/subtensor/src/migrations/migrate_set_first_emission_block_number.rs b/pallets/subtensor/src/migrations/migrate_set_first_emission_block_number.rs new file mode 100644 index 0000000000..04ad306218 --- /dev/null +++ b/pallets/subtensor/src/migrations/migrate_set_first_emission_block_number.rs @@ -0,0 +1,53 @@ +use super::*; +use crate::HasMigrationRun; +use frame_support::{traits::Get, weights::Weight}; +use scale_info::prelude::string::String; + +pub fn migrate_set_first_emission_block_number() -> Weight { + let migration_name = b"migrate_set_first_emission_block_number".to_vec(); + + let mut weight = T::DbWeight::get().reads(1); + if HasMigrationRun::::get(&migration_name) { + log::info!( + "Migration '{:?}' has already run. Skipping.", + String::from_utf8_lossy(&migration_name) + ); + return weight; + } + + log::info!( + "Running migration '{:?}'", + String::from_utf8_lossy(&migration_name) + ); + + // ------------------------------ + // Step 1: Set the first emission block for all subnets except root + // ------------------------------ + let netuids = Pallet::::get_all_subnet_netuids(); + let current_block_number = Pallet::::get_current_block_as_u64(); + for netuid in netuids.iter() { + if *netuid != 0 { + FirstEmissionBlockNumber::::insert(netuid, current_block_number); + } + } + + // ------------------------------ + // Step 2: Mark Migration as Completed + // ------------------------------ + + HasMigrationRun::::insert(&migration_name, true); + weight = weight.saturating_add(T::DbWeight::get().reads(2)); + + if netuids.is_empty() { + weight = weight.saturating_add(T::DbWeight::get().writes(1_u64)); + } else { + weight = weight.saturating_add(T::DbWeight::get().writes(netuids.len() as u64)); + } + + log::info!( + "Migration '{:?}' completed successfully.", + String::from_utf8_lossy(&migration_name) + ); + + weight +} diff --git a/pallets/subtensor/src/migrations/mod.rs b/pallets/subtensor/src/migrations/mod.rs index 6af6ad2a56..19e057e3ec 100644 --- a/pallets/subtensor/src/migrations/mod.rs +++ b/pallets/subtensor/src/migrations/mod.rs @@ -11,6 +11,7 @@ pub mod migrate_populate_owned_hotkeys; pub mod migrate_rao; pub mod migrate_remove_stake_map; pub mod migrate_remove_unused_maps_and_values; +pub mod migrate_set_first_emission_block_number; pub mod migrate_set_min_burn; pub mod migrate_set_min_difficulty; pub mod migrate_stake_threshold; diff --git a/pallets/subtensor/src/subnets/subnet.rs b/pallets/subtensor/src/subnets/subnet.rs index bf1806da14..310c1d5fde 100644 --- a/pallets/subtensor/src/subnets/subnet.rs +++ b/pallets/subtensor/src/subnets/subnet.rs @@ -320,4 +320,58 @@ impl Pallet { ); } } + + /// Execute the start call for a subnet. + /// + /// This function is used to trigger the start call process for a subnet identified by `netuid`. + /// It ensures that the subnet exists, the caller is the subnet owner, + /// and the last emission block number has not been set yet. + /// It then sets the last emission block number to the current block number. + /// + /// # Parameters + /// + /// * `origin`: The origin of the call, which is used to ensure the caller is the subnet owner. + /// * `netuid`: The unique identifier of the subnet for which the start call process is being initiated. + /// + /// # Raises + /// + /// * `Error::::SubNetworkDoesNotExist`: If the subnet does not exist. + /// * `DispatchError::BadOrigin`: If the caller is not the subnet owner. + /// * `Error::::FirstEmissionBlockNumberAlreadySet`: If the last emission block number has already been set. + /// + /// # Returns + /// + /// * `DispatchResult`: A result indicating the success or failure of the operation. + pub fn do_start_call(origin: T::RuntimeOrigin, netuid: u16) -> DispatchResult { + ensure!( + Self::if_subnet_exist(netuid), + Error::::SubNetworkDoesNotExist + ); + Self::ensure_subnet_owner(origin, netuid)?; + ensure!( + FirstEmissionBlockNumber::::get(netuid).is_none(), + Error::::FirstEmissionBlockNumberAlreadySet + ); + + let registration_block_number = NetworkRegisteredAt::::get(netuid); + let current_block_number = Self::get_current_block_as_u64(); + + ensure!( + current_block_number + >= registration_block_number.saturating_add(T::DurationOfStartCall::get()), + Error::::NeedWaitingMoreBlocksToStarCall + ); + let next_block_number = current_block_number.saturating_add(1); + + FirstEmissionBlockNumber::::insert(netuid, next_block_number); + Self::deposit_event(Event::FirstEmissionBlockNumberSet( + netuid, + next_block_number, + )); + Ok(()) + } + + pub fn is_valid_subnet_for_emission(netuid: u16) -> bool { + FirstEmissionBlockNumber::::get(netuid).is_some() + } } diff --git a/pallets/subtensor/src/tests/migration.rs b/pallets/subtensor/src/tests/migration.rs index c0cbe1b81a..2fac8c5db5 100644 --- a/pallets/subtensor/src/tests/migration.rs +++ b/pallets/subtensor/src/tests/migration.rs @@ -11,6 +11,7 @@ use frame_support::{ traits::{StorageInstance, StoredMap}, weights::Weight, }; + use frame_system::Config; use sp_core::{H256, U256, crypto::Ss58Codec}; use sp_io::hashing::twox_128; @@ -416,3 +417,24 @@ fn test_migrate_subnet_volume() { assert_eq!(new_value, Some(old_value as u128)); }); } + +#[test] +fn test_migrate_set_first_emission_block_number() { + new_test_ext(1).execute_with(|| { + let netuids: [u16; 3] = [1, 2, 3]; + let block_number = 100; + for netuid in netuids.iter() { + add_network(*netuid, 1, 0); + } + run_to_block(block_number); + let weight = crate::migrations::migrate_set_first_emission_block_number::migrate_set_first_emission_block_number::(); + + let expected_weight: Weight = ::DbWeight::get().reads(3) + ::DbWeight::get().writes(netuids.len() as u64); + assert_eq!(weight, expected_weight); + + assert_eq!(FirstEmissionBlockNumber::::get(0), None); + for netuid in netuids.iter() { + assert_eq!(FirstEmissionBlockNumber::::get(netuid), Some(block_number)); + } +}); +} diff --git a/pallets/subtensor/src/tests/mock.rs b/pallets/subtensor/src/tests/mock.rs index 0d979a6126..ea5e3d4492 100644 --- a/pallets/subtensor/src/tests/mock.rs +++ b/pallets/subtensor/src/tests/mock.rs @@ -185,6 +185,7 @@ parameter_types! { pub const InitialDissolveNetworkScheduleDuration: u64 = 5 * 24 * 60 * 60 / 12; // Default as 5 days pub const InitialTaoWeight: u64 = 0; // 100% global weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks + pub const DurationOfStartCall: u64 = 7 * 24 * 60 * 60 / 12; // Default as 7 days } // Configure collective pallet for council @@ -408,6 +409,7 @@ impl crate::Config for Test { type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialTaoWeight = InitialTaoWeight; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; + type DurationOfStartCall = DurationOfStartCall; } pub struct OriginPrivilegeCmp; @@ -662,6 +664,14 @@ pub fn add_network(netuid: u16, 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); +} + +#[allow(dead_code)] +pub fn add_network_without_emission_block(netuid: u16, 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); } #[allow(dead_code)] @@ -670,6 +680,22 @@ pub fn add_dynamic_network(hotkey: &U256, coldkey: &U256) -> u16 { let lock_cost = SubtensorModule::get_network_lock_cost(); SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost); + assert_ok!(SubtensorModule::register_network( + RawOrigin::Signed(*coldkey).into(), + *hotkey + )); + NetworkRegistrationAllowed::::insert(netuid, true); + NetworkPowRegistrationAllowed::::insert(netuid, true); + FirstEmissionBlockNumber::::insert(netuid, 0); + netuid +} + +#[allow(dead_code)] +pub fn add_dynamic_network_without_emission_block(hotkey: &U256, coldkey: &U256) -> u16 { + let netuid = SubtensorModule::get_next_netuid(); + let lock_cost = SubtensorModule::get_network_lock_cost(); + SubtensorModule::add_balance_to_coldkey_account(coldkey, lock_cost); + assert_ok!(SubtensorModule::register_network( RawOrigin::Signed(*coldkey).into(), *hotkey diff --git a/pallets/subtensor/src/tests/mod.rs b/pallets/subtensor/src/tests/mod.rs index 8a9fa6b103..efd45ddef1 100644 --- a/pallets/subtensor/src/tests/mod.rs +++ b/pallets/subtensor/src/tests/mod.rs @@ -17,6 +17,7 @@ mod senate; mod serving; mod staking; mod staking2; +mod subnet; mod swap_coldkey; mod swap_hotkey; mod uids; diff --git a/pallets/subtensor/src/tests/subnet.rs b/pallets/subtensor/src/tests/subnet.rs new file mode 100644 index 0000000000..1daf19159f --- /dev/null +++ b/pallets/subtensor/src/tests/subnet.rs @@ -0,0 +1,224 @@ +use super::mock::*; +use crate::*; +use frame_support::{assert_noop, assert_ok}; +use frame_system::Config; +use sp_core::U256; + +/*************************** + pub fn do_start_call() tests +*****************************/ + +#[test] +fn test_do_start_call_ok() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let tempo: u16 = 13; + let coldkey_account_id = U256::from(0); + let hotkey_account_id = U256::from(1); + let burn_cost = 1000; + //add network + SubtensorModule::set_burn(netuid, burn_cost); + add_network_without_emission_block(netuid, tempo, 0); + assert_eq!(FirstEmissionBlockNumber::::get(netuid), None); + + // 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 + )); + + assert_eq!(SubnetOwner::::get(netuid), coldkey_account_id); + + let block_number = System::block_number() + DurationOfStartCall::get(); + System::set_block_number(block_number); + + assert_ok!(SubtensorModule::start_call( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid + )); + + assert_eq!( + FirstEmissionBlockNumber::::get(netuid), + Some(block_number + 1) + ); + }); +} + +#[test] +fn test_do_start_call_fail_with_not_existed_subnet() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let coldkey_account_id = U256::from(0); + assert_noop!( + SubtensorModule::start_call( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid + ), + Error::::SubNetworkDoesNotExist + ); + }); +} + +#[test] +fn test_do_start_call_fail_not_owner() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let tempo: u16 = 13; + let coldkey_account_id = U256::from(0); + let hotkey_account_id = U256::from(1); + let wrong_owner_account_id = U256::from(2); + let burn_cost = 1000; + //add network + SubtensorModule::set_burn(netuid, burn_cost); + add_network_without_emission_block(netuid, tempo, 0); + // 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 + )); + + assert_eq!(SubnetOwner::::get(netuid), coldkey_account_id); + + System::set_block_number(System::block_number() + DurationOfStartCall::get()); + + assert_noop!( + SubtensorModule::start_call( + <::RuntimeOrigin>::signed(wrong_owner_account_id), + netuid + ), + DispatchError::BadOrigin + ); + }); +} + +#[test] +fn test_do_start_call_fail_with_cannot_start_call_now() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let tempo: u16 = 13; + let coldkey_account_id = U256::from(0); + let hotkey_account_id = U256::from(1); + let burn_cost = 1000; + //add network + SubtensorModule::set_burn(netuid, burn_cost); + add_network_without_emission_block(netuid, tempo, 0); + // 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 + )); + + assert_eq!(SubnetOwner::::get(netuid), coldkey_account_id); + + assert_noop!( + SubtensorModule::start_call( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid + ), + Error::::NeedWaitingMoreBlocksToStarCall + ); + }); +} + +#[test] +fn test_do_start_call_fail_for_set_again() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let tempo: u16 = 13; + let coldkey_account_id = U256::from(0); + let hotkey_account_id = U256::from(1); + let burn_cost = 1000; + //add network + SubtensorModule::set_burn(netuid, burn_cost); + add_network_without_emission_block(netuid, tempo, 0); + assert_eq!(FirstEmissionBlockNumber::::get(netuid), None); + + // 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 + )); + + assert_eq!(SubnetOwner::::get(netuid), coldkey_account_id); + + let block_number = System::block_number() + DurationOfStartCall::get(); + System::set_block_number(block_number); + + assert_ok!(SubtensorModule::start_call( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid + )); + + assert_noop!( + SubtensorModule::start_call( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid + ), + Error::::FirstEmissionBlockNumberAlreadySet + ); + }); +} + +#[test] +fn test_do_start_call_ok_with_same_block_number_after_coinbase() { + new_test_ext(0).execute_with(|| { + let netuid: u16 = 1; + let tempo: u16 = 13; + let coldkey_account_id = U256::from(0); + let hotkey_account_id = U256::from(1); + let burn_cost = 1000; + //add network + SubtensorModule::set_burn(netuid, burn_cost); + add_network_without_emission_block(netuid, tempo, 0); + assert_eq!(FirstEmissionBlockNumber::::get(netuid), None); + + // 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 + )); + + assert_eq!(SubnetOwner::::get(netuid), coldkey_account_id); + + let block_number = System::block_number() + DurationOfStartCall::get(); + System::set_block_number(block_number); + + assert_ok!(SubtensorModule::start_call( + <::RuntimeOrigin>::signed(coldkey_account_id), + netuid + )); + + assert_eq!( + FirstEmissionBlockNumber::::get(netuid), + Some(block_number + 1) + ); + + step_block(tempo); + match FirstEmissionBlockNumber::::get(netuid) { + Some(new_emission_block_number) => { + assert_eq!(new_emission_block_number, block_number + 1) + } + None => assert!(FirstEmissionBlockNumber::::get(netuid).is_some()), + } + }); +} diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 94e82ca8f3..8b390aaa59 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -205,7 +205,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 253, + spec_version: 254, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -1018,6 +1018,7 @@ parameter_types! { pub const InitialDissolveNetworkScheduleDuration: BlockNumber = 5 * 24 * 60 * 60 / 12; // 5 days pub const SubtensorInitialTaoWeight: u64 = 971_718_665_099_567_868; // 0.05267697438728329% tao weight. pub const InitialEmaPriceHalvingPeriod: u64 = 201_600_u64; // 4 weeks + pub const DurationOfStartCall: u64 = 7 * 24 * 60 * 60 / 12; // 7 days } impl pallet_subtensor::Config for Runtime { @@ -1082,6 +1083,7 @@ impl pallet_subtensor::Config for Runtime { type InitialColdkeySwapScheduleDuration = InitialColdkeySwapScheduleDuration; type InitialDissolveNetworkScheduleDuration = InitialDissolveNetworkScheduleDuration; type InitialEmaPriceHalvingPeriod = InitialEmaPriceHalvingPeriod; + type DurationOfStartCall = DurationOfStartCall; } use sp_runtime::BoundedVec;