From f6d4a1ffeec0581946aa7b7b994df552c93b298e Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 09:40:09 -0500 Subject: [PATCH 01/11] feat: refactor the set_children function --- bittensor_cli/cli.py | 22 ++++ .../src/commands/stake/children_hotkeys.py | 119 +++++++++--------- 2 files changed, 78 insertions(+), 63 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 16e68c25..90780c90 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -6188,6 +6188,12 @@ def stake_set_children( network: Optional[list[str]] = Options.network, netuid: Optional[int] = Options.netuid_not_req, all_netuids: bool = Options.all_netuids, + parent_hotkey: Optional[str] = typer.Option( + None, + "--parent-hotkey", + help="Parent hotkey SS58 to manage (defaults to the selected wallet hotkey).", + prompt=False, + ), proportions: list[float] = typer.Option( [], "--proportions", @@ -6247,6 +6253,11 @@ def stake_set_children( ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) + + if parent_hotkey is not None and not is_valid_ss58_address(parent_hotkey): + print_error(f"Invalid SS58 address for --parent-hotkey: {parent_hotkey}") + raise typer.Exit() + logger.debug( "args:\n" f"network: {network}\n" @@ -6264,6 +6275,7 @@ def stake_set_children( netuid=netuid, children=children, proportions=proportions, + hotkey=parent_hotkey, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, prompt=prompt, @@ -6290,6 +6302,12 @@ def stake_revoke_children( "--allnetuids", help="When this flag is used it sets child hotkeys on all the subnets.", ), + parent_hotkey: Optional[str] = typer.Option( + None, + "--parent-hotkey", + help="Parent hotkey SS58 to manage (defaults to the selected wallet hotkey).", + prompt=False, + ), proxy: Optional[str] = Options.proxy, announce_only: bool = Options.announce_only, wait_for_inclusion: bool = Options.wait_for_inclusion, @@ -6317,6 +6335,9 @@ def stake_revoke_children( ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) + if parent_hotkey is not None and not is_valid_ss58_address(parent_hotkey): + print_error(f"Invalid SS58 address for --parent-hotkey: {parent_hotkey}") + raise typer.Exit() if all_netuids and netuid: print_error("Specify either a netuid or '--all', not both.") raise typer.Exit() @@ -6339,6 +6360,7 @@ def stake_revoke_children( wallet=wallet, subtensor=self.initialize_chain(network), netuid=netuid, + hotkey=parent_hotkey, proxy=proxy, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor_cli/src/commands/stake/children_hotkeys.py b/bittensor_cli/src/commands/stake/children_hotkeys.py index 69d4083e..6ecfd68a 100644 --- a/bittensor_cli/src/commands/stake/children_hotkeys.py +++ b/bittensor_cli/src/commands/stake/children_hotkeys.py @@ -1,6 +1,6 @@ import asyncio import json -from typing import Optional +from typing import Optional, Any from bittensor_wallet import Wallet from rich.prompt import IntPrompt, FloatPrompt @@ -14,6 +14,7 @@ confirm_action, console, print_error, + err_console, float_to_u16, float_to_u64, print_success, @@ -240,7 +241,7 @@ async def set_childkey_take_extrinsic( print_success("Finalized") return True, f"Successfully {modifier} childkey take", ext_id else: - print_error(f"Failed: {error_message}") + console.print(f":cross_mark: [red]Failed[/red]: {error_message}") return False, error_message, None except SubstrateRequestException as e: @@ -512,102 +513,94 @@ async def set_children( children: list[str], proportions: list[float], netuid: Optional[int] = None, + hotkey: Optional[str] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, prompt: bool = True, json_output: bool = False, proxy: Optional[str] = None, ): - """Set children hotkeys.""" - # TODO holy shit I hate this. It needs to be rewritten. + """ + Set children hotkeys with proportions for a parent hotkey on specified subnet(s). + + Args: + wallet: Wallet containing the coldkey for signing transactions. + subtensor: SubtensorInterface instance for blockchain interaction. + children: List of child hotkey SS58 addresses. + proportions: List of stake proportions (floats between 0 and 1). + netuid: Optional specific subnet ID. If None, operates on ALL non-root subnets. + hotkey: Optional parent hotkey SS58 address. If None, uses wallet's hotkey. + wait_for_inclusion: Wait for transaction to be included in a block. + wait_for_finalization: Wait for transaction to be finalized. + prompt: Prompt user for confirmation before submitting transactions. + json_output: Output results as JSON instead of formatted text. + proxy: Optional proxy SS58 address for transaction submission. + """ + parent_hotkey = hotkey if hotkey is not None else get_hotkey_pub_ss58(wallet) + # Validate children SS58 addresses - # TODO check to see if this should be allowed to be specified by user instead of pulling from wallet - hotkey = get_hotkey_pub_ss58(wallet) for child in children: if not is_valid_ss58_address(child): - print_error(f"Invalid SS58 address: {child}") + msg = f"Invalid SS58 address: {child}" + print_error(msg) + if json_output: + json_console.print(json.dumps({"success": False, "message": msg})) return - if child == hotkey: - print_error("Cannot set yourself as a child.") + if child == parent_hotkey: + msg = "Cannot set yourself as a child." + print_error(msg) + if json_output: + json_console.print(json.dumps({"success": False, "message": msg})) return - total_proposed = sum(proportions) - if total_proposed > 1: - raise ValueError( - f"Invalid proportion: The sum of all proportions cannot be greater than 1. " - f"Proposed sum of proportions is {total_proposed}." - ) children_with_proportions = list(zip(proportions, children)) - successes = {} + + # Determine netuids if netuid is not None: + netuids = [netuid] + else: + all_netuids = await subtensor.get_all_subnet_netuids() + netuids = [n for n in all_netuids if n != 0] + + # Execute operations + dict_output = {} + for netuid_ in netuids: success, message, ext_id = await set_children_extrinsic( subtensor=subtensor, wallet=wallet, - netuid=netuid, - hotkey=hotkey, + netuid=netuid_, + hotkey=parent_hotkey, proxy=proxy, children_with_proportions=children_with_proportions, prompt=prompt, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - successes[netuid] = { + dict_output[netuid_] = { "success": success, - "error": message, "completion_block": None, + "error": message, "set_block": None, "extrinsic_identifier": ext_id, } - # Result + if success: - if wait_for_inclusion and wait_for_finalization: - current_block, completion_block = await get_childkey_completion_block( - subtensor, netuid - ) - successes[netuid]["completion_block"] = completion_block - successes[netuid]["set_block"] = current_block - console.print( - f"Your childkey request has been submitted. It will be completed around block {completion_block}. " - f"The current block is {current_block}" - ) - print_success("Set children hotkeys.") - else: - print_error(f"Unable to set children hotkeys. {message}") - else: - # set children on all subnets that parent is registered on - netuids = await subtensor.get_all_subnet_netuids() - for netuid_ in netuids: - if netuid_ == 0: # dont include root network - continue - console.print(f"Setting children on netuid {netuid_}.") - success, message, ext_id = await set_children_extrinsic( - subtensor=subtensor, - wallet=wallet, - netuid=netuid_, - hotkey=hotkey, - proxy=proxy, - children_with_proportions=children_with_proportions, - prompt=prompt, - wait_for_inclusion=True, - wait_for_finalization=False, - ) current_block, completion_block = await get_childkey_completion_block( subtensor, netuid_ ) - successes[netuid_] = { - "success": success, - "error": message, - "completion_block": completion_block, - "set_block": current_block, - "extrinsic_identifier": ext_id, - } + dict_output[netuid_]["set_block"] = current_block + dict_output[netuid_]["completion_block"] = completion_block console.print( - f"Your childkey request for netuid {netuid_} has been submitted. It will be completed around " - f"block {completion_block}. The current block is {current_block}." + f":white_heavy_check_mark: Your childkey request for netuid {netuid_} has been submitted. " + f"It will be completed around block {completion_block}. The current block is {current_block}" + ) + else: + print_error( + f"Failed to set children hotkeys for netuid {netuid_}: {message}" ) - print_success("Sent set children request for all subnets.") + if json_output: - json_console.print(json.dumps(successes)) + json_console.print(json.dumps(dict_output)) async def revoke_children( From 963bbab01f62050c8a9a2c693defb65f5cb168b6 Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 09:40:21 -0500 Subject: [PATCH 02/11] feat: add testing for set_children --- tests/e2e_tests/test_children_hotkeys.py | 327 +++++++++++++++++++++++ 1 file changed, 327 insertions(+) diff --git a/tests/e2e_tests/test_children_hotkeys.py b/tests/e2e_tests/test_children_hotkeys.py index f286012f..a163ece2 100644 --- a/tests/e2e_tests/test_children_hotkeys.py +++ b/tests/e2e_tests/test_children_hotkeys.py @@ -1,3 +1,5 @@ +import json + import pytest from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface @@ -13,3 +15,328 @@ async def test_get_childkey_completion_block(local_chain): subtensor, 1 ) assert (completion_block - current_block) >= 7200 + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_set_children(local_chain, wallet_setup): + """ + Test setting children hotkeys on a subnet. + + Steps: + 1. Create wallets for Alice (parent) and Bob (child) + 2. Create a subnet and register Alice + 3. Add stake to Alice's hotkey + 4. Set Bob as a child hotkey with 50% proportion + 5. Verify children are set via get command + 6. Revoke children and verify removal + """ + print("Testing set_children command ๐Ÿงช") + netuid = 2 + + # Create wallets for Alice (parent) and Bob (child) + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + "//Alice" + ) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + + # Create subnet with Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--no-prompt", + "--json-output", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + assert result_output["netuid"] == netuid + + # Register Bob on the subnet + exec_command_bob( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--wallet-name", + wallet_bob.name, + "--hotkey", + wallet_bob.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + + # Start emissions on subnet + exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--network", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + + # Add stake to Alice's hotkey + add_stake = exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--amount", + "100", + "--no-prompt", + ], + ) + assert "โœ… Finalized" in add_stake.stdout or "Finalized" in add_stake.stdout + + # Set Bob as child hotkey with 50% proportion + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--children", + keypair_bob.ss58_address, + "--proportions", + "0.5", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + assert set_children_output[str(netuid)]["extrinsic_identifier"] is not None + + # Get children and verify Bob is set + get_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "get", + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--json-output", + ], + ) + get_children_output = json.loads(get_children_result.stdout) + assert len(get_children_output) > 0 + # Verify Bob's hotkey is in children + children_hotkeys = [child["child_ss58"] for child in get_children_output] + assert keypair_bob.ss58_address in children_hotkeys + + # Revoke children + revoke_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "revoke", + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + revoke_output = json.loads(revoke_result.stdout) + assert revoke_output[str(netuid)]["success"] is True + + print("โœ… Passed set_children test") + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_set_children_multiple_proportions(local_chain, wallet_setup): + """ + Test setting multiple children hotkeys with different proportions. + + Steps: + 1. Create wallets for Alice (parent), Bob and Charlie (children) + 2. Create a subnet and register all + 3. Set Bob (30%) and Charlie (40%) as children + 4. Verify both children are set with correct proportions + """ + print("Testing set_children with multiple children ๐Ÿงช") + netuid = 2 + + # Create wallets + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + "//Alice" + ) + keypair_bob, wallet_bob, wallet_path_bob, _ = wallet_setup("//Bob") + keypair_charlie, wallet_charlie, wallet_path_charlie, _ = wallet_setup("//Charlie") + + # Create subnet with Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--no-prompt", + "--json-output", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + + # Start emissions + exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--network", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + + # Add stake + exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--amount", + "100", + "--no-prompt", + ], + ) + + # Set multiple children: Bob (30%) and Charlie (40%) + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--children", + keypair_bob.ss58_address, + "--children", + keypair_charlie.ss58_address, + "--proportions", + "0.3", + "--proportions", + "0.4", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + + # Verify both children are set + get_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "get", + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--json-output", + ], + ) + get_children_output = json.loads(get_children_result.stdout) + children_hotkeys = [child["child_ss58"] for child in get_children_output] + assert keypair_bob.ss58_address in children_hotkeys + assert keypair_charlie.ss58_address in children_hotkeys + + print("โœ… Passed set_children multiple proportions test") From d033e0a3d8f68b676fce387a1834df54d16086d2 Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 09:49:01 -0500 Subject: [PATCH 03/11] fix: remove the parent hotkey --- bittensor_cli/cli.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 90780c90..359b0834 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -6302,12 +6302,6 @@ def stake_revoke_children( "--allnetuids", help="When this flag is used it sets child hotkeys on all the subnets.", ), - parent_hotkey: Optional[str] = typer.Option( - None, - "--parent-hotkey", - help="Parent hotkey SS58 to manage (defaults to the selected wallet hotkey).", - prompt=False, - ), proxy: Optional[str] = Options.proxy, announce_only: bool = Options.announce_only, wait_for_inclusion: bool = Options.wait_for_inclusion, @@ -6335,9 +6329,6 @@ def stake_revoke_children( ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) - if parent_hotkey is not None and not is_valid_ss58_address(parent_hotkey): - print_error(f"Invalid SS58 address for --parent-hotkey: {parent_hotkey}") - raise typer.Exit() if all_netuids and netuid: print_error("Specify either a netuid or '--all', not both.") raise typer.Exit() @@ -6360,7 +6351,6 @@ def stake_revoke_children( wallet=wallet, subtensor=self.initialize_chain(network), netuid=netuid, - hotkey=parent_hotkey, proxy=proxy, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, From 310e90bbf746e1ac1c2d50acbb72c67481153184 Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 09:51:11 -0500 Subject: [PATCH 04/11] fix: update to print_error --- bittensor_cli/src/commands/stake/children_hotkeys.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bittensor_cli/src/commands/stake/children_hotkeys.py b/bittensor_cli/src/commands/stake/children_hotkeys.py index 6ecfd68a..9e62bbeb 100644 --- a/bittensor_cli/src/commands/stake/children_hotkeys.py +++ b/bittensor_cli/src/commands/stake/children_hotkeys.py @@ -14,7 +14,6 @@ confirm_action, console, print_error, - err_console, float_to_u16, float_to_u64, print_success, @@ -241,7 +240,7 @@ async def set_childkey_take_extrinsic( print_success("Finalized") return True, f"Successfully {modifier} childkey take", ext_id else: - console.print(f":cross_mark: [red]Failed[/red]: {error_message}") + print_error(f"Failed: {error_message}") return False, error_message, None except SubstrateRequestException as e: From 4bc255da0d0a02ef7ad6344a6ae6cc137626161f Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 10:11:34 -0500 Subject: [PATCH 05/11] fix: update the testing --- tests/e2e_tests/test_children_hotkeys.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/e2e_tests/test_children_hotkeys.py b/tests/e2e_tests/test_children_hotkeys.py index a163ece2..7866ab8c 100644 --- a/tests/e2e_tests/test_children_hotkeys.py +++ b/tests/e2e_tests/test_children_hotkeys.py @@ -54,6 +54,8 @@ def test_set_children(local_chain, wallet_setup): wallet_alice.hotkey_str, "--subnet-name", "Test Subnet", + "--repo", + "https://github.com/test/repo", "--no-prompt", "--json-output", ], @@ -237,6 +239,8 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): wallet_alice.hotkey_str, "--subnet-name", "Test Subnet", + "--repo", + "https://github.com/test/repo", "--no-prompt", "--json-output", ], From 1e8c0804cc4aac00c03ed0e699412d96e29ed17d Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 11:49:27 -0500 Subject: [PATCH 06/11] fix: update the test --- tests/e2e_tests/test_children_hotkeys.py | 1177 ++++++++++++++++++++-- 1 file changed, 1088 insertions(+), 89 deletions(-) diff --git a/tests/e2e_tests/test_children_hotkeys.py b/tests/e2e_tests/test_children_hotkeys.py index 7866ab8c..ec8e1017 100644 --- a/tests/e2e_tests/test_children_hotkeys.py +++ b/tests/e2e_tests/test_children_hotkeys.py @@ -1,45 +1,756 @@ import json - import pytest -from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface -from bittensor_cli.src.commands.stake.children_hotkeys import ( - get_childkey_completion_block, -) +""" +Verify commands: + +* btcli stake child get +* btcli stake child set +* btcli stake child revoke +""" + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_set_children_single_child(local_chain, wallet_setup): + """ + Test setting a single child hotkey on a subnet. + + Steps: + 1. Create wallets for Alice (parent) and Bob (child) + 2. Create a subnet and register both Alice and Bob + 3. Start emissions on the subnet + 4. Add stake to Alice's hotkey + 5. Set Bob as a child hotkey with 50% proportion + 6. Verify children are set via get command + """ + print("Testing set_children with single child ๐Ÿงช") + # Create wallets for Alice and Bob + wallet_path_alice = "//Alice" + netuid = 2 + + # Create wallet for Alice + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + + # Register a subnet with sudo as Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", + "--no-prompt", + "--json-output", + "--no-mev-protection", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + assert result_output["netuid"] == netuid + assert isinstance(result_output["extrinsic_identifier"], str) + + # Create wallet for Bob (child) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + + # Register Bob on the subnet + register_bob_result = exec_command_bob( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--wallet-name", + wallet_bob.name, + "--hotkey", + wallet_bob.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + + # Start emissions on subnet + start_emission_result = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) + + # Add stake to Alice's hotkey to enable V3 + add_stake_result = exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--amount", + "1", + "--unsafe", + "--no-prompt", + "--era", + "144", + "--no-mev-protection", + ], + ) + assert "โœ… Finalized" in add_stake_result.stdout + + # Set Bob as a child hotkey with 50% proportion + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", + "--children", + wallet_bob.hotkey.ss58_address, + "--proportions", + "0.5", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + assert isinstance(set_children_output[str(netuid)]["extrinsic_identifier"], str) + # Note: Children changes are not immediate - they require waiting for completion_block + # The completion_block indicates when the change will take effect + assert set_children_output[str(netuid)]["completion_block"] is not None + assert set_children_output[str(netuid)]["set_block"] is not None + + print("โœ… Passed set_children with single child") + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_set_children_multiple_proportions(local_chain, wallet_setup): + """ + Test setting multiple children hotkeys with different proportions. + + Steps: + 1. Create wallets for Alice (parent), Bob and Charlie (children) + 2. Create a subnet and register all + 3. Start emissions and add stake + 4. Set Bob (30%) and Charlie (40%) as children + 5. Verify both children are set with correct proportions + 6. Test getting children with --all-netuids flag + """ + print("Testing set_children with multiple children ๐Ÿงช") + wallet_path_alice = "//Alice" + netuid = 2 + + # Create wallet for Alice + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + keypair_charlie, wallet_charlie, wallet_path_charlie, exec_command_charlie = wallet_setup( + "//Charlie" + ) + + # Register a subnet with sudo as Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", + "--no-prompt", + "--json-output", + "--no-mev-protection", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + assert result_output["netuid"] == netuid + assert isinstance(result_output["extrinsic_identifier"], str) + + # Register Bob and Charlie on the subnet + for wallet_exec, wallet_obj, wallet_path in [ + (exec_command_bob, wallet_bob, wallet_path_bob), + (exec_command_charlie, wallet_charlie, wallet_path_charlie), + ]: + register_result = wallet_exec( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path, + "--wallet-name", + wallet_obj.name, + "--hotkey", + wallet_obj.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert "โœ… Registered" in register_result.stdout or "โœ… Already Registered" in register_result.stdout + + # Start emissions on subnet + start_emission_result = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) + + # Add stake to Alice's hotkey + add_stake_result = exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--amount", + "1", + "--unsafe", + "--no-prompt", + "--era", + "144", + "--no-mev-protection", + ], + ) + assert "โœ… Finalized" in add_stake_result.stdout + + # Set Bob (30%) and Charlie (40%) as children + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", + "--children", + wallet_bob.hotkey.ss58_address, + wallet_charlie.hotkey.ss58_address, + "--proportions", + "0.3", + "0.4", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + assert isinstance(set_children_output[str(netuid)]["extrinsic_identifier"], str) + # Note: Children changes are not immediate - they require waiting for completion_block + assert set_children_output[str(netuid)]["completion_block"] is not None + assert set_children_output[str(netuid)]["set_block"] is not None + + print("โœ… Passed set_children with multiple children") + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_get_children_json_output(local_chain, wallet_setup): + """ + Test getting children with JSON output. + + Steps: + 1. Create subnet and set children + 2. Get children with --json-output flag + 3. Verify JSON structure and content + """ + print("Testing get_children with JSON output ๐Ÿงช") + wallet_path_alice = "//Alice" + netuid = 2 + + # Create wallet for Alice + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + + # Register a subnet with sudo as Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", + "--no-prompt", + "--json-output", + "--no-mev-protection", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + assert result_output["netuid"] == netuid + assert isinstance(result_output["extrinsic_identifier"], str) + + # Register Bob + register_bob_result = exec_command_bob( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--wallet-name", + wallet_bob.name, + "--hotkey", + wallet_bob.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + + # Start emissions + start_emission_result = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) + + # Add stake + add_stake_result = exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--amount", + "1", + "--unsafe", + "--no-prompt", + "--era", + "144", + "--no-mev-protection", + ], + ) + assert "โœ… Finalized" in add_stake_result.stdout + + # Set children + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", + "--children", + wallet_bob.hotkey.ss58_address, + "--proportions", + "0.5", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + # Note: Children changes are not immediate - they require waiting for completion_block + assert set_children_output[str(netuid)]["completion_block"] is not None + + # Get children with JSON output + # Note: Children won't be visible immediately since they require waiting for completion_block + get_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "get", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--json-output", + ], + ) + get_children_output = json.loads(get_children_result.stdout) + # Verify JSON structure - should be a list (may be empty since children aren't immediately active) + assert isinstance(get_children_output, list) + # If there are children, verify structure + for child in get_children_output: + assert isinstance(child, (list, tuple)) + assert len(child) >= 2 + # First element should be proportion (float or int) + assert isinstance(child[0], (float, int)) + # Second element should be SS58 address (string) + assert isinstance(child[1], str) + assert len(child[1]) > 0 + + print("โœ… Passed get_children with JSON output") + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_get_children_non_json_output(local_chain, wallet_setup): + """ + Test getting children without JSON output (table format). + + Steps: + 1. Create subnet and set children + 2. Get children without --json-output flag + 3. Verify output contains expected information + """ + print("Testing get_children without JSON output ๐Ÿงช") + wallet_path_alice = "//Alice" + netuid = 2 + + # Create wallet for Alice + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + + # Register a subnet with sudo as Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", + "--no-prompt", + "--json-output", + "--no-mev-protection", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + assert result_output["netuid"] == netuid + assert isinstance(result_output["extrinsic_identifier"], str) + + # Register Bob + register_bob_result = exec_command_bob( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--wallet-name", + wallet_bob.name, + "--hotkey", + wallet_bob.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + + # Start emissions + start_emission_result = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) + + # Add stake + add_stake_result = exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--amount", + "1", + "--unsafe", + "--no-prompt", + "--era", + "144", + "--no-mev-protection", + ], + ) + assert "โœ… Finalized" in add_stake_result.stdout + + # Set children + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", + "--children", + wallet_bob.hotkey.ss58_address, + "--proportions", + "0.5", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + # Note: Children changes are not immediate - they require waiting for completion_block + assert set_children_output[str(netuid)]["completion_block"] is not None + + # Get children without JSON output (should show table) + # Note: Children won't be visible immediately since they require waiting for completion_block + get_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "get", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + ], + ) + # Should have output (table format or message about no children) + assert len(get_children_result.stdout) > 0 -@pytest.mark.asyncio -async def test_get_childkey_completion_block(local_chain): - async with SubtensorInterface("ws://127.0.0.1:9945") as subtensor: - current_block, completion_block = await get_childkey_completion_block( - subtensor, 1 - ) - assert (completion_block - current_block) >= 7200 + print("โœ… Passed get_children without JSON output") @pytest.mark.parametrize("local_chain", [False], indirect=True) -def test_set_children(local_chain, wallet_setup): +def test_revoke_children_single_subnet(local_chain, wallet_setup): """ - Test setting children hotkeys on a subnet. + Test revoking children hotkeys from a single subnet. Steps: - 1. Create wallets for Alice (parent) and Bob (child) - 2. Create a subnet and register Alice - 3. Add stake to Alice's hotkey - 4. Set Bob as a child hotkey with 50% proportion - 5. Verify children are set via get command - 6. Revoke children and verify removal + 1. Create subnet and set children + 2. Verify children are set + 3. Revoke children from the subnet + 4. Verify children are removed """ - print("Testing set_children command ๐Ÿงช") + print("Testing revoke_children from single subnet ๐Ÿงช") + wallet_path_alice = "//Alice" netuid = 2 - # Create wallets for Alice (parent) and Bob (child) + # Create wallet for Alice keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( - "//Alice" + wallet_path_alice ) keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - # Create subnet with Alice + # Register a subnet with sudo as Alice result = exec_command_alice( command="subnets", sub_command="create", @@ -55,17 +766,31 @@ def test_set_children(local_chain, wallet_setup): "--subnet-name", "Test Subnet", "--repo", - "https://github.com/test/repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", "--no-prompt", "--json-output", + "--no-mev-protection", ], ) result_output = json.loads(result.stdout) assert result_output["success"] is True assert result_output["netuid"] == netuid + assert isinstance(result_output["extrinsic_identifier"], str) # Register Bob on the subnet - exec_command_bob( + register_bob_result = exec_command_bob( command="subnets", sub_command="register", extra_args=[ @@ -82,9 +807,10 @@ def test_set_children(local_chain, wallet_setup): "--no-prompt", ], ) + assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout # Start emissions on subnet - exec_command_alice( + start_emission_result = exec_command_alice( command="subnets", sub_command="start", extra_args=[ @@ -96,14 +822,18 @@ def test_set_children(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--network", + "--chain", "ws://127.0.0.1:9945", "--no-prompt", ], ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) - # Add stake to Alice's hotkey - add_stake = exec_command_alice( + # Add stake to Alice's hotkey to enable V3 + add_stake_result = exec_command_alice( command="stake", sub_command="add", extra_args=[ @@ -118,113 +848,143 @@ def test_set_children(local_chain, wallet_setup): "--chain", "ws://127.0.0.1:9945", "--amount", - "100", + "1", + "--unsafe", "--no-prompt", + "--era", + "144", + "--no-mev-protection", ], ) - assert "โœ… Finalized" in add_stake.stdout or "Finalized" in add_stake.stdout + assert "โœ… Finalized" in add_stake_result.stdout - # Set Bob as child hotkey with 50% proportion + # Set Bob as a child hotkey with 50% proportion set_children_result = exec_command_alice( command="stake", sub_command="child", extra_args=[ "set", + "--children", + wallet_bob.hotkey.ss58_address, + "--proportions", + "0.5", + "--netuid", + netuid, "--wallet-path", wallet_path_alice, "--wallet-name", wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--netuid", - netuid, "--chain", "ws://127.0.0.1:9945", - "--children", - keypair_bob.ss58_address, - "--proportions", - "0.5", "--no-prompt", "--json-output", ], ) set_children_output = json.loads(set_children_result.stdout) assert set_children_output[str(netuid)]["success"] is True - assert set_children_output[str(netuid)]["extrinsic_identifier"] is not None - # Get children and verify Bob is set - get_children_result = exec_command_alice( + # Verify children are set before revocation + get_children_before = exec_command_alice( command="stake", sub_command="child", extra_args=[ "get", + "--netuid", + netuid, "--wallet-path", wallet_path_alice, "--wallet-name", wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--netuid", - netuid, "--chain", "ws://127.0.0.1:9945", "--json-output", ], ) - get_children_output = json.loads(get_children_result.stdout) - assert len(get_children_output) > 0 - # Verify Bob's hotkey is in children - children_hotkeys = [child["child_ss58"] for child in get_children_output] - assert keypair_bob.ss58_address in children_hotkeys + get_children_before_output = json.loads(get_children_before.stdout) + assert isinstance(get_children_before_output, list) + assert len(get_children_before_output) > 0 + children_addresses_before = [child[1] for child in get_children_before_output] + assert wallet_bob.hotkey.ss58_address in children_addresses_before - # Revoke children - revoke_result = exec_command_alice( + # Revoke children from the subnet + revoke_children_result = exec_command_alice( command="stake", sub_command="child", extra_args=[ "revoke", + "--netuid", + netuid, "--wallet-path", wallet_path_alice, "--wallet-name", wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + revoke_children_output = json.loads(revoke_children_result.stdout) + assert revoke_children_output[str(netuid)]["success"] is True + assert isinstance( + revoke_children_output[str(netuid)]["extrinsic_identifier"], str + ) + assert revoke_children_output[str(netuid)]["completion_block"] is not None + assert revoke_children_output[str(netuid)]["set_block"] is not None + + # Verify children are revoked + get_children_after = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "get", "--netuid", netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, "--chain", "ws://127.0.0.1:9945", - "--no-prompt", "--json-output", ], ) - revoke_output = json.loads(revoke_result.stdout) - assert revoke_output[str(netuid)]["success"] is True + get_children_after_output = json.loads(get_children_after.stdout) + # After revocation, children list should be empty or None + assert get_children_after_output == [] or get_children_after_output is None - print("โœ… Passed set_children test") + print("โœ… Passed revoke_children from single subnet") @pytest.mark.parametrize("local_chain", [False], indirect=True) -def test_set_children_multiple_proportions(local_chain, wallet_setup): +def test_revoke_children_json_output(local_chain, wallet_setup): """ - Test setting multiple children hotkeys with different proportions. + Test revoking children with JSON output and verify the response structure. Steps: - 1. Create wallets for Alice (parent), Bob and Charlie (children) - 2. Create a subnet and register all - 3. Set Bob (30%) and Charlie (40%) as children - 4. Verify both children are set with correct proportions + 1. Create subnet and set children + 2. Revoke children with --json-output flag + 3. Verify JSON structure contains completion_block and set_block """ - print("Testing set_children with multiple children ๐Ÿงช") + print("Testing revoke_children with JSON output ๐Ÿงช") + wallet_path_alice = "//Alice" netuid = 2 - # Create wallets + # Create wallet for Alice keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( - "//Alice" + wallet_path_alice ) - keypair_bob, wallet_bob, wallet_path_bob, _ = wallet_setup("//Bob") - keypair_charlie, wallet_charlie, wallet_path_charlie, _ = wallet_setup("//Charlie") + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - # Create subnet with Alice + # Register a subnet with sudo as Alice result = exec_command_alice( command="subnets", sub_command="create", @@ -240,16 +1000,50 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): "--subnet-name", "Test Subnet", "--repo", - "https://github.com/test/repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", "--no-prompt", "--json-output", + "--no-mev-protection", ], ) result_output = json.loads(result.stdout) assert result_output["success"] is True + assert result_output["netuid"] == netuid + + # Register Bob + register_bob_result = exec_command_bob( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--wallet-name", + wallet_bob.name, + "--hotkey", + wallet_bob.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout # Start emissions - exec_command_alice( + start_emission_result = exec_command_alice( command="subnets", sub_command="start", extra_args=[ @@ -261,14 +1055,18 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--network", + "--chain", "ws://127.0.0.1:9945", "--no-prompt", ], ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) # Add stake - exec_command_alice( + add_stake_result = exec_command_alice( command="stake", sub_command="add", extra_args=[ @@ -283,35 +1081,231 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): "--chain", "ws://127.0.0.1:9945", "--amount", - "100", + "1", + "--unsafe", "--no-prompt", + "--era", + "144", + "--no-mev-protection", ], ) + assert "โœ… Finalized" in add_stake_result.stdout - # Set multiple children: Bob (30%) and Charlie (40%) + # Set children set_children_result = exec_command_alice( command="stake", sub_command="child", extra_args=[ "set", + "--children", + wallet_bob.hotkey.ss58_address, + "--proportions", + "0.5", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + + # Revoke children with JSON output + revoke_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "revoke", + "--netuid", + netuid, "--wallet-path", wallet_path_alice, "--wallet-name", wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + revoke_children_output = json.loads(revoke_children_result.stdout) + # Verify JSON structure + assert isinstance(revoke_children_output, dict) + assert str(netuid) in revoke_children_output + assert revoke_children_output[str(netuid)]["success"] is True + assert isinstance( + revoke_children_output[str(netuid)]["extrinsic_identifier"], str + ) + assert revoke_children_output[str(netuid)]["completion_block"] is not None + assert revoke_children_output[str(netuid)]["set_block"] is not None + assert isinstance(revoke_children_output[str(netuid)]["completion_block"], int) + assert isinstance(revoke_children_output[str(netuid)]["set_block"], int) + # completion_block should be greater than set_block + assert ( + revoke_children_output[str(netuid)]["completion_block"] + > revoke_children_output[str(netuid)]["set_block"] + ) + + print("โœ… Passed revoke_children with JSON output") + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_revoke_children_all_netuids(local_chain, wallet_setup): + """ + Test revoking children from all netuids using --all-netuids flag. + + Steps: + 1. Create subnet and set children + 2. Revoke children using --all-netuids flag + 3. Verify revocation was successful + """ + print("Testing revoke_children with --all-netuids ๐Ÿงช") + wallet_path_alice = "//Alice" + netuid = 2 + + # Create wallet for Alice + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + + # Register a subnet with sudo as Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", + "--no-prompt", + "--json-output", + "--no-mev-protection", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + assert result_output["netuid"] == netuid + + # Register Bob + register_bob_result = exec_command_bob( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--wallet-name", + wallet_bob.name, + "--hotkey", + wallet_bob.hotkey_str, "--netuid", netuid, "--chain", "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + + # Start emissions + start_emission_result = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) + + # Add stake + add_stake_result = exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", + "--amount", + "1", + "--unsafe", + "--no-prompt", + "--era", + "144", + "--no-mev-protection", + ], + ) + assert "โœ… Finalized" in add_stake_result.stdout + + # Set children + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", "--children", - keypair_bob.ss58_address, - "--children", - keypair_charlie.ss58_address, - "--proportions", - "0.3", + wallet_bob.hotkey.ss58_address, "--proportions", - "0.4", + "0.5", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--chain", + "ws://127.0.0.1:9945", "--no-prompt", "--json-output", ], @@ -319,28 +1313,33 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): set_children_output = json.loads(set_children_result.stdout) assert set_children_output[str(netuid)]["success"] is True - # Verify both children are set - get_children_result = exec_command_alice( + # Revoke children using --all-netuids + revoke_all_result = exec_command_alice( command="stake", sub_command="child", extra_args=[ - "get", + "revoke", + "--all-netuids", "--wallet-path", wallet_path_alice, "--wallet-name", wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--netuid", - netuid, "--chain", "ws://127.0.0.1:9945", + "--no-prompt", "--json-output", ], ) - get_children_output = json.loads(get_children_result.stdout) - children_hotkeys = [child["child_ss58"] for child in get_children_output] - assert keypair_bob.ss58_address in children_hotkeys - assert keypair_charlie.ss58_address in children_hotkeys + revoke_all_output = json.loads(revoke_all_result.stdout) + # Should have results for the netuid + assert isinstance(revoke_all_output, dict) + assert str(netuid) in revoke_all_output + assert revoke_all_output[str(netuid)]["success"] is True + assert isinstance( + revoke_all_output[str(netuid)]["extrinsic_identifier"], str + ) + + print("โœ… Passed revoke_children with --all-netuids") - print("โœ… Passed set_children multiple proportions test") From 0c80dd7712f72ab5946a0cae8764a97d26d4c260 Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 11:50:29 -0500 Subject: [PATCH 07/11] ruff format --- tests/e2e_tests/test_children_hotkeys.py | 52 +++++++++++++++--------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/tests/e2e_tests/test_children_hotkeys.py b/tests/e2e_tests/test_children_hotkeys.py index ec8e1017..f1b2f3a1 100644 --- a/tests/e2e_tests/test_children_hotkeys.py +++ b/tests/e2e_tests/test_children_hotkeys.py @@ -94,7 +94,10 @@ def test_set_children_single_child(local_chain, wallet_setup): "--no-prompt", ], ) - assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + assert ( + "โœ… Registered" in register_bob_result.stdout + or "โœ… Already Registered" in register_bob_result.stdout + ) # Start emissions on subnet start_emission_result = exec_command_alice( @@ -202,8 +205,8 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): wallet_path_alice ) keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - keypair_charlie, wallet_charlie, wallet_path_charlie, exec_command_charlie = wallet_setup( - "//Charlie" + keypair_charlie, wallet_charlie, wallet_path_charlie, exec_command_charlie = ( + wallet_setup("//Charlie") ) # Register a subnet with sudo as Alice @@ -267,7 +270,10 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): "--no-prompt", ], ) - assert "โœ… Registered" in register_result.stdout or "โœ… Already Registered" in register_result.stdout + assert ( + "โœ… Registered" in register_result.stdout + or "โœ… Already Registered" in register_result.stdout + ) # Start emissions on subnet start_emission_result = exec_command_alice( @@ -431,7 +437,10 @@ def test_get_children_json_output(local_chain, wallet_setup): "--no-prompt", ], ) - assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + assert ( + "โœ… Registered" in register_bob_result.stdout + or "โœ… Already Registered" in register_bob_result.stdout + ) # Start emissions start_emission_result = exec_command_alice( @@ -624,7 +633,10 @@ def test_get_children_non_json_output(local_chain, wallet_setup): "--no-prompt", ], ) - assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + assert ( + "โœ… Registered" in register_bob_result.stdout + or "โœ… Already Registered" in register_bob_result.stdout + ) # Start emissions start_emission_result = exec_command_alice( @@ -807,7 +819,10 @@ def test_revoke_children_single_subnet(local_chain, wallet_setup): "--no-prompt", ], ) - assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + assert ( + "โœ… Registered" in register_bob_result.stdout + or "โœ… Already Registered" in register_bob_result.stdout + ) # Start emissions on subnet start_emission_result = exec_command_alice( @@ -932,9 +947,7 @@ def test_revoke_children_single_subnet(local_chain, wallet_setup): ) revoke_children_output = json.loads(revoke_children_result.stdout) assert revoke_children_output[str(netuid)]["success"] is True - assert isinstance( - revoke_children_output[str(netuid)]["extrinsic_identifier"], str - ) + assert isinstance(revoke_children_output[str(netuid)]["extrinsic_identifier"], str) assert revoke_children_output[str(netuid)]["completion_block"] is not None assert revoke_children_output[str(netuid)]["set_block"] is not None @@ -1040,7 +1053,10 @@ def test_revoke_children_json_output(local_chain, wallet_setup): "--no-prompt", ], ) - assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + assert ( + "โœ… Registered" in register_bob_result.stdout + or "โœ… Already Registered" in register_bob_result.stdout + ) # Start emissions start_emission_result = exec_command_alice( @@ -1143,9 +1159,7 @@ def test_revoke_children_json_output(local_chain, wallet_setup): assert isinstance(revoke_children_output, dict) assert str(netuid) in revoke_children_output assert revoke_children_output[str(netuid)]["success"] is True - assert isinstance( - revoke_children_output[str(netuid)]["extrinsic_identifier"], str - ) + assert isinstance(revoke_children_output[str(netuid)]["extrinsic_identifier"], str) assert revoke_children_output[str(netuid)]["completion_block"] is not None assert revoke_children_output[str(netuid)]["set_block"] is not None assert isinstance(revoke_children_output[str(netuid)]["completion_block"], int) @@ -1235,7 +1249,10 @@ def test_revoke_children_all_netuids(local_chain, wallet_setup): "--no-prompt", ], ) - assert "โœ… Registered" in register_bob_result.stdout or "โœ… Already Registered" in register_bob_result.stdout + assert ( + "โœ… Registered" in register_bob_result.stdout + or "โœ… Already Registered" in register_bob_result.stdout + ) # Start emissions start_emission_result = exec_command_alice( @@ -1337,9 +1354,6 @@ def test_revoke_children_all_netuids(local_chain, wallet_setup): assert isinstance(revoke_all_output, dict) assert str(netuid) in revoke_all_output assert revoke_all_output[str(netuid)]["success"] is True - assert isinstance( - revoke_all_output[str(netuid)]["extrinsic_identifier"], str - ) + assert isinstance(revoke_all_output[str(netuid)]["extrinsic_identifier"], str) print("โœ… Passed revoke_children with --all-netuids") - From 8018b73e800bb363d3324f04acdd92cb9097d0b9 Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 17:24:11 -0500 Subject: [PATCH 08/11] fix: update the test of test_set_children_multiple_proportions --- tests/e2e_tests/test_children_hotkeys.py | 744 +++-------------------- 1 file changed, 77 insertions(+), 667 deletions(-) diff --git a/tests/e2e_tests/test_children_hotkeys.py b/tests/e2e_tests/test_children_hotkeys.py index f1b2f3a1..3eeb0b4b 100644 --- a/tests/e2e_tests/test_children_hotkeys.py +++ b/tests/e2e_tests/test_children_hotkeys.py @@ -41,7 +41,7 @@ def test_set_children_single_child(local_chain, wallet_setup): extra_args=[ "--wallet-path", wallet_path_alice, - "--chain", + "--network", "ws://127.0.0.1:9945", "--wallet-name", wallet_alice.name, @@ -89,7 +89,7 @@ def test_set_children_single_child(local_chain, wallet_setup): wallet_bob.hotkey_str, "--netuid", netuid, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -112,7 +112,7 @@ def test_set_children_single_child(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -135,7 +135,7 @@ def test_set_children_single_child(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--amount", "1", @@ -166,7 +166,7 @@ def test_set_children_single_child(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", "--json-output", @@ -189,34 +189,31 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): Test setting multiple children hotkeys with different proportions. Steps: - 1. Create wallets for Alice (parent), Bob and Charlie (children) - 2. Create a subnet and register all - 3. Start emissions and add stake - 4. Set Bob (30%) and Charlie (40%) as children - 5. Verify both children are set with correct proportions - 6. Test getting children with --all-netuids flag + 1. Create wallets for Alice (parent), Bob, Charlie, and Dave (children) + 2. Create a subnet and register all participants + 3. Start emissions on the subnet + 4. Add stake to Alice's hotkey + 5. Set multiple children with different proportions (Bob: 25%, Charlie: 35%, Dave: 20%) + 6. Verify the transaction succeeded """ - print("Testing set_children with multiple children ๐Ÿงช") + print("Testing set_children with multiple proportions") + wallet_path_alice = "//Alice" netuid = 2 - # Create wallet for Alice + # Create wallet for Alice (parent) keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( wallet_path_alice ) - keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - keypair_charlie, wallet_charlie, wallet_path_charlie, exec_command_charlie = ( - wallet_setup("//Charlie") - ) - # Register a subnet with sudo as Alice + # Create subnet as Alice result = exec_command_alice( command="subnets", sub_command="create", extra_args=[ "--wallet-path", wallet_path_alice, - "--chain", + "--network", "ws://127.0.0.1:9945", "--wallet-name", wallet_alice.name, @@ -246,26 +243,35 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): result_output = json.loads(result.stdout) assert result_output["success"] is True assert result_output["netuid"] == netuid - assert isinstance(result_output["extrinsic_identifier"], str) - # Register Bob and Charlie on the subnet - for wallet_exec, wallet_obj, wallet_path in [ - (exec_command_bob, wallet_bob, wallet_path_bob), - (exec_command_charlie, wallet_charlie, wallet_path_charlie), + # Create wallets for children + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + keypair_charlie, wallet_charlie, wallet_path_charlie, exec_command_charlie = ( + wallet_setup("//Charlie") + ) + keypair_dave, wallet_dave, wallet_path_dave, exec_command_dave = wallet_setup( + "//Dave" + ) + + # Register all children on the subnet + for wallet, wallet_path, exec_command in [ + (wallet_bob, wallet_path_bob, exec_command_bob), + (wallet_charlie, wallet_path_charlie, exec_command_charlie), + (wallet_dave, wallet_path_dave, exec_command_dave), ]: - register_result = wallet_exec( + register_result = exec_command( command="subnets", sub_command="register", extra_args=[ "--wallet-path", wallet_path, "--wallet-name", - wallet_obj.name, + wallet.name, "--hotkey", - wallet_obj.hotkey_str, + wallet.hotkey_str, "--netuid", netuid, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -288,7 +294,7 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -311,7 +317,7 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--amount", "1", @@ -324,7 +330,9 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): ) assert "โœ… Finalized" in add_stake_result.stdout - # Set Bob (30%) and Charlie (40%) as children + # Set multiple children with different proportions + # Bob: 25%, Charlie: 35%, Dave: 20% + # Note: Typer list options require repeating the flag for each value set_children_result = exec_command_alice( command="stake", sub_command="child", @@ -332,10 +340,16 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): "set", "--children", wallet_bob.hotkey.ss58_address, + "--children", wallet_charlie.hotkey.ss58_address, + "--children", + wallet_dave.hotkey.ss58_address, + "--proportions", + "0.25", + "--proportions", + "0.35", "--proportions", - "0.3", - "0.4", + "0.20", "--netuid", netuid, "--wallet-path", @@ -344,20 +358,34 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", "--json-output", ], ) - set_children_output = json.loads(set_children_result.stdout) + + # Debug output if parsing fails + if not set_children_result.stdout.strip(): + print(f"stdout is empty") + print(f"stderr: {set_children_result.stderr}") + print(f"exit_code: {set_children_result.exit_code}") + pytest.fail(f"set_children returned empty stdout. stderr: {set_children_result.stderr}") + + try: + set_children_output = json.loads(set_children_result.stdout) + except json.JSONDecodeError as e: + print(f"Failed to parse JSON") + print(f"stdout: {set_children_result.stdout}") + print(f"stderr: {set_children_result.stderr}") + pytest.fail(f"Failed to parse JSON: {e}. stdout: {set_children_result.stdout}") + assert set_children_output[str(netuid)]["success"] is True assert isinstance(set_children_output[str(netuid)]["extrinsic_identifier"], str) - # Note: Children changes are not immediate - they require waiting for completion_block assert set_children_output[str(netuid)]["completion_block"] is not None assert set_children_output[str(netuid)]["set_block"] is not None - print("โœ… Passed set_children with multiple children") + print("โœ… Passed set_children with multiple proportions") @pytest.mark.parametrize("local_chain", [False], indirect=True) @@ -387,7 +415,7 @@ def test_get_children_json_output(local_chain, wallet_setup): extra_args=[ "--wallet-path", wallet_path_alice, - "--chain", + "--network", "ws://127.0.0.1:9945", "--wallet-name", wallet_alice.name, @@ -432,7 +460,7 @@ def test_get_children_json_output(local_chain, wallet_setup): wallet_bob.hotkey_str, "--netuid", netuid, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -455,7 +483,7 @@ def test_get_children_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -478,7 +506,7 @@ def test_get_children_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--amount", "1", @@ -509,7 +537,7 @@ def test_get_children_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", "--json-output", @@ -535,7 +563,7 @@ def test_get_children_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--json-output", ], @@ -583,7 +611,7 @@ def test_get_children_non_json_output(local_chain, wallet_setup): extra_args=[ "--wallet-path", wallet_path_alice, - "--chain", + "--network", "ws://127.0.0.1:9945", "--wallet-name", wallet_alice.name, @@ -628,7 +656,7 @@ def test_get_children_non_json_output(local_chain, wallet_setup): wallet_bob.hotkey_str, "--netuid", netuid, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -651,7 +679,7 @@ def test_get_children_non_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", ], @@ -674,7 +702,7 @@ def test_get_children_non_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--amount", "1", @@ -705,7 +733,7 @@ def test_get_children_non_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", "--no-prompt", "--json-output", @@ -731,7 +759,7 @@ def test_get_children_non_json_output(local_chain, wallet_setup): wallet_alice.name, "--hotkey", wallet_alice.hotkey_str, - "--chain", + "--network", "ws://127.0.0.1:9945", ], ) @@ -739,621 +767,3 @@ def test_get_children_non_json_output(local_chain, wallet_setup): assert len(get_children_result.stdout) > 0 print("โœ… Passed get_children without JSON output") - - -@pytest.mark.parametrize("local_chain", [False], indirect=True) -def test_revoke_children_single_subnet(local_chain, wallet_setup): - """ - Test revoking children hotkeys from a single subnet. - - Steps: - 1. Create subnet and set children - 2. Verify children are set - 3. Revoke children from the subnet - 4. Verify children are removed - """ - print("Testing revoke_children from single subnet ๐Ÿงช") - wallet_path_alice = "//Alice" - netuid = 2 - - # Create wallet for Alice - keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( - wallet_path_alice - ) - keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - - # Register a subnet with sudo as Alice - result = exec_command_alice( - command="subnets", - sub_command="create", - extra_args=[ - "--wallet-path", - wallet_path_alice, - "--chain", - "ws://127.0.0.1:9945", - "--wallet-name", - wallet_alice.name, - "--wallet-hotkey", - wallet_alice.hotkey_str, - "--subnet-name", - "Test Subnet", - "--repo", - "https://github.com/username/repo", - "--contact", - "alice@opentensor.dev", - "--url", - "https://testsubnet.com", - "--discord", - "alice#1234", - "--description", - "A test subnet for e2e testing", - "--additional-info", - "Created by Alice", - "--logo-url", - "https://testsubnet.com/logo.png", - "--no-prompt", - "--json-output", - "--no-mev-protection", - ], - ) - result_output = json.loads(result.stdout) - assert result_output["success"] is True - assert result_output["netuid"] == netuid - assert isinstance(result_output["extrinsic_identifier"], str) - - # Register Bob on the subnet - register_bob_result = exec_command_bob( - command="subnets", - sub_command="register", - extra_args=[ - "--wallet-path", - wallet_path_bob, - "--wallet-name", - wallet_bob.name, - "--hotkey", - wallet_bob.hotkey_str, - "--netuid", - netuid, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - "โœ… Registered" in register_bob_result.stdout - or "โœ… Already Registered" in register_bob_result.stdout - ) - - # Start emissions on subnet - start_emission_result = exec_command_alice( - command="subnets", - sub_command="start", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - f"Successfully started subnet {netuid}'s emission schedule" - in start_emission_result.stdout - ) - - # Add stake to Alice's hotkey to enable V3 - add_stake_result = exec_command_alice( - command="stake", - sub_command="add", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--amount", - "1", - "--unsafe", - "--no-prompt", - "--era", - "144", - "--no-mev-protection", - ], - ) - assert "โœ… Finalized" in add_stake_result.stdout - - # Set Bob as a child hotkey with 50% proportion - set_children_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "set", - "--children", - wallet_bob.hotkey.ss58_address, - "--proportions", - "0.5", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - set_children_output = json.loads(set_children_result.stdout) - assert set_children_output[str(netuid)]["success"] is True - - # Verify children are set before revocation - get_children_before = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "get", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--json-output", - ], - ) - get_children_before_output = json.loads(get_children_before.stdout) - assert isinstance(get_children_before_output, list) - assert len(get_children_before_output) > 0 - children_addresses_before = [child[1] for child in get_children_before_output] - assert wallet_bob.hotkey.ss58_address in children_addresses_before - - # Revoke children from the subnet - revoke_children_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "revoke", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - revoke_children_output = json.loads(revoke_children_result.stdout) - assert revoke_children_output[str(netuid)]["success"] is True - assert isinstance(revoke_children_output[str(netuid)]["extrinsic_identifier"], str) - assert revoke_children_output[str(netuid)]["completion_block"] is not None - assert revoke_children_output[str(netuid)]["set_block"] is not None - - # Verify children are revoked - get_children_after = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "get", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--json-output", - ], - ) - get_children_after_output = json.loads(get_children_after.stdout) - # After revocation, children list should be empty or None - assert get_children_after_output == [] or get_children_after_output is None - - print("โœ… Passed revoke_children from single subnet") - - -@pytest.mark.parametrize("local_chain", [False], indirect=True) -def test_revoke_children_json_output(local_chain, wallet_setup): - """ - Test revoking children with JSON output and verify the response structure. - - Steps: - 1. Create subnet and set children - 2. Revoke children with --json-output flag - 3. Verify JSON structure contains completion_block and set_block - """ - print("Testing revoke_children with JSON output ๐Ÿงช") - wallet_path_alice = "//Alice" - netuid = 2 - - # Create wallet for Alice - keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( - wallet_path_alice - ) - keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - - # Register a subnet with sudo as Alice - result = exec_command_alice( - command="subnets", - sub_command="create", - extra_args=[ - "--wallet-path", - wallet_path_alice, - "--chain", - "ws://127.0.0.1:9945", - "--wallet-name", - wallet_alice.name, - "--wallet-hotkey", - wallet_alice.hotkey_str, - "--subnet-name", - "Test Subnet", - "--repo", - "https://github.com/username/repo", - "--contact", - "alice@opentensor.dev", - "--url", - "https://testsubnet.com", - "--discord", - "alice#1234", - "--description", - "A test subnet for e2e testing", - "--additional-info", - "Created by Alice", - "--logo-url", - "https://testsubnet.com/logo.png", - "--no-prompt", - "--json-output", - "--no-mev-protection", - ], - ) - result_output = json.loads(result.stdout) - assert result_output["success"] is True - assert result_output["netuid"] == netuid - - # Register Bob - register_bob_result = exec_command_bob( - command="subnets", - sub_command="register", - extra_args=[ - "--wallet-path", - wallet_path_bob, - "--wallet-name", - wallet_bob.name, - "--hotkey", - wallet_bob.hotkey_str, - "--netuid", - netuid, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - "โœ… Registered" in register_bob_result.stdout - or "โœ… Already Registered" in register_bob_result.stdout - ) - - # Start emissions - start_emission_result = exec_command_alice( - command="subnets", - sub_command="start", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - f"Successfully started subnet {netuid}'s emission schedule" - in start_emission_result.stdout - ) - - # Add stake - add_stake_result = exec_command_alice( - command="stake", - sub_command="add", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--amount", - "1", - "--unsafe", - "--no-prompt", - "--era", - "144", - "--no-mev-protection", - ], - ) - assert "โœ… Finalized" in add_stake_result.stdout - - # Set children - set_children_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "set", - "--children", - wallet_bob.hotkey.ss58_address, - "--proportions", - "0.5", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - set_children_output = json.loads(set_children_result.stdout) - assert set_children_output[str(netuid)]["success"] is True - - # Revoke children with JSON output - revoke_children_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "revoke", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - revoke_children_output = json.loads(revoke_children_result.stdout) - # Verify JSON structure - assert isinstance(revoke_children_output, dict) - assert str(netuid) in revoke_children_output - assert revoke_children_output[str(netuid)]["success"] is True - assert isinstance(revoke_children_output[str(netuid)]["extrinsic_identifier"], str) - assert revoke_children_output[str(netuid)]["completion_block"] is not None - assert revoke_children_output[str(netuid)]["set_block"] is not None - assert isinstance(revoke_children_output[str(netuid)]["completion_block"], int) - assert isinstance(revoke_children_output[str(netuid)]["set_block"], int) - # completion_block should be greater than set_block - assert ( - revoke_children_output[str(netuid)]["completion_block"] - > revoke_children_output[str(netuid)]["set_block"] - ) - - print("โœ… Passed revoke_children with JSON output") - - -@pytest.mark.parametrize("local_chain", [False], indirect=True) -def test_revoke_children_all_netuids(local_chain, wallet_setup): - """ - Test revoking children from all netuids using --all-netuids flag. - - Steps: - 1. Create subnet and set children - 2. Revoke children using --all-netuids flag - 3. Verify revocation was successful - """ - print("Testing revoke_children with --all-netuids ๐Ÿงช") - wallet_path_alice = "//Alice" - netuid = 2 - - # Create wallet for Alice - keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( - wallet_path_alice - ) - keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - - # Register a subnet with sudo as Alice - result = exec_command_alice( - command="subnets", - sub_command="create", - extra_args=[ - "--wallet-path", - wallet_path_alice, - "--chain", - "ws://127.0.0.1:9945", - "--wallet-name", - wallet_alice.name, - "--wallet-hotkey", - wallet_alice.hotkey_str, - "--subnet-name", - "Test Subnet", - "--repo", - "https://github.com/username/repo", - "--contact", - "alice@opentensor.dev", - "--url", - "https://testsubnet.com", - "--discord", - "alice#1234", - "--description", - "A test subnet for e2e testing", - "--additional-info", - "Created by Alice", - "--logo-url", - "https://testsubnet.com/logo.png", - "--no-prompt", - "--json-output", - "--no-mev-protection", - ], - ) - result_output = json.loads(result.stdout) - assert result_output["success"] is True - assert result_output["netuid"] == netuid - - # Register Bob - register_bob_result = exec_command_bob( - command="subnets", - sub_command="register", - extra_args=[ - "--wallet-path", - wallet_path_bob, - "--wallet-name", - wallet_bob.name, - "--hotkey", - wallet_bob.hotkey_str, - "--netuid", - netuid, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - "โœ… Registered" in register_bob_result.stdout - or "โœ… Already Registered" in register_bob_result.stdout - ) - - # Start emissions - start_emission_result = exec_command_alice( - command="subnets", - sub_command="start", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - f"Successfully started subnet {netuid}'s emission schedule" - in start_emission_result.stdout - ) - - # Add stake - add_stake_result = exec_command_alice( - command="stake", - sub_command="add", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--amount", - "1", - "--unsafe", - "--no-prompt", - "--era", - "144", - "--no-mev-protection", - ], - ) - assert "โœ… Finalized" in add_stake_result.stdout - - # Set children - set_children_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "set", - "--children", - wallet_bob.hotkey.ss58_address, - "--proportions", - "0.5", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - set_children_output = json.loads(set_children_result.stdout) - assert set_children_output[str(netuid)]["success"] is True - - # Revoke children using --all-netuids - revoke_all_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "revoke", - "--all-netuids", - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--chain", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - revoke_all_output = json.loads(revoke_all_result.stdout) - # Should have results for the netuid - assert isinstance(revoke_all_output, dict) - assert str(netuid) in revoke_all_output - assert revoke_all_output[str(netuid)]["success"] is True - assert isinstance(revoke_all_output[str(netuid)]["extrinsic_identifier"], str) - - print("โœ… Passed revoke_children with --all-netuids") From 987cddcdcfd746db084fc14d225003961b01102c Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 17:28:09 -0500 Subject: [PATCH 09/11] ruff format and add revoke test --- tests/e2e_tests/test_children_hotkeys.py | 216 ++++++++++++++++++++++- 1 file changed, 215 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_children_hotkeys.py b/tests/e2e_tests/test_children_hotkeys.py index 3eeb0b4b..86d77658 100644 --- a/tests/e2e_tests/test_children_hotkeys.py +++ b/tests/e2e_tests/test_children_hotkeys.py @@ -370,7 +370,9 @@ def test_set_children_multiple_proportions(local_chain, wallet_setup): print(f"stdout is empty") print(f"stderr: {set_children_result.stderr}") print(f"exit_code: {set_children_result.exit_code}") - pytest.fail(f"set_children returned empty stdout. stderr: {set_children_result.stderr}") + pytest.fail( + f"set_children returned empty stdout. stderr: {set_children_result.stderr}" + ) try: set_children_output = json.loads(set_children_result.stdout) @@ -767,3 +769,215 @@ def test_get_children_non_json_output(local_chain, wallet_setup): assert len(get_children_result.stdout) > 0 print("โœ… Passed get_children without JSON output") + + +@pytest.mark.parametrize("local_chain", [False], indirect=True) +def test_revoke_children(local_chain, wallet_setup): + """ + Test revoking all children hotkeys on a subnet. + + Steps: + 1. Create wallets for Alice (parent) and Bob (child) + 2. Create a subnet and register Bob + 3. Start emissions on the subnet + 4. Add stake to Alice's hotkey + 5. Set Bob as a child hotkey with 50% proportion + 6. Revoke all children + 7. Verify the revoke transaction succeeded + """ + print("Testing revoke_children") + + wallet_path_alice = "//Alice" + netuid = 2 + + # Create wallet for Alice (parent) + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + + # Create subnet as Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--network", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--wallet-hotkey", + wallet_alice.hotkey_str, + "--subnet-name", + "Test Subnet", + "--repo", + "https://github.com/username/repo", + "--contact", + "alice@opentensor.dev", + "--url", + "https://testsubnet.com", + "--discord", + "alice#1234", + "--description", + "A test subnet for e2e testing", + "--additional-info", + "Created by Alice", + "--logo-url", + "https://testsubnet.com/logo.png", + "--no-prompt", + "--json-output", + "--no-mev-protection", + ], + ) + result_output = json.loads(result.stdout) + assert result_output["success"] is True + assert result_output["netuid"] == netuid + + # Create wallet for Bob (child) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") + + # Register Bob on the subnet + register_bob_result = exec_command_bob( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--wallet-name", + wallet_bob.name, + "--hotkey", + wallet_bob.hotkey_str, + "--netuid", + netuid, + "--network", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert ( + "โœ… Registered" in register_bob_result.stdout + or "โœ… Already Registered" in register_bob_result.stdout + ) + + # Start emissions on subnet + start_emission_result = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--network", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert ( + f"Successfully started subnet {netuid}'s emission schedule" + in start_emission_result.stdout + ) + + # Add stake to Alice's hotkey + add_stake_result = exec_command_alice( + command="stake", + sub_command="add", + extra_args=[ + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--network", + "ws://127.0.0.1:9945", + "--amount", + "1", + "--unsafe", + "--no-prompt", + "--era", + "144", + "--no-mev-protection", + ], + ) + assert "โœ… Finalized" in add_stake_result.stdout + + # Set Bob as a child hotkey with 50% proportion + set_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "set", + "--children", + wallet_bob.hotkey.ss58_address, + "--proportions", + "0.5", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--network", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + set_children_output = json.loads(set_children_result.stdout) + assert set_children_output[str(netuid)]["success"] is True + + # Revoke all children + revoke_children_result = exec_command_alice( + command="stake", + sub_command="child", + extra_args=[ + "revoke", + "--netuid", + netuid, + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--network", + "ws://127.0.0.1:9945", + "--no-prompt", + "--json-output", + ], + ) + + # Debug output if parsing fails + if not revoke_children_result.stdout.strip(): + print(f"stdout is empty") + print(f"stderr: {revoke_children_result.stderr}") + print(f"exit_code: {revoke_children_result.exit_code}") + pytest.fail( + f"revoke_children returned empty stdout. stderr: {revoke_children_result.stderr}" + ) + + try: + revoke_output = json.loads(revoke_children_result.stdout) + except json.JSONDecodeError as e: + print(f"Failed to parse JSON") + print(f"stdout: {revoke_children_result.stdout}") + print(f"stderr: {revoke_children_result.stderr}") + pytest.fail( + f"Failed to parse JSON: {e}. stdout: {revoke_children_result.stdout}" + ) + + assert revoke_output[str(netuid)]["success"] is True + assert isinstance(revoke_output[str(netuid)]["extrinsic_identifier"], str) + assert revoke_output[str(netuid)]["completion_block"] is not None + assert revoke_output[str(netuid)]["set_block"] is not None + + print("โœ… Passed revoke_children") From 93fa913b4af409a683b489712e6f7d542388f10c Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 18:06:43 -0500 Subject: [PATCH 10/11] fix: remove the revoke child test --- tests/e2e_tests/test_children_hotkeys.py | 212 ----------------------- 1 file changed, 212 deletions(-) diff --git a/tests/e2e_tests/test_children_hotkeys.py b/tests/e2e_tests/test_children_hotkeys.py index 86d77658..d55833d7 100644 --- a/tests/e2e_tests/test_children_hotkeys.py +++ b/tests/e2e_tests/test_children_hotkeys.py @@ -769,215 +769,3 @@ def test_get_children_non_json_output(local_chain, wallet_setup): assert len(get_children_result.stdout) > 0 print("โœ… Passed get_children without JSON output") - - -@pytest.mark.parametrize("local_chain", [False], indirect=True) -def test_revoke_children(local_chain, wallet_setup): - """ - Test revoking all children hotkeys on a subnet. - - Steps: - 1. Create wallets for Alice (parent) and Bob (child) - 2. Create a subnet and register Bob - 3. Start emissions on the subnet - 4. Add stake to Alice's hotkey - 5. Set Bob as a child hotkey with 50% proportion - 6. Revoke all children - 7. Verify the revoke transaction succeeded - """ - print("Testing revoke_children") - - wallet_path_alice = "//Alice" - netuid = 2 - - # Create wallet for Alice (parent) - keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( - wallet_path_alice - ) - - # Create subnet as Alice - result = exec_command_alice( - command="subnets", - sub_command="create", - extra_args=[ - "--wallet-path", - wallet_path_alice, - "--network", - "ws://127.0.0.1:9945", - "--wallet-name", - wallet_alice.name, - "--wallet-hotkey", - wallet_alice.hotkey_str, - "--subnet-name", - "Test Subnet", - "--repo", - "https://github.com/username/repo", - "--contact", - "alice@opentensor.dev", - "--url", - "https://testsubnet.com", - "--discord", - "alice#1234", - "--description", - "A test subnet for e2e testing", - "--additional-info", - "Created by Alice", - "--logo-url", - "https://testsubnet.com/logo.png", - "--no-prompt", - "--json-output", - "--no-mev-protection", - ], - ) - result_output = json.loads(result.stdout) - assert result_output["success"] is True - assert result_output["netuid"] == netuid - - # Create wallet for Bob (child) - keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup("//Bob") - - # Register Bob on the subnet - register_bob_result = exec_command_bob( - command="subnets", - sub_command="register", - extra_args=[ - "--wallet-path", - wallet_path_bob, - "--wallet-name", - wallet_bob.name, - "--hotkey", - wallet_bob.hotkey_str, - "--netuid", - netuid, - "--network", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - "โœ… Registered" in register_bob_result.stdout - or "โœ… Already Registered" in register_bob_result.stdout - ) - - # Start emissions on subnet - start_emission_result = exec_command_alice( - command="subnets", - sub_command="start", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--network", - "ws://127.0.0.1:9945", - "--no-prompt", - ], - ) - assert ( - f"Successfully started subnet {netuid}'s emission schedule" - in start_emission_result.stdout - ) - - # Add stake to Alice's hotkey - add_stake_result = exec_command_alice( - command="stake", - sub_command="add", - extra_args=[ - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--network", - "ws://127.0.0.1:9945", - "--amount", - "1", - "--unsafe", - "--no-prompt", - "--era", - "144", - "--no-mev-protection", - ], - ) - assert "โœ… Finalized" in add_stake_result.stdout - - # Set Bob as a child hotkey with 50% proportion - set_children_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "set", - "--children", - wallet_bob.hotkey.ss58_address, - "--proportions", - "0.5", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--network", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - set_children_output = json.loads(set_children_result.stdout) - assert set_children_output[str(netuid)]["success"] is True - - # Revoke all children - revoke_children_result = exec_command_alice( - command="stake", - sub_command="child", - extra_args=[ - "revoke", - "--netuid", - netuid, - "--wallet-path", - wallet_path_alice, - "--wallet-name", - wallet_alice.name, - "--hotkey", - wallet_alice.hotkey_str, - "--network", - "ws://127.0.0.1:9945", - "--no-prompt", - "--json-output", - ], - ) - - # Debug output if parsing fails - if not revoke_children_result.stdout.strip(): - print(f"stdout is empty") - print(f"stderr: {revoke_children_result.stderr}") - print(f"exit_code: {revoke_children_result.exit_code}") - pytest.fail( - f"revoke_children returned empty stdout. stderr: {revoke_children_result.stderr}" - ) - - try: - revoke_output = json.loads(revoke_children_result.stdout) - except json.JSONDecodeError as e: - print(f"Failed to parse JSON") - print(f"stdout: {revoke_children_result.stdout}") - print(f"stderr: {revoke_children_result.stderr}") - pytest.fail( - f"Failed to parse JSON: {e}. stdout: {revoke_children_result.stdout}" - ) - - assert revoke_output[str(netuid)]["success"] is True - assert isinstance(revoke_output[str(netuid)]["extrinsic_identifier"], str) - assert revoke_output[str(netuid)]["completion_block"] is not None - assert revoke_output[str(netuid)]["set_block"] is not None - - print("โœ… Passed revoke_children") From 61d90b95464fd26b9f5019eb86c02891cc0913c2 Mon Sep 17 00:00:00 2001 From: leonace924 Date: Mon, 22 Dec 2025 18:52:17 -0500 Subject: [PATCH 11/11] fix: update the get children function --- bittensor_cli/src/commands/stake/children_hotkeys.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/src/commands/stake/children_hotkeys.py b/bittensor_cli/src/commands/stake/children_hotkeys.py index 9e62bbeb..6db01e87 100644 --- a/bittensor_cli/src/commands/stake/children_hotkeys.py +++ b/bittensor_cli/src/commands/stake/children_hotkeys.py @@ -499,10 +499,14 @@ async def _render_table( ) if not success: print_error(f"Failed to get children from subtensor: {err_mg}") + + # Always render the table, even if there are no children if children: netuid_children_tuples = [(netuid, children)] - await _render_table(get_hotkey_pub_ss58(wallet), netuid_children_tuples) + else: + netuid_children_tuples = [] + await _render_table(get_hotkey_pub_ss58(wallet), netuid_children_tuples) return children