Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add childkey white list #1107

Open
wants to merge 2 commits into
base: devnet-ready
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pallets/subtensor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,11 @@ pub mod pallet {
vec![]
}
#[pallet::type_value]
/// Default account linkage
pub fn DefaultChildkeyWhitelist<T: Config>() -> Vec<T::AccountId> {
vec![]
}
#[pallet::type_value]
/// Default pending childkeys
pub fn DefaultPendingChildkeys<T: Config>() -> (Vec<(u64, T::AccountId)>, u64) {
(vec![], 0)
Expand Down Expand Up @@ -876,6 +881,18 @@ pub mod pallet {
ValueQuery,
DefaultAccountLinkage<T>,
>;
#[pallet::storage]
/// DMAP ( child, netuid ) --> Vec<coldkey>
pub type ChildkeyWhitelist<T: Config> = StorageDoubleMap<
_,
Blake2_128Concat,
T::AccountId,
Identity,
u16,
Vec<T::AccountId>,
ValueQuery,
DefaultChildkeyWhitelist<T>,
>;
#[pallet::storage] // --- DMAP ( cold ) --> Vec<hot> | Maps coldkey to hotkeys that stake to it
pub type StakingHotkeys<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, Vec<T::AccountId>, ValueQuery>;
Expand Down
14 changes: 14 additions & 0 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(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<T>,
childkey: T::AccountId,
netuid: u16,
coldkey_list: Vec<T::AccountId>,
) -> DispatchResult {
Self::do_set_childkey_whitelist(coldkey, childkey, netuid, coldkey_list)
}
}
}
4 changes: 4 additions & 0 deletions pallets/subtensor/src/macros/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
2 changes: 2 additions & 0 deletions pallets/subtensor/src/macros/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T::AccountId>),
/// The hotkey emission tempo has been set
HotkeyEmissionTempoSet(u64),
/// The network maximum stake has been set
Expand Down
2 changes: 2 additions & 0 deletions pallets/subtensor/src/macros/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ mod hooks {
// Migrate Delegate Ids on chain
.saturating_add(migrations::migrate_chain_identity::migrate_set_hotkey_identities::<T>())
// Migrate Commit-Reval 2.0
.saturating_add(migrations::migrate_commit_reveal_v2::migrate_commit_reveal_2::<T>())
// Migrate rename WeightMinStake to WeightThreshold
.saturating_add(migrations::migrate_commit_reveal_v2::migrate_commit_reveal_2::<T>());
weight
}
Expand Down
93 changes: 93 additions & 0 deletions pallets/subtensor/src/staking/set_children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@ impl<T: Config> Pallet<T> {
// Ensure that the number of children does not exceed 5.
ensure!(children.len() <= 5, Error::<T>::TooManyChildren);

// Check that the parent coldkey is whitelisted or childkey whitelist is empty
for (_, child_i) in &children {
let child_whitelist = ChildkeyWhitelist::<T>::get(child_i, netuid);
ensure!(
child_whitelist.is_empty() || child_whitelist.contains(&coldkey),
Error::<T>::ColdkeyIsNotWhitelisted
);
}

// Ensure that each child is not the hotkey.
for (_, child_i) in &children {
ensure!(child_i != &hotkey, Error::<T>::InvalidChild);
Expand Down Expand Up @@ -234,6 +243,90 @@ impl<T: Config> Pallet<T> {
);
}

/// 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<T::AccountId>,
) -> 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::<T>::RegistrationNotPermittedOnRootSubnet
);

// Check that the specified network exists.
ensure!(
Self::if_subnet_exist(netuid),
Error::<T>::SubNetworkDoesNotExist
);

// Check that the coldkey owns the childkey.
ensure!(
Self::coldkey_owns_hotkey(&coldkey, &childkey),
Error::<T>::NonAssociatedColdKey
);

// Ensure that the white list length does not exceed 5.
ensure!(coldkey_list.len() <= 5, Error::<T>::TooManyWhitelistEntries);

// Insert or update ChildkeyWhitelist
ChildkeyWhitelist::<T>::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
Expand Down
74 changes: 74 additions & 0 deletions pallets/subtensor/src/tests/children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Test>::insert(parent, StakeThreshold::<Test>::get());

// Add parent coldkey to the child whitelist
ChildkeyWhitelist::<Test>::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::<Test>::insert(parent, StakeThreshold::<Test>::get());

// Add a different parent coldkey to the child whitelist so that it is not empty
ChildkeyWhitelist::<Test>::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::<Test>::ColdkeyIsNotWhitelisted
);
});
}
Loading