Skip to content
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
4 changes: 2 additions & 2 deletions pallets/subtensor/src/macros/dispatches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1247,7 +1247,7 @@ mod dispatches {
/// User register a new subnetwork
#[pallet::call_index(59)]
#[pallet::weight((Weight::from_parts(235_400_000, 0)
.saturating_add(T::DbWeight::get().reads(36_u64))
.saturating_add(T::DbWeight::get().reads(37_u64))
.saturating_add(T::DbWeight::get().writes(52_u64)), DispatchClass::Normal, Pays::Yes))]
pub fn register_network(origin: OriginFor<T>, hotkey: T::AccountId) -> DispatchResult {
Self::do_register_network(origin, &hotkey, 1, None)
Expand Down Expand Up @@ -1456,7 +1456,7 @@ mod dispatches {
/// User register a new subnetwork
#[pallet::call_index(79)]
#[pallet::weight((Weight::from_parts(396_000_000, 0)
.saturating_add(T::DbWeight::get().reads(35_u64))
.saturating_add(T::DbWeight::get().reads(36_u64))
.saturating_add(T::DbWeight::get().writes(51_u64)), DispatchClass::Normal, Pays::Yes))]
pub fn register_network_with_identity(
origin: OriginFor<T>,
Expand Down
61 changes: 61 additions & 0 deletions pallets/subtensor/src/staking/set_children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,67 @@ impl<T: Config> Pallet<T> {
// State cleaners (for use in migration)
// TODO: Deprecate when the state is clean for a while

/// Establishes parent-child relationships between all root validators and
/// a subnet owner's hotkey on the specified subnet.
///
/// For each validator on the root network (netuid 0), this function calls
/// `do_schedule_children` to schedule the subnet owner hotkey as a child
/// of that root validator on the given subnet, with full proportion (u64::MAX).
///
/// # Arguments
/// * `netuid` - The subnet on which to establish relationships.
///
/// # Returns
/// * `DispatchResult` - Ok if at least the setup completes; individual
/// scheduling failures per validator are logged but do not abort the loop.
pub fn do_set_root_validators_for_subnet(netuid: NetUid) -> DispatchResult {
// Cannot set children on root network itself.
ensure!(
!netuid.is_root(),
Error::<T>::RegistrationNotPermittedOnRootSubnet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need a different error type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems close enough. Also, I don't expect errors here at all because it's called only during subnet registration with concrete netuid. What do you suggest instead?

);

// Subnet must exist.
ensure!(Self::if_subnet_exist(netuid), Error::<T>::SubnetNotExists);

// Get the subnet owner hotkey.
let subnet_owner_hotkey =
SubnetOwnerHotkey::<T>::try_get(netuid).map_err(|_| Error::<T>::SubnetNotExists)?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need a different error type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a better one? It seems appropriate. We don't have subnet owner hotkey only when it doesn't exist. What do you suggest?


// Iterate over all root validators and schedule each one as a parent
// of the subnet owner hotkey.
for (_uid, root_validator_hotkey) in Keys::<T>::iter_prefix(NetUid::ROOT) {
// Skip if the root validator is the subnet owner hotkey itself
// (cannot be both parent and child).
if root_validator_hotkey == subnet_owner_hotkey {
continue;
}

// Look up the coldkey that owns this root validator hotkey.
let coldkey = Self::get_owning_coldkey_for_hotkey(&root_validator_hotkey);

// Build a signed origin from the coldkey.
let origin: <T as frame_system::Config>::RuntimeOrigin =
frame_system::RawOrigin::Signed(coldkey).into();

// Schedule the subnet owner hotkey as a child with full proportion.
let children = vec![(u64::MAX, subnet_owner_hotkey.clone())];

if let Err(e) =
Self::do_schedule_children(origin, root_validator_hotkey.clone(), netuid, children)
{
log::warn!(
"Failed to schedule children for root validator {:?} on netuid {:?}: {:?}",
root_validator_hotkey,
netuid,
e
);
}
}

Ok(())
}

pub fn clean_zero_childkey_vectors(weight: &mut Weight) {
// Collect keys to delete first to avoid mutating while iterating.
let mut to_remove: Vec<(T::AccountId, NetUid)> = Vec::new();
Expand Down
11 changes: 10 additions & 1 deletion pallets/subtensor/src/subnets/subnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,16 @@ impl<T: Config> Pallet<T> {
Self::deposit_event(Event::SubnetIdentitySet(netuid_to_register));
}

// --- 18. Emit the NetworkAdded event.
// --- 18. Schedule root validators as parents of the subnet owner hotkey.
if let Err(e) = Self::do_set_root_validators_for_subnet(netuid_to_register) {
log::warn!(
"Failed to set root validators for netuid {:?}: {:?}",
netuid_to_register,
e
);
}

// --- 19. Emit the NetworkAdded event.
log::info!("NetworkAdded( netuid:{netuid_to_register:?}, mechanism:{mechid:?} )");
Self::deposit_event(Event::NetworkAdded(netuid_to_register, mechid));

Expand Down
273 changes: 273 additions & 0 deletions pallets/subtensor/src/tests/children.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4220,3 +4220,276 @@ fn test_set_child_keys_empty_vector_clears_storage() {
assert!(ParentKeys::<Test>::get(child, netuid).is_empty());
});
}

// Test that do_set_root_children_for_subnet enables a subnet owner to set weights
// by inheriting stake from root validators.
// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_root_children_enable_subnet_owner_set_weights --exact --show-output --nocapture
#[test]
fn test_root_children_enable_subnet_owner_set_weights() {
new_test_ext(1).execute_with(|| {
// --- Setup accounts ---
let subnet_owner_coldkey = U256::from(1001);
let subnet_owner_hotkey = U256::from(1002);

let root_val_coldkey_1 = U256::from(100);
let root_val_hotkey_1 = U256::from(101);
let root_val_coldkey_2 = U256::from(200);
let root_val_hotkey_2 = U256::from(201);

// --- Create root network and subnet ---
add_network(NetUid::ROOT, 1, 0);
let netuid =
add_dynamic_network_disable_commit_reveal(&subnet_owner_hotkey, &subnet_owner_coldkey);

// --- Register root validators on a subnet first (required before root_register) ---
register_ok_neuron(netuid, root_val_hotkey_1, root_val_coldkey_1, 0);
register_ok_neuron(netuid, root_val_hotkey_2, root_val_coldkey_2, 0);

// --- Register root validators on root network ---
assert_ok!(SubtensorModule::root_register(
RuntimeOrigin::signed(root_val_coldkey_1),
root_val_hotkey_1,
));
assert_ok!(SubtensorModule::root_register(
RuntimeOrigin::signed(root_val_coldkey_2),
root_val_hotkey_2,
));

// Subnet owner hotkey is auto-registered on the subnet via add_dynamic_network.

// --- Add significant stake for root validators on root and the subnet ---
let root_stake = AlphaBalance::from(1_000_000_000);
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_1,
&root_val_coldkey_1,
NetUid::ROOT,
root_stake,
);
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_1,
&root_val_coldkey_1,
netuid,
root_stake,
);
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_2,
&root_val_coldkey_2,
NetUid::ROOT,
root_stake,
);
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_2,
&root_val_coldkey_2,
netuid,
root_stake,
);

