From 67a74cd18060a96652b077d03b15ee37bd787fcb Mon Sep 17 00:00:00 2001 From: JZ <115138297+Supremesource@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:51:01 +0100 Subject: [PATCH] feat: adding DAO whitelist to subnet 0 (#42) * Update global_params function to use generic type * fix: manually impl debug for GlobalParams * added whitelist * refac: updated whitelist calls * refac: turning LegitWhitelist to Vec * refac: reverting back to the mutlisig * fix: test cases * refac: change whitelist to a map --------- Co-authored-by: saiintbrisson --- node/src/chain_spec.rs | 2 +- pallets/subspace/src/global.rs | 44 +++++++++--- pallets/subspace/src/lib.rs | 93 +++++++++++++++++++++++--- pallets/subspace/src/registration.rs | 52 ++++++++++++++ pallets/subspace/src/step.rs | 2 +- pallets/subspace/src/voting.rs | 2 +- pallets/subspace/tests/mock.rs | 2 +- pallets/subspace/tests/registration.rs | 24 ++++++- pallets/subspace/tests/weights.rs | 2 +- 9 files changed, 196 insertions(+), 27 deletions(-) diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs index e318ae19f..9f36c49eb 100644 --- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -195,7 +195,7 @@ pub fn generate_config(network: String) -> Result { authority_keys_from_seed("Alice"), authority_keys_from_seed("Bob"), ], - // Sudo account + // Sudo account, this is multisig of 5 Ss58Codec::from_ss58check("5FXymAnjbb7p57pNyfdLb6YCdzm73ZhVq6oFF1AdCEPEg8Uw") .unwrap(), // Pre-funded a diff --git a/pallets/subspace/src/global.rs b/pallets/subspace/src/global.rs index faf1b4bd5..55728aa20 100644 --- a/pallets/subspace/src/global.rs +++ b/pallets/subspace/src/global.rs @@ -7,7 +7,7 @@ use sp_arithmetic::per_things::Percent; use system::ensure_root; impl Pallet { - pub fn global_params() -> GlobalParams { + pub fn global_params() -> GlobalParams { GlobalParams { max_name_length: Self::get_global_max_name_length(), max_allowed_subnets: Self::get_global_max_allowed_subnets(), @@ -20,6 +20,7 @@ impl Pallet { vote_threshold: Self::get_global_vote_threshold(), max_proposals: Self::get_max_proposals(), vote_mode: Self::get_vote_mode_global(), + nominator: Self::get_nominator(), burn_rate: Self::get_burn_rate(), min_burn: Self::get_min_burn(), max_burn: Self::get_max_burn(), @@ -31,7 +32,7 @@ impl Pallet { } } - pub fn check_global_params(params: &GlobalParams) -> DispatchResult { + pub fn check_global_params(params: &GlobalParams) -> DispatchResult { // checks if params are valid let old_params = Self::global_params(); @@ -107,7 +108,7 @@ impl Pallet { Ok(()) } - pub fn set_global_params(params: GlobalParams) { + pub fn set_global_params(params: GlobalParams) { // Check if the params are valid Self::check_global_params(¶ms).expect("global params are invalid"); @@ -129,6 +130,15 @@ impl Pallet { Self::set_min_weight_stake(params.min_weight_stake); Self::set_min_stake_global(params.min_stake); Self::set_floor_delegation_fee(params.floor_delegation_fee); + Self::set_nominator(params.nominator); + } + + pub fn get_nominator() -> T::AccountId { + Nominator::::get() + } + + pub fn set_nominator(nominator: T::AccountId) { + Nominator::::put(nominator) } pub fn get_registrations_this_interval() -> u16 { @@ -179,6 +189,9 @@ impl Pallet { pub fn get_burn_rate() -> u16 { BurnRate::::get() } + pub fn set_burn_rate(burn_rate: u16) { + BurnRate::::put(burn_rate.min(100)); + } pub fn get_burn() -> u64 { Burn::::get() @@ -190,18 +203,13 @@ impl Pallet { Self::deposit_event(Event::RegistrationBurnChanged(burn)); } - pub fn set_burn_rate(burn_rate: u16) { - BurnRate::::put(burn_rate.min(100)); + pub fn get_max_proposals() -> u64 { + MaxProposals::::get() } - pub fn set_max_proposals(max_proposals: u64) { MaxProposals::::put(max_proposals); } - pub fn get_max_proposals() -> u64 { - MaxProposals::::get() - } - pub fn get_global_vote_threshold() -> u16 { GlobalVoteThreshold::::get() } @@ -234,7 +242,7 @@ impl Pallet { MaxNameLength::::put(max_name_length) } - pub fn do_update_global(origin: T::RuntimeOrigin, params: GlobalParams) -> DispatchResult { + pub fn do_update_global(origin: T::RuntimeOrigin, params: GlobalParams) -> DispatchResult { ensure_root(origin)?; // TODO, once decentralization is reached, remove this @@ -301,4 +309,18 @@ impl Pallet { pub fn set_adjustment_alpha(adjustment_alpha: u64) { AdjustmentAlpha::::put(adjustment_alpha); } + + // Whitelist management + + pub fn is_in_legit_whitelist(account_id: &T::AccountId) -> bool { + LegitWhitelist::::contains_key(account_id) + } + + pub fn insert_to_whitelist(module_key: T::AccountId) { + LegitWhitelist::::insert(module_key, ()); + } + + pub fn rm_from_whitelist(module_key: &T::AccountId) { + LegitWhitelist::::remove(&module_key); + } } diff --git a/pallets/subspace/src/lib.rs b/pallets/subspace/src/lib.rs index d32074aa6..8353dd18d 100644 --- a/pallets/subspace/src/lib.rs +++ b/pallets/subspace/src/lib.rs @@ -271,9 +271,9 @@ pub mod pallet { pub controller: T::AccountId, } - #[derive(Decode, Encode, PartialEq, Eq, Clone, Debug, TypeInfo)] - // skip - pub struct GlobalParams { + #[derive(Decode, Encode, PartialEq, Eq, Clone, TypeInfo)] + #[scale_info(skip_type_params(T))] + pub struct GlobalParams { pub burn_rate: u16, // max pub max_name_length: u16, // max length of a network name @@ -299,10 +299,50 @@ pub mod pallet { pub tx_rate_limit: u64, // tx rate limit pub vote_threshold: u16, // out of 100 pub vote_mode: Vec, // out of 100 + pub nominator: T::AccountId, + } + + impl core::fmt::Debug for GlobalParams + where + T::AccountId: core::fmt::Debug, + { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("GlobalParams") + .field("burn_rate", &self.burn_rate) + .field("max_name_length", &self.max_name_length) + .field("max_allowed_subnets", &self.max_allowed_subnets) + .field("max_allowed_modules", &self.max_allowed_modules) + .field( + "max_registrations_per_block", + &self.max_registrations_per_block, + ) + .field("max_allowed_weights", &self.max_allowed_weights) + .field("max_proposals", &self.max_proposals) + .field("min_burn", &self.min_burn) + .field("max_burn", &self.max_burn) + .field("min_stake", &self.min_stake) + .field("floor_delegation_fee", &self.floor_delegation_fee) + .field("min_weight_stake", &self.min_weight_stake) + .field( + "target_registrations_per_interval", + &self.target_registrations_per_interval, + ) + .field( + "target_registrations_interval", + &self.target_registrations_interval, + ) + .field("adjustment_alpha", &self.adjustment_alpha) + .field("unit_emission", &self.unit_emission) + .field("tx_rate_limit", &self.tx_rate_limit) + .field("vote_threshold", &self.vote_threshold) + .field("vote_mode", &self.vote_mode) + .field("nominator", &self.nominator) + .finish() + } } #[pallet::type_value] - pub fn DefaultGlobalParams() -> GlobalParams { + pub fn DefaultGlobalParams() -> GlobalParams { GlobalParams { burn_rate: DefaultBurnRate::::get(), max_allowed_subnets: DefaultMaxAllowedSubnets::::get(), @@ -323,6 +363,7 @@ pub mod pallet { tx_rate_limit: DefaultTxRateLimit::::get(), vote_threshold: DefaultVoteThreshold::::get(), vote_mode: DefaultVoteMode::::get(), + nominator: DefaultNominator::::get(), } } @@ -522,6 +563,13 @@ pub mod pallet { #[pallet::storage] // --- MAP ( netuid ) --> epoch pub type GlobalVoteThreshold = StorageValue<_, u16, ValueQuery, DefaultVoteThreshold>; + #[pallet::type_value] + pub fn DefaultNominator() -> T::AccountId { + T::AccountId::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap() + } + #[pallet::storage] + pub type Nominator = StorageValue<_, T::AccountId, ValueQuery, DefaultNominator>; + // VOTING MODE // OPTIONS -> [stake, authority, quadratic] #[pallet::type_value] @@ -588,7 +636,6 @@ pub mod pallet { #[pallet::storage] // --- DMAP ( netuid, uid ) --> module_key pub(super) type Keys = StorageDoubleMap<_, Identity, u16, Identity, u16, T::AccountId, ValueQuery, DefaultKey>; - #[pallet::type_value] pub fn DefaultName() -> Vec { vec![] @@ -622,7 +669,6 @@ pub mod pallet { >; // STATE OF THE MODULE - #[pallet::type_value] pub fn DefaultBlockAtRegistration() -> u64 { 0 @@ -746,6 +792,11 @@ pub mod pallet { DefaultWeights, >; + // whitelist for the base subnet (netuid 0) + #[pallet::storage] + pub(super) type LegitWhitelist = + StorageMap<_, Identity, T::AccountId, (), ValueQuery, GetDefault>; + // ======================================================== // ==== Voting System to Update Global and Subnet ==== // ======================================================== @@ -754,7 +805,7 @@ pub mod pallet { pub struct Proposal { // --- parameters pub subnet_params: SubnetParams, - pub global_params: GlobalParams, + pub global_params: GlobalParams, pub netuid: u16, // FOR SUBNET PROPOSAL ONLY pub votes: u64, pub participants: Vec, @@ -807,6 +858,10 @@ pub mod pallet { * account has been registered to the chain. */ ModuleDeregistered(u16, u16, T::AccountId), /* --- Event created when a module account * has been deregistered from the chain. */ + WhitelistModuleAdded(T::AccountId), /* --- Event created when a module account has been + * added to the whitelist. */ + WhitelistModuleRemoved(T::AccountId), /* --- Event created when a module account has + * been removed from the whitelist. */ BulkModulesRegistered(u16, u16), /* --- Event created when multiple uids have been * concurrently registered. */ BulkBalancesSet(u16, u16), @@ -827,7 +882,8 @@ pub mod pallet { MaxAllowedModulesSet(u16), // --- Event created when setting the maximum allowed modules MaxRegistrationsPerBlockSet(u16), // --- Event created when we set max registrations target_registrations_intervalSet(u16), // --- Event created when we set target registrations - GlobalParamsUpdated(GlobalParams), // --- Event created when global parameters are updated + GlobalParamsUpdated(GlobalParams), /* --- Event created when global parameters are + * updated */ SubnetParamsUpdated(u16), // --- Event created when subnet parameters are updated GlobalProposalAccepted(u64), // (id) CustomProposalAccepted(u64), // (id) @@ -902,6 +958,12 @@ pub mod pallet { ModuleNameDoesNotExist, /* --- Thrown when the user tries to remove a module name that * does not exist. */ EmptyKeys, + NotNominator, /* --- Thrown when the user tries to set the nominator and is not the + * nominator */ + AlreadyWhitelisted, /* --- Thrown when the user tries to whitelist an account that is + * already whitelisted. */ + NotWhitelisted, /* --- Thrown when the user tries to remove an account from the + * whitelist that is not whitelisted. */ InvalidShares, ProfitSharesNotAdded, NotFounder, @@ -1169,6 +1231,19 @@ pub mod pallet { Self::do_add_profit_shares(origin, keys, shares) } + #[pallet::weight((Weight::zero(), DispatchClass::Normal, Pays::No))] + pub fn add_to_whitelist(origin: OriginFor, module_key: T::AccountId) -> DispatchResult { + Self::do_add_to_whitelist(origin, module_key) + } + + #[pallet::weight((Weight::zero(), DispatchClass::Normal, Pays::No))] + pub fn remove_from_whitelist( + origin: OriginFor, + module_key: T::AccountId, + ) -> DispatchResult { + Self::do_remove_from_whitelist(origin, module_key) + } + #[pallet::weight((Weight::zero(), DispatchClass::Normal, Pays::No))] pub fn update_global( origin: OriginFor, @@ -1190,6 +1265,7 @@ pub mod pallet { floor_delegation_fee: Percent, target_registrations_per_interval: u16, target_registrations_interval: u16, + nominator: T::AccountId, ) -> DispatchResult { let mut params = Self::global_params(); @@ -1211,6 +1287,7 @@ pub mod pallet { params.floor_delegation_fee = floor_delegation_fee; params.target_registrations_per_interval = target_registrations_per_interval; params.target_registrations_interval = target_registrations_interval; + params.nominator = nominator; // Check if the parameters are valid Self::check_global_params(¶ms)?; diff --git a/pallets/subspace/src/registration.rs b/pallets/subspace/src/registration.rs index b72e8fb04..ea819a882 100644 --- a/pallets/subspace/src/registration.rs +++ b/pallets/subspace/src/registration.rs @@ -6,6 +6,58 @@ use frame_system::ensure_signed; use sp_std::vec::Vec; impl Pallet { + pub fn do_add_to_whitelist( + origin: T::RuntimeOrigin, + module_key: T::AccountId, + ) -> DispatchResult { + // --- 1. Check that the caller has signed the transaction. + let key = ensure_signed(origin)?; + + // --- 2. Ensure that the key is the nominator multisig. + ensure!(Self::get_nominator() == key, Error::::NotNominator); + + // --- 3. Ensure that the module_key is not already in the whitelist. + ensure!( + !Self::is_in_legit_whitelist(&module_key), + Error::::AlreadyWhitelisted + ); + + // --- 4. Insert the module_key into the whitelist. + Self::insert_to_whitelist(module_key.clone()); + + // -- deposit event + Self::deposit_event(Event::WhitelistModuleAdded(module_key)); + + // --- 5. Ok and done. + Ok(()) + } + + pub fn do_remove_from_whitelist( + origin: T::RuntimeOrigin, + module_key: T::AccountId, + ) -> DispatchResult { + // --- 1. Check that the caller has signed the transaction. + let key = ensure_signed(origin)?; + + // --- 2. Ensure that the key is the nominator multisig. + ensure!(Self::get_nominator() == key, Error::::NotNominator); + + // --- 3. Ensure that the module_key is in the whitelist. + ensure!( + Self::is_in_legit_whitelist(&module_key), + Error::::NotWhitelisted + ); + + // --- 4. Remove the module_key from the whitelist. + Self::rm_from_whitelist(&module_key); + + // -- deposit event + Self::deposit_event(Event::WhitelistModuleRemoved(module_key)); + + // --- 5. Ok and done. + Ok(()) + } + // TODO: //- check ip // - add ability to set delegaiton fee, straight in registration diff --git a/pallets/subspace/src/step.rs b/pallets/subspace/src/step.rs index d731e5459..ef5e766c9 100644 --- a/pallets/subspace/src/step.rs +++ b/pallets/subspace/src/step.rs @@ -424,7 +424,7 @@ impl Pallet { fn process_weights( netuid: u16, n: u16, - global_params: &GlobalParams, + global_params: &GlobalParams, subnet_params: &SubnetParams, current_block: u64, stake_f64: &[I64F64], diff --git a/pallets/subspace/src/voting.rs b/pallets/subspace/src/voting.rs index c27c7b1b6..ca69e1aed 100644 --- a/pallets/subspace/src/voting.rs +++ b/pallets/subspace/src/voting.rs @@ -55,7 +55,7 @@ impl Pallet { pub fn do_add_global_proposal( origin: T::RuntimeOrigin, // params - params: GlobalParams, + params: GlobalParams, ) -> DispatchResult { let mut proposal = Self::default_proposal(); proposal.global_params = params; diff --git a/pallets/subspace/tests/mock.rs b/pallets/subspace/tests/mock.rs index a8d763681..ef797a3db 100644 --- a/pallets/subspace/tests/mock.rs +++ b/pallets/subspace/tests/mock.rs @@ -243,7 +243,7 @@ pub fn delegate_register_module( let result = SubspaceModule::register(origin, network, name.clone(), address, stake, module_key); - log::info!("Register ok neuron: network: {name:?}, module_key: {module_key:?} key: {key:?}",); + log::info!("Register ok module: network: {name:?}, module_key: {module_key:?} key: {key:?}",); result } diff --git a/pallets/subspace/tests/registration.rs b/pallets/subspace/tests/registration.rs index 9189c7173..65904a4ba 100644 --- a/pallets/subspace/tests/registration.rs +++ b/pallets/subspace/tests/registration.rs @@ -105,13 +105,13 @@ fn test_registration_ok() { register_module(netuid, key, 0) .unwrap_or_else(|_| panic!("register module failed for key {key:?}")); - // Check if neuron has added to the specified network(netuid) + // Check if module has added to the specified network(netuid) assert_eq!(SubspaceModule::get_subnet_n(netuid), 1); - // Check if the neuron has added to the Keys + // Check if the module has added to the Keys let neuron_uid = SubspaceModule::get_uid_for_key(netuid, &key); assert_eq!(SubspaceModule::get_uid_for_key(netuid, &key), 0); - // Check if neuron has added to Uids + // Check if module has added to Uids let neuro_uid = SubspaceModule::get_uid_for_key(netuid, &key); assert_eq!(neuro_uid, neuron_uid); @@ -177,3 +177,21 @@ fn register_same_key_twice() { ); }); } + +#[test] +fn test_whitelist() { + new_test_ext().execute_with(|| { + let key = U256::from(0); + let adding_key = U256::from(1); + let mut params = SubspaceModule::global_params(); + params.nominator = key; + SubspaceModule::set_global_params(params); + + // add key to whitelist + assert_ok!(SubspaceModule::add_to_whitelist( + get_origin(key), + adding_key + )); + assert!(SubspaceModule::is_in_legit_whitelist(&adding_key)); + }); +} diff --git a/pallets/subspace/tests/weights.rs b/pallets/subspace/tests/weights.rs index e259b81f9..f52014534 100644 --- a/pallets/subspace/tests/weights.rs +++ b/pallets/subspace/tests/weights.rs @@ -215,7 +215,7 @@ fn test_normalize_weights_does_not_mutate_when_sum_not_zero() { #[test] fn test_min_weight_stake() { new_test_ext().execute_with(|| { - let mut global_params: GlobalParams = SubspaceModule::global_params(); + let mut global_params = SubspaceModule::global_params(); global_params.min_weight_stake = to_nano(20); SubspaceModule::set_global_params(global_params);