From 2a76a7617da0086cd3d06b5089d3acce47c0671b Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 18 Dec 2024 15:20:03 -0500 Subject: [PATCH 1/2] Add childkey white list --- pallets/subtensor/src/lib.rs | 17 ++++ pallets/subtensor/src/macros/dispatches.rs | 14 +++ pallets/subtensor/src/macros/errors.rs | 4 + pallets/subtensor/src/macros/events.rs | 2 + pallets/subtensor/src/macros/hooks.rs | 2 + pallets/subtensor/src/staking/set_children.rs | 93 +++++++++++++++++++ pallets/subtensor/src/tests/children.rs | 74 +++++++++++++++ 7 files changed, 206 insertions(+) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 2f92fd60e..dc283853c 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -311,6 +311,11 @@ pub mod pallet { vec![] } #[pallet::type_value] + /// Default account linkage + pub fn DefaultChildkeyWhitelist() -> Vec { + vec![] + } + #[pallet::type_value] /// Default pending childkeys pub fn DefaultPendingChildkeys() -> (Vec<(u64, T::AccountId)>, u64) { (vec![], 0) @@ -876,6 +881,18 @@ pub mod pallet { ValueQuery, DefaultAccountLinkage, >; + #[pallet::storage] + /// DMAP ( child, netuid ) --> Vec + pub type ChildkeyWhitelist = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Identity, + u16, + Vec, + ValueQuery, + DefaultChildkeyWhitelist, + >; #[pallet::storage] // --- DMAP ( cold ) --> Vec | Maps coldkey to hotkeys that stake to it pub type StakingHotkeys = StorageMap<_, Blake2_128Concat, T::AccountId, Vec, ValueQuery>; diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index b62ef4f8c..d32e05af7 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1484,5 +1484,19 @@ mod dispatches { ) -> DispatchResult { Self::user_add_network(origin, identity) } + + /// User register a new subnetwork + #[pallet::call_index(81)] + #[pallet::weight((Weight::from_parts(157_000_000, 0) + .saturating_add(T::DbWeight::get().reads(16)) + .saturating_add(T::DbWeight::get().writes(30)), DispatchClass::Operational, Pays::No))] + pub fn set_childkey_whitelist( + coldkey: OriginFor, + childkey: T::AccountId, + netuid: u16, + coldkey_list: Vec, + ) -> DispatchResult { + Self::do_set_childkey_whitelist(coldkey, childkey, netuid, coldkey_list) + } } } diff --git a/pallets/subtensor/src/macros/errors.rs b/pallets/subtensor/src/macros/errors.rs index 1f1939758..98c558131 100644 --- a/pallets/subtensor/src/macros/errors.rs +++ b/pallets/subtensor/src/macros/errors.rs @@ -179,5 +179,9 @@ mod errors { InputLengthsUnequal, /// A transactor exceeded the rate limit for setting weights. CommittingWeightsTooFast, + /// Too many entries in the white list + TooManyWhitelistEntries, + /// The coldkey is not whitelisted to add child. + ColdkeyIsNotWhitelisted, } } diff --git a/pallets/subtensor/src/macros/events.rs b/pallets/subtensor/src/macros/events.rs index 668eae7fe..ef7d6fcd9 100644 --- a/pallets/subtensor/src/macros/events.rs +++ b/pallets/subtensor/src/macros/events.rs @@ -185,6 +185,8 @@ mod events { SetChildrenScheduled(T::AccountId, u16, u64, Vec<(u64, T::AccountId)>), /// The children of a hotkey have been set SetChildren(T::AccountId, u16, Vec<(u64, T::AccountId)>), + /// The whitelist for childkey is set + ChildkeyWhitelistSet(T::AccountId, u16, Vec), /// The hotkey emission tempo has been set HotkeyEmissionTempoSet(u64), /// The network maximum stake has been set diff --git a/pallets/subtensor/src/macros/hooks.rs b/pallets/subtensor/src/macros/hooks.rs index 1077acb76..96267291f 100644 --- a/pallets/subtensor/src/macros/hooks.rs +++ b/pallets/subtensor/src/macros/hooks.rs @@ -72,6 +72,8 @@ mod hooks { // Migrate Delegate Ids on chain .saturating_add(migrations::migrate_chain_identity::migrate_set_hotkey_identities::()) // Migrate Commit-Reval 2.0 + .saturating_add(migrations::migrate_commit_reveal_v2::migrate_commit_reveal_2::()) + // Migrate rename WeightMinStake to WeightThreshold .saturating_add(migrations::migrate_commit_reveal_v2::migrate_commit_reveal_2::()); weight } diff --git a/pallets/subtensor/src/staking/set_children.rs b/pallets/subtensor/src/staking/set_children.rs index 682475abe..053b549e3 100644 --- a/pallets/subtensor/src/staking/set_children.rs +++ b/pallets/subtensor/src/staking/set_children.rs @@ -89,6 +89,15 @@ impl Pallet { // Ensure that the number of children does not exceed 5. ensure!(children.len() <= 5, Error::::TooManyChildren); + // Check that the parent coldkey is whitelisted or childkey whitelist is empty + for (_, child_i) in &children { + let child_whitelist = ChildkeyWhitelist::::get(child_i, netuid); + ensure!( + child_whitelist.is_empty() || child_whitelist.contains(&coldkey), + Error::::ColdkeyIsNotWhitelisted + ); + } + // Ensure that each child is not the hotkey. for (_, child_i) in &children { ensure!(child_i != &hotkey, Error::::InvalidChild); @@ -234,6 +243,90 @@ impl Pallet { ); } + /// The implementation for the extrinsic set_childkey_whitelist: Sets the white list of coldkeys + /// that a childkey can have as parents. + /// + /// Sets the value of ChildkeyWhitelist and performs a few checks: + /// **Signature Verification**: Ensures that the caller has signed the transaction, verifying the coldkey. + /// **Root Network Check**: Ensures netuid is not the root network, as child hotkeys are not valid on the root. + /// **Network Existence Check**: Ensures that the specified network exists. + /// **Ownership Verification**: Ensures that the coldkey owns the hotkey. + /// **Hotkey Account Existence Check**: Ensures that the hotkey account already exists. + /// **Coldkey count**: Only allow to add up to 5 entries in the white list + /// + /// # Events: + /// * `ChildkeyWhitelistSet`: + /// - If all checks pass and the white list is set. + /// + /// # Errors: + /// * `SubNetworkDoesNotExist`: + /// - Attempting to register to a non-existent network. + /// * `RegistrationNotPermittedOnRootSubnet`: + /// - Attempting to register a child on the root network. + /// * `NonAssociatedColdKey`: + /// - The coldkey does not own the hotkey or the child is the same as the hotkey. + /// * `HotKeyAccountNotExists`: + /// - The hotkey account does not exist. + /// * `TooManyWhitelistEntries`: + /// - Too many white list entries + /// + pub fn do_set_childkey_whitelist( + coldkey: T::RuntimeOrigin, + childkey: T::AccountId, + netuid: u16, + coldkey_list: Vec, + ) -> DispatchResult { + // Check that the caller has signed the transaction. (the coldkey of the pairing) + let coldkey = ensure_signed(coldkey)?; + log::trace!( + "do_set_childkey_whitelist( coldkey:{:?} childkey:{:?} netuid:{:?} coldkey_list:{:?} )", + coldkey, + childkey, + netuid, + coldkey_list, + ); + + // Ensures netuid is not the root network. Child hotkeys are not valid on root. + ensure!( + netuid != Self::get_root_netuid(), + Error::::RegistrationNotPermittedOnRootSubnet + ); + + // Check that the specified network exists. + ensure!( + Self::if_subnet_exist(netuid), + Error::::SubNetworkDoesNotExist + ); + + // Check that the coldkey owns the childkey. + ensure!( + Self::coldkey_owns_hotkey(&coldkey, &childkey), + Error::::NonAssociatedColdKey + ); + + // Ensure that the white list length does not exceed 5. + ensure!(coldkey_list.len() <= 5, Error::::TooManyWhitelistEntries); + + // Insert or update ChildkeyWhitelist + ChildkeyWhitelist::::insert(childkey.clone(), netuid, coldkey_list.clone()); + + // --- 8. Log and return. + log::trace!( + "ChildkeyWhitelistSet( childkey:{:?}, netuid:{:?}, coldkey_list:{:?} )", + childkey, + netuid, + coldkey_list.clone() + ); + Self::deposit_event(Event::ChildkeyWhitelistSet( + childkey.clone(), + netuid, + coldkey_list, + )); + + // Ok and return. + Ok(()) + } + /* Retrieves the list of children for a given hotkey and network. /// /// # Arguments diff --git a/pallets/subtensor/src/tests/children.rs b/pallets/subtensor/src/tests/children.rs index 10087d7b2..e45578228 100644 --- a/pallets/subtensor/src/tests/children.rs +++ b/pallets/subtensor/src/tests/children.rs @@ -3747,3 +3747,77 @@ fn test_do_set_child_cooldown_period() { assert_eq!(children_after, vec![(proportion, child)]); }); } + +/// Test that a coldkey can add a childkey if it is whitelisted +#[test] +fn test_childkey_whitelisted_coldkey_ok() { + new_test_ext(1).execute_with(|| { + let coldkey_parent = U256::from(1); + let coldkey_child = U256::from(5); + let parent = U256::from(2); + let child = U256::from(3); + let netuid: u16 = 1; + let root_id: u16 = 0; + let subnet_tempo = 10; + let proportion: u64 = u64::MAX; + + // Add network, register hotkeys, and setup network parameters + add_network(root_id, subnet_tempo, 0); + add_network(netuid, subnet_tempo, 0); + register_ok_neuron(netuid, child, coldkey_child, 0); + register_ok_neuron(netuid, parent, coldkey_parent, 1); + + // Set minimum stake for setting children + TotalHotkeyStake::::insert(parent, StakeThreshold::::get()); + + // Add parent coldkey to the child whitelist + ChildkeyWhitelist::::insert(child, netuid, vec![coldkey_parent]); + + // Set parent-child relationship + assert_ok!(SubtensorModule::do_schedule_children( + RuntimeOrigin::signed(coldkey_parent), + parent, + netuid, + vec![(proportion, child)] + )); + }); +} + +/// Test that a coldkey can not add a childkey if it is not whitelisted +#[test] +fn test_childkey_not_whitelisted_coldkey_fail() { + new_test_ext(1).execute_with(|| { + let coldkey_parent = U256::from(1); + let coldkey_child = U256::from(5); + let coldkey_random = U256::from(6); + let parent = U256::from(2); + let child = U256::from(3); + let netuid: u16 = 1; + let root_id: u16 = 0; + let subnet_tempo = 10; + let proportion: u64 = u64::MAX; + + // Add network, register hotkeys, and setup network parameters + add_network(root_id, subnet_tempo, 0); + add_network(netuid, subnet_tempo, 0); + register_ok_neuron(netuid, child, coldkey_child, 0); + register_ok_neuron(netuid, parent, coldkey_parent, 1); + + // Set minimum stake for setting children + TotalHotkeyStake::::insert(parent, StakeThreshold::::get()); + + // Add a different parent coldkey to the child whitelist so that it is not empty + ChildkeyWhitelist::::insert(child, netuid, vec![coldkey_random]); + + // Attempt to set child + assert_err!( + SubtensorModule::do_schedule_children( + RuntimeOrigin::signed(coldkey_parent), + parent, + netuid, + vec![(proportion, child)] + ), + Error::::ColdkeyIsNotWhitelisted + ); + }); +} From 421b161b4d45bab3bbb8c0b99f2d59810e6e0836 Mon Sep 17 00:00:00 2001 From: Greg Zaitsev Date: Wed, 18 Dec 2024 15:29:40 -0500 Subject: [PATCH 2/2] Adjust weight for set_childkey_whitelist --- pallets/subtensor/src/macros/dispatches.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/subtensor/src/macros/dispatches.rs b/pallets/subtensor/src/macros/dispatches.rs index d32e05af7..78a74cb04 100644 --- a/pallets/subtensor/src/macros/dispatches.rs +++ b/pallets/subtensor/src/macros/dispatches.rs @@ -1487,9 +1487,9 @@ mod dispatches { /// User register a new subnetwork #[pallet::call_index(81)] - #[pallet::weight((Weight::from_parts(157_000_000, 0) - .saturating_add(T::DbWeight::get().reads(16)) - .saturating_add(T::DbWeight::get().writes(30)), DispatchClass::Operational, Pays::No))] + #[pallet::weight((Weight::from_parts(100_000_000, 0) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)), DispatchClass::Operational, Pays::Yes))] pub fn set_childkey_whitelist( coldkey: OriginFor, childkey: T::AccountId,