// --- Set a high stake threshold so the subnet owner alone cannot set weights ---
let high_threshold = 500_000_000u64;
SubtensorModule::set_stake_threshold(high_threshold);

// Disable rate limits for clean testing
SubtensorModule::set_weights_set_rate_limit(netuid, 0);

let version_key = SubtensorModule::get_weights_version_key(netuid);
let uids: Vec<u16> = vec![0];
let values: Vec<u16> = vec![u16::MAX];

// Show that subnet owner CANNOT set weights (insufficient stake) ---
assert!(
!SubtensorModule::check_weights_min_stake(&subnet_owner_hotkey, netuid),
"Subnet owner should NOT have enough stake to set weights initially"
);
assert_noop!(
SubtensorModule::set_weights(
RuntimeOrigin::signed(subnet_owner_hotkey),
netuid,
uids.clone(),
values.clone(),
version_key
),
Error::<Test>::NotEnoughStakeToSetWeights
);

// Minimize the pending children cooldown via root extrinsic ---
assert_ok!(SubtensorModule::set_pending_childkey_cooldown(
RuntimeOrigin::root(),
0, // zero block cooldown
));

assert_ok!(SubtensorModule::do_set_root_validators_for_subnet(netuid));

// Activate pending children (cooldown is 0, advance 1 block) ---
step_block(1);
SubtensorModule::do_set_pending_children(netuid);

// Verify that child-parent relationships were created:
// Each root validator should have the subnet owner hotkey as a child on netuid
let children_1 = SubtensorModule::get_children(&root_val_hotkey_1, netuid);
assert_eq!(
children_1,
vec![(u64::MAX, subnet_owner_hotkey)],
"Root validator 1 should have subnet owner as child"
);
let children_2 = SubtensorModule::get_children(&root_val_hotkey_2, netuid);
assert_eq!(
children_2,
vec![(u64::MAX, subnet_owner_hotkey)],
"Root validator 2 should have subnet owner as child"
);

// Verify that the subnet owner has both root validators as parents
let parents = SubtensorModule::get_parents(&subnet_owner_hotkey, netuid);
assert_eq!(parents.len(), 2, "Subnet owner should have 2 parents");

// Show that subnet owner CAN now set weights ---
assert!(
SubtensorModule::check_weights_min_stake(&subnet_owner_hotkey, netuid),
"Subnet owner should now have enough inherited stake to set weights"
);
assert_ok!(SubtensorModule::set_weights(
RuntimeOrigin::signed(subnet_owner_hotkey),
netuid,
uids,
values,
version_key
));
});
}

