From 0227cce2f56f7c65a7057fd016904bb3a5d2b9a0 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 9 Oct 2025 15:57:28 +0200 Subject: [PATCH 1/7] Changed bool to enum in hyperparams which allows us to use a third type for complicated extrinsics --- bittensor_cli/src/__init__.py | 83 ++++++++++++--------- bittensor_cli/src/commands/sudo.py | 111 +++++++++++++++++------------ 2 files changed, 113 insertions(+), 81 deletions(-) diff --git a/bittensor_cli/src/__init__.py b/bittensor_cli/src/__init__.py index bc46bd485..1151a2c45 100644 --- a/bittensor_cli/src/__init__.py +++ b/bittensor_cli/src/__init__.py @@ -626,47 +626,62 @@ class WalletValidationTypes(Enum): } +class RootSudoOnly(Enum): + FALSE = 0 + TRUE = 1 + COMPLICATED = 2 + + HYPERPARAMS = { - # btcli name: (subtensor method, root-only bool) - "rho": ("sudo_set_rho", False), - "kappa": ("sudo_set_kappa", False), - "immunity_period": ("sudo_set_immunity_period", False), - "min_allowed_weights": ("sudo_set_min_allowed_weights", False), - "max_weights_limit": ("sudo_set_max_weight_limit", False), - "tempo": ("sudo_set_tempo", True), - "min_difficulty": ("sudo_set_min_difficulty", True), - "max_difficulty": ("sudo_set_max_difficulty", False), - "weights_version": ("sudo_set_weights_version_key", False), - "weights_rate_limit": ("sudo_set_weights_set_rate_limit", True), - "adjustment_interval": ("sudo_set_adjustment_interval", True), - "activity_cutoff": ("sudo_set_activity_cutoff", False), - "target_regs_per_interval": ("sudo_set_target_registrations_per_interval", True), - "min_burn": ("sudo_set_min_burn", False), - "max_burn": ("sudo_set_max_burn", True), - "bonds_moving_avg": ("sudo_set_bonds_moving_average", False), - "max_regs_per_block": ("sudo_set_max_registrations_per_block", True), - "serving_rate_limit": ("sudo_set_serving_rate_limit", False), - "max_validators": ("sudo_set_max_allowed_validators", True), - "adjustment_alpha": ("sudo_set_adjustment_alpha", False), - "difficulty": ("sudo_set_difficulty", True), + # btcli name: (subtensor method, root-only enum) + "rho": ("sudo_set_rho", RootSudoOnly.FALSE), + "kappa": ("sudo_set_kappa", RootSudoOnly.FALSE), + "immunity_period": ("sudo_set_immunity_period", RootSudoOnly.FALSE), + "min_allowed_weights": ("sudo_set_min_allowed_weights", RootSudoOnly.FALSE), + "max_weights_limit": ("sudo_set_max_weight_limit", RootSudoOnly.FALSE), + "tempo": ("sudo_set_tempo", RootSudoOnly.TRUE), + "min_difficulty": ("sudo_set_min_difficulty", RootSudoOnly.TRUE), + "max_difficulty": ("sudo_set_max_difficulty", RootSudoOnly.FALSE), + "weights_version": ("sudo_set_weights_version_key", RootSudoOnly.FALSE), + "weights_rate_limit": ("sudo_set_weights_set_rate_limit", RootSudoOnly.TRUE), + "adjustment_interval": ("sudo_set_adjustment_interval", RootSudoOnly.TRUE), + "activity_cutoff": ("sudo_set_activity_cutoff", RootSudoOnly.FALSE), + "target_regs_per_interval": ( + "sudo_set_target_registrations_per_interval", + RootSudoOnly.TRUE, + ), + "min_burn": ("sudo_set_min_burn", RootSudoOnly.FALSE), + "max_burn": ("sudo_set_max_burn", RootSudoOnly.TRUE), + "bonds_moving_avg": ("sudo_set_bonds_moving_average", RootSudoOnly.FALSE), + "max_regs_per_block": ("sudo_set_max_registrations_per_block", RootSudoOnly.TRUE), + "serving_rate_limit": ("sudo_set_serving_rate_limit", RootSudoOnly.FALSE), + "max_validators": ("sudo_set_max_allowed_validators", RootSudoOnly.TRUE), + "adjustment_alpha": ("sudo_set_adjustment_alpha", RootSudoOnly.FALSE), + "difficulty": ("sudo_set_difficulty", RootSudoOnly.TRUE), "commit_reveal_period": ( "sudo_set_commit_reveal_weights_interval", - False, + RootSudoOnly.FALSE, + ), + "commit_reveal_weights_enabled": ( + "sudo_set_commit_reveal_weights_enabled", + RootSudoOnly.FALSE, + ), + "alpha_values": ("sudo_set_alpha_values", RootSudoOnly.FALSE), + "liquid_alpha_enabled": ("sudo_set_liquid_alpha_enabled", RootSudoOnly.FALSE), + "registration_allowed": ( + "sudo_set_network_registration_allowed", + RootSudoOnly.TRUE, ), - "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), - "registration_allowed": ("sudo_set_network_registration_allowed", True), "network_pow_registration_allowed": ( "sudo_set_network_pow_registration_allowed", - False, + RootSudoOnly.FALSE, ), - "yuma3_enabled": ("sudo_set_yuma3_enabled", False), - "alpha_sigmoid_steepness": ("sudo_set_alpha_sigmoid_steepness", True), - "user_liquidity_enabled": ("toggle_user_liquidity", True), - "bonds_reset_enabled": ("sudo_set_bonds_reset_enabled", False), - "transfers_enabled": ("sudo_set_toggle_transfer", False), - "min_allowed_uids": ("sudo_set_min_allowed_uids", True), + "yuma3_enabled": ("sudo_set_yuma3_enabled", RootSudoOnly.FALSE), + "alpha_sigmoid_steepness": ("sudo_set_alpha_sigmoid_steepness", RootSudoOnly.TRUE), + "user_liquidity_enabled": ("toggle_user_liquidity", RootSudoOnly.COMPLICATED), + "bonds_reset_enabled": ("sudo_set_bonds_reset_enabled", RootSudoOnly.FALSE), + "transfers_enabled": ("sudo_set_toggle_transfer", RootSudoOnly.FALSE), + "min_allowed_uids": ("sudo_set_min_allowed_uids", RootSudoOnly.TRUE), } HYPERPARAMS_MODULE = { diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 955f52435..023cf39c9 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -12,6 +12,7 @@ from bittensor_cli.src import ( HYPERPARAMS, HYPERPARAMS_MODULE, + RootSudoOnly, DelegatesDetails, COLOR_PALETTE, ) @@ -295,7 +296,7 @@ async def set_hyperparameter_extrinsic( arbitrary_extrinsic = False - extrinsic, sudo_ = HYPERPARAMS.get(parameter, ("", False)) + extrinsic, sudo_ = HYPERPARAMS.get(parameter, ("", RootSudoOnly.FALSE)) call_params = {"netuid": netuid} if not extrinsic: arbitrary_extrinsic, call_params = search_metadata( @@ -305,8 +306,8 @@ async def set_hyperparameter_extrinsic( if not arbitrary_extrinsic: err_msg = ":cross_mark: [red]Invalid hyperparameter specified.[/red]" err_console.print(err_msg) - return False, err_msg - if sudo_ and prompt: + return False, err_msg, None + if sudo_ is RootSudoOnly.TRUE and prompt: if not Confirm.ask( "This hyperparam is only settable by root sudo users. If you are not, this will fail. Please confirm" ): @@ -316,63 +317,77 @@ async def set_hyperparameter_extrinsic( msg_value = value if not arbitrary_extrinsic else call_params pallet = HYPERPARAMS_MODULE.get(parameter) or DEFAULT_PALLET - with console.status( - 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", - ): - if not arbitrary_extrinsic: - extrinsic_params = await substrate.get_metadata_call_function( - module_name=pallet, call_function_name=extrinsic - ) - - # 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 = [ - pn_str - for param in extrinsic_params["fields"] - if "netuid" not in (pn_str := str(param["name"])) - ] - - if len(value) < len(non_netuid_fields): - err_msg = ( - "Not enough values provided in the list for all parameters" - ) - err_console.print(err_msg) - return False, err_msg, None - - call_params.update( - {name: val for name, val in zip(non_netuid_fields, value)} - ) + if not arbitrary_extrinsic: + extrinsic_params = await substrate.get_metadata_call_function( + module_name=pallet, call_function_name=extrinsic + ) - else: - if requires_bool( - substrate.metadata, param_name=extrinsic, pallet=pallet - ) and isinstance(value, str): - value = string_to_bool(value) - value_argument = extrinsic_params["fields"][ - len(extrinsic_params["fields"]) - 1 - ] - call_params[str(value_argument["name"])] = value + # 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 = [ + pn_str + for param in extrinsic_params["fields"] + if "netuid" not in (pn_str := str(param["name"])) + ] + + if len(value) < len(non_netuid_fields): + err_msg = "Not enough values provided in the list for all parameters" + err_console.print(err_msg) + return False, err_msg, None + + call_params.update( + {name: val for name, val in zip(non_netuid_fields, value)} + ) + else: + if requires_bool( + substrate.metadata, param_name=extrinsic, pallet=pallet + ) and isinstance(value, str): + value = string_to_bool(value) + 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( call_module=pallet, call_function=extrinsic, call_params=call_params, ) - if sudo_: + if sudo_ is RootSudoOnly.TRUE: call = await substrate.compose_call( call_module="Sudo", call_function="sudo", call_params={"call": call_} ) + elif sudo_ is RootSudoOnly.COMPLICATED: + if not prompt: + return ( + False, + "This hyperparam requires interactivity to set, and cannot be run with --no-prompt", + None, + ) + to_sudo_or_not_to_sudo = Confirm.ask( + f"This hyperparam can be executed as sudo or not. Do you want to execute as sudo [y] or not [n]?" + ) + if to_sudo_or_not_to_sudo: + call = await substrate.compose_call( + call_module="Sudo", + call_function="sudo", + call_params={"call": call_}, + ) + else: + call = call_ else: call = call_ - success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic( - call, wallet, wait_for_inclusion, wait_for_finalization - ) + with console.status( + f":satellite: Setting hyperparameter [{COLOR_PALETTE.G.SUBHEAD}]{parameter}[/{COLOR_PALETTE.G.SUBHEAD}]" + f" to [{COLOR_PALETTE.G.SUBHEAD}]{msg_value}[/{COLOR_PALETTE.G.SUBHEAD}]" + f" on subnet: [{COLOR_PALETTE.G.SUBHEAD}]{netuid}[/{COLOR_PALETTE.G.SUBHEAD}] ...", + spinner="earth", + ): + success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic( + call, wallet, wait_for_inclusion, wait_for_finalization + ) if not success: err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}") return False, err_msg, None @@ -725,6 +740,8 @@ async def sudo_set_hyperparameter( ) err_console.print(err_msg) return False, err_msg, None + if json_output: + prompt = False success, err_msg, ext_id = await set_hyperparameter_extrinsic( subtensor, wallet, netuid, param_name, value, prompt=prompt ) From b1d972871b523907efbc8df3887c8758729cef7e Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 9 Oct 2025 16:18:39 +0200 Subject: [PATCH 2/7] Default for no prompt --- bittensor_cli/src/commands/sudo.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 023cf39c9..870d7ed5a 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -132,6 +132,7 @@ def type_converter_with_retry(type_, val, arg_name): pallet = metadata.get_metadata_pallet(pallet_name) for call in pallet.calls: if call.name == param_name: + print(call.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"] @@ -361,14 +362,13 @@ async def set_hyperparameter_extrinsic( ) elif sudo_ is RootSudoOnly.COMPLICATED: if not prompt: - return ( - False, - "This hyperparam requires interactivity to set, and cannot be run with --no-prompt", - None, + to_sudo_or_not_to_sudo = ( + True # default to sudo true when no-prompt is set + ) + else: + to_sudo_or_not_to_sudo = Confirm.ask( + f"This hyperparam can be executed as sudo or not. Do you want to execute as sudo [y] or not [n]?" ) - to_sudo_or_not_to_sudo = Confirm.ask( - f"This hyperparam can be executed as sudo or not. Do you want to execute as sudo [y] or not [n]?" - ) if to_sudo_or_not_to_sudo: call = await substrate.compose_call( call_module="Sudo", From 5e230bb6393256dd8168ba252738842511f27324 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 9 Oct 2025 16:37:50 +0200 Subject: [PATCH 3/7] Missed indent + typing fixes --- .../src/bittensor/subtensor_interface.py | 2 +- bittensor_cli/src/commands/sudo.py | 105 +++++++++--------- 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index bb841d9f3..fe5e7af2e 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -500,7 +500,7 @@ async def get_total_stake_for_hotkey( async def current_take( self, - hotkey_ss58: int, + hotkey_ss58: str, block_hash: Optional[str] = None, reuse_block: bool = False, ) -> Optional[float]: diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 870d7ed5a..e305299ac 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -1,6 +1,6 @@ import asyncio import json -from typing import TYPE_CHECKING, Union, Optional +from typing import TYPE_CHECKING, Union, Optional, Type from async_substrate_interface import AsyncExtrinsicReceipt from bittensor_wallet import Wallet @@ -84,7 +84,7 @@ def allowed_value( return True, value -def string_to_bool(val) -> bool: +def string_to_bool(val) -> Union[bool, Type[ValueError]]: try: return {"true": True, "1": True, "0": False, "false": False}[val.lower()] except KeyError: @@ -160,7 +160,7 @@ def requires_bool(metadata, param_name, pallet: str = DEFAULT_PALLET) -> bool: for call in pallet.calls: if call.name == param_name: if "netuid" not in [x.name for x in call.args]: - return False, None + return False call_args = [arg for arg in call.args if arg.value["name"] != "netuid"] if len(call_args) != 1: return False @@ -272,6 +272,7 @@ async def set_hyperparameter_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. :param wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + :param prompt: If set to False, will not prompt the user. :return: tuple including: success: `True` if extrinsic was finalized or included in the block. If we did not wait for @@ -350,63 +351,61 @@ async def set_hyperparameter_extrinsic( len(extrinsic_params["fields"]) - 1 ] call_params[str(value_argument["name"])] = value - # create extrinsic call - call_ = await substrate.compose_call( - call_module=pallet, - call_function=extrinsic, - call_params=call_params, + # create extrinsic call + call_ = await substrate.compose_call( + call_module=pallet, + call_function=extrinsic, + call_params=call_params, + ) + if sudo_ is RootSudoOnly.TRUE: + call = await substrate.compose_call( + call_module="Sudo", call_function="sudo", call_params={"call": call_} ) - if sudo_ is RootSudoOnly.TRUE: + elif sudo_ is RootSudoOnly.COMPLICATED: + if not prompt: + to_sudo_or_not_to_sudo = True # default to sudo true when no-prompt is set + else: + to_sudo_or_not_to_sudo = Confirm.ask( + f"This hyperparam can be executed as sudo or not. Do you want to execute as sudo [y] or not [n]?" + ) + if to_sudo_or_not_to_sudo: call = await substrate.compose_call( - call_module="Sudo", call_function="sudo", call_params={"call": call_} + call_module="Sudo", + call_function="sudo", + call_params={"call": call_}, ) - elif sudo_ is RootSudoOnly.COMPLICATED: - if not prompt: - to_sudo_or_not_to_sudo = ( - True # default to sudo true when no-prompt is set - ) - else: - to_sudo_or_not_to_sudo = Confirm.ask( - f"This hyperparam can be executed as sudo or not. Do you want to execute as sudo [y] or not [n]?" - ) - if to_sudo_or_not_to_sudo: - call = await substrate.compose_call( - call_module="Sudo", - call_function="sudo", - call_params={"call": call_}, - ) - else: - call = call_ else: call = call_ - with console.status( - f":satellite: Setting hyperparameter [{COLOR_PALETTE.G.SUBHEAD}]{parameter}[/{COLOR_PALETTE.G.SUBHEAD}]" - f" to [{COLOR_PALETTE.G.SUBHEAD}]{msg_value}[/{COLOR_PALETTE.G.SUBHEAD}]" - f" on subnet: [{COLOR_PALETTE.G.SUBHEAD}]{netuid}[/{COLOR_PALETTE.G.SUBHEAD}] ...", - spinner="earth", - ): - success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic( - call, wallet, wait_for_inclusion, wait_for_finalization + else: + call = call_ + with console.status( + f":satellite: Setting hyperparameter [{COLOR_PALETTE.G.SUBHEAD}]{parameter}[/{COLOR_PALETTE.G.SUBHEAD}]" + f" to [{COLOR_PALETTE.G.SUBHEAD}]{msg_value}[/{COLOR_PALETTE.G.SUBHEAD}]" + f" on subnet: [{COLOR_PALETTE.G.SUBHEAD}]{netuid}[/{COLOR_PALETTE.G.SUBHEAD}] ...", + spinner="earth", + ): + success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic( + call, wallet, wait_for_inclusion, wait_for_finalization + ) + if not success: + err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}") + return False, err_msg, None + else: + ext_id = await ext_receipt.get_extrinsic_identifier() + await print_extrinsic_id(ext_receipt) + if arbitrary_extrinsic: + console.print( + f":white_heavy_check_mark: " + f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]" ) - if not success: - err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}") - return False, err_msg, None + return True, "", ext_id + # Successful registration, final check for membership else: - ext_id = await ext_receipt.get_extrinsic_identifier() - await print_extrinsic_id(ext_receipt) - if 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, "", ext_id - # Successful registration, final check for membership - else: - console.print( - f":white_heavy_check_mark: " - f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]" - ) - return True, "", ext_id + console.print( + f":white_heavy_check_mark: " + f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]" + ) + return True, "", ext_id async def _get_senate_members( From 03b6b8378aef7d74ac689cfbf27f013d70db7078 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 9 Oct 2025 16:45:24 +0200 Subject: [PATCH 4/7] Debug removed --- bittensor_cli/src/commands/sudo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index e305299ac..76cc0addd 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -132,7 +132,6 @@ def type_converter_with_retry(type_, val, arg_name): pallet = metadata.get_metadata_pallet(pallet_name) for call in pallet.calls: if call.name == param_name: - print(call.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"] From f908df7ae0e98fc4b7bb865a6eef657cdab6b5a5 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 9 Oct 2025 16:55:56 +0200 Subject: [PATCH 5/7] tf? From 3c903951fe1cb267b03acb158c2586ad80581d50 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 9 Oct 2025 17:10:39 +0200 Subject: [PATCH 6/7] test --- bittensor_cli/src/commands/sudo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 76cc0addd..263cb44b2 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -318,6 +318,7 @@ async def set_hyperparameter_extrinsic( msg_value = value if not arbitrary_extrinsic else call_params pallet = HYPERPARAMS_MODULE.get(parameter) or DEFAULT_PALLET + if not arbitrary_extrinsic: extrinsic_params = await substrate.get_metadata_call_function( module_name=pallet, call_function_name=extrinsic From 3d46c83ad69c85a490f7fd5febaf085f6e2a0779 Mon Sep 17 00:00:00 2001 From: bdhimes Date: Thu, 9 Oct 2025 17:14:24 +0200 Subject: [PATCH 7/7] Ruff --- bittensor_cli/src/commands/sudo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 263cb44b2..76cc0addd 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -318,7 +318,6 @@ async def set_hyperparameter_extrinsic( msg_value = value if not arbitrary_extrinsic else call_params pallet = HYPERPARAMS_MODULE.get(parameter) or DEFAULT_PALLET - if not arbitrary_extrinsic: extrinsic_params = await substrate.get_metadata_call_function( module_name=pallet, call_function_name=extrinsic