Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bittensor_cli/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,7 @@ class WalletValidationTypes(Enum):
"sudo_set_network_pow_registration_allowed",
False,
),
"yuma3_enabled": ("sudo_set_yuma3_enabled", False),
}

# Help Panels for cli help
Expand Down
2 changes: 2 additions & 0 deletions bittensor_cli/src/bittensor/chain_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class SubnetHyperparameters(InfoBase):
alpha_high: int
alpha_low: int
liquid_alpha_enabled: bool
yuma3_enabled: bool

@classmethod
def _fix_decoded(
Expand Down Expand Up @@ -210,6 +211,7 @@ def _fix_decoded(
alpha_high=decoded.get("alpha_high"),
alpha_low=decoded.get("alpha_low"),
liquid_alpha_enabled=decoded.get("liquid_alpha_enabled"),
yuma3_enabled=decoded.get("yuma3_enabled"),
)


Expand Down
16 changes: 10 additions & 6 deletions bittensor_cli/src/bittensor/subtensor_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1128,14 +1128,18 @@ async def get_subnet_hyperparameters(
Understanding the hyperparameters is crucial for comprehending how subnets are configured and
managed, and how they interact with the network's consensus and incentive mechanisms.
"""
result = await self.query_runtime_api(
runtime_api="SubnetInfoRuntimeApi",
method="get_subnet_hyperparams",
params=[netuid],
block_hash=block_hash,
main_result, yuma3_result = await asyncio.gather(
self.query_runtime_api(
runtime_api="SubnetInfoRuntimeApi",
method="get_subnet_hyperparams",
params=[netuid],
block_hash=block_hash,
),
self.query("SubtensorModule", "Yuma3On", [netuid]),
)
result = {**main_result, **{"yuma3_enabled": yuma3_result}}

if not result:
if not main_result:
return []

return SubnetHyperparameters.from_any(result)
Expand Down
12 changes: 9 additions & 3 deletions bittensor_cli/src/bittensor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import math
import os
import sqlite3
import platform
import webbrowser
from pathlib import Path
from typing import TYPE_CHECKING, Any, Collection, Optional, Union, Callable
Expand Down Expand Up @@ -719,11 +718,14 @@ def millify_tao(n: float, start_at: str = "K") -> str:

def normalize_hyperparameters(
subnet: "SubnetHyperparameters",
json_output: bool = False,
) -> list[tuple[str, str, str]]:
"""
Normalizes the hyperparameters of a subnet.

:param subnet: The subnet hyperparameters object.
:param json_output: Whether this normalisation will be for a JSON output or console string (determines whether
items get stringified or safe for JSON encoding)

:return: A list of tuples containing the parameter name, value, and normalized value.
"""
Expand All @@ -750,13 +752,17 @@ def normalize_hyperparameters(
norm_value = param_mappings[param](value)
if isinstance(norm_value, float):
norm_value = f"{norm_value:.{10}g}"
if isinstance(norm_value, Balance) and json_output:
norm_value = norm_value.to_dict()
else:
norm_value = value
except Exception:
# bittensor.logging.warning(f"Error normalizing parameter '{param}': {e}")
norm_value = "-"

normalized_values.append((param, str(value), str(norm_value)))
if not json_output:
normalized_values.append((param, str(value), str(norm_value)))
else:
normalized_values.append((param, value, norm_value))

return normalized_values

Expand Down
132 changes: 71 additions & 61 deletions bittensor_cli/src/commands/sudo.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ def allowed_value(
return True, value


def string_to_bool(val) -> bool:
try:
return {"true": True, "1": True, "0": False, "false": False}[val.lower()]
except KeyError:
return ValueError


def search_metadata(
param_name: str, value: Union[str, bool, float, list[float]], netuid: int, metadata
) -> tuple[bool, Optional[dict]]:
Expand All @@ -91,12 +98,6 @@ def search_metadata(

"""

def string_to_bool(val) -> bool:
try:
return {"true": True, "1": True, "0": False, "false": False}[val.lower()]
except KeyError:
return ValueError

def type_converter_with_retry(type_, val, arg_name):
try:
if val is None:
Expand All @@ -112,37 +113,55 @@ def type_converter_with_retry(type_, val, arg_name):

call_crafter = {"netuid": netuid}

for pallet in metadata.pallets:
if pallet.name == "AdminUtils":
for call in pallet.calls:
if call.name == param_name:
if "netuid" not in [x.name for x in call.args]:
return False, None
call_args = [
arg for arg in call.args if arg.value["name"] != "netuid"
]
if len(call_args) == 1:
arg = call_args[0].value
call_crafter[arg["name"]] = type_converter_with_retry(
arg["typeName"], value, arg["name"]
)
else:
for arg_ in call_args:
arg = arg_.value
call_crafter[arg["name"]] = type_converter_with_retry(
arg["typeName"], None, arg["name"]
)
return True, call_crafter
pallet = metadata.get_metadata_pallet("AdminUtils")
for call in pallet.calls:
if call.name == param_name:
if "netuid" not in [x.name for x in call.args]:
return False, None
call_args = [arg for arg in call.args if arg.value["name"] != "netuid"]
if len(call_args) == 1:
arg = call_args[0].value
call_crafter[arg["name"]] = type_converter_with_retry(
arg["typeName"], value, arg["name"]
)
else:
for arg_ in call_args:
arg = arg_.value
call_crafter[arg["name"]] = type_converter_with_retry(
arg["typeName"], None, arg["name"]
)
return True, call_crafter
else:
return False, None


def requires_bool(metadata, param_name) -> bool:
"""
Determines whether a given hyperparam takes a single arg (besides netuid) that is of bool type.
"""
pallet = metadata.get_metadata_pallet("AdminUtils")
for call in pallet.calls:
if call.name == param_name:
if "netuid" not in [x.name for x in call.args]:
return False, None
call_args = [arg for arg in call.args if arg.value["name"] != "netuid"]
if len(call_args) != 1:
return False
else:
arg = call_args[0].value
if arg["typeName"] == "bool":
return True
else:
return False
raise ValueError(f"{param_name} not found in pallet.")


async def set_hyperparameter_extrinsic(
subtensor: "SubtensorInterface",
wallet: "Wallet",
netuid: int,
parameter: str,
value: Optional[Union[str, bool, float, list[float]]],
value: Optional[Union[str, float, list[float]]],
wait_for_inclusion: bool = False,
wait_for_finalization: bool = True,
prompt: bool = True,
Expand Down Expand Up @@ -221,15 +240,20 @@ async def set_hyperparameter_extrinsic(
]

if len(value) < len(non_netuid_fields):
raise ValueError(
err_console.print(
"Not enough values provided in the list for all parameters"
)
return False

call_params.update(
{str(name): val for name, val in zip(non_netuid_fields, value)}
)

else:
if requires_bool(
substrate.metadata, param_name=extrinsic
) and isinstance(value, str):
value = string_to_bool(value)
value_argument = extrinsic_params["fields"][
len(extrinsic_params["fields"]) - 1
]
Expand All @@ -252,12 +276,13 @@ async def set_hyperparameter_extrinsic(
)
if not success:
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
await asyncio.sleep(0.5)
return False
elif arbitrary_extrinsic:
console.print(
f":white_heavy_check_mark: "
f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
)
return True
# Successful registration, final check for membership
else:
console.print(
Expand Down Expand Up @@ -581,28 +606,11 @@ async def sudo_set_hyperparameter(
json_output: bool,
):
"""Set subnet hyperparameters."""

normalized_value: Union[str, bool]
if param_name in [
"registration_allowed",
"network_pow_registration_allowed",
"commit_reveal_weights_enabled",
"liquid_alpha_enabled",
]:
normalized_value = param_value.lower() in ["true", "1"]
elif param_value in ("True", "False"):
normalized_value = {
"True": True,
"False": False,
}[param_value]
else:
normalized_value = param_value

is_allowed_value, value = allowed_value(param_name, normalized_value)
is_allowed_value, value = allowed_value(param_name, param_value)
if not is_allowed_value:
err_console.print(
f"Hyperparameter [dark_orange]{param_name}[/dark_orange] value is not within bounds. "
f"Value is {normalized_value} but must be {value}"
f"Value is {param_value} but must be {value}"
)
return False
success = await set_hyperparameter_extrinsic(
Expand All @@ -625,8 +633,9 @@ async def get_hyperparameters(
if not await subtensor.subnet_exists(netuid):
print_error(f"Subnet with netuid {netuid} does not exist.")
return False
subnet = await subtensor.get_subnet_hyperparameters(netuid)
subnet_info = await subtensor.subnet(netuid)
subnet, subnet_info = await asyncio.gather(
subtensor.get_subnet_hyperparameters(netuid), subtensor.subnet(netuid)
)
if subnet_info is None:
print_error(f"Subnet with netuid {netuid} does not exist.")
return False
Expand All @@ -648,17 +657,18 @@ async def get_hyperparameters(
)
dict_out = []

normalized_values = normalize_hyperparameters(subnet)

normalized_values = normalize_hyperparameters(subnet, json_output=json_output)
for param, value, norm_value in normalized_values:
table.add_row(" " + param, value, norm_value)
dict_out.append(
{
"hyperparameter": param,
"value": value,
"normalized_value": norm_value,
}
)
if not json_output:
table.add_row(" " + param, value, norm_value)
else:
dict_out.append(
{
"hyperparameter": param,
"value": value,
"normalized_value": norm_value,
}
)
if json_output:
json_console.print(json.dumps(dict_out))
else:
Expand Down
47 changes: 47 additions & 0 deletions tests/e2e_tests/test_staking_sudo.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,4 +461,51 @@ def test_staking(local_chain, wallet_setup):
)["value"]
assert Balance.from_rao(max_burn_tao_from_json) == Balance.from_tao(10.0)

change_yuma3_hyperparam = exec_command_alice(
command="sudo",
sub_command="set",
extra_args=[
"--wallet-path",
wallet_path_alice,
"--wallet-name",
wallet_alice.name,
"--hotkey",
wallet_alice.hotkey_str,
"--chain",
"ws://127.0.0.1:9945",
"--netuid",
netuid,
"--param",
"yuma3_enabled",
"--value",
"true",
"--no-prompt",
"--json-output",
],
)
change_yuma3_hyperparam_json = json.loads(change_yuma3_hyperparam.stdout)
assert change_yuma3_hyperparam_json["success"] is True, (
change_yuma3_hyperparam.stdout
)

changed_yuma3_hyperparam = exec_command_alice(
command="sudo",
sub_command="get",
extra_args=[
"--chain",
"ws://127.0.0.1:9945",
"--netuid",
netuid,
"--json-output",
],
)

yuma3_val = next(
filter(
lambda x: x["hyperparameter"] == "yuma3_enabled",
json.loads(changed_yuma3_hyperparam.stdout),
)
)
assert yuma3_val["value"] is True
assert yuma3_val["normalized_value"] is True
print("✅ Passed staking and sudo commands")
Loading