From e9492d268ae0dcf4ffafd1dbc21bbaa17aedba06 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 17 Sep 2024 16:59:37 +0200 Subject: [PATCH] Handle custom errors from subtensor --- .../src/bittensor/extrinsics/registration.py | 8 ++- .../src/bittensor/subtensor_interface.py | 4 +- bittensor_cli/src/bittensor/utils.py | 64 ++++++++++++++++--- bittensor_cli/src/commands/subnets.py | 2 +- bittensor_cli/src/commands/weights.py | 8 ++- 5 files changed, 70 insertions(+), 16 deletions(-) diff --git a/bittensor_cli/src/bittensor/extrinsics/registration.py b/bittensor_cli/src/bittensor/extrinsics/registration.py index 5bf66cbb..3bcd6c17 100644 --- a/bittensor_cli/src/bittensor/extrinsics/registration.py +++ b/bittensor_cli/src/bittensor/extrinsics/registration.py @@ -615,7 +615,10 @@ async def get_neuron_for_pubkey_and_subnet(): if not await response.is_success: success, err_msg = ( False, - format_error_message(await response.error_message), + format_error_message( + await response.error_message, + substrate=subtensor.substrate, + ), ) if not success: @@ -786,7 +789,8 @@ async def run_faucet_extrinsic( await response.process_events() if not await response.is_success: err_console.print( - f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message)}" + f":cross_mark: [red]Failed[/red]: " + f"{format_error_message(await response.error_message, subtensor.substrate)}" ) if attempts == max_allowed_attempts: raise MaxAttemptsException diff --git a/bittensor_cli/src/bittensor/subtensor_interface.py b/bittensor_cli/src/bittensor/subtensor_interface.py index 85c3e322..e839707d 100644 --- a/bittensor_cli/src/bittensor/subtensor_interface.py +++ b/bittensor_cli/src/bittensor/subtensor_interface.py @@ -883,7 +883,9 @@ async def sign_and_send_extrinsic( if await response.is_success: return True, "" else: - return False, format_error_message(await response.error_message) + return False, format_error_message( + await response.error_message, substrate=self.substrate + ) except SubstrateRequestException as e: return False, e diff --git a/bittensor_cli/src/bittensor/utils.py b/bittensor_cli/src/bittensor/utils.py index 7ddf8733..8d1a58e6 100644 --- a/bittensor_cli/src/bittensor/utils.py +++ b/bittensor_cli/src/bittensor/utils.py @@ -20,6 +20,9 @@ if TYPE_CHECKING: from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters + from bittensor_cli.src.bittensor.async_substrate_interface import ( + AsyncSubstrateInterface, + ) console = Console() err_console = Console(stderr=True) @@ -442,24 +445,65 @@ def get_explorer_url_for_network( return explorer_urls -def format_error_message(error_message: dict) -> str: +def format_error_message( + error_message: dict, substrate: "AsyncSubstrateInterface" +) -> str: """ - Formats an error message from the Subtensor error information to using in extrinsics. + Formats an error message from the Subtensor error information for use in extrinsics. - :param error_message: A dictionary containing the error information from Subtensor. + Args: + error_message: A dictionary containing the error information from Subtensor. + substrate: The substrate interface to use. - :return: A formatted error message string. + Returns: + str: A formatted error message string. """ - err_type = "UnknownType" err_name = "UnknownError" + err_type = "UnknownType" err_description = "Unknown Description" if isinstance(error_message, dict): - err_type = error_message.get("type", err_type) - err_name = error_message.get("name", err_name) - err_docs = error_message.get("docs", []) - err_description = err_docs[0] if len(err_docs) > 0 else err_description - return f"Subtensor returned `{err_name} ({err_type})` error. This means: `{err_description}`" + # subtensor error structure + if ( + error_message.get("code") + and error_message.get("message") + and error_message.get("data") + ): + err_name = "SubstrateRequestException" + err_type = error_message.get("message", "") + err_data = error_message.get("data", "") + + # subtensor custom error marker + if err_data.startswith("Custom error:") and substrate: + if substrate.metadata: + try: + pallet = substrate.metadata.get_metadata_pallet( + "SubtensorModule" + ) + error_index = int(err_data.split("Custom error:")[-1]) + + error_dict = pallet.errors[error_index].value + err_type = error_dict.get("message", err_type) + err_docs = error_dict.get("docs", []) + err_description = err_docs[0] if err_docs else err_description + except AttributeError: + err_console.print( + "Substrate pallets data unavailable. This is usually caused by an uninitialized substrate." + ) + else: + err_description = err_data + + elif ( + error_message.get("type") + and error_message.get("name") + and error_message.get("docs") + ): + err_type = error_message.get("type", err_type) + err_name = error_message.get("name", err_name) + 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}`." def convert_blocks_to_time(blocks: int, block_time: int = 12) -> tuple[int, int, int]: diff --git a/bittensor_cli/src/commands/subnets.py b/bittensor_cli/src/commands/subnets.py index 8ae45ace..6ef1aa1e 100644 --- a/bittensor_cli/src/commands/subnets.py +++ b/bittensor_cli/src/commands/subnets.py @@ -129,7 +129,7 @@ async def _find_event_attributes_in_extrinsic_receipt( await response.process_events() if not await response.is_success: err_console.print( - f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message)}" + f":cross_mark: [red]Failed[/red]: {format_error_message(await response.error_message, substrate)}" ) await asyncio.sleep(0.5) return False diff --git a/bittensor_cli/src/commands/weights.py b/bittensor_cli/src/commands/weights.py index 7dbb18a6..66c063b2 100644 --- a/bittensor_cli/src/commands/weights.py +++ b/bittensor_cli/src/commands/weights.py @@ -266,7 +266,9 @@ async def _do_set_weights(): if await response.is_success: return True, "Successfully set weights." else: - return False, format_error_message(await response.error_message) + return False, format_error_message( + await response.error_message, self.subtensor.substrate + ) with console.status( f":satellite: Setting weights on [white]{self.subtensor.network}[/white] ..." @@ -327,7 +329,9 @@ async def reveal_weights_extrinsic( else: success, error_message = ( False, - format_error_message(await response.error_message), + format_error_message( + await response.error_message, self.subtensor.substrate + ), ) if success: