From 0d236db63370425a892db0a5a27c260b2a439105 Mon Sep 17 00:00:00 2001 From: opendansor Date: Thu, 29 Aug 2024 11:24:29 -0700 Subject: [PATCH 1/7] Child Hotkeys netuid Refactor --- bittensor/commands/stake.py | 59 ++++++++++++------- .../subcommands/stake/test_childkeys.py | 8 +-- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/bittensor/commands/stake.py b/bittensor/commands/stake.py index 132529a131..24868d0484 100644 --- a/bittensor/commands/stake.py +++ b/bittensor/commands/stake.py @@ -44,19 +44,25 @@ def get_netuid( - cli: "bittensor.cli", subtensor: "bittensor.subtensor" + cli: "bittensor.cli", subtensor: "bittensor.subtensor", prompt: bool = True ) -> Tuple[bool, int]: """Retrieve and validate the netuid from the user or configuration.""" console = Console() - if not cli.config.is_set("netuid"): - try: - cli.config.netuid = int(Prompt.ask("Enter netuid")) - except ValueError: - console.print( - "[red]Invalid input. Please enter a valid integer for netuid.[/red]" - ) - return False, -1 + if not cli.config.is_set("netuid") and prompt: + cli.config.netuid = Prompt.ask("Enter netuid") + try: + cli.config.netuid = int(cli.config.netuid) + except ValueError: + console.print( + "[red]Invalid input. Please enter a valid integer for netuid.[/red]" + ) + return False, -1 netuid = cli.config.netuid + if netuid < 0 or netuid > 2**32 - 1: + console.print( + "[red]Invalid input. Please enter a valid integer for netuid in subnet range.[/red]" + ) + return False, -1 if not subtensor.subnet_exists(netuid=netuid): console.print( "[red]Network with netuid {} does not exist. Please try again.[/red]".format( @@ -1136,10 +1142,27 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"): wallet = bittensor.wallet(config=cli.config) # check all - if not cli.config.is_set("all"): - exists, netuid = get_netuid(cli, subtensor) - if not exists: - return + if cli.config.is_set("all"): + cli.config.netuid = None + cli.config.all = True + elif cli.config.is_set("netuid"): + if cli.config.netuid == "all": + cli.config.all = True + else: + cli.config.netuid = int(cli.config.netuid) + exists, netuid = get_netuid(cli, subtensor) + if not exists: + return + else: + netuid_input = Prompt.ask("Enter netuid or 'all'", default="all") + if netuid_input == "all": + cli.config.netuid = None + cli.config.all = True + else: + cli.config.netuid = int(netuid_input) + exists, netuid = get_netuid(cli, subtensor, False) + if not exists: + return # get parent hotkey hotkey = get_hotkey(wallet, cli.config) @@ -1148,11 +1171,7 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"): return try: - netuids = ( - subtensor.get_all_subnet_netuids() - if cli.config.is_set("all") - else [netuid] - ) + netuids = subtensor.get_all_subnet_netuids() if cli.config.all else [netuid] hotkey_stake = GetChildrenCommand.get_parent_stake_info( console, subtensor, hotkey ) @@ -1236,7 +1255,7 @@ def add_args(parser: argparse.ArgumentParser): parser = parser.add_parser( "get_children", help="""Get child hotkeys on subnet.""" ) - parser.add_argument("--netuid", dest="netuid", type=int, required=False) + parser.add_argument("--netuid", dest="netuid", type=str, required=False) parser.add_argument("--hotkey", dest="hotkey", type=str, required=False) parser.add_argument( "--all", @@ -1294,7 +1313,7 @@ def render_table( # Add columns to the table with specific styles table.add_column("Index", style="bold yellow", no_wrap=True, justify="center") - table.add_column("ChildHotkey", style="bold green") + table.add_column("Child Hotkey", style="bold green") table.add_column("Proportion", style="bold cyan", no_wrap=True, justify="right") table.add_column( "Childkey Take", style="bold blue", no_wrap=True, justify="right" diff --git a/tests/e2e_tests/subcommands/stake/test_childkeys.py b/tests/e2e_tests/subcommands/stake/test_childkeys.py index 080d01263d..a8f6518fcc 100644 --- a/tests/e2e_tests/subcommands/stake/test_childkeys.py +++ b/tests/e2e_tests/subcommands/stake/test_childkeys.py @@ -54,7 +54,7 @@ async def test_set_revoke_children_multiple(local_chain, capsys): assert local_chain.query("SubtensorModule", "NetworksAdded", [1]).serialize() for exec_command in [alice_exec_command, bob_exec_command, eve_exec_command]: - exec_command(RegisterCommand, ["s", "register", "--netuid", "1"]) + exec_command(RegisterCommand, ["s", "register", "--netuid", "4"]) alice_exec_command(StakeCommand, ["stake", "add", "--amount", "100000"]) @@ -75,8 +75,8 @@ async def wait(): await wait() children_with_proportions = [ - [0.4, bob_keypair.ss58_address], - [0.2, eve_keypair.ss58_address], + [0.2, bob_keypair.ss58_address], + [0.1, eve_keypair.ss58_address], ] # Test 1: Set multiple children @@ -86,7 +86,7 @@ async def wait(): "stake", "set_children", "--netuid", - "1", + "2", "--children", f"{children_with_proportions[0][1]},{children_with_proportions[1][1]}", "--hotkey", From 4379c596fa4a21c27c21cc9a08410be9e4c904d9 Mon Sep 17 00:00:00 2001 From: opendansor Date: Wed, 28 Aug 2024 17:09:10 -0700 Subject: [PATCH 2/7] CHK Test --- tests/e2e_tests/subcommands/stake/test_childkeys.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e_tests/subcommands/stake/test_childkeys.py b/tests/e2e_tests/subcommands/stake/test_childkeys.py index a8f6518fcc..080d01263d 100644 --- a/tests/e2e_tests/subcommands/stake/test_childkeys.py +++ b/tests/e2e_tests/subcommands/stake/test_childkeys.py @@ -54,7 +54,7 @@ async def test_set_revoke_children_multiple(local_chain, capsys): assert local_chain.query("SubtensorModule", "NetworksAdded", [1]).serialize() for exec_command in [alice_exec_command, bob_exec_command, eve_exec_command]: - exec_command(RegisterCommand, ["s", "register", "--netuid", "4"]) + exec_command(RegisterCommand, ["s", "register", "--netuid", "1"]) alice_exec_command(StakeCommand, ["stake", "add", "--amount", "100000"]) @@ -75,8 +75,8 @@ async def wait(): await wait() children_with_proportions = [ - [0.2, bob_keypair.ss58_address], - [0.1, eve_keypair.ss58_address], + [0.4, bob_keypair.ss58_address], + [0.2, eve_keypair.ss58_address], ] # Test 1: Set multiple children @@ -86,7 +86,7 @@ async def wait(): "stake", "set_children", "--netuid", - "2", + "1", "--children", f"{children_with_proportions[0][1]},{children_with_proportions[1][1]}", "--hotkey", From 4b046c3351ce2695445a789bdc7a5d1836ac26a9 Mon Sep 17 00:00:00 2001 From: opendansor Date: Thu, 29 Aug 2024 11:29:07 -0700 Subject: [PATCH 3/7] u16 float limit --- bittensor/commands/stake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/commands/stake.py b/bittensor/commands/stake.py index 24868d0484..eff415d1a1 100644 --- a/bittensor/commands/stake.py +++ b/bittensor/commands/stake.py @@ -58,7 +58,7 @@ def get_netuid( ) return False, -1 netuid = cli.config.netuid - if netuid < 0 or netuid > 2**32 - 1: + if netuid < 0 or netuid > 65535: console.print( "[red]Invalid input. Please enter a valid integer for netuid in subnet range.[/red]" ) From c7c6c5dd82a89b9874cd49b084b2d47f653c3c67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=BC?= Date: Sun, 4 Aug 2024 18:32:39 +0000 Subject: [PATCH 4/7] test_metagraph.py: fix assertions The following does not do what the author probably thought: assert 'a' and 'b' in s as it evaluates as: assert 'a' and ('b' in s) and hence: assert 'b' in s This is fixed by splitting the assertions in two or by changing the condition to ('a' in s and 'b' in s). --- tests/e2e_tests/subcommands/subnet/test_metagraph.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/subcommands/subnet/test_metagraph.py b/tests/e2e_tests/subcommands/subnet/test_metagraph.py index e8e18ef617..9dc0b013ab 100644 --- a/tests/e2e_tests/subcommands/subnet/test_metagraph.py +++ b/tests/e2e_tests/subcommands/subnet/test_metagraph.py @@ -81,7 +81,7 @@ def test_metagraph_command(local_chain, capsys): # Assert the neuron is registered and displayed assert ( - "Metagraph: net: local:1" and "N: 1/1" in captured.out + "Metagraph: net: local:1" in captured.out and "N: 1/1" in captured.out ), "Neuron isn't displayed in metagraph" # Register Dave as neuron to the subnet @@ -117,6 +117,7 @@ def test_metagraph_command(local_chain, capsys): captured = capsys.readouterr() # Assert the neuron is registered and displayed - assert "Metagraph: net: local:1" and "N: 2/2" in captured.out + assert "Metagraph: net: local:1" in captured.out + assert "N: 2/2" in captured.out logging.info("Passed test_metagraph_command") From 60bc47a0dd07d005f0c42f0cc5e3765fc9849ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=BC?= Date: Sun, 4 Aug 2024 18:39:56 +0000 Subject: [PATCH 5/7] test_metagraph.py: replace magic constant with netuid Using variable netuid instead of magic constant 1 (which happens to be the netuid of the added subnet) provides context to the usage of the value 1 and makes the test more future proof. --- .../subcommands/subnet/test_metagraph.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/e2e_tests/subcommands/subnet/test_metagraph.py b/tests/e2e_tests/subcommands/subnet/test_metagraph.py index 9dc0b013ab..10284e99db 100644 --- a/tests/e2e_tests/subcommands/subnet/test_metagraph.py +++ b/tests/e2e_tests/subcommands/subnet/test_metagraph.py @@ -21,32 +21,33 @@ def test_metagraph_command(local_chain, capsys): logging.info("Testing test_metagraph_command") + netuid = 1 # Register root as Alice keypair, exec_command, wallet = setup_wallet("//Alice") exec_command(RegisterSubnetworkCommand, ["s", "create"]) - # Verify subnet 1 created successfully + # Verify subnet created successfully assert local_chain.query( - "SubtensorModule", "NetworksAdded", [1] + "SubtensorModule", "NetworksAdded", [netuid] ).serialize(), "Subnet wasn't created successfully" subtensor = bittensor.subtensor(network="ws://localhost:9945") - metagraph = subtensor.metagraph(netuid=1) + metagraph = subtensor.metagraph(netuid=netuid) # Assert metagraph is empty assert len(metagraph.uids) == 0, "Metagraph is not empty" # Execute btcli metagraph command - exec_command(MetagraphCommand, ["subnet", "metagraph", "--netuid", "1"]) + exec_command(MetagraphCommand, ["subnet", "metagraph", "--netuid", str(netuid)]) captured = capsys.readouterr() - # Assert metagraph is printed for netuid 1 + # Assert metagraph is printed for netuid assert ( - "Metagraph: net: local:1" in captured.out - ), "Netuid 1 was not displayed in metagraph" + f"Metagraph: net: local:{netuid}" in captured.out + ), f"Netuid {netuid} was not displayed in metagraph" # Register Bob as neuron to the subnet bob_keypair, bob_exec_command, bob_wallet = setup_wallet("//Bob") @@ -56,7 +57,7 @@ def test_metagraph_command(local_chain, capsys): "s", "register", "--netuid", - "1", + str(netuid), ], ) @@ -67,7 +68,7 @@ def test_metagraph_command(local_chain, capsys): assert "✅ Registered" in captured.out, "Neuron was not registered" # Refresh the metagraph - metagraph = subtensor.metagraph(netuid=1) + metagraph = subtensor.metagraph(netuid=netuid) # Assert metagraph has registered neuron assert len(metagraph.uids) == 1, "Metagraph doesn't have exactly 1 neuron" @@ -75,13 +76,13 @@ def test_metagraph_command(local_chain, capsys): metagraph.hotkeys[0] == "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" ), "Neuron's hotkey in metagraph doesn't match" # Execute btcli metagraph command - exec_command(MetagraphCommand, ["subnet", "metagraph", "--netuid", "1"]) + exec_command(MetagraphCommand, ["subnet", "metagraph", "--netuid", str(netuid)]) captured = capsys.readouterr() # Assert the neuron is registered and displayed assert ( - "Metagraph: net: local:1" in captured.out and "N: 1/1" in captured.out + f"Metagraph: net: local:{netuid}" in captured.out and "N: 1/1" in captured.out ), "Neuron isn't displayed in metagraph" # Register Dave as neuron to the subnet @@ -92,7 +93,7 @@ def test_metagraph_command(local_chain, capsys): "s", "register", "--netuid", - "1", + str(netuid), ], ) @@ -103,7 +104,7 @@ def test_metagraph_command(local_chain, capsys): assert "✅ Registered" in captured.out, "Neuron was not registered" # Refresh the metagraph - metagraph = subtensor.metagraph(netuid=1) + metagraph = subtensor.metagraph(netuid=netuid) # Assert metagraph has registered neuron assert len(metagraph.uids) == 2 @@ -112,12 +113,12 @@ def test_metagraph_command(local_chain, capsys): ), "Neuron's hotkey in metagraph doesn't match" # Execute btcli metagraph command - exec_command(MetagraphCommand, ["subnet", "metagraph", "--netuid", "1"]) + exec_command(MetagraphCommand, ["subnet", "metagraph", "--netuid", str(netuid)]) captured = capsys.readouterr() # Assert the neuron is registered and displayed - assert "Metagraph: net: local:1" in captured.out + assert f"Metagraph: net: local:{netuid}" in captured.out assert "N: 2/2" in captured.out logging.info("Passed test_metagraph_command") From c560a464940988a14f63aabd1dece8997133e264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=BC?= Date: Sun, 4 Aug 2024 18:41:58 +0000 Subject: [PATCH 6/7] test_metagraph.py: test .save() and .load() on metagraph Saving and loading should transfer the complete state of the metagraph. --- .../subcommands/subnet/test_metagraph.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/e2e_tests/subcommands/subnet/test_metagraph.py b/tests/e2e_tests/subcommands/subnet/test_metagraph.py index 10284e99db..8fc5936180 100644 --- a/tests/e2e_tests/subcommands/subnet/test_metagraph.py +++ b/tests/e2e_tests/subcommands/subnet/test_metagraph.py @@ -85,6 +85,9 @@ def test_metagraph_command(local_chain, capsys): f"Metagraph: net: local:{netuid}" in captured.out and "N: 1/1" in captured.out ), "Neuron isn't displayed in metagraph" + # Create a secondary metagraph to test .load() later on + metagraph_pre_dave = subtensor.metagraph(netuid=netuid) + # Register Dave as neuron to the subnet dave_keypair, dave_exec_command, dave_wallet = setup_wallet("//Dave") dave_exec_command( @@ -121,4 +124,19 @@ def test_metagraph_command(local_chain, capsys): assert f"Metagraph: net: local:{netuid}" in captured.out assert "N: 2/2" in captured.out + # Check save/load cycle + metagraph.save() + metagraph_pre_dave.load() + + # Assert in progressive detail + assert len(metagraph.uids) == len(metagraph_pre_dave.uids) + assert (metagraph.uids == metagraph_pre_dave.uids).all() + + assert len(metagraph.axons) == len(metagraph_pre_dave.axons) + assert metagraph.axons[1].hotkey == metagraph_pre_dave.axons[1].hotkey + assert metagraph.axons == metagraph_pre_dave.axons + + assert len(metagraph.neurons) == len(metagraph_pre_dave.neurons) + assert metagraph.neurons == metagraph_pre_dave.neurons + logging.info("Passed test_metagraph_command") From b63fae30a39925a2f765937b628c061c6652c8b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=BC?= Date: Sun, 4 Aug 2024 18:44:08 +0000 Subject: [PATCH 7/7] test_metagraph.py: test adding multiple subnets and using netuid 2 A minor tweak to the test that increases coverage of the test. --- .../subcommands/subnet/test_metagraph.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/e2e_tests/subcommands/subnet/test_metagraph.py b/tests/e2e_tests/subcommands/subnet/test_metagraph.py index 8fc5936180..a57bf67a82 100644 --- a/tests/e2e_tests/subcommands/subnet/test_metagraph.py +++ b/tests/e2e_tests/subcommands/subnet/test_metagraph.py @@ -21,15 +21,19 @@ def test_metagraph_command(local_chain, capsys): logging.info("Testing test_metagraph_command") - netuid = 1 # Register root as Alice keypair, exec_command, wallet = setup_wallet("//Alice") - exec_command(RegisterSubnetworkCommand, ["s", "create"]) - # Verify subnet created successfully - assert local_chain.query( - "SubtensorModule", "NetworksAdded", [netuid] - ).serialize(), "Subnet wasn't created successfully" + # The rest of the test will use subnet 2 + netuid = 2 + + for i in range(netuid): + # Register subnet + exec_command(RegisterSubnetworkCommand, ["s", "create"]) + # Verify subnet created successfully + assert local_chain.query( + "SubtensorModule", "NetworksAdded", [i + 1] + ).serialize(), f"Subnet {netuid+1} wasn't created successfully" subtensor = bittensor.subtensor(network="ws://localhost:9945")