diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 70ff7bd5..32d76d73 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -26,6 +26,7 @@ WalletValidationTypes as WV, Constants, COLOR_PALETTE, + HYPERPARAMS, ) from bittensor_cli.src.bittensor import utils from bittensor_cli.src.bittensor.balances import Balance @@ -3229,10 +3230,11 @@ def stake_remove( ask_for=[WO.NAME, WO.PATH], ) else: - if not hotkey_ss58_address and not wallet_hotkey: + if not hotkey_ss58_address and not wallet_hotkey: hotkey_or_ss58 = Prompt.ask( "Enter the [blue]hotkey[/blue] name or [blue]ss58 address[/blue] to unstake all from [dim](or enter 'all' to unstake from all hotkeys)[/dim]", - default=self.config.get("wallet_hotkey") or defaults.wallet.hotkey, + default=self.config.get("wallet_hotkey") + or defaults.wallet.hotkey, ) else: hotkey_or_ss58 = hotkey_ss58_address or wallet_hotkey @@ -3983,7 +3985,7 @@ def sudo_set( param_name: str = typer.Option( "", "--param", "--parameter", help="The subnet hyperparameter to set" ), - param_value: str = typer.Option( + param_value: Optional[str] = typer.Option( "", "--value", help="Value to set the hyperparameter to." ), quiet: bool = Options.quiet, @@ -4032,9 +4034,12 @@ def sudo_set( param_value = f"{low_val},{high_val}" if not param_value: - param_value = Prompt.ask( - f"Enter the new value for [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{param_name}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] in the VALUE column format" - ) + if HYPERPARAMS.get(param_name): + param_value = Prompt.ask( + f"Enter the new value for [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{param_name}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] in the VALUE column format" + ) + else: + param_value = None wallet = self.wallet_ask( wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] diff --git a/bittensor_cli/src/__init__.py b/bittensor_cli/src/__init__.py index 0a6d09a3..e0156309 100644 --- a/bittensor_cli/src/__init__.py +++ b/bittensor_cli/src/__init__.py @@ -672,7 +672,7 @@ class WalletValidationTypes(Enum): "commit_reveal_weights_enabled": ("sudo_set_commit_reveal_weights_enabled", False), "alpha_values": ("sudo_set_alpha_values", False), "liquid_alpha_enabled": ("sudo_set_liquid_alpha_enabled", False), - "network_registration_allowed": ("sudo_set_network_registration_allowed", False), + "registration_allowed": ("sudo_set_network_registration_allowed", False), "network_pow_registration_allowed": ( "sudo_set_network_pow_registration_allowed", False, diff --git a/bittensor_cli/src/commands/stake/add.py b/bittensor_cli/src/commands/stake/add.py index cbc1ae24..4aba39e6 100644 --- a/bittensor_cli/src/commands/stake/add.py +++ b/bittensor_cli/src/commands/stake/add.py @@ -107,9 +107,7 @@ async def safe_stake_extrinsic( ) return else: - err_out( - f"\n{failure_prelude} with error: {format_error_message(e)}" - ) + err_out(f"\n{failure_prelude} with error: {format_error_message(e)}") return else: await response.process_events() @@ -180,9 +178,7 @@ async def stake_extrinsic( extrinsic, wait_for_inclusion=True, wait_for_finalization=False ) except SubstrateRequestException as e: - err_out( - f"\n{failure_prelude} with error: {format_error_message(e)}" - ) + err_out(f"\n{failure_prelude} with error: {format_error_message(e)}") return else: await response.process_events() diff --git a/bittensor_cli/src/commands/subnets/subnets.py b/bittensor_cli/src/commands/subnets/subnets.py index d71d726f..f4e01486 100644 --- a/bittensor_cli/src/commands/subnets/subnets.py +++ b/bittensor_cli/src/commands/subnets/subnets.py @@ -889,8 +889,8 @@ async def show_root(): total_emission_per_block = 0 for netuid_ in range(len(all_subnets)): subnet = all_subnets[netuid_] - emission_on_subnet = ( - root_state.emission_history[netuid_][idx] / (subnet.tempo or 1) + emission_on_subnet = root_state.emission_history[netuid_][idx] / ( + subnet.tempo or 1 ) total_emission_per_block += subnet.alpha_to_tao( Balance.from_rao(emission_on_subnet) @@ -2135,9 +2135,7 @@ async def get_identity(subtensor: "SubtensorInterface", netuid: int, title: str title = "Subnet Identity" if not await subtensor.subnet_exists(netuid): - print_error( - f"Subnet {netuid} does not exist." - ) + print_error(f"Subnet {netuid} does not exist.") raise typer.Exit() with console.status( diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 8530f3f9..78106ae3 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -18,6 +18,8 @@ normalize_hyperparameters, unlock_key, blocks_to_duration, + float_to_u64, + float_to_u16, ) if TYPE_CHECKING: @@ -70,12 +72,76 @@ def allowed_value( return True, value +def search_metadata( + param_name: str, value: Union[str, bool, float, list[float]], netuid: int, metadata +) -> tuple[bool, Optional[dict]]: + """ + Searches the substrate metadata AdminUtils pallet for a given parameter name. Crafts a response dict to be used + as call parameters for setting this hyperparameter. + + Args: + param_name: the name of the hyperparameter + value: the value to set the hyperparameter + netuid: the specified netuid + metadata: the subtensor.substrate.metadata + + Returns: + (success, dict of call params) + + """ + + 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: + val = input( + f"Enter a value for field '{arg_name}' with type '{arg_type_output[type_]}': " + ) + return arg_types[type_](val) + except ValueError: + return type_converter_with_retry(type_, None, arg_name) + + arg_types = {"bool": string_to_bool, "u16": float_to_u16, "u64": float_to_u64} + arg_type_output = {"bool": "bool", "u16": "float", "u64": "float"} + + 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 + else: + return False, None + + async def set_hyperparameter_extrinsic( subtensor: "SubtensorInterface", wallet: "Wallet", netuid: int, parameter: str, - value: Union[str, bool, float, list[float]], + value: Optional[Union[str, bool, float, list[float]]], wait_for_inclusion: bool = False, wait_for_finalization: bool = True, ) -> bool: @@ -110,44 +176,58 @@ async def set_hyperparameter_extrinsic( if not unlock_key(wallet).success: return False + arbitrary_extrinsic = False + extrinsic, sudo_ = HYPERPARAMS.get(parameter, ("", False)) - if extrinsic is None: - err_console.print(":cross_mark: [red]Invalid hyperparameter specified.[/red]") - return False + if not extrinsic: + arbitrary_extrinsic, call_params = search_metadata( + parameter, value, netuid, subtensor.substrate.metadata + ) + extrinsic = parameter + if not arbitrary_extrinsic: + err_console.print( + ":cross_mark: [red]Invalid hyperparameter specified.[/red]" + ) + return False + substrate = subtensor.substrate + msg_value = value if not arbitrary_extrinsic else call_params with console.status( - f":satellite: Setting hyperparameter [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{parameter}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] to [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{value}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] on subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] ...", + f":satellite: Setting hyperparameter [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{parameter}" + f"[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] to [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{msg_value}" + f"[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] on subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]" + f"{netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] ...", spinner="earth", ): - substrate = subtensor.substrate - extrinsic_params = await substrate.get_metadata_call_function( - "AdminUtils", extrinsic - ) - call_params: dict[str, Union[str, bool, float]] = {"netuid": netuid} - - # if input value is a list, iterate through the list and assign values - if isinstance(value, list): - # Ensure that there are enough values for all non-netuid parameters - non_netuid_fields = [ - param["name"] - for param in extrinsic_params["fields"] - if "netuid" not in param["name"] - ] - - if len(value) < len(non_netuid_fields): - raise ValueError( - "Not enough values provided in the list for all parameters" - ) - - call_params.update( - {str(name): val for name, val in zip(non_netuid_fields, value)} + if not arbitrary_extrinsic: + extrinsic_params = await substrate.get_metadata_call_function( + "AdminUtils", extrinsic ) + call_params = {"netuid": netuid} + + # if input value is a list, iterate through the list and assign values + if isinstance(value, list): + # Ensure that there are enough values for all non-netuid parameters + non_netuid_fields = [ + param["name"] + for param in extrinsic_params["fields"] + if "netuid" not in param["name"] + ] + + if len(value) < len(non_netuid_fields): + raise ValueError( + "Not enough values provided in the list for all parameters" + ) - else: - value_argument = extrinsic_params["fields"][ - len(extrinsic_params["fields"]) - 1 - ] - call_params[str(value_argument["name"])] = value + call_params.update( + {str(name): val for name, val in zip(non_netuid_fields, value)} + ) + + else: + value_argument = extrinsic_params["fields"][ + len(extrinsic_params["fields"]) - 1 + ] + call_params[str(value_argument["name"])] = value # create extrinsic call call_ = await substrate.compose_call( @@ -167,11 +247,16 @@ async def set_hyperparameter_extrinsic( if not success: err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}") await asyncio.sleep(0.5) - + elif arbitrary_extrinsic: + console.print( + f":white_heavy_check_mark: " + f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]" + ) # Successful registration, final check for membership else: console.print( - f":white_heavy_check_mark: [dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]" + f":white_heavy_check_mark: " + f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]" ) return True @@ -470,7 +555,7 @@ async def sudo_set_hyperparameter( subtensor: "SubtensorInterface", netuid: int, param_name: str, - param_value: str, + param_value: Optional[str], ): """Set subnet hyperparameters.""" @@ -481,7 +566,12 @@ async def sudo_set_hyperparameter( "commit_reveal_weights_enabled", "liquid_alpha_enabled", ]: - normalized_value = param_value.lower() in ["true", "True", "1"] + 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 @@ -492,7 +582,6 @@ async def sudo_set_hyperparameter( f"Value is {normalized_value} but must be {value}" ) return - success = await set_hyperparameter_extrinsic( subtensor, wallet, netuid, param_name, value )