// Test that register_network automatically schedules root validators as parents of the
// subnet owner, enabling the owner to set weights after cooldown.
// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::children::test_register_network_schedules_root_validators --exact --show-output --nocapture
#[test]
fn test_register_network_schedules_root_validators() {
new_test_ext(1).execute_with(|| {
// --- Setup root network and root validators ---
let root_val_coldkey_1 = U256::from(100);
let root_val_hotkey_1 = U256::from(101);
let root_val_coldkey_2 = U256::from(200);
let root_val_hotkey_2 = U256::from(201);

add_network(NetUid::ROOT, 1, 0);

// Root validators need to be registered on some subnet before root_register.
// Create a bootstrap subnet for that purpose.
let bootstrap_netuid = NetUid::from(1);
add_network(bootstrap_netuid, 1, 0);
register_ok_neuron(bootstrap_netuid, root_val_hotkey_1, root_val_coldkey_1, 0);
register_ok_neuron(bootstrap_netuid, root_val_hotkey_2, root_val_coldkey_2, 0);

assert_ok!(SubtensorModule::root_register(
RuntimeOrigin::signed(root_val_coldkey_1),
root_val_hotkey_1,
));
assert_ok!(SubtensorModule::root_register(
RuntimeOrigin::signed(root_val_coldkey_2),
root_val_hotkey_2,
));

// Give root validators significant stake on root and bootstrap subnet
let root_stake = AlphaBalance::from(1_000_000_000);
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_1,
&root_val_coldkey_1,
NetUid::ROOT,
root_stake,
);
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_2,
&root_val_coldkey_2,
NetUid::ROOT,
root_stake,
);

// --- Minimize cooldown so pending children activate quickly ---
assert_ok!(SubtensorModule::set_pending_childkey_cooldown(
RuntimeOrigin::root(),
0,
));

// --- Set a high stake threshold ---
let high_threshold = 500_000_000u64;
SubtensorModule::set_stake_threshold(high_threshold);

// --- Register a new subnet (this should automatically call do_set_root_validators_for_subnet) ---
let subnet_owner_coldkey = U256::from(1001);
let subnet_owner_hotkey = U256::from(1002);
let lock_cost = SubtensorModule::get_network_lock_cost();
SubtensorModule::add_balance_to_coldkey_account(&subnet_owner_coldkey, lock_cost.into());
TotalIssuance::<Test>::mutate(|total| {
*total = total.saturating_add(lock_cost);
});
assert_ok!(SubtensorModule::register_network(
RuntimeOrigin::signed(subnet_owner_coldkey),
subnet_owner_hotkey,
));

// Determine the netuid that was just created
let netuid: NetUid = (TotalNetworks::<Test>::get().saturating_sub(1)).into();
assert_eq!(
SubnetOwnerHotkey::<Test>::get(netuid),
subnet_owner_hotkey,
"Subnet owner hotkey should be set"
);

// Root validators need stake on the new subnet for child stake inheritance to work
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_1,
&root_val_coldkey_1,
netuid,
root_stake,
);
SubtensorModule::increase_stake_for_hotkey_and_coldkey_on_subnet(
&root_val_hotkey_2,
&root_val_coldkey_2,
netuid,
root_stake,
);

// --- Verify pending children were scheduled during registration ---
assert!(
PendingChildKeys::<Test>::contains_key(netuid, root_val_hotkey_1),
"Root validator 1 should have pending children on the new subnet"
);
assert!(
PendingChildKeys::<Test>::contains_key(netuid, root_val_hotkey_2),
"Root validator 2 should have pending children on the new subnet"
);

// --- Activate pending children ---
step_block(1);
SubtensorModule::do_set_pending_children(netuid);

// --- Verify child-parent relationships ---
let children_1 = SubtensorModule::get_children(&root_val_hotkey_1, netuid);
assert_eq!(
children_1,
vec![(u64::MAX, subnet_owner_hotkey)],
"Root validator 1 should have subnet owner as child"
);
let children_2 = SubtensorModule::get_children(&root_val_hotkey_2, netuid);
assert_eq!(
children_2,
vec![(u64::MAX, subnet_owner_hotkey)],
"Root validator 2 should have subnet owner as child"
);

// --- Verify subnet owner can now set weights ---
SubtensorModule::set_weights_set_rate_limit(netuid, 0);
SubtensorModule::set_commit_reveal_weights_enabled(netuid, false);
let version_key = SubtensorModule::get_weights_version_key(netuid);

assert!(
SubtensorModule::check_weights_min_stake(&subnet_owner_hotkey, netuid),
"Subnet owner should have enough inherited stake to set weights"
);
assert_ok!(SubtensorModule::set_weights(
RuntimeOrigin::signed(subnet_owner_hotkey),
netuid,
vec![0],
vec![u16::MAX],
version_key
));
});
}
Loading