From 1fcee62b218867573deb0415a6a36de582eb1c09 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 15 Oct 2024 17:04:04 +0200 Subject: [PATCH 01/20] Better handle incorrect password. --- bittensor_cli/src/bittensor/async_substrate_interface.py | 2 +- bittensor_cli/src/bittensor/extrinsics/registration.py | 1 + bittensor_cli/src/bittensor/extrinsics/root.py | 2 ++ bittensor_cli/src/bittensor/extrinsics/transfer.py | 1 + bittensor_cli/src/commands/root.py | 5 +++++ bittensor_cli/src/commands/stake/stake.py | 4 ++++ bittensor_cli/src/commands/subnets.py | 1 + bittensor_cli/src/commands/sudo.py | 1 + bittensor_cli/src/commands/wallets.py | 1 + 9 files changed, 17 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/src/bittensor/async_substrate_interface.py b/bittensor_cli/src/bittensor/async_substrate_interface.py index 0f4dd0d43..0c40830d6 100644 --- a/bittensor_cli/src/bittensor/async_substrate_interface.py +++ b/bittensor_cli/src/bittensor/async_substrate_interface.py @@ -682,9 +682,9 @@ async def shutdown(self): try: self._receiving_task.cancel() await self._receiving_task + await self.ws.close() except (AttributeError, asyncio.CancelledError): pass - await self.ws.close() self.ws = None self._initialized = False self._receiving_task = None diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py index 5354ac520..6373824f9 100644 --- a/bittensor_cli/src/bittensor/extrinsics/registration.py +++ b/bittensor_cli/src/bittensor/extrinsics/registration.py @@ -1630,6 +1630,7 @@ async def swap_hotkey_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False if prompt: diff --git a/bittensor_cli/src/bittensor/extrinsics/root.py b/bittensor_cli/src/bittensor/extrinsics/root.py index fcbf7d0c1..295ee640b 100644 --- a/bittensor_cli/src/bittensor/extrinsics/root.py +++ b/bittensor_cli/src/bittensor/extrinsics/root.py @@ -309,6 +309,7 @@ async def root_register_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False print_verbose(f"Checking if hotkey ({wallet.hotkey_str}) is registered on root") @@ -429,6 +430,7 @@ async def _do_set_weights(): try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False # First convert types. diff --git a/bittensor_cli/src/bittensor/extrinsics/transfer.py b/bittensor_cli/src/bittensor/extrinsics/transfer.py index 8ae37e9b6..620c20d76 100644 --- a/bittensor_cli/src/bittensor/extrinsics/transfer.py +++ b/bittensor_cli/src/bittensor/extrinsics/transfer.py @@ -111,6 +111,7 @@ async def do_transfer() -> tuple[bool, str, str]: try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False # Check balance. diff --git a/bittensor_cli/src/commands/root.py b/bittensor_cli/src/commands/root.py index 9a19c7361..2401eb002 100644 --- a/bittensor_cli/src/commands/root.py +++ b/bittensor_cli/src/commands/root.py @@ -283,6 +283,7 @@ async def burned_register_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False with console.status( @@ -539,6 +540,7 @@ async def get_stake_for_coldkey_and_hotkey( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False print_verbose("Checking if hotkey is a delegate") @@ -1100,6 +1102,7 @@ async def senate_vote( wallet.unlock_hotkey() wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False console.print(f"Fetching proposals in [dark_orange]network: {subtensor.network}") @@ -1323,6 +1326,7 @@ async def _do_set_take() -> bool: wallet.unlock_hotkey() wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False result_ = await _do_set_take() @@ -1724,6 +1728,7 @@ async def nominate(wallet: Wallet, subtensor: SubtensorInterface, prompt: bool): wallet.unlock_hotkey() wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False print_verbose(f"Checking hotkey ({wallet.hotkey_str}) is a delegate") diff --git a/bittensor_cli/src/commands/stake/stake.py b/bittensor_cli/src/commands/stake/stake.py index 0f48d6195..819f02cca 100644 --- a/bittensor_cli/src/commands/stake/stake.py +++ b/bittensor_cli/src/commands/stake/stake.py @@ -106,6 +106,7 @@ async def add_stake_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False # Default to wallet's own hotkey if the value is not passed. @@ -312,6 +313,7 @@ async def add_stake_multiple_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False with console.status( @@ -493,6 +495,7 @@ async def unstake_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False if hotkey_ss58 is None: @@ -663,6 +666,7 @@ async def unstake_multiple_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False with console.status( diff --git a/bittensor_cli/src/commands/subnets.py b/bittensor_cli/src/commands/subnets.py index 122fec6ae..3e9349827 100644 --- a/bittensor_cli/src/commands/subnets.py +++ b/bittensor_cli/src/commands/subnets.py @@ -103,6 +103,7 @@ async def _find_event_attributes_in_extrinsic_receipt( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False with console.status(":satellite: Registering subnet...", spinner="earth"): diff --git a/bittensor_cli/src/commands/sudo.py b/bittensor_cli/src/commands/sudo.py index 26cdab5f0..6ebbb0eca 100644 --- a/bittensor_cli/src/commands/sudo.py +++ b/bittensor_cli/src/commands/sudo.py @@ -104,6 +104,7 @@ async def set_hyperparameter_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False extrinsic = HYPERPARAMS.get(parameter) diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index a1dd22623..3faefd23c 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -1619,6 +1619,7 @@ async def set_id( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False with console.status( From 16d59bde799a5d4074bcd50885202158574ed47f Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 15 Oct 2024 14:17:19 -0700 Subject: [PATCH 02/20] Fixes success path of pow register --- .../src/bittensor/extrinsics/registration.py | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py index 6373824f9..6e69d6898 100644 --- a/bittensor_cli/src/bittensor/extrinsics/registration.py +++ b/bittensor_cli/src/bittensor/extrinsics/registration.py @@ -35,6 +35,7 @@ millify, get_human_readable, print_verbose, + print_error, ) if typing.TYPE_CHECKING: @@ -512,12 +513,13 @@ async def get_neuron_for_pubkey_and_subnet(): with console.status( f":satellite: Checking Account on [bold]subnet:{netuid}[/bold]...", spinner="aesthetic", - ): + ) as status: neuron = await get_neuron_for_pubkey_and_subnet() if not neuron.is_null: - # bittensor.logging.debug( - # f"Wallet {wallet} is already registered on {neuron.netuid} with {neuron.uid}" - # ) + print_error( + f"Wallet {wallet} is already registered on subnet {neuron.netuid} with uid {neuron.uid}", + status, + ) return True if prompt: @@ -611,7 +613,8 @@ async def get_neuron_for_pubkey_and_subnet(): success, err_msg = True, "" else: await response.process_events() - if not await response.is_success: + success = await response.is_success + if not success: success, err_msg = ( False, format_error_message( @@ -619,23 +622,23 @@ async def get_neuron_for_pubkey_and_subnet(): substrate=subtensor.substrate, ), ) - - if not success: - # Look error here - # https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs - if "HotKeyAlreadyRegisteredInSubNet" in err_msg: - console.print( - f":white_heavy_check_mark: [green]Already Registered on " - f"[bold]subnet:{netuid}[/bold][/green]" + # Look error here + # https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs + + if "HotKeyAlreadyRegisteredInSubNet" in err_msg: + console.print( + f":white_heavy_check_mark: [green]Already Registered on " + f"[bold]subnet:{netuid}[/bold][/green]" + ) + return True + err_console.print( + f":cross_mark: [red]Failed[/red]: {err_msg}" ) - return True - - err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}") - await asyncio.sleep(0.5) + await asyncio.sleep(0.5) # Successful registration, final check for neuron and pubkey - else: - console.print(":satellite: Checking Balance...") + if success: + console.print(":satellite: Checking Registration status...") is_registered = await is_hotkey_registered( subtensor, netuid=netuid, @@ -1630,7 +1633,6 @@ async def swap_hotkey_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: - err_console.print("Error decrypting coldkey (possibly incorrect password)") return False if prompt: From cf4e6e7f6f1dd9cf8bd501fe861162e03d15ce20 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 15 Oct 2024 14:18:44 -0700 Subject: [PATCH 03/20] Adds back decryption err_msg --- bittensor_cli/src/bittensor/extrinsics/registration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py index 6e69d6898..c8bbde07f 100644 --- a/bittensor_cli/src/bittensor/extrinsics/registration.py +++ b/bittensor_cli/src/bittensor/extrinsics/registration.py @@ -1633,6 +1633,7 @@ async def swap_hotkey_extrinsic( try: wallet.unlock_coldkey() except KeyFileError: + err_console.print("Error decrypting coldkey (possibly incorrect password)") return False if prompt: From a111077ca09b168ab1f6117f8d2f1995d53eab9d Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 15 Oct 2024 14:45:46 -0700 Subject: [PATCH 04/20] Fixes termination of workers --- bittensor_cli/src/bittensor/extrinsics/registration.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py index c8bbde07f..cd8864a75 100644 --- a/bittensor_cli/src/bittensor/extrinsics/registration.py +++ b/bittensor_cli/src/bittensor/extrinsics/registration.py @@ -1234,8 +1234,11 @@ def _terminate_workers_and_wait_for_exit( if isinstance(worker, Queue_Type): worker.join_thread() else: - worker.join() - worker.close() + worker.join(3.0) + try: + worker.close() + except ValueError: + worker.terminate() # TODO verify this works with async From 7e0634be0f360287a8c8fab5629d2422588198aa Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 15 Oct 2024 14:55:10 -0700 Subject: [PATCH 05/20] Enhance terminate_workets --- bittensor_cli/src/bittensor/extrinsics/registration.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py index cd8864a75..af6962106 100644 --- a/bittensor_cli/src/bittensor/extrinsics/registration.py +++ b/bittensor_cli/src/bittensor/extrinsics/registration.py @@ -16,6 +16,7 @@ import time import typing from typing import Optional +import subprocess import backoff from bittensor_wallet import Wallet @@ -1234,7 +1235,10 @@ def _terminate_workers_and_wait_for_exit( if isinstance(worker, Queue_Type): worker.join_thread() else: - worker.join(3.0) + try: + worker.join(3.0) + except subprocess.TimeoutExpired: + worker.terminate() try: worker.close() except ValueError: From f958ec8d7196b98383010f5f36338a51f12ab355 Mon Sep 17 00:00:00 2001 From: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:23:55 +0200 Subject: [PATCH 06/20] Adds `--all` flag to transfer (#181) * Adds `--all` flag to transfer * Conflict * Adds check for -ve balance * Uses print_error --------- Co-authored-by: ibraheem-opentensor --- bittensor_cli/cli.py | 21 ++++++++++++++++--- .../bittensor/async_substrate_interface.py | 4 +--- .../src/bittensor/extrinsics/transfer.py | 9 ++++++++ bittensor_cli/src/commands/wallets.py | 8 ++++++- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index b012c989e..b3cc16ff7 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -1404,9 +1404,12 @@ def wallet_transfer( None, "--amount", "-a", - prompt=True, + prompt=False, help="Amount (in TAO) to transfer.", ), + transfer_all: bool = typer.Option( + False, "--all", prompt=False, help="Transfer all available balance." + ), wallet_name: str = Options.wallet_name, wallet_path: str = Options.wallet_path, wallet_hotkey: str = Options.wallet_hotkey, @@ -1446,9 +1449,21 @@ def wallet_transfer( validate=WV.WALLET, ) subtensor = self.initialize_chain(network) + if transfer_all and amount: + print_error("Cannot specify an amount and '--all' flag.") + raise typer.Exit() + elif transfer_all: + amount = 0 + elif not amount: + amount = FloatPrompt.ask("Enter amount (in TAO) to transfer.") return self._run_command( wallets.transfer( - wallet, subtensor, destination_ss58_address, amount, prompt + wallet, + subtensor, + destination_ss58_address, + amount, + transfer_all, + prompt, ) ) @@ -3269,7 +3284,7 @@ def stake_add( self.verbosity_handler(quiet, verbose) if stake_all and amount: - err_console.print( + print_error( "Cannot specify an amount and 'stake-all'. Choose one or the other." ) raise typer.Exit() diff --git a/bittensor_cli/src/bittensor/async_substrate_interface.py b/bittensor_cli/src/bittensor/async_substrate_interface.py index 0c40830d6..60ec9dce9 100644 --- a/bittensor_cli/src/bittensor/async_substrate_interface.py +++ b/bittensor_cli/src/bittensor/async_substrate_interface.py @@ -1707,9 +1707,7 @@ async def rpc_request( ) result = await self._make_rpc_request(payloads, runtime=runtime) if "error" in result[payload_id][0]: - raise SubstrateRequestException( - result[payload_id][0]["error"]["message"] - ) + raise SubstrateRequestException(result[payload_id][0]["error"]["message"]) if "result" in result[payload_id][0]: return result[payload_id][0] else: diff --git a/bittensor_cli/src/bittensor/extrinsics/transfer.py b/bittensor_cli/src/bittensor/extrinsics/transfer.py index 620c20d76..d5311c068 100644 --- a/bittensor_cli/src/bittensor/extrinsics/transfer.py +++ b/bittensor_cli/src/bittensor/extrinsics/transfer.py @@ -15,6 +15,7 @@ format_error_message, get_explorer_url_for_network, is_valid_bittensor_address_or_public_key, + print_error, ) @@ -23,6 +24,7 @@ async def transfer_extrinsic( wallet: Wallet, destination: str, amount: Balance, + transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, keep_alive: bool = True, @@ -34,6 +36,7 @@ async def transfer_extrinsic( :param wallet: Bittensor wallet object to make transfer from. :param destination: Destination public key address (ss58_address or ed25519) of recipient. :param amount: Amount to stake as Bittensor balance. + :param transfer_all: Whether to transfer all funds from this wallet to the destination address. :param wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns `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 @@ -136,6 +139,12 @@ async def do_transfer() -> tuple[bool, str, str]: existential_deposit = Balance(0) # Check if we have enough balance. + if transfer_all is True: + amount = account_balance - fee - existential_deposit + if amount < Balance(0): + print_error("Not enough balance to transfer") + return False + if account_balance < (amount + fee + existential_deposit): err_console.print( ":cross_mark: [bold red]Not enough balance[/bold red]:\n\n" diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index 3faefd23c..c790d7fb3 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -1256,11 +1256,17 @@ async def transfer( subtensor: SubtensorInterface, destination: str, amount: float, + transfer_all: bool, prompt: bool, ): """Transfer token of amount to destination.""" await transfer_extrinsic( - subtensor, wallet, destination, Balance.from_tao(amount), prompt=prompt + subtensor, + wallet, + destination, + Balance.from_tao(amount), + transfer_all, + prompt=prompt, ) From 1bb0ec7b4e4b2d3246a51573178bdf469469bcc5 Mon Sep 17 00:00:00 2001 From: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:38:23 +0200 Subject: [PATCH 07/20] Various fixes (#199) * Use correct dict key * Clean up error handling for main CLI. Add KeyboardInterrupt. * Race conditions for metadata set. * CLI error handling cleanup * Change error description output in format_error_message to use single quotes instead of backticks for easier formatting when pasting error. * Fix taostats link * Updates set-id tests * Adds config missing values * Updates set-id test * Fix sudo set. * Fix no-prompt on confirmation for unstake --------- Co-authored-by: ibraheem-opentensor --- bittensor_cli/cli.py | 58 +++++++++++++------ .../bittensor/async_substrate_interface.py | 34 +++++++---- .../src/bittensor/extrinsics/transfer.py | 8 ++- .../src/bittensor/subtensor_interface.py | 20 +++---- bittensor_cli/src/bittensor/utils.py | 4 +- tests/e2e_tests/test_wallet_interactions.py | 39 ++++++++++++- 6 files changed, 119 insertions(+), 44 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index b3cc16ff7..5008a03c5 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -816,30 +816,35 @@ def initialize_chain( self.subtensor = SubtensorInterface(defaults.subtensor.network) return self.subtensor - def _run_command(self, cmd: Coroutine) -> None: + def _run_command(self, cmd: Coroutine): """ Runs the supplied coroutine with `asyncio.run` """ async def _run(): + initiated = False try: if self.subtensor: async with self.subtensor: + initiated = True result = await cmd else: + initiated = True result = await cmd return result except (ConnectionRefusedError, ssl.SSLError): err_console.print(f"Unable to connect to the chain: {self.subtensor}") - asyncio.create_task(cmd).cancel() - raise typer.Exit() - except ConnectionClosed: - asyncio.create_task(cmd).cancel() - raise typer.Exit() - except SubstrateRequestException as e: - err_console.print(str(e)) - asyncio.create_task(cmd).cancel() - raise typer.Exit() + except ( + ConnectionClosed, + SubstrateRequestException, + KeyboardInterrupt, + ) as e: + if isinstance(e, SubstrateRequestException): + err_console.print(str(e)) + finally: + if initiated is False: + asyncio.create_task(cmd).cancel() + raise typer.Exit() if sys.version_info < (3, 10): # For Python 3.9 or lower @@ -857,15 +862,32 @@ def main_callback( """ Command line interface (CLI) for Bittensor. Uses the values in the configuration file. These values can be overriden by passing them explicitly in the command line. """ - # create config file if it does not exist - if not os.path.exists(self.config_path): + # Load or create the config file + if os.path.exists(self.config_path): + with open(self.config_path, "r") as f: + config = safe_load(f) + else: directory_path = Path(self.config_base_path) directory_path.mkdir(exist_ok=True, parents=True) - with open(self.config_path, "w+") as f: - safe_dump(defaults.config.dictionary, f) - # check config - with open(self.config_path, "r") as f: - config = safe_load(f) + config = defaults.config.dictionary.copy() + with open(self.config_path, "w") as f: + safe_dump(config, f) + + # Update missing values + updated = False + for key, value in defaults.config.dictionary.items(): + if key not in config: + config[key] = value + updated = True + elif isinstance(value, dict): + for sub_key, sub_value in value.items(): + if sub_key not in config[key]: + config[key][sub_key] = sub_value + updated = True + if updated: + with open(self.config_path, "w") as f: + safe_dump(config, f) + for k, v in config.items(): if k in self.config.keys(): self.config[k] = v @@ -3459,7 +3481,7 @@ def stake_remove( if not unstake_all and not amount and not keep_stake: amount = FloatPrompt.ask("[blue bold]Amount to unstake (TAO ฯ„)[/blue bold]") - if unstake_all and not amount: + if unstake_all and not amount and prompt: if not Confirm.ask("Unstake all staked TAO tokens?", default=False): raise typer.Exit() diff --git a/bittensor_cli/src/bittensor/async_substrate_interface.py b/bittensor_cli/src/bittensor/async_substrate_interface.py index 60ec9dce9..bd6bbb987 100644 --- a/bittensor_cli/src/bittensor/async_substrate_interface.py +++ b/bittensor_cli/src/bittensor/async_substrate_interface.py @@ -460,6 +460,9 @@ def __init__(self, chain, runtime_config, metadata, type_registry): self.runtime_config = runtime_config self.metadata = metadata + def __str__(self): + return f"Runtime: {self.chain} | {self.config}" + @property def implements_scaleinfo(self) -> bool: """ @@ -897,9 +900,10 @@ async def init_runtime( async def get_runtime(block_hash, block_id) -> Runtime: # Check if runtime state already set to current block - if (block_hash and block_hash == self.last_block_hash) or ( - block_id and block_id == self.block_id - ): + if ( + (block_hash and block_hash == self.last_block_hash) + or (block_id and block_id == self.block_id) + ) and self.metadata is not None: return Runtime( self.chain, self.runtime_config, @@ -945,9 +949,11 @@ async def get_runtime(block_hash, block_id) -> Runtime: raise SubstrateRequestException( f"No runtime information for block '{block_hash}'" ) - # Check if runtime state already set to current block - if runtime_info.get("specVersion") == self.runtime_version: + if ( + runtime_info.get("specVersion") == self.runtime_version + and self.metadata is not None + ): return Runtime( self.chain, self.runtime_config, @@ -962,16 +968,19 @@ async def get_runtime(block_hash, block_id) -> Runtime: if self.runtime_version in self.__metadata_cache: # Get metadata from cache # self.debug_message('Retrieved metadata for {} from memory'.format(self.runtime_version)) - self.metadata = self.__metadata_cache[self.runtime_version] + metadata = self.metadata = self.__metadata_cache[ + self.runtime_version + ] else: - self.metadata = await self.get_block_metadata( + metadata = self.metadata = await self.get_block_metadata( block_hash=runtime_block_hash, decode=True ) # self.debug_message('Retrieved metadata for {} from Substrate node'.format(self.runtime_version)) # Update metadata cache self.__metadata_cache[self.runtime_version] = self.metadata - + else: + metadata = self.metadata # Update type registry self.reload_type_registry(use_remote_preset=False, auto_discover=True) @@ -1012,7 +1021,10 @@ async def get_runtime(block_hash, block_id) -> Runtime: if block_id and block_hash: raise ValueError("Cannot provide block_hash and block_id at the same time") - if not (runtime := self.runtime_cache.retrieve(block_id, block_hash)): + if ( + not (runtime := self.runtime_cache.retrieve(block_id, block_hash)) + or runtime.metadata is None + ): runtime = await get_runtime(block_hash, block_id) self.runtime_cache.add_item(block_id, block_hash, runtime) return runtime @@ -1123,7 +1135,7 @@ async def create_storage_key( ------- StorageKey """ - await self.init_runtime(block_hash=block_hash) + runtime = await self.init_runtime(block_hash=block_hash) return StorageKey.create_from_storage_function( pallet, @@ -2272,7 +2284,7 @@ async def get_metadata_constant(self, module_name, constant_name, block_hash=Non MetadataModuleConstants """ - # await self.init_runtime(block_hash=block_hash) + await self.init_runtime(block_hash=block_hash) for module in self.metadata.pallets: if module_name == module.name and module.constants: diff --git a/bittensor_cli/src/bittensor/extrinsics/transfer.py b/bittensor_cli/src/bittensor/extrinsics/transfer.py index d5311c068..eada5ec70 100644 --- a/bittensor_cli/src/bittensor/extrinsics/transfer.py +++ b/bittensor_cli/src/bittensor/extrinsics/transfer.py @@ -101,7 +101,11 @@ async def do_transfer() -> tuple[bool, str, str]: block_hash_ = response.block_hash return True, block_hash_, "" else: - return False, "", format_error_message(await response.error_message) + return ( + False, + "", + format_error_message(await response.error_message, subtensor.substrate), + ) # Validate destination address. if not is_valid_bittensor_address_or_public_key(destination): @@ -193,7 +197,7 @@ async def do_transfer() -> tuple[bool, str, str]: ) console.print( f"Balance:\n" - f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance[wallet.coldkey.ss58_address]}[/green]" + f" [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance[wallet.coldkeypub.ss58_address]}[/green]" ) return True diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index 62063c206..b056937aa 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -111,18 +111,18 @@ def __str__(self): return f"Network: {self.network}, Chain: {self.chain_endpoint}" async def __aenter__(self): - with console.status( - f"[yellow]Connecting to Substrate:[/yellow][bold white] {self}..." - ): - try: + try: + with console.status( + f"[yellow]Connecting to Substrate:[/yellow][bold white] {self}..." + ): async with self.substrate: return self - except TimeoutException: - err_console.print( - "\n[red]Error[/red]: Timeout occurred connecting to substrate. " - f"Verify your chain and network settings: {self}" - ) - raise typer.Exit(code=1) + except TimeoutException: + err_console.print( + "\n[red]Error[/red]: Timeout occurred connecting to substrate. " + f"Verify your chain and network settings: {self}" + ) + raise typer.Exit(code=1) async def __aexit__(self, exc_type, exc_val, exc_tb): await self.substrate.close() diff --git a/bittensor_cli/src/bittensor/utils.py b/bittensor_cli/src/bittensor/utils.py index e63807a83..b6b05baae 100644 --- a/bittensor_cli/src/bittensor/utils.py +++ b/bittensor_cli/src/bittensor/utils.py @@ -443,7 +443,7 @@ def get_explorer_url_for_network( explorer_opentensor_url = "{root_url}/query/{block_hash}".format( root_url=explorer_root_urls.get("opentensor"), block_hash=block_hash ) - explorer_taostats_url = "{root_url}/extrinsic/{block_hash}".format( + explorer_taostats_url = "{root_url}/hash/{block_hash}".format( root_url=explorer_root_urls.get("taostats"), block_hash=block_hash ) explorer_urls["opentensor"] = explorer_opentensor_url @@ -532,7 +532,7 @@ def format_error_message( err_docs = error_message.get("docs", [err_description]) err_description = err_docs[0] if err_docs else err_description - return f"Subtensor returned `{err_name}({err_type})` error. This means: `{err_description}`." + return f"Subtensor returned `{err_name}({err_type})` error. This means: '{err_description}'." def convert_blocks_to_time(blocks: int, block_time: int = 12) -> tuple[int, int, int]: diff --git a/tests/e2e_tests/test_wallet_interactions.py b/tests/e2e_tests/test_wallet_interactions.py index 8641f3698..c43efb90b 100644 --- a/tests/e2e_tests/test_wallet_interactions.py +++ b/tests/e2e_tests/test_wallet_interactions.py @@ -353,6 +353,7 @@ def test_wallet_identities(local_chain, wallet_setup): """ print("Testing wallet set-id, get-id, sign command ๐Ÿงช") + netuid = 1 wallet_path_alice = "//Alice" # Create wallet for Alice @@ -361,7 +362,7 @@ def test_wallet_identities(local_chain, wallet_setup): ) # Register Alice to the root network (0) - # Either root list neurons can set-id or subnet owners + # Either root list neurons + subnet registered can set-id or subnet owners root_register = exec_command_alice( command="root", sub_command="register", @@ -379,6 +380,42 @@ def test_wallet_identities(local_chain, wallet_setup): ) assert "โœ… Registered" in root_register.stdout + # Register a subnet with sudo as Alice + result = exec_command_alice( + command="subnets", + sub_command="create", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + wallet_alice.name, + "--no-prompt", + ], + ) + assert f"โœ… Registered subnetwork with netuid: {netuid}" in result.stdout + + # Register Alice in netuid = 1 using her hotkey + register_subnet = exec_command_alice( + command="subnets", + sub_command="register", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--wallet-name", + wallet_alice.name, + "--hotkey", + wallet_alice.hotkey_str, + "--netuid", + netuid, + "--chain", + "ws://127.0.0.1:9945", + "--no-prompt", + ], + ) + assert "โœ… Registered" in register_subnet.stdout + # Define values for Alice's identity alice_identity = { "display_name": "Alice", From e4c2c62adeffda6af85bc16d752b9b27dbee5f63 Mon Sep 17 00:00:00 2001 From: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:36:20 +0200 Subject: [PATCH 08/20] Allow wallet-like objects for use in parsing keys out (#197) --- bittensor_cli/src/commands/wallets.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index c790d7fb3..33635c663 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -59,6 +59,13 @@ ) +class WalletLike: + def __init__(self, name=None, hotkey_ss58=None, hotkey_str=None): + self.name = name + self.hotkey_ss58 = hotkey_ss58 + self.hotkey_str = hotkey_str + + async def regen_coldkey( wallet: Wallet, mnemonic: Optional[str], @@ -697,9 +704,7 @@ async def overview( de_registered_neurons.append(de_registered_neuron) # Add this hotkey to the wallets dict - wallet_ = Wallet(name=wallet) - wallet_.hotkey_ss58 = hotkey_addr - wallet.hotkey_str = hotkey_addr[:5] # Max length of 5 characters + wallet_ = WalletLike(name=wallet.name, hotkey_ss58=hotkey_addr, hotkey_str=hotkey_addr[:5]) # Indicates a hotkey not on local machine but exists in stake_info obj on-chain if hotkey_coldkey_to_hotkey_wallet.get(hotkey_addr) is None: hotkey_coldkey_to_hotkey_wallet[hotkey_addr] = {} @@ -762,8 +767,7 @@ async def overview( if not hotwallet: # Indicates a mismatch between what the chain says the coldkey # is for this hotkey and the local wallet coldkey-hotkey pair - hotwallet = Wallet(name=nn.coldkey[:7]) - hotwallet.hotkey_str = nn.hotkey[:7] + hotwallet = WalletLike(name=nn.coldkey[:7], hotkey_str=nn.hotkey[:7]) nn: NeuronInfoLite uid = nn.uid From 2b2053a93f4deddb7dfa6ca5ba790c3b580ea80c Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 4 Nov 2024 18:12:20 +0200 Subject: [PATCH 09/20] Sets num_processes correctly for CUDA registration. --- bittensor_cli/src/bittensor/extrinsics/registration.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py index af6962106..584d1291c 100644 --- a/bittensor_cli/src/bittensor/extrinsics/registration.py +++ b/bittensor_cli/src/bittensor/extrinsics/registration.py @@ -918,6 +918,9 @@ async def _block_solver( stop_event.clear() solution_queue = Queue() + if cuda: + num_processes = len(dev_id) + finished_queues = [Queue() for _ in range(num_processes)] check_block = Lock() @@ -927,7 +930,6 @@ async def _block_solver( if cuda: ## Create a worker per CUDA device - num_processes = len(dev_id) solvers = [ _CUDASolver( i, From c75b1d2de291fe78b289b950b3bc9ab63c847db8 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 31 Oct 2024 16:22:18 -0700 Subject: [PATCH 10/20] Adds confirmation after each successful regen --- bittensor_cli/src/commands/wallets.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index 33635c663..11135a860 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -82,13 +82,21 @@ async def regen_coldkey( with open(json_path, "r") as f: json_str = f.read() try: - wallet.regenerate_coldkey( + new_wallet = wallet.regenerate_coldkey( mnemonic=mnemonic, seed=seed, json=(json_str, json_password) if all([json_str, json_password]) else None, use_password=use_password, overwrite=False, ) + + if isinstance(new_wallet, Wallet): + console.print( + "\nโœ… [dark_sea_green]Regenerated coldkey successfully!\n", + f"[dark_sea_green]Wallet name: ({new_wallet.name}), path: ({new_wallet.path}), coldkey ss58: ({new_wallet.coldkeypub.ss58_address})", + ) + except ValueError: + print_error("Mnemonic phrase is invalid") except KeyFileError: print_error("KeyFileError: File is not writable") @@ -100,11 +108,16 @@ async def regen_coldkey_pub( ): """Creates a new coldkeypub under this wallet.""" try: - wallet.regenerate_coldkeypub( + new_coldkeypub = wallet.regenerate_coldkeypub( ss58_address=ss58_address, public_key=public_key_hex, overwrite=False, ) + if isinstance(new_coldkeypub, Wallet): + console.print( + "\nโœ… [dark_sea_green]Regenerated coldkeypub successfully!\n", + f"[dark_sea_green]Wallet name: ({new_coldkeypub.name}), path: ({new_coldkeypub.path}), coldkey ss58: ({new_coldkeypub.coldkeypub.ss58_address})", + ) except KeyFileError: print_error("KeyFileError: File is not writable") @@ -127,13 +140,20 @@ async def regen_hotkey( json_str = f.read() try: - wallet.regenerate_hotkey( + new_hotkey = wallet.regenerate_hotkey( mnemonic=mnemonic, seed=seed, json=(json_str, json_password) if all([json_str, json_password]) else None, use_password=use_password, overwrite=False, ) + if isinstance(new_hotkey, Wallet): + console.print( + "\nโœ… [dark_sea_green]Regenerated hotkey successfully!\n", + f"[dark_sea_green]Wallet name: ({new_hotkey.name}), path: ({new_hotkey.path}), hotkey ss58: ({new_hotkey.hotkey.ss58_address})", + ) + except ValueError: + print_error("Mnemonic phrase is invalid") except KeyFileError: print_error("KeyFileError: File is not writable") From 8abb2c83712fbd0eaa5dc14a38be0f4790b22e53 Mon Sep 17 00:00:00 2001 From: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> Date: Mon, 4 Nov 2024 19:39:48 +0200 Subject: [PATCH 11/20] fix handling null neurons (#214) Handles decoding of null neuron lite results. --- .../src/bittensor/subtensor_interface.py | 2 +- bittensor_cli/src/commands/wallets.py | 69 +++++-------------- 2 files changed, 18 insertions(+), 53 deletions(-) diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index b056937aa..b8cfb7afd 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -283,7 +283,7 @@ async def query_runtime_api( self, runtime_api: str, method: str, - params: Optional[Union[list[list[int]], dict[str, int]]], + params: Optional[Union[list[list[int]], list[int], dict[str, int]]], block_hash: Optional[str] = None, reuse_block: Optional[bool] = False, ) -> Optional[str]: diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index 33635c663..bba4ef0ec 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -4,10 +4,9 @@ import os import sys from collections import defaultdict -from concurrent.futures import ProcessPoolExecutor from functools import partial from sys import getsizeof -from typing import Any, Collection, Generator, Optional +from typing import Collection, Generator, Optional import aiohttp from bittensor_wallet import Wallet @@ -1106,7 +1105,7 @@ def _map_hotkey_to_neurons( async def _fetch_neuron_for_netuid( netuid: int, subtensor: SubtensorInterface -) -> tuple[int, dict[str, list[ScaleBytes]]]: +) -> tuple[int, Optional[str]]: """ Retrieves all neurons for a specified netuid @@ -1116,18 +1115,13 @@ async def _fetch_neuron_for_netuid( :return: the original netuid, and a mapping of the neurons to their NeuronInfoLite objects """ - async def neurons_lite_for_uid(uid: int) -> dict[Any, Any]: - call_definition = TYPE_REGISTRY["runtime_api"]["NeuronInfoRuntimeApi"][ - "methods" - ]["get_neurons_lite"] - data = await subtensor.encode_params( - call_definition=call_definition, params=[uid] - ) + async def neurons_lite_for_uid(uid: int) -> Optional[str]: block_hash = subtensor.substrate.last_block_hash - hex_bytes_result = await subtensor.substrate.rpc_request( - method="state_call", - params=["NeuronInfoRuntimeApi_get_neurons_lite", data, block_hash], - reuse_block_hash=True, + hex_bytes_result = await subtensor.query_runtime_api( + runtime_api="NeuronInfoRuntimeApi", + method="get_neurons_lite", + params=[uid], + block_hash=block_hash, ) return hex_bytes_result @@ -1138,7 +1132,7 @@ async def neurons_lite_for_uid(uid: int) -> dict[Any, Any]: async def _fetch_all_neurons( netuids: list[int], subtensor -) -> list[tuple[int, list[ScaleBytes]]]: +) -> list[tuple[int, Optional[str]]]: """Retrieves all neurons for each of the specified netuids""" return list( await asyncio.gather( @@ -1147,50 +1141,21 @@ async def _fetch_all_neurons( ) -def _partial_decode(args): - """ - Helper function for passing to ProcessPoolExecutor that decodes scale bytes based on a set return type and - rpc type registry, passing this back to the Executor with its specified netuid for easier mapping - - :param args: (return type, scale bytes object, custom rpc type registry, netuid) - - :return: (original netuid, decoded object) - """ - return_type, as_scale_bytes, custom_rpc_type_registry_, netuid_ = args - decoded = decode_scale_bytes(return_type, as_scale_bytes, custom_rpc_type_registry_) - if decoded.startswith("0x"): - bytes_result = bytes.fromhex(decoded[2:]) - else: - bytes_result = bytes.fromhex(decoded) - - return netuid_, NeuronInfoLite.list_from_vec_u8(bytes_result) - - def _process_neurons_for_netuids( - netuids_with_all_neurons_hex_bytes: list[tuple[int, list[ScaleBytes]]], + netuids_with_all_neurons_hex_bytes: list[tuple[int, Optional[str]]], ) -> list[tuple[int, list[NeuronInfoLite]]]: """ - Using multiprocessing to decode a list of hex-bytes neurons with their respective netuid + Decode a list of hex-bytes neurons with their respective netuid :param netuids_with_all_neurons_hex_bytes: netuids with hex-bytes neurons :return: netuids mapped to decoded neurons """ - - def make_map(res_): - netuid_, json_result = res_ - hex_bytes_result = json_result["result"] - as_scale_bytes = scalecodec.ScaleBytes(hex_bytes_result) - return [return_type, as_scale_bytes, custom_rpc_type_registry, netuid_] - - return_type = TYPE_REGISTRY["runtime_api"]["NeuronInfoRuntimeApi"]["methods"][ - "get_neurons_lite" - ]["type"] - - preprocessed = [make_map(r) for r in netuids_with_all_neurons_hex_bytes] - with ProcessPoolExecutor() as executor: - results = list(executor.map(_partial_decode, preprocessed)) - - all_results = [(netuid, result) for netuid, result in results] + all_results = [ + (netuid, NeuronInfoLite.list_from_vec_u8(bytes.fromhex(result[2:]))) + if result + else (netuid, []) + for netuid, result in netuids_with_all_neurons_hex_bytes + ] return all_results From 143bbad73c7c7223756bcd4c461156256a4c4a67 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Mon, 4 Nov 2024 09:54:52 -0800 Subject: [PATCH 12/20] Adds verification for json file used in regenerating --- bittensor_cli/cli.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 5008a03c5..f1b3b879e 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -331,6 +331,12 @@ def get_creation_data( json = prompt_answer elif mnemonic: mnemonic = parse_mnemonic(mnemonic) + + if json: + if not os.path.exists(json): + print_error(f"The JSON file '{json}' does not exist.") + raise typer.Exit() + if json and not json_password: json_password = Prompt.ask( "Enter the backup password for JSON file.", password=True From c29fdecc1848b90e92692704f8163c0176cb273f Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:55:51 -0800 Subject: [PATCH 13/20] Removes wallet path prompt (#205) * Removes prompts for wallet path * Improves colour * Tweak * Fixes subnet create requiring hotkey --- bittensor_cli/cli.py | 161 +++++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 84 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index f1b3b879e..3eae77c6d 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -1174,6 +1174,29 @@ def wallet_ask( :return: created Wallet object """ # Prompt for missing attributes specified in ask_for + + if wallet_path: + if wallet_path == "default": + wallet_path = defaults.wallet.path + + elif self.config.get("wallet_path"): + wallet_path = self.config.get("wallet_path") + console.print( + f"Using the wallet path from config:[bold magenta] {wallet_path}" + ) + + if WO.PATH in ask_for and not wallet_path: + wallet_path = Prompt.ask( + "Enter the [blue]wallet path[/blue]" + + " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-path`)[/dark_sea_green3 italic]", + default=defaults.wallet.path, + ) + if wallet_path: + wallet_path = os.path.expanduser(wallet_path) + else: + wallet_path = os.path.expanduser(defaults.wallet.path) + console.print(f"Using default wallet path: ({defaults.wallet.path})") + if WO.NAME in ask_for and not wallet_name: if self.config.get("wallet_name"): wallet_name = self.config.get("wallet_name") @@ -1181,13 +1204,9 @@ def wallet_ask( f"Using the wallet name from config:[bold cyan] {wallet_name}" ) else: - wallet_name = typer.prompt( - typer.style("Enter the wallet name", fg="blue") - + typer.style( - " (Hint: You can set this with `btcli config set --wallet-name`)", - fg="green", - italic=True, - ), + wallet_name = Prompt.ask( + "Enter the [blue]wallet name[/blue]" + + " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-name`)[/dark_sea_green3 italic]", default=defaults.wallet.name, ) @@ -1198,38 +1217,13 @@ def wallet_ask( f"Using the wallet hotkey from config:[bold cyan] {wallet_hotkey}" ) else: - wallet_hotkey = typer.prompt( - typer.style("Enter the wallet hotkey", fg="blue") - + typer.style( - " (Hint: You can set this with `btcli config set --wallet-hotkey`)", - fg="green", - italic=True, - ), + wallet_hotkey = Prompt.ask( + "Enter the [blue]wallet hotkey[/blue]" + + " [dark_sea_green3 italic](Hint: You can set this with `btcli config set --wallet-hotkey`)[/dark_sea_green3 italic]", default=defaults.wallet.hotkey, ) - if wallet_path: - if wallet_path == "default": - wallet_path = defaults.wallet.path - elif self.config.get("wallet_path"): - wallet_path = self.config.get("wallet_path") - console.print( - f"Using the wallet path from config:[bold magenta] {wallet_path}" - ) - - if WO.PATH in ask_for and not wallet_path: - wallet_path = typer.prompt( - typer.style("Enter the wallet path", fg="blue") - + typer.style( - " (Hint: You can set this with `btcli config set --wallet-path`)", - fg="green", - italic=True, - ), - default=defaults.wallet.path, - ) # Create the Wallet object - if wallet_path: - wallet_path = os.path.expanduser(wallet_path) wallet = Wallet(name=wallet_name, path=wallet_path, hotkey=wallet_hotkey) # Validate the wallet if required @@ -1385,7 +1379,7 @@ def wallet_overview( "Netuids must be a comma-separated list of ints, e.g., `--netuids 1,2,3,4`.", ) - ask_for = [WO.NAME, WO.PATH] if not all_wallets else [WO.PATH] + ask_for = [WO.NAME] if not all_wallets else [] validate = WV.WALLET if not all_wallets else WV.NONE wallet = self.wallet_ask( wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate @@ -1473,7 +1467,7 @@ def wallet_transfer( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH], + ask_for=[WO.NAME], validate=WV.WALLET, ) subtensor = self.initialize_chain(network) @@ -1530,7 +1524,7 @@ def wallet_swap_hotkey( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) if not destination_hotkey_name: @@ -1542,7 +1536,7 @@ def wallet_swap_hotkey( wallet_name, wallet_path, destination_hotkey_name, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) self.initialize_chain(network) @@ -1608,7 +1602,7 @@ def wallet_inspect( ) # if all-wallets is entered, ask for path - ask_for = [WO.NAME, WO.PATH] if not all_wallets else [WO.PATH] + ask_for = [WO.NAME] if not all_wallets else [] validate = WV.WALLET if not all_wallets else WV.NONE wallet = self.wallet_ask( wallet_name, wallet_path, wallet_hotkey, ask_for=ask_for, validate=validate @@ -1697,7 +1691,7 @@ def wallet_faucet( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH], + ask_for=[WO.NAME], validate=WV.WALLET, ) return self._run_command( @@ -2128,7 +2122,7 @@ def wallet_balance( else: raise typer.Exit() else: - ask_for = [WO.PATH] if all_balances else [WO.NAME, WO.PATH] + ask_for = [] if all_balances else [WO.NAME] validate = WV.NONE if all_balances else WV.WALLET wallet = self.wallet_ask( wallet_name, @@ -2174,7 +2168,7 @@ def wallet_history( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH], + ask_for=[WO.NAME], validate=WV.WALLET, ) return self._run_command(wallets.wallet_history(wallet)) @@ -2275,7 +2269,7 @@ def wallet_set_id( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.HOTKEY, WO.PATH, WO.NAME], + ask_for=[WO.HOTKEY, WO.NAME], validate=WV.WALLET_AND_HOTKEY, ) @@ -2430,7 +2424,7 @@ def wallet_sign( default=False, ) - ask_for = [WO.HOTKEY, WO.PATH, WO.NAME] if use_hotkey else [WO.NAME, WO.PATH] + ask_for = [WO.HOTKEY, WO.NAME] if use_hotkey else [WO.NAME] validate = WV.WALLET_AND_HOTKEY if use_hotkey else WV.WALLET wallet = self.wallet_ask( @@ -2533,7 +2527,7 @@ def root_set_weights( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.HOTKEY, WO.PATH, WO.NAME], + ask_for=[WO.HOTKEY, WO.NAME], validate=WV.WALLET_AND_HOTKEY, ) self._run_command( @@ -2624,7 +2618,7 @@ def root_boost( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( @@ -2665,7 +2659,7 @@ def root_slash( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( @@ -2715,7 +2709,7 @@ def root_senate_vote( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( @@ -2770,7 +2764,7 @@ def root_register( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( @@ -2839,7 +2833,7 @@ def root_set_take( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) @@ -2896,10 +2890,10 @@ def root_delegate_stake( if not stake_all and not amount: while True: amount = FloatPrompt.ask( - "[blue bold]Amount to stake (TAO ฯ„)[/blue bold]", console=console + "Amount to [blue]stake (TAO ฯ„)[/blue]", console=console ) confirmation = FloatPrompt.ask( - "[blue bold]Confirm the amount to stake (TAO ฯ„)[/blue bold]", + "Confirm the amount to stake [blue](TAO ฯ„)[/blue]", console=console, ) if amount == confirmation: @@ -2910,7 +2904,7 @@ def root_delegate_stake( ) wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) return self._run_command( root.delegate_stake( @@ -2966,10 +2960,10 @@ def root_undelegate_stake( if not unstake_all and not amount: while True: amount = FloatPrompt.ask( - "[blue bold]Amount to unstake (TAO ฯ„)[/blue bold]", console=console + "Amount to [blue]unstake (TAO ฯ„)[/blue]", console=console ) confirmation = FloatPrompt.ask( - "[blue bold]Confirm the amount to unstake (TAO ฯ„)[/blue bold]", + "Confirm the amount to unstake [blue](TAO ฯ„)[/blue]", console=console, ) if amount == confirmation: @@ -2980,7 +2974,7 @@ def root_undelegate_stake( ) wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) self._run_command( root.delegate_unstake( @@ -3052,7 +3046,7 @@ def root_my_delegates( wallet_name, wallet_path, wallet_hotkey, - ask_for=([WO.NAME, WO.PATH] if not all_wallets else [WO.PATH]), + ask_for=([WO.NAME] if not all_wallets else []), validate=WV.WALLET if not all_wallets else WV.NONE, ) self._run_command( @@ -3166,7 +3160,7 @@ def root_nominate( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( @@ -3231,12 +3225,12 @@ def stake_show( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.PATH], + ask_for=[], validate=WV.NONE, ) else: wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) return self._run_command( @@ -3318,7 +3312,7 @@ def stake_add( raise typer.Exit() if not stake_all and not amount and not max_stake: - amount = FloatPrompt.ask("[blue bold]Amount to stake (TAO ฯ„)[/blue bold]") + amount = FloatPrompt.ask("Amount to [blue]stake (TAO ฯ„)[/blue]") if stake_all and not amount: if not Confirm.ask("Stake all the available TAO tokens?", default=False): @@ -3349,7 +3343,7 @@ def stake_add( if is_valid_ss58_address(hotkey_or_ss58): hotkey_ss58_address = hotkey_or_ss58 wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) else: wallet_hotkey = hotkey_or_ss58 @@ -3357,20 +3351,20 @@ def stake_add( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.HOTKEY, WO.PATH], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) elif all_hotkeys or include_hotkeys or exclude_hotkeys or hotkey_ss58_address: wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) else: wallet = self.wallet_ask( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) @@ -3485,7 +3479,7 @@ def stake_remove( raise typer.Exit() if not unstake_all and not amount and not keep_stake: - amount = FloatPrompt.ask("[blue bold]Amount to unstake (TAO ฯ„)[/blue bold]") + amount = FloatPrompt.ask("Amount to [blue]unstake (TAO ฯ„)[/blue]") if unstake_all and not amount and prompt: if not Confirm.ask("Unstake all staked TAO tokens?", default=False): @@ -3503,7 +3497,7 @@ def stake_remove( if is_valid_ss58_address(hotkey_or_ss58): hotkey_ss58_address = hotkey_or_ss58 wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) else: wallet_hotkey = hotkey_or_ss58 @@ -3511,13 +3505,13 @@ def stake_remove( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) elif all_hotkeys or include_hotkeys or exclude_hotkeys or hotkey_ss58_address: wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) else: @@ -3525,7 +3519,7 @@ def stake_remove( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) @@ -3596,7 +3590,7 @@ def stake_get_children( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) @@ -3697,7 +3691,7 @@ def stake_set_children( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( @@ -3749,7 +3743,7 @@ def stake_revoke_children( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) if all_netuids and netuid: @@ -3823,7 +3817,7 @@ def stake_childkey_take( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) if all_netuids and netuid: @@ -3901,7 +3895,7 @@ def sudo_set( ) wallet = self.wallet_ask( - wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME, WO.PATH] + wallet_name, wallet_path, wallet_hotkey, ask_for=[WO.NAME] ) return self._run_command( sudo.sudo_set_hyperparameter( @@ -4002,7 +3996,6 @@ def subnets_create( self, wallet_name: str = Options.wallet_name, wallet_path: str = Options.wallet_path, - wallet_hotkey: str = Options.wallet_hotkey, network: Optional[list[str]] = Options.network, prompt: bool = Options.prompt, quiet: bool = Options.quiet, @@ -4019,9 +4012,9 @@ def subnets_create( wallet = self.wallet_ask( wallet_name, wallet_path, - wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], - validate=WV.WALLET_AND_HOTKEY, + None, + ask_for=[WO.NAME], + validate=WV.WALLET, ) return self._run_command( subnets.create(wallet, self.initialize_chain(network), prompt) @@ -4099,7 +4092,7 @@ def subnets_pow_register( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ), self.initialize_chain(network), @@ -4141,7 +4134,7 @@ def subnets_register( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( @@ -4328,7 +4321,7 @@ def weights_reveal( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) @@ -4424,7 +4417,7 @@ def weights_commit( wallet_name, wallet_path, wallet_hotkey, - ask_for=[WO.NAME, WO.PATH, WO.HOTKEY], + ask_for=[WO.NAME, WO.HOTKEY], validate=WV.WALLET_AND_HOTKEY, ) return self._run_command( From f5fb08d8b43fd6eceac2a88ebdb723915ad0d78d Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 4 Nov 2024 23:18:28 +0200 Subject: [PATCH 14/20] Allows for using hotkey names or ss58 addresses in include/exclude options for stake add and stake remove --- bittensor_cli/cli.py | 40 ++++++++++++++--------- bittensor_cli/src/commands/stake/stake.py | 2 ++ bittensor_cli/src/commands/wallets.py | 6 +++- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 3eae77c6d..e0609e039 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -3369,20 +3369,24 @@ def stake_add( ) if include_hotkeys: - include_hotkeys = parse_to_list( + included_hotkeys = parse_to_list( include_hotkeys, str, - "Hotkeys must be a comma-separated list of ss58s, e.g., `--include-hotkeys 5Grw....,5Grw....`.", - is_ss58=True, + "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., " + "`--include-hotkeys 5Grw....,5Grw....`.", ) + else: + included_hotkeys = [] if exclude_hotkeys: - exclude_hotkeys = parse_to_list( + excluded_hotkeys = parse_to_list( exclude_hotkeys, str, - "Hotkeys must be a comma-separated list of ss58s, e.g., `--exclude-hotkeys 5Grw....,5Grw....`.", - is_ss58=True, + "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., " + "`--exclude-hotkeys 5Grw....,5Grw....`.", ) + else: + excluded_hotkeys = [] return self._run_command( stake.stake_add( @@ -3391,8 +3395,8 @@ def stake_add( amount, stake_all, max_stake, - include_hotkeys, - exclude_hotkeys, + included_hotkeys, + excluded_hotkeys, all_hotkeys, prompt, hotkey_ss58_address, @@ -3524,20 +3528,24 @@ def stake_remove( ) if include_hotkeys: - include_hotkeys = parse_to_list( + included_hotkeys = parse_to_list( include_hotkeys, str, - "Hotkeys must be a comma-separated list of ss58s, e.g., `--include-hotkeys 5Grw....,5Grw....`.", - is_ss58=True, + "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., " + "`--include-hotkeys 5Grw....,5Grw....`.", ) + else: + included_hotkeys = [] if exclude_hotkeys: - exclude_hotkeys = parse_to_list( + excluded_hotkeys = parse_to_list( exclude_hotkeys, str, - "Hotkeys must be a comma-separated list of ss58s, e.g., `--exclude-hotkeys 5Grw....,5Grw....`.", - is_ss58=True, + "Hotkeys must be a comma-separated list of ss58s or hotkey names, e.g., " + "`--exclude-hotkeys 5Grw....,5Grw....`.", ) + else: + excluded_hotkeys = [] return self._run_command( stake.unstake( @@ -3545,8 +3553,8 @@ def stake_remove( self.initialize_chain(network), hotkey_ss58_address, all_hotkeys, - include_hotkeys, - exclude_hotkeys, + included_hotkeys, + excluded_hotkeys, amount, keep_stake, unstake_all, diff --git a/bittensor_cli/src/commands/stake/stake.py b/bittensor_cli/src/commands/stake/stake.py index 819f02cca..144e40f84 100644 --- a/bittensor_cli/src/commands/stake/stake.py +++ b/bittensor_cli/src/commands/stake/stake.py @@ -1182,6 +1182,7 @@ async def is_hotkey_registered_any(hk: str, bh: str) -> bool: (wallet.hotkey_str, wallet.hotkey.ss58_address) for wallet in all_hotkeys_ if wallet.hotkey_str not in exclude_hotkeys + and wallet.hotkey.ss58_address not in exclude_hotkeys ] # definitely wallets elif include_hotkeys: @@ -1349,6 +1350,7 @@ async def unstake( (wallet.hotkey_str, wallet.hotkey.ss58_address) for wallet in all_hotkeys_ if wallet.hotkey_str not in exclude_hotkeys + and wallet.hotkey.ss58_address not in hotkeys_to_unstake_from ] # definitely wallets elif include_hotkeys: diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index f4460e272..ff8e981ff 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -723,7 +723,11 @@ async def overview( de_registered_neurons.append(de_registered_neuron) # Add this hotkey to the wallets dict - wallet_ = WalletLike(name=wallet.name, hotkey_ss58=hotkey_addr, hotkey_str=hotkey_addr[:5]) + wallet_ = WalletLike( + name=wallet.name, + hotkey_ss58=hotkey_addr, + hotkey_str=hotkey_addr[:5], + ) # Indicates a hotkey not on local machine but exists in stake_info obj on-chain if hotkey_coldkey_to_hotkey_wallet.get(hotkey_addr) is None: hotkey_coldkey_to_hotkey_wallet[hotkey_addr] = {} From 78ec7f8d6dc5c7302a0f26665f99251c5cd45456 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Mon, 4 Nov 2024 16:50:40 -0800 Subject: [PATCH 15/20] Bumps version and adds changelog --- CHANGELOG.md | 17 +++++++++++++++++ bittensor_cli/__init__.py | 2 +- bittensor_cli/cli.py | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43ae097df..f8206b593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 8.3.0 /2024-11-06 + +## What's Changed + +* Better handle incorrect password by @thewhaleking in https://github.com/opentensor/btcli/pull/187 +* Fixes success path of pow register by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/189 +* Adds `--all` flag to transfer by @thewhaleking in https://github.com/opentensor/btcli/pull/181 +* Various fixes by @thewhaleking in https://github.com/opentensor/btcli/pull/199 +* Fix wallets in overview by @thewhaleking in https://github.com/opentensor/btcli/pull/197 +* fix handling null neurons by @thewhaleking in https://github.com/opentensor/btcli/pull/214 +* Fix cuda pow registration by @thewhaleking in https://github.com/opentensor/btcli/pull/215 +* Adds confirmation after each successful regen by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/203 +* Removes wallet path prompt by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/205 +* Support hotkey names for include/exclude in st add/remove by @thewhaleking in https://github.com/opentensor/btcli/pull/216 + +**Full Changelog**: https://github.com/opentensor/btcli/compare/v8.2.0...v8.3.0 + ## 8.2.0 /2024-10-10 ## What's Changed diff --git a/bittensor_cli/__init__.py b/bittensor_cli/__init__.py index 8fff761ce..6eaa6edc4 100644 --- a/bittensor_cli/__init__.py +++ b/bittensor_cli/__init__.py @@ -18,6 +18,6 @@ from .cli import CLIManager -__version__ = "8.2.0" +__version__ = "8.3.0" __all__ = ["CLIManager", "__version__"] diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index e0609e039..db87ca6b8 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -57,7 +57,7 @@ class GitError(Exception): pass -__version__ = "8.2.0" +__version__ = "8.3.0" _core_version = re.match(r"^\d+\.\d+\.\d+", __version__).group(0) From 2d6b4dd1493d328be0419b6974dffd8074e0b6e1 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 5 Nov 2024 19:30:32 +0200 Subject: [PATCH 16/20] Adds subvortex network --- bittensor_cli/src/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor_cli/src/__init__.py b/bittensor_cli/src/__init__.py index a5a87e16d..04b708c17 100644 --- a/bittensor_cli/src/__init__.py +++ b/bittensor_cli/src/__init__.py @@ -4,16 +4,18 @@ class Constants: - networks = ["local", "finney", "test", "archive"] + networks = ["local", "finney", "test", "archive", "subvortex"] finney_entrypoint = "wss://entrypoint-finney.opentensor.ai:443" finney_test_entrypoint = "wss://test.finney.opentensor.ai:443" archive_entrypoint = "wss://archive.chain.opentensor.ai:443" + subvortex_entrypoint = "ws://subvortex.info:9944" local_entrypoint = "ws://127.0.0.1:9444" network_map = { "finney": finney_entrypoint, "test": finney_test_entrypoint, "archive": archive_entrypoint, "local": local_entrypoint, + "subvortex": subvortex_entrypoint, } delegates_detail_url = "https://raw.githubusercontent.com/opentensor/bittensor-delegates/main/public/delegates.json" From 67225ba4416109fcbde109ae489eb2516b358fe7 Mon Sep 17 00:00:00 2001 From: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> Date: Wed, 6 Nov 2024 23:49:58 +0200 Subject: [PATCH 17/20] Add prompt option to all commands which use Confirm prompts (#227) * Added all prompt options. --- bittensor_cli/cli.py | 15 ++++++++++++++- .../src/commands/stake/children_hotkeys.py | 12 +++++++----- bittensor_cli/src/commands/subnets.py | 3 ++- bittensor_cli/src/commands/wallets.py | 3 ++- bittensor_cli/src/commands/weights.py | 6 ++++-- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index e0609e039..87c11998e 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -1668,6 +1668,7 @@ def wallet_faucet( "--max-successes", help="Set the maximum number of times to successfully run the faucet for this command.", ), + prompt: bool = Options.prompt, ): """ Obtain test TAO tokens by performing Proof of Work (PoW). @@ -1706,6 +1707,7 @@ def wallet_faucet( output_in_place, verbose, max_successes, + prompt, ) ) @@ -2420,7 +2422,8 @@ def wallet_sign( if use_hotkey is None: use_hotkey = Confirm.ask( "Would you like to sign the transaction using your [red]hotkey[/red]?" - "\n[Type [red]y[/red] for [red]hotkey[/red] and [blue]n[/blue] for [blue]coldkey[/blue]] (default is [blue]coldkey[/blue])", + "\n[Type [red]y[/red] for [red]hotkey[/red] and [blue]n[/blue] for [blue]coldkey[/blue]] " + "(default is [blue]coldkey[/blue])", default=False, ) @@ -3653,6 +3656,7 @@ def stake_set_children( wait_for_finalization: bool = Options.wait_for_finalization, quiet: bool = Options.quiet, verbose: bool = Options.verbose, + prompt: bool = Options.prompt, ): """ Set child hotkeys on specified subnets. @@ -3711,6 +3715,7 @@ def stake_set_children( proportions=proportions, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + prompt=prompt, ) ) @@ -3736,6 +3741,7 @@ def stake_revoke_children( wait_for_finalization: bool = Options.wait_for_finalization, quiet: bool = Options.quiet, verbose: bool = Options.verbose, + prompt: bool = Options.prompt, ): """ Remove all children hotkeys on a specified subnet. @@ -3770,6 +3776,7 @@ def stake_revoke_children( netuid, wait_for_inclusion, wait_for_finalization, + prompt=prompt, ) ) @@ -4075,6 +4082,7 @@ def subnets_pow_register( "-tbp", help="Set the number of threads per block for CUDA.", ), + prompt: bool = Options.prompt, ): """ Register a neuron (a subnet validator or a subnet miner) using Proof of Work (POW). @@ -4112,6 +4120,7 @@ def subnets_pow_register( use_cuda, dev_id, threads_per_block, + prompt=prompt, ) ) @@ -4271,6 +4280,7 @@ def weights_reveal( ), quiet: bool = Options.quiet, verbose: bool = Options.verbose, + prompt: bool = Options.prompt, ): """ Reveal weights for a specific subnet. @@ -4342,6 +4352,7 @@ def weights_reveal( weights, salt, __version_as_int__, + prompt=prompt, ) ) @@ -4367,6 +4378,7 @@ def weights_commit( ), quiet: bool = Options.quiet, verbose: bool = Options.verbose, + prompt: bool = Options.prompt, ): """ @@ -4437,6 +4449,7 @@ def weights_commit( weights, salt, __version_as_int__, + prompt=prompt, ) ) diff --git a/bittensor_cli/src/commands/stake/children_hotkeys.py b/bittensor_cli/src/commands/stake/children_hotkeys.py index 074722403..97f8e5fc9 100644 --- a/bittensor_cli/src/commands/stake/children_hotkeys.py +++ b/bittensor_cli/src/commands/stake/children_hotkeys.py @@ -494,6 +494,7 @@ async def set_children( netuid: Optional[int] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + prompt: bool = True, ): """Set children hotkeys.""" # Validate children SS58 addresses @@ -520,7 +521,7 @@ async def set_children( netuid=netuid, hotkey=wallet.hotkey.ss58_address, children_with_proportions=children_with_proportions, - prompt=True, + prompt=prompt, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) @@ -549,7 +550,7 @@ async def set_children( netuid=netuid, hotkey=wallet.hotkey.ss58_address, children_with_proportions=children_with_proportions, - prompt=False, + prompt=prompt, wait_for_inclusion=True, wait_for_finalization=False, ) @@ -564,6 +565,7 @@ async def revoke_children( netuid: Optional[int] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + prompt: bool = True, ): """ Revokes the children hotkeys associated with a given network identifier (netuid). @@ -575,7 +577,7 @@ async def revoke_children( netuid=netuid, hotkey=wallet.hotkey.ss58_address, children_with_proportions=[], - prompt=True, + prompt=prompt, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) @@ -604,7 +606,7 @@ async def revoke_children( netuid=netuid, hotkey=wallet.hotkey.ss58_address, children_with_proportions=[], - prompt=False, + prompt=prompt, wait_for_inclusion=True, wait_for_finalization=False, ) @@ -764,7 +766,7 @@ async def set_chk_take_subnet(subnet, chk_take): netuid=netuid, hotkey=wallet.hotkey.ss58_address, take=take, - prompt=False, + prompt=prompt, wait_for_inclusion=True, wait_for_finalization=False, ) diff --git a/bittensor_cli/src/commands/subnets.py b/bittensor_cli/src/commands/subnets.py index 3e9349827..dba96bfd2 100644 --- a/bittensor_cli/src/commands/subnets.py +++ b/bittensor_cli/src/commands/subnets.py @@ -400,6 +400,7 @@ async def pow_register( use_cuda, dev_id, threads_per_block, + prompt: bool, ): """Register neuron.""" @@ -407,7 +408,7 @@ async def pow_register( subtensor, wallet=wallet, netuid=netuid, - prompt=True, + prompt=prompt, tpb=threads_per_block, update_interval=update_interval, num_processes=processors, diff --git a/bittensor_cli/src/commands/wallets.py b/bittensor_cli/src/commands/wallets.py index ff8e981ff..acce3b1a4 100644 --- a/bittensor_cli/src/commands/wallets.py +++ b/bittensor_cli/src/commands/wallets.py @@ -1413,13 +1413,14 @@ async def faucet( output_in_place: bool, log_verbose: bool, max_successes: int = 3, + prompt: bool = True, ): # TODO: - work out prompts to be passed through the cli success = await run_faucet_extrinsic( subtensor, wallet, tpb=threads_per_block, - prompt=False, + prompt=prompt, update_interval=update_interval, num_processes=processes, cuda=use_cuda, diff --git a/bittensor_cli/src/commands/weights.py b/bittensor_cli/src/commands/weights.py index cc5a7d379..33988cf94 100644 --- a/bittensor_cli/src/commands/weights.py +++ b/bittensor_cli/src/commands/weights.py @@ -378,6 +378,7 @@ async def reveal_weights( weights: list[float], salt: list[int], version: int, + prompt: bool = True, ) -> None: """Reveal weights for a specific subnet.""" uids_ = np.array( @@ -397,7 +398,7 @@ async def reveal_weights( ) # Call the reveal function in the module set_weights from extrinsics package extrinsic = SetWeightsExtrinsic( - subtensor, wallet, netuid, uids_, weights_, list(salt_), version + subtensor, wallet, netuid, uids_, weights_, list(salt_), version, prompt=prompt ) success, message = await extrinsic.reveal(weight_uids, weight_vals) @@ -415,6 +416,7 @@ async def commit_weights( weights: list[float], salt: list[int], version: int, + prompt: bool = True, ): """Commits weights and then reveals them for a specific subnet""" uids_ = np.array( @@ -430,7 +432,7 @@ async def commit_weights( dtype=np.int64, ) extrinsic = SetWeightsExtrinsic( - subtensor, wallet, netuid, uids_, weights_, list(salt_), version + subtensor, wallet, netuid, uids_, weights_, list(salt_), version, prompt=prompt ) success, message = await extrinsic.set_weights_extrinsic() if success: From 458269a4b84c41301db712620fe395ad7652f619 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Wed, 6 Nov 2024 13:54:05 -0800 Subject: [PATCH 18/20] Updates changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8206b593..a8b0c3be6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * Adds confirmation after each successful regen by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/203 * Removes wallet path prompt by @ibraheem-opentensor in https://github.com/opentensor/btcli/pull/205 * Support hotkey names for include/exclude in st add/remove by @thewhaleking in https://github.com/opentensor/btcli/pull/216 +* Subvortex network added by @thewhaleking in https://github.com/opentensor/btcli/pull/223 +* Add prompt option to all commands which use Confirm prompts by @thewhaleking in https://github.com/opentensor/btcli/pull/227 **Full Changelog**: https://github.com/opentensor/btcli/compare/v8.2.0...v8.3.0 From e82c1ad5f3a0ec8be77f09bc0532a56b50f75f26 Mon Sep 17 00:00:00 2001 From: distributedstatemachine Date: Thu, 7 Nov 2024 02:30:19 +0400 Subject: [PATCH 19/20] fix: local subtensor port --- bittensor_cli/src/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor_cli/src/__init__.py b/bittensor_cli/src/__init__.py index a5a87e16d..81b784ad1 100644 --- a/bittensor_cli/src/__init__.py +++ b/bittensor_cli/src/__init__.py @@ -8,7 +8,7 @@ class Constants: finney_entrypoint = "wss://entrypoint-finney.opentensor.ai:443" finney_test_entrypoint = "wss://test.finney.opentensor.ai:443" archive_entrypoint = "wss://archive.chain.opentensor.ai:443" - local_entrypoint = "ws://127.0.0.1:9444" + local_entrypoint = "ws://127.0.0.1:9944" network_map = { "finney": finney_entrypoint, "test": finney_test_entrypoint, From 1cd1f8ab0b90ba4c7bba505ac64114c0351ff9de Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Wed, 6 Nov 2024 14:58:34 -0800 Subject: [PATCH 20/20] Updates changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8b0c3be6..e3eeac9b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Support hotkey names for include/exclude in st add/remove by @thewhaleking in https://github.com/opentensor/btcli/pull/216 * Subvortex network added by @thewhaleking in https://github.com/opentensor/btcli/pull/223 * Add prompt option to all commands which use Confirm prompts by @thewhaleking in https://github.com/opentensor/btcli/pull/227 +* Update local subtensor port by @distributedstatemachine in https://github.com/opentensor/btcli/pull/228 **Full Changelog**: https://github.com/opentensor/btcli/compare/v8.2.0...v8.3.0