Skip to content

Commit

Permalink
subnets list command
Browse files Browse the repository at this point in the history
  • Loading branch information
thewhaleking committed Aug 14, 2024
1 parent 46215ee commit 1eca517
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 22 deletions.
63 changes: 59 additions & 4 deletions cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from yaml import safe_load, safe_dump

from src import defaults, utils, HYPERPARAMS
from src.commands import wallets, root, stake, sudo
from src.commands import wallets, root, stake, sudo, subnets
from src.subtensor_interface import SubtensorInterface
from src.bittensor.async_substrate_interface import SubstrateRequestException
from src.utils import console, err_console
Expand Down Expand Up @@ -234,7 +234,9 @@ def __init__(self):
self.app.add_typer(self.sudo_app, name="su", hidden=True)

# subnets aliases
self.app.add_typer(self.subnets_app, name="subnets", short_help="Subnets commands")
self.app.add_typer(
self.subnets_app, name="subnets", short_help="Subnets commands"
)

# config commands
self.config_app.command("set")(self.set_config)
Expand Down Expand Up @@ -288,11 +290,12 @@ def __init__(self):

# subnets commands
self.subnets_app.command("hyperparameters")(self.sudo_get)
self.subnets_app.command("list")(self.subnets_list)

def initialize_chain(
self,
network: Optional[str] = typer.Option("default_network", help="Network name"),
chain: Optional[str] = typer.Option("default_chain", help="Chain name"),
network: Optional[str] = None,
chain: Optional[str] = None,
) -> SubtensorInterface:
"""
Intelligently initializes a connection to the chain, depending on the supplied (or in config) values. Set's the
Expand Down Expand Up @@ -2760,6 +2763,58 @@ def sudo_get(
sudo.get_hyperparameters(self.initialize_chain(network, chain), netuid)
)

def subnets_list(self, network: str = Options.network, chain: str = Options.chain):
"""
# subnets list
Executes the `list` command to list all subnets and their detailed information on the Bittensor network.
This command is designed to provide users with comprehensive information about each subnet within the
network, including its unique identifier (netuid), the number of neurons, maximum neuron capacity,
emission rate, tempo, recycle register cost (burn), proof of work (PoW) difficulty, and the name or
SS58 address of the subnet owner.
## Usage:
Upon invocation, the command performs the following actions:
1. It initializes the Bittensor subtensor object with the user's configuration.
2. It retrieves a list of all subnets in the network along with their detailed information.
3. The command compiles this data into a table format, displaying key information about each subnet.
In addition to the basic subnet details, the command also fetches delegate information to provide the
name of the subnet owner where available. If the owner's name is not available, the owner's ``SS58``
address is displayed.
The command structure includes:
- Initializing the Bittensor subtensor and retrieving subnet information.
- Calculating the total number of neurons across all subnets.
- Constructing a table that includes columns for `NETUID`, `N` (current neurons), `MAX_N`
(maximum neurons), `EMISSION`, `TEMPO`, `BURN`, `POW` (proof of work difficulty), and
`SUDO` (owner's name or `SS58` address).
- Displaying the table with a footer that summarizes the total number of subnets and neurons.
### Example usage:
```
btcli subnets list
```
#### Note:
This command is particularly useful for users seeking an overview of the Bittensor network's structure and the
distribution of its resources and ownership information for each subnet.
"""
return self._run_command(
subnets.subnets_list(self.initialize_chain(network, chain))
)

def run(self):
self.app()

Expand Down
1 change: 1 addition & 0 deletions src/bittensor/chain_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ def from_neuron_info(cls, neuron_info: dict) -> "AxonInfo":
coldkey=neuron_info["coldkey"],
)


@dataclass
class SubnetHyperparameters:
"""Dataclass for subnet hyperparameters."""
Expand Down
4 changes: 1 addition & 3 deletions src/commands/stake.py
Original file line number Diff line number Diff line change
Expand Up @@ -1523,9 +1523,7 @@ async def render_table(

success, children, err_mg = await subtensor.get_children(wallet.hotkey, netuid)
if not success:
err_console.print(
f"Failed to get children from subtensor: {err_mg}"
)
err_console.print(f"Failed to get children from subtensor: {err_mg}")
await render_table(wallet.hotkey, children, netuid)

return children
Expand Down
90 changes: 75 additions & 15 deletions src/commands/subnets.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,94 @@
import asyncio
from typing import TYPE_CHECKING

from rich.table import Table, Column

from src.utils import console, normalize_hyperparameters
from src import Constants, DelegatesDetails
from src.bittensor.chain_data import SubnetInfo
from src.utils import (
console,
err_console,
get_delegates_details_from_github,
millify,
RAO_PER_TAO,
)

if TYPE_CHECKING:
from src.subtensor_interface import SubtensorInterface


async def hyperparameters(subtensor: "SubtensorInterface", netuid: int):
"""View hyperparameters of a subnetwork."""
subnet = await subtensor.get_subnet_hyperparameters(
netuid
async def subnets_list(subtensor: "SubtensorInterface"):
"""List all subnet netuids in the network."""

async def _get_all_subnets_info():
json_body = await subtensor.substrate.rpc_request(
method="subnetInfo_getSubnetsInfo", # custom rpc method
params=[],
)

return (
SubnetInfo.list_from_vec_u8(result)
if (result := json_body.get("result"))
else []
)

subnets: list[SubnetInfo]
delegate_info: dict[str, DelegatesDetails]

subnets, delegate_info = await asyncio.gather(
_get_all_subnets_info(),
get_delegates_details_from_github(url=Constants.delegates_detail_url),
)

if not subnets:
err_console.print("[red]No subnets found[/red]")
return

rows = []
total_neurons = 0

for subnet in subnets:
total_neurons += subnet.max_n
rows.append(
(
str(subnet.netuid),
str(subnet.subnetwork_n),
str(millify(subnet.max_n)),
f"{subnet.emission_value / RAO_PER_TAO * 100:0.2f}%",
str(subnet.tempo),
f"{subnet.burn!s:8.8}",
str(millify(subnet.difficulty)),
f"{delegate_info[subnet.owner_ss58].name if subnet.owner_ss58 in delegate_info else subnet.owner_ss58}",
)
)
table = Table(
Column("[overline white]HYPERPARAMETER", style="white"),
Column("[overline white]VALUE", style="green"),
Column("[overline white]NORMALIZED", style="cyan"),
title=f"[white]Subnet Hyperparameters - NETUID: {netuid} - {subtensor}",
Column(
"[overline white]NETUID",
str(len(subnets)),
footer_style="overline white",
style="bold green",
justify="center",
),
Column(
"[overline white]N",
str(total_neurons),
footer_style="overline white",
style="green",
justify="center",
),
Column("[overline white]MAX_N", style="white", justify="center"),
Column("[overline white]EMISSION", style="white", justify="center"),
Column("[overline white]TEMPO", style="white", justify="center"),
Column("[overline white]RECYCLE", style="white", justify="center"),
Column("[overline white]POW", style="white", justify="center"),
Column("[overline white]SUDO", style="white"),
title=f"[white]Subnets - {subtensor.network}",
show_footer=True,
width=None,
pad_edge=True,
box=None,
show_edge=True,
)

normalized_values = normalize_hyperparameters(subnet)

for param, value, norm_value in normalized_values:
table.add_row(" " + param, value, norm_value)

for row in rows:
table.add_row(*row)
console.print(table)

0 comments on commit 1eca517

Please sign in to comment.