diff --git a/pallets/subspace/src/global.rs b/pallets/subspace/src/global.rs index 2370c9e87..57303a90c 100644 --- a/pallets/subspace/src/global.rs +++ b/pallets/subspace/src/global.rs @@ -1,4 +1,5 @@ use crate::voting::AUTHORITY_MODE; +// TODO: deposit events on sets use super::*; use frame_support::pallet_prelude::DispatchResult; @@ -12,6 +13,8 @@ impl Pallet { max_allowed_subnets: Self::get_global_max_allowed_subnets(), max_allowed_modules: Self::get_max_allowed_modules(), max_registrations_per_block: Self::get_max_registrations_per_block(), + target_registrations_interval: Self::get_target_registrations_interval(), + target_registrations_per_interval: Self::get_target_registrations_per_interval(), unit_emission: Self::get_unit_emission(), tx_rate_limit: Self::get_tx_rate_limit(), vote_threshold: Self::get_global_vote_threshold(), @@ -19,6 +22,8 @@ impl Pallet { vote_mode: Self::get_vote_mode_global(), burn_rate: Self::get_burn_rate(), min_burn: Self::get_min_burn(), + max_burn: Self::get_max_burn(), + adjustment_alpha: Self::get_adjustment_alpha(), min_stake: Self::get_min_stake_global(), min_weight_stake: Self::get_min_weight_stake(), max_allowed_weights: Self::get_max_allowed_weights_global(), @@ -47,6 +52,11 @@ impl Pallet { Error::::InvalidMaxRegistrationsPerBlock ); + ensure!( + params.target_registrations_interval > 0, + Error::::InvalidTargetRegistrationsInterval + ); + ensure!( params.vote_threshold < 100, Error::::InvalidVoteThreshold @@ -73,6 +83,9 @@ impl Pallet { Self::set_global_max_allowed_subnets(params.max_allowed_subnets); Self::set_max_allowed_modules(params.max_allowed_modules); Self::set_max_registrations_per_block(params.max_registrations_per_block); + Self::set_target_registrations_interval(params.target_registrations_interval); + Self::set_target_registrations_per_interval(params.target_registrations_per_interval); + Self::set_adjustment_alpha(params.adjustment_alpha); Self::set_unit_emission(params.unit_emission); Self::set_tx_rate_limit(params.tx_rate_limit); Self::set_global_vote_threshold(params.vote_threshold); @@ -80,10 +93,23 @@ impl Pallet { Self::set_vote_mode_global(params.vote_mode); Self::set_burn_rate(params.burn_rate); Self::set_min_burn(params.min_burn); + Self::set_max_burn(params.max_burn); Self::set_min_weight_stake(params.min_weight_stake); Self::set_min_stake_global(params.min_stake); } + pub fn get_registrations_this_interval() -> u16 { + RegistrationsThisInterval::::get() + } + + pub fn get_target_registrations_per_interval() -> u16 { + TargetRegistrationsPerInterval::::get() + } + + pub fn set_target_registrations_per_interval(target_interval: u16) { + TargetRegistrationsPerInterval::::set(target_interval) + } + pub fn get_min_weight_stake() -> u64 { MinWeightStake::::get() } @@ -113,6 +139,14 @@ impl Pallet { BurnRate::::get().min(100) } + pub fn get_burn() -> u64 { + Burn::::get() + } + + pub fn set_burn(burn: u64) { + Burn::::set(burn) + } + pub fn set_burn_rate(burn_rate: u16) { BurnRate::::put(burn_rate.min(100)); } @@ -134,6 +168,15 @@ impl Pallet { pub fn get_max_registrations_per_block() -> u16 { MaxRegistrationsPerBlock::::get() } + pub fn set_max_registrations_per_block(max_registrations_per_block: u16) { + MaxRegistrationsPerBlock::::set(max_registrations_per_block); + } + pub fn get_target_registrations_interval() -> u16 { + TargetRegistrationsInterval::::get() + } + pub fn set_target_registrations_interval(target_registrations_interval: u16) { + TargetRegistrationsInterval::::set(target_registrations_interval); + } pub fn get_global_max_name_length() -> u16 { MaxNameLength::::get() } @@ -180,11 +223,27 @@ impl Pallet { TxRateLimit::::put(tx_rate_limit) } + pub fn get_min_burn() -> u64 { + MinBurn::::get() + } + pub fn set_min_burn(min_burn: u64) { MinBurn::::put(min_burn); } - pub fn get_min_burn() -> u64 { - MinBurn::::get() + pub fn get_max_burn() -> u64 { + MaxBurn::::get() + } + + pub fn set_max_burn(max_burn: u64) { + MaxBurn::::put(max_burn); + } + + pub fn get_adjustment_alpha() -> u64 { + AdjustmentAlpha::::get() + } + + pub fn set_adjustment_alpha(adjustment_alpha: u64) { + AdjustmentAlpha::::put(adjustment_alpha); } } diff --git a/pallets/subspace/src/lib.rs b/pallets/subspace/src/lib.rs index d9a9f83c1..fff6cf11f 100644 --- a/pallets/subspace/src/lib.rs +++ b/pallets/subspace/src/lib.rs @@ -127,11 +127,27 @@ pub mod pallet { #[pallet::type_value] pub fn DefaultMinBurn() -> u64 { - 0 + 200_000_000 // 2 $COMAI } - #[pallet::storage] // --- MAP ( netuid ) --> min_allowed_weights + #[pallet::storage] // --- MinBurn pub type MinBurn = StorageValue<_, u64, ValueQuery, DefaultMinBurn>; + #[pallet::type_value] + pub fn DefaultMaxBurn() -> u64 { + 250_000_000_000 // 250 $COMAI + } + + #[pallet::type_value] + pub fn DefaultAdjustmentAlpha() -> u64 { + 0 // u64::MAX / 2 + } + + #[pallet::storage] // --- adjusment alpha + pub type AdjustmentAlpha = StorageValue<_, u64, ValueQuery, DefaultAdjustmentAlpha>; + + #[pallet::storage] // --- MaxBurn + pub type MaxBurn = StorageValue<_, u64, ValueQuery, DefaultMaxBurn>; + #[pallet::type_value] pub fn DefaultLastTxBlock() -> u64 { 0 @@ -156,6 +172,23 @@ pub mod pallet { pub(super) type MaxAllowedSubnets = StorageValue<_, u16, ValueQuery, DefaultMaxAllowedSubnets>; + #[pallet::type_value] + pub fn DefaultRegistrationsThisInterval() -> u16 { + 0 + } + + #[pallet::storage] // --- ITEM ( registrations_this_interval ) + pub(super) type RegistrationsThisInterval = + StorageValue<_, u16, ValueQuery, DefaultRegistrationsThisInterval>; + + #[pallet::type_value] + pub fn DefaultBurn() -> u64 { + 0 + } + + #[pallet::storage] // --- ITEM ( burn ) + pub(super) type Burn = StorageValue<_, u64, ValueQuery, DefaultBurn>; + #[pallet::type_value] pub fn DefaultMaxAllowedModules() -> u16 { 10_000 @@ -180,6 +213,23 @@ pub mod pallet { pub type MaxRegistrationsPerBlock = StorageValue<_, u16, ValueQuery, DefaultMaxRegistrationsPerBlock>; + #[pallet::type_value] + pub fn DefaultTargetRegistrationsPerInterval() -> u16 { + DefaultTargetRegistrationsInterval::::get() / 2 + } + #[pallet::storage] // --- ITEM( global_target_registrations_interval ) + pub type TargetRegistrationsPerInterval = + StorageValue<_, u16, ValueQuery, DefaultTargetRegistrationsPerInterval>; + + #[pallet::type_value] // --- ITEM( global_target_registrations_interval ) Measured in the number of blocks + pub fn DefaultTargetRegistrationsInterval() -> u16 { + DefaultTempo::::get() * 2 // 2 times the epoch + } + + #[pallet::storage] // --- ITEM( global_target_registrations_interval ) + pub type TargetRegistrationsInterval = + StorageValue<_, u16, ValueQuery, DefaultTargetRegistrationsInterval>; + #[pallet::type_value] pub fn DefaultMinStakeGlobal() -> u64 { 100 @@ -225,14 +275,19 @@ pub mod pallet { // mins pub min_burn: u64, // min burn required + pub max_burn: u64, // max burn allowed pub min_stake: u64, // min stake required pub min_weight_stake: u64, // min weight stake required // other - pub unit_emission: u64, // emission per block - pub tx_rate_limit: u64, // tx rate limit - pub vote_threshold: u16, // out of 100 - pub vote_mode: Vec, // out of 100 + pub target_registrations_per_interval: u16, // desired number of registrations per interval + pub target_registrations_interval: u16, /* the number of blocks that defines the + * registration interval */ + pub adjustment_alpha: u64, // adjustment alpha + pub unit_emission: u64, // emission per block + pub tx_rate_limit: u64, // tx rate limit + pub vote_threshold: u16, // out of 100 + pub vote_mode: Vec, // out of 100 } #[pallet::type_value] @@ -243,11 +298,15 @@ pub mod pallet { max_allowed_modules: DefaultMaxAllowedModules::::get(), max_allowed_weights: DefaultMaxAllowedWeightsGlobal::::get(), max_registrations_per_block: DefaultMaxRegistrationsPerBlock::::get(), + target_registrations_per_interval: DefaultTargetRegistrationsPerInterval::::get(), + target_registrations_interval: DefaultTargetRegistrationsInterval::::get(), max_name_length: DefaultMaxNameLength::::get(), max_proposals: DefaultMaxProposals::::get(), min_burn: DefaultMinBurn::::get(), + max_burn: DefaultMaxBurn::::get(), min_stake: DefaultMinStakeGlobal::::get(), min_weight_stake: DefaultMinWeightStake::::get(), + adjustment_alpha: DefaultAdjustmentAlpha::::get(), unit_emission: DefaultUnitEmission::::get(), tx_rate_limit: DefaultTxRateLimit::::get(), vote_threshold: DefaultVoteThreshold::::get(), @@ -724,6 +783,8 @@ pub mod pallet { * weights on a subnetwork. */ ModuleRegistered(u16, u16, T::AccountId), /* --- Event created when a new module * account has been registered to the chain. */ + ModuleDeregistered(u16, u16, T::AccountId), /* --- Event created when a module account + * has been deregistered from the chain. */ BulkModulesRegistered(u16, u16), /* --- Event created when multiple uids have been * concurrently registered. */ BulkBalancesSet(u16, u16), @@ -742,8 +803,8 @@ pub mod pallet { MaxNameLengthSet(u16), // --- Event created when setting the maximum network name length MaxAllowedSubnetsSet(u16), // --- Event created when setting the maximum allowed subnets MaxAllowedModulesSet(u16), // --- Event created when setting the maximum allowed modules - MaxRegistrationsPerBlockSet(u16), /* --- Event created when we set max registrations - * per block */ + MaxRegistrationsPerBlockSet(u16), // --- Event created when we set max registrations + target_registrations_intervalSet(u16), // --- Event created when we set target registrations GlobalUpdate(u16, u16, u16, u16, u64, u64), GlobalProposalAccepted(u64), // (id) CustomProposalAccepted(u64), // (id) @@ -848,6 +909,7 @@ pub mod pallet { InvalidMaxAllowedSubnets, InvalidMaxAllowedModules, InvalidMaxRegistrationsPerBlock, + InvalidTargetRegistrationsInterval, InvalidVoteThreshold, InvalidMaxProposals, InvalidUnitEmission, @@ -1136,6 +1198,7 @@ pub mod pallet { max_name_length: u16, max_proposals: u64, max_registrations_per_block: u16, + target_registrations_interval: u16, min_burn: u64, min_stake: u64, min_weight_stake: u64, @@ -1152,6 +1215,7 @@ pub mod pallet { params.max_name_length = max_name_length; params.max_proposals = max_proposals; params.max_registrations_per_block = max_registrations_per_block; + params.target_registrations_interval = target_registrations_interval; params.min_burn = min_burn; params.min_stake = min_stake; params.min_weight_stake = min_weight_stake; diff --git a/pallets/subspace/src/registration.rs b/pallets/subspace/src/registration.rs index e3464ecf6..aa132a5fc 100644 --- a/pallets/subspace/src/registration.rs +++ b/pallets/subspace/src/registration.rs @@ -45,7 +45,7 @@ impl Pallet { // --- 5. Ensure the caller has enough stake to register. let min_stake: u64 = MinStake::::get(netuid); - let min_burn: u64 = Self::get_min_burn(); + let min_burn: u64 = Self::get_burn(); // also ensures that in the case min_burn is present, the stake is enough // as burn, will be decreased from the stake on the module @@ -54,12 +54,18 @@ impl Pallet { Error::::NotEnoughStakeToRegister ); - // --- 6. Ensure the module key is not already registered. + // --- 6. Ensure the module key is not already registered, + // and namespace is not already taken. ensure!( !Self::key_registered(netuid, &key), Error::::KeyAlreadyRegistered ); + ensure!( + !Self::does_module_name_exist(netuid, name.clone()), + Error::::NameAlreadyRegistered + ); + // --- 7. Check if we are exceeding the max allowed modules per network. // If we do deregister slot. Self::check_module_limits(netuid); @@ -74,10 +80,24 @@ impl Pallet { if min_burn > 0 { // if min burn is present, decrease the stake by the min burn Self::decrease_stake(netuid, &key, &module_key, min_burn); + + // Ensure that the stake decreased after the burn. + let current_stake: u64 = Self::get_total_stake_to(netuid, &key); + ensure!( + current_stake == stake_amount.saturating_sub(min_burn), + Error::::NotEnoughStakeToRegister + ); } - // --- 10. Increment the number of registrations per block. + // Make sure that the registration went through. + ensure!( + Self::key_registered(netuid, &key), + Error::::NotRegistered + ); + + // --- 10. Increment the number of registrations. RegistrationsPerBlock::::mutate(|val| *val += 1); + RegistrationsThisInterval::::mutate(|val| *val += 1); // --- Deposit successful event. Self::deposit_event(Event::ModuleRegistered(netuid, uid, module_key.clone())); @@ -104,7 +124,9 @@ impl Pallet { Error::::StillRegistered ); - // --- 5. Ok and done. + // --- Deposit successful event. + Self::deposit_event(Event::ModuleDeregistered(netuid, uid, key)); + // --- 3. Ok and done. Ok(()) } @@ -127,7 +149,7 @@ impl Pallet { if (uid as usize) < vec.len() { vec[uid as usize] } else { - 0_u64 + 0u64 } } pub fn get_lowest_uid(netuid: u16) -> u16 { diff --git a/pallets/subspace/src/staking.rs b/pallets/subspace/src/staking.rs index 6ed5a7013..afefeb836 100644 --- a/pallets/subspace/src/staking.rs +++ b/pallets/subspace/src/staking.rs @@ -1,6 +1,5 @@ use super::*; -// import vec use sp_arithmetic::per_things::Percent; use sp_std::vec::Vec; diff --git a/pallets/subspace/src/step.rs b/pallets/subspace/src/step.rs index 668a27961..cf3fda9b9 100644 --- a/pallets/subspace/src/step.rs +++ b/pallets/subspace/src/step.rs @@ -2,12 +2,18 @@ use super::*; use crate::math::*; use frame_support::storage::{IterableStorageDoubleMap, IterableStorageMap}; use sp_std::vec; -use substrate_fixed::types::{I32F32, I64F64}; +use substrate_fixed::types::{I110F18, I32F32, I64F64}; impl Pallet { pub fn block_step() { let block_number: u64 = Self::get_current_block_as_u64(); RegistrationsPerBlock::::mutate(|val| *val = 0); + + let registration_this_interval = Self::get_registrations_this_interval(); + + // adjust registrations parameters + Self::adjust_registration(block_number, registration_this_interval); + log::debug!("block_step for block: {:?} ", block_number); for (netuid, tempo) in as IterableStorageMap>::iter() { let new_queued_emission: u64 = Self::calculate_network_emission(netuid); @@ -226,7 +232,7 @@ impl Pallet { // sum the votes per module for sparse_row in bonds.iter_mut() { for (j, value) in sparse_row.iter_mut() { - if col_sum[*j as usize] > I32F32::from_num(0.0_f32) { + if col_sum[*j as usize] > I32F32::from_num(0.0f32) { *value /= col_sum[*j as usize]; } } @@ -461,4 +467,43 @@ impl Pallet { } burn_amount_per_epoch } + + pub fn adjust_registration(block_number: u64, registrations_this_interval: u16) { + let target_registrations_interval = Self::get_target_registrations_interval(); + if block_number % u64::from(target_registrations_interval) == 0 { + let current_burn = Self::get_burn(); + let target_registrations_per_interval = Self::get_target_registrations_per_interval(); + + Self::set_burn(Self::adjust_burn( + current_burn, + registrations_this_interval, + target_registrations_per_interval, + )); + + RegistrationsThisInterval::::mutate(|val| *val = 0); + } + } + + pub fn adjust_burn( + current_burn: u64, + registrations_this_interval: u16, + target_registrations_per_interval: u16, + ) -> u64 { + let updated_burn: I110F18 = I110F18::from_num(current_burn) + * I110F18::from_num(registrations_this_interval + target_registrations_per_interval) + / I110F18::from_num( + target_registrations_per_interval + target_registrations_per_interval, + ); + let alpha: I110F18 = + I110F18::from_num(Self::get_adjustment_alpha()) / I110F18::from_num(u64::MAX); + let next_value: I110F18 = alpha * I110F18::from_num(current_burn) + + (I110F18::from_num(1.0) - alpha) * updated_burn; + if next_value >= I110F18::from_num(Self::get_max_burn()) { + Self::get_max_burn() + } else if next_value <= I110F18::from_num(Self::get_min_burn()) { + Self::get_min_burn() + } else { + next_value.to_num::() + } + } } diff --git a/pallets/subspace/src/subnet.rs b/pallets/subspace/src/subnet.rs index 54f91a249..35e121ae3 100644 --- a/pallets/subspace/src/subnet.rs +++ b/pallets/subspace/src/subnet.rs @@ -254,6 +254,12 @@ impl Pallet { } } + // TODO: see if we can optimize this further + pub fn does_module_name_exist(netuid: u16, name: Vec) -> bool { + as IterableStorageDoubleMap>>::iter_prefix(netuid) + .any(|(_uid, _name)| _name == name) + } + pub fn is_subnet_founder(netuid: u16, key: &T::AccountId) -> bool { Founder::::get(netuid) == *key } @@ -823,9 +829,6 @@ impl Pallet { pub fn get_last_update(netuid: u16) -> Vec { LastUpdate::::get(netuid) } - pub fn set_max_registrations_per_block(max_registrations_per_block: u16) { - MaxRegistrationsPerBlock::::set(max_registrations_per_block); - } pub fn is_registered(netuid: u16, key: &T::AccountId) -> bool { Uids::::contains_key(netuid, key)