diff --git a/.github/workflows/_run-e2e-single.yaml b/.github/workflows/_run-e2e-single.yaml index 60bf153065..29b09650da 100644 --- a/.github/workflows/_run-e2e-single.yaml +++ b/.github/workflows/_run-e2e-single.yaml @@ -41,8 +41,11 @@ jobs: path: | ~/.cache/uv .venv - key: uv-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} - restore-keys: uv-${{ runner.os }}-py${{ matrix.python-version }}- + key: | + uv-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} + restore-keys: | + uv-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} + uv-${{ runner.os }}-${{ runner.arch }}-py${{ matrix.python-version }}- - name: Install dependencies run: uv sync --extra dev --dev diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 14f91c1854..c344013713 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -65,7 +65,7 @@ from bittensor.core.extrinsics.asyncex.root import root_register_extrinsic from bittensor.core.extrinsics.asyncex.serving import ( get_last_bonds_reset, - publish_metadata, + publish_metadata_extrinsic, get_metadata, ) from bittensor.core.extrinsics.asyncex.serving import serve_axon_extrinsic @@ -91,7 +91,7 @@ ) from bittensor.core.metagraph import AsyncMetagraph from bittensor.core.settings import version_as_int, TYPE_REGISTRY -from bittensor.core.types import ParamWithTypes, SubtensorMixin +from bittensor.core.types import ExtrinsicResponse, ParamWithTypes, SubtensorMixin from bittensor.utils import ( Certificate, decode_hex_identity_dict, @@ -3793,60 +3793,6 @@ async def recycle( ) return None if call is None else Balance.from_rao(int(call)) - async def set_reveal_commitment( - self, - wallet, - netuid: int, - data: str, - blocks_until_reveal: int = 360, - block_time: Union[int, float] = 12, - period: Optional[int] = None, - raise_error: bool = False, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = True, - ) -> tuple[bool, int]: - """ - Commits arbitrary data to the Bittensor network by publishing metadata. - - Parameters: - wallet: The wallet associated with the neuron committing the data. - netuid: The unique identifier of the subnetwork. - data: The data to be committed to the network. - blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of - blocks in one epoch. - block_time: The number of seconds between each block. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. - raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. - wait_for_inclusion: Whether to wait for the inclusion of the transaction. - wait_for_finalization: Whether to wait for the finalization of the transaction. - - Returns: - bool: `True` if the commitment was successful, `False` otherwise. - - Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically. - """ - - encrypted, reveal_round = get_encrypted_commitment( - data, blocks_until_reveal, block_time - ) - - # increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed - # and stored. - data_ = {"encrypted": encrypted, "reveal_round": reveal_round} - return await publish_metadata( - subtensor=self, - wallet=wallet, - netuid=netuid, - data_type="TimelockEncrypted", - data=data_, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ), reveal_round - async def subnet( self, netuid: int, @@ -4202,30 +4148,32 @@ async def sign_and_send_extrinsic( period: Optional[int] = None, nonce_key: str = "hotkey", raise_error: bool = False, - ) -> tuple[bool, str]: + calling_function: Optional[str] = None, + ) -> ExtrinsicResponse: """ Helper method to sign and submit an extrinsic call to chain. - Arguments: + Parameters: call: a prepared Call object wallet: the wallet whose coldkey will be used to sign the extrinsic wait_for_inclusion: whether to wait until the extrinsic call is included on the chain wait_for_finalization: whether to wait until the extrinsic call is finalized on the chain sign_with: the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" use_nonce: unique identifier for the transaction related with hot/coldkey. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". - nonce_key: the type on nonce to use. Options are "hotkey", "coldkey", or "coldkeypub". - raise_error: raises a relevant exception rather than returning ``False`` if unsuccessful. + raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + calling_function: the name of the calling function. Returns: - (success, error message) + ExtrinsicResponse: The result object of the extrinsic execution. Raises: SubstrateRequestException: Substrate request exception. """ + extrinsic_response = ExtrinsicResponse(extrinsic_function=calling_function) possible_keys = ("coldkey", "hotkey", "coldkeypub") if sign_with not in possible_keys: raise AttributeError( @@ -4245,32 +4193,43 @@ async def sign_and_send_extrinsic( if period is not None: extrinsic_data["era"] = {"period": period} - extrinsic = await self.substrate.create_signed_extrinsic(**extrinsic_data) + extrinsic_response.extrinsic = await self.substrate.create_signed_extrinsic( + **extrinsic_data + ) try: response = await self.substrate.submit_extrinsic( - extrinsic, + extrinsic=extrinsic_response.extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - message = "Not waiting for finalization or inclusion." - logging.debug(f"{message}. Extrinsic: {extrinsic}") - return True, message + extrinsic_response.message = ( + "Not waiting for finalization or inclusion." + ) + return extrinsic_response if await response.is_success: - return True, "" + extrinsic_response.message = "Success" + return extrinsic_response - if raise_error: - raise ChainError.from_error(await response.error_message) + response_error_message = await response.error_message - return False, format_error_message(await response.error_message) + if raise_error: + raise ChainError.from_error(response_error_message) + extrinsic_response.success = False + extrinsic_response.message = format_error_message(response_error_message) + extrinsic_response.error = response_error_message + return extrinsic_response - except SubstrateRequestException as e: + except SubstrateRequestException as error: if raise_error: raise - return False, format_error_message(e) + extrinsic_response.success = False + extrinsic_response.message = format_error_message(error) + extrinsic_response.error = error + return extrinsic_response # Extrinsics ======================================================================================================= @@ -4287,7 +4246,7 @@ async def add_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. Staking is a fundamental process in the Bittensor network that enables neurons to participate actively @@ -4313,7 +4272,7 @@ async def add_stake( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: ``True`` if the staking is successful, ``False`` otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function enables neurons to increase their stake in the network, enhancing their influence and potential. When safe_staking is enabled, it provides protection against price fluctuations during the time stake is @@ -4347,7 +4306,7 @@ async def add_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -4366,9 +4325,7 @@ async def add_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call ``toggle_user_liquidity`` method to enable/disable user liquidity. @@ -4397,7 +4354,7 @@ async def add_stake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. This bulk operation allows for efficient staking across different neurons from a single wallet. @@ -4415,7 +4372,7 @@ async def add_stake_multiple( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the staking is successful for all specified neurons, ``False`` otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function is essential for managing stakes across multiple neurons, reflecting the dynamic and collaborative nature of the Bittensor network. @@ -4440,7 +4397,7 @@ async def burned_register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling TAO tokens, allowing them to be re-mined by performing work on the network. @@ -4456,7 +4413,7 @@ async def burned_register( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the registration is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ async with self: if netuid == 0: @@ -4492,7 +4449,7 @@ async def commit_weights( raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Commits a hash of the subnet validator's weight vector to the Bittensor blockchain using the provided wallet. This action serves as a commitment or snapshot of the validator's current weight distribution. @@ -4513,9 +4470,7 @@ async def commit_weights( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, False otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. This function allows subnet validators to create a tamper-proof record of their weight vector at a specific point in time, creating a foundation of transparency and accountability for the Bittensor network. @@ -4524,8 +4479,9 @@ async def commit_weights( See also: , """ retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to commit weights!" + response = ExtrinsicResponse( + False, "No attempt made. Perhaps it is too soon to commit weights!" + ) logging.info( f"Committing weights with params: " @@ -4543,9 +4499,9 @@ async def commit_weights( version_key=version_key, ) - while retries < max_retries and success is False: + while retries < max_retries and response.success is False: try: - success, message = await commit_weights_extrinsic( + response = await commit_weights_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -4555,13 +4511,12 @@ async def commit_weights( period=period, raise_error=raise_error, ) - if success: - break - except Exception as e: - logging.error(f"Error committing weights: {e}") - retries += 1 + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error committing weights: {error}") + retries += 1 - return success, message + return response async def modify_liquidity( self, @@ -4574,7 +4529,7 @@ async def modify_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. Parameters: @@ -4591,9 +4546,7 @@ async def modify_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Example: import bittensor as bt @@ -4650,7 +4603,7 @@ async def move_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Moves stake to a different hotkey and/or subnet. @@ -4670,7 +4623,7 @@ async def move_stake( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - success: True if the stake movement was successful. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount = check_and_convert_to_balance(amount) return await move_stake_extrinsic( @@ -4704,7 +4657,7 @@ async def register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ): + ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. @@ -4731,7 +4684,7 @@ async def register( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: ``True`` if the registration is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function facilitates the entry of new neurons into the network, supporting the decentralized growth and scalability of the Bittensor ecosystem. @@ -4761,7 +4714,7 @@ async def register_subnet( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor network. @@ -4775,7 +4728,7 @@ async def register_subnet( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ return await register_subnet_extrinsic( subtensor=self, @@ -4796,7 +4749,7 @@ async def remove_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. Parameters: @@ -4812,9 +4765,7 @@ async def remove_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: - Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` @@ -4846,7 +4797,7 @@ async def reveal_weights( raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Reveals the weight vector for a specific subnet on the Bittensor blockchain using the provided wallet. This action serves as a revelation of the subnet validator's previously committed weight distribution as part @@ -4868,21 +4819,20 @@ async def reveal_weights( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. This function allows subnet validators to reveal their previously committed weight vector. See also: , """ retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to reveal weights!" + response = ExtrinsicResponse( + False, "No attempt made. Perhaps it is too soon to reveal weights!" + ) - while retries < max_retries and success is False: + while retries < max_retries and response.success is False: try: - success, message = await reveal_weights_extrinsic( + response = await reveal_weights_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -4895,80 +4845,76 @@ async def reveal_weights( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - if success: - break - except Exception as e: - logging.error(f"Error revealing weights: {e}") - retries += 1 + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error revealing weights: {error}") + retries += 1 - return success, message + return response - async def root_set_pending_childkey_cooldown( + async def root_register( self, wallet: "Wallet", - cooldown: int, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: - """Sets the pending childkey cooldown. + ) -> ExtrinsicResponse: + """ + Register neuron by recycling some TAO. Parameters: - wallet: bittensor wallet instance. - cooldown: the number of blocks to setting pending childkey cooldown. - period: The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + wallet: The wallet associated with the neuron to be registered. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. - - Note: This operation can only be successfully performed if your wallet has root privileges. + ExtrinsicResponse: The result object of the extrinsic execution. """ - return await root_set_pending_childkey_cooldown_extrinsic( + + return await root_register_extrinsic( subtensor=self, wallet=wallet, - cooldown=cooldown, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - # TODO: remove `block_hash` argument - async def root_register( + async def root_set_pending_childkey_cooldown( self, wallet: "Wallet", + cooldown: int, period: Optional[int] = None, raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: - """ - Register neuron by recycling some TAO. + ) -> ExtrinsicResponse: + """Sets the pending childkey cooldown. Parameters: - wallet: The wallet associated with the neuron to be registered. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. + wallet: bittensor wallet instance. + cooldown: the number of blocks to setting pending childkey cooldown. + period: The number of blocks during which the transaction will remain valid after it's + submitted. If the transaction is not included in a block within that number of blocks, it will expire + and be rejected. You can think of it as an expiration date for the transaction. raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. wait_for_inclusion: Waits for the transaction to be included in a block. wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the registration is successful, False otherwise. - """ + ExtrinsicResponse: The result object of the extrinsic execution. - return await root_register_extrinsic( + Note: This operation can only be successfully performed if your wallet has root privileges. + """ + return await root_set_pending_childkey_cooldown_extrinsic( subtensor=self, wallet=wallet, + cooldown=cooldown, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, @@ -4985,7 +4931,7 @@ async def set_children( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Allows a coldkey to set children-keys. @@ -5002,9 +4948,7 @@ async def set_children( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: DuplicateChild: There are duplicates in the list of children. @@ -5040,7 +4984,7 @@ async def set_delegate_take( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. @@ -5057,9 +5001,7 @@ async def set_delegate_take( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: DelegateTakeTooHigh: Delegate take is too high. @@ -5081,8 +5023,9 @@ async def set_delegate_take( current_take_u16 = int(current_take * 0xFFFF) if current_take_u16 == take_u16: - logging.info(":white_heavy_check_mark: [green]Already Set[/green]") - return True, "" + message = f"The take for {hotkey_ss58} is already set to {take}" + logging.info(f":white_heavy_check_mark: [green]{message}[/green].") + return ExtrinsicResponse(True, message) logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") @@ -5092,7 +5035,7 @@ async def set_delegate_take( else decrease_take_extrinsic ) - success, message = await extrinsic_call( + response = await extrinsic_call( subtensor=self, wallet=wallet, hotkey_ss58=hotkey_ss58, @@ -5103,10 +5046,10 @@ async def set_delegate_take( wait_for_inclusion=wait_for_inclusion, ) - if success: + if response.success: logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") - return success, message + return response async def set_subnet_identity( self, @@ -5117,7 +5060,7 @@ async def set_subnet_identity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Sets the identity of a subnet for a specific wallet and network. @@ -5134,9 +5077,7 @@ async def set_subnet_identity( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ return await set_subnet_identity_extrinsic( subtensor=self, @@ -5170,7 +5111,7 @@ async def set_weights( raise_error: bool = True, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ): + ) -> ExtrinsicResponse: """ Sets the weight vector for a neuron acting as a validator, specifying the weights assigned to subnet miners based on their performance evaluation. @@ -5197,9 +5138,7 @@ async def set_weights( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. This function is crucial in the Yuma Consensus mechanism, where each validator's weight vector contributes to the overall weight matrix used to calculate emissions and maintain network consensus. @@ -5216,14 +5155,15 @@ async def _blocks_weight_limit() -> bool: return bslu > wrl retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to set weights!" + response = ExtrinsicResponse( + False, "No attempt made. Perhaps it is too soon to set weights!" + ) if ( uid := await self.get_uid_for_hotkey_on_subnet( wallet.hotkey.ss58_address, netuid ) ) is None: - return ( + return ExtrinsicResponse( False, f"Hotkey {wallet.hotkey.ss58_address} not registered in subnet {netuid}", ) @@ -5233,7 +5173,7 @@ async def _blocks_weight_limit() -> bool: while ( retries < max_retries - and success is False + and response.success is False and await _blocks_weight_limit() ): logging.info( @@ -5241,7 +5181,7 @@ async def _blocks_weight_limit() -> bool: f"Attempt [blue]{retries + 1}[blue] of [green]{max_retries}[/green]." ) try: - success, message = await commit_reveal_extrinsic( + response = await commit_reveal_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -5255,17 +5195,18 @@ async def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - except Exception as e: - logging.error(f"Error setting weights: {e}") - retries += 1 + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error setting weights: {error}") + retries += 1 - return success, message + return response else: # go with classic `set weights extrinsic` while ( retries < max_retries - and success is False + and response.success is False and await _blocks_weight_limit() ): try: @@ -5273,7 +5214,7 @@ async def _blocks_weight_limit() -> bool: f"Setting weights for subnet #[blue]{netuid}[/blue]. " f"Attempt [blue]{retries + 1}[/blue] of [green]{max_retries}[/green]." ) - success, message = await set_weights_extrinsic( + response = await set_weights_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -5285,11 +5226,12 @@ async def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - except Exception as e: - logging.error(f"Error setting weights: {e}") - retries += 1 + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error setting weights: {error}") + retries += 1 - return success, message + return response async def serve_axon( self, @@ -5300,7 +5242,7 @@ async def serve_axon( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. @@ -5319,7 +5261,7 @@ async def serve_axon( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the Axon serve registration is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. By registering an Axon, the neuron becomes an active part of the network's distributed computing infrastructure, contributing to the collective intelligence of Bittensor. @@ -5344,7 +5286,7 @@ async def set_commitment( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -5363,7 +5305,7 @@ async def set_commitment( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: `True` if the commitment was successful, `False` otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. Example: # Commit some data to subnet 1 @@ -5374,7 +5316,7 @@ async def set_commitment( Note: See """ - return await publish_metadata( + return await publish_metadata_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -5386,6 +5328,62 @@ async def set_commitment( wait_for_finalization=wait_for_finalization, ) + async def set_reveal_commitment( + self, + wallet, + netuid: int, + data: str, + blocks_until_reveal: int = 360, + block_time: Union[int, float] = 12, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + ) -> ExtrinsicResponse: + """ + Commits arbitrary data to the Bittensor network by publishing metadata. + + Parameters: + wallet: The wallet associated with the neuron committing the data. + netuid: The unique identifier of the subnetwork. + data: The data to be committed to the network. + blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of + blocks in one epoch. + block_time: The number of seconds between each block. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically. + """ + + encrypted, reveal_round = get_encrypted_commitment( + data, blocks_until_reveal, block_time + ) + + # increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed + # and stored. + data_ = {"encrypted": encrypted, "reveal_round": reveal_round} + response = await publish_metadata_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + data_type="TimelockEncrypted", + data=data_, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + response.data = {"reveal_round": reveal_round} + return response + async def start_call( self, wallet: "Wallet", @@ -5394,7 +5392,7 @@ async def start_call( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). @@ -5410,9 +5408,7 @@ async def start_call( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ return await start_call_extrinsic( subtensor=self, @@ -5438,7 +5434,7 @@ async def swap_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. Like subnet hopping - same owner, same hotkey, just changing which subnet the stake is in. @@ -5464,7 +5460,7 @@ async def swap_stake( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: True if the extrinsic was successful. + ExtrinsicResponse: The result object of the extrinsic execution. The price ratio for swap_stake in safe mode is calculated as: origin_subnet_price / destination_subnet_price When safe_staking is enabled, the swap will only execute if: @@ -5499,7 +5495,7 @@ async def toggle_user_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. Parameters: @@ -5514,9 +5510,7 @@ async def toggle_user_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: The call can be executed successfully by the subnet owner only. """ @@ -5542,7 +5536,7 @@ async def transfer( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - ) -> bool: + ) -> ExtrinsicResponse: """ Transfer token of amount to destination. @@ -5560,7 +5554,7 @@ async def transfer( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - `True` if the transferring was successful, otherwise `False`. + ExtrinsicResponse: The result object of the extrinsic execution. """ if amount is not None: amount = check_and_convert_to_balance(amount) @@ -5589,7 +5583,7 @@ async def transfer_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -5608,7 +5602,7 @@ async def transfer_stake( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - success: True if the transfer was successful. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount = check_and_convert_to_balance(amount) return await transfer_stake_extrinsic( @@ -5638,7 +5632,7 @@ async def unstake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting individual neuron stakes within the Bittensor network. @@ -5663,7 +5657,7 @@ async def unstake( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: `True` if the unstaking process is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function supports flexible stake management, allowing neurons to adjust their network participation and potential reward accruals. @@ -5694,7 +5688,7 @@ async def unstake_all( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. Parameters: @@ -5711,10 +5705,7 @@ async def unstake_all( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. Example: # If you would like to unstake all stakes in all subnets safely, use default `rate_tolerance` or pass your @@ -5782,7 +5773,7 @@ async def unstake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts efficiently. This function is useful for managing the distribution of stakes across multiple neurons. @@ -5801,7 +5792,7 @@ async def unstake_multiple( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: `True` if the batch unstaking is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function allows for strategic reallocation or withdrawal of stakes, aligning with the dynamic stake management aspect of the Bittensor network. diff --git a/bittensor/core/extrinsics/asyncex/children.py b/bittensor/core/extrinsics/asyncex/children.py index 7476ef8fb8..4f33a3c99b 100644 --- a/bittensor/core/extrinsics/asyncex/children.py +++ b/bittensor/core/extrinsics/asyncex/children.py @@ -1,5 +1,8 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import float_to_u64, unlock_key + +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import float_to_u64, unlock_key, get_function_name +from bittensor.utils.btlogging import logging if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -16,7 +19,7 @@ async def set_children_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Allows a coldkey to set children-keys. @@ -34,9 +37,7 @@ async def set_children_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: DuplicateChild: There are duplicates in the list of children. @@ -54,7 +55,10 @@ async def set_children_extrinsic( unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: - return False, unlock.message + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) async with subtensor.substrate as substrate: call = await substrate.compose_call( @@ -73,22 +77,23 @@ async def set_children_extrinsic( }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True, message + return response - if success: - return True, "Success with `set_children_extrinsic` response." + if response.success: + return response - return True, message + return response async def root_set_pending_childkey_cooldown_extrinsic( @@ -99,7 +104,7 @@ async def root_set_pending_childkey_cooldown_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Allows a root coldkey to set children-keys. @@ -115,14 +120,13 @@ async def root_set_pending_childkey_cooldown_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ - unlock = unlock_key(wallet) - - if not unlock.success: - return False, unlock.message + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) async with subtensor.substrate as substrate: call = await substrate.compose_call( @@ -137,22 +141,20 @@ async def root_set_pending_childkey_cooldown_extrinsic( call_params={"call": call}, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=sudo_call, wallet=wallet, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True, message + return response - if success: - return ( - True, - "Success with `root_set_pending_childkey_cooldown_extrinsic` response.", - ) + if response.success: + return response - return True, message + return response diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index fd1bd00f53..88da6b9736 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -7,6 +7,8 @@ from numpy.typing import NDArray from bittensor.core.settings import version_as_int +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import get_function_name, unlock_key from bittensor.utils.btlogging import logging from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids @@ -29,7 +31,7 @@ async def commit_reveal_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Commits and reveals weights for a given subtensor and wallet with provided uids and weights. @@ -50,67 +52,68 @@ async def commit_reveal_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ - try: - uids, weights = convert_and_normalize_weights_and_uids(uids, weights) - - current_block = await subtensor.substrate.get_block(None) - subnet_hyperparameters = await subtensor.get_subnet_hyperparameters( - netuid, block_hash=current_block["header"]["hash"] + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() ) - tempo = subnet_hyperparameters.tempo - subnet_reveal_period_epochs = subnet_hyperparameters.commit_reveal_period - # Encrypt `commit_hash` with t-lock and `get reveal_round` - commit_for_reveal, reveal_round = get_encrypted_commit( - uids=uids, - weights=weights, - version_key=version_key, - tempo=tempo, - current_block=current_block["header"]["number"], - netuid=netuid, - subnet_reveal_period_epochs=subnet_reveal_period_epochs, - block_time=block_time, - hotkey=wallet.hotkey.public_key, - ) + uids, weights = convert_and_normalize_weights_and_uids(uids, weights) - logging.info( - f"Committing weights hash [blue]{commit_for_reveal.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " - f"reveal round [blue]{reveal_round}[/blue]..." - ) + current_block = await subtensor.substrate.get_block(None) + subnet_hyperparameters = await subtensor.get_subnet_hyperparameters( + netuid, block_hash=current_block["header"]["hash"] + ) + tempo = subnet_hyperparameters.tempo + subnet_reveal_period_epochs = subnet_hyperparameters.commit_reveal_period - call = await subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="commit_timelocked_weights", - call_params={ - "netuid": netuid, - "commit": commit_for_reveal, - "reveal_round": reveal_round, - "commit_reveal_version": commit_reveal_version, - }, - ) - success, message = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - sign_with="hotkey", - period=period, - raise_error=raise_error, - ) + # Encrypt `commit_hash` with t-lock and `get reveal_round` + commit_for_reveal, reveal_round = get_encrypted_commit( + uids=uids, + weights=weights, + version_key=version_key, + tempo=tempo, + current_block=current_block["header"]["number"], + netuid=netuid, + subnet_reveal_period_epochs=subnet_reveal_period_epochs, + block_time=block_time, + hotkey=wallet.hotkey.public_key, + ) + + logging.info( + f"Committing weights hash [blue]{commit_for_reveal.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " + f"reveal round [blue]{reveal_round}[/blue]..." + ) - if not success: - logging.error(message) - return False, message + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="commit_timelocked_weights", + call_params={ + "netuid": netuid, + "commit": commit_for_reveal, + "reveal_round": reveal_round, + "commit_reveal_version": commit_reveal_version, + }, + ) + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) + if response.success: logging.success( f"[green]Finalized![/green] Weights committed with reveal round [blue]{reveal_round}[/blue]." ) - return True, f"reveal_round:{reveal_round}" + response.data = {"reveal_round": reveal_round} + return response - except Exception as e: - logging.error(f":cross_mark: [red]Failed. Error:[/red] {e}") - return False, str(e) + logging.error(response.message) + return response diff --git a/bittensor/core/extrinsics/asyncex/liquidity.py b/bittensor/core/extrinsics/asyncex/liquidity.py index fc98f46631..c2826b83e2 100644 --- a/bittensor/core/extrinsics/asyncex/liquidity.py +++ b/bittensor/core/extrinsics/asyncex/liquidity.py @@ -1,6 +1,7 @@ from typing import Optional, TYPE_CHECKING -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging from bittensor.utils.liquidity import price_to_tick @@ -22,7 +23,7 @@ async def add_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -42,16 +43,16 @@ async def add_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) tick_low = price_to_tick(price_low.tao) tick_high = price_to_tick(price_high.tao) @@ -76,6 +77,7 @@ async def add_liquidity_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -90,7 +92,7 @@ async def modify_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. Parameters: @@ -108,16 +110,16 @@ async def modify_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Modifying is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = await subtensor.substrate.compose_call( call_module="Swap", @@ -138,6 +140,7 @@ async def modify_liquidity_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -151,7 +154,7 @@ async def remove_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. Parameters: @@ -168,16 +171,16 @@ async def remove_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = await subtensor.substrate.compose_call( call_module="Swap", @@ -197,6 +200,7 @@ async def remove_liquidity_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -209,7 +213,7 @@ async def toggle_user_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. Parameters: @@ -225,13 +229,13 @@ async def toggle_user_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = await subtensor.substrate.compose_call( call_module="Swap", @@ -246,4 +250,5 @@ async def toggle_user_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index a401350251..525f780b0a 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -1,6 +1,8 @@ import asyncio from typing import TYPE_CHECKING, Optional +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -49,7 +51,7 @@ async def transfer_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Transfers stake from one coldkey to another in the Bittensor network. @@ -69,7 +71,7 @@ async def transfer_stake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the transfer was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount.set_unit(netuid=origin_netuid) @@ -85,71 +87,65 @@ async def transfer_stake_extrinsic( destination_netuid=destination_netuid, ) if stake_in_origin < amount: - logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " - f"Stake: {stake_in_origin}, amount: {amount}" - ) - return False + message = f"Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}." + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message) + + logging.info( + f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey " + f"[blue]{destination_coldkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" + ) + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="transfer_stake", + call_params={ + "destination_coldkey": destination_coldkey_ss58, + "hotkey": hotkey_ss58, + "origin_netuid": origin_netuid, + "destination_netuid": destination_netuid, + "alpha_amount": amount.rao, + }, + ) + + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) - try: + if response.success: + if not wait_for_finalization and not wait_for_inclusion: + return response + + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + + # Get updated stakes + origin_stake, dest_stake = await _get_stake_in_origin_and_dest( + subtensor=subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=destination_coldkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + ) logging.info( - f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey " - f"[blue]{destination_coldkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " - f"[yellow]{destination_netuid}[/yellow]" + f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" ) - call = await subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="transfer_stake", - call_params={ - "destination_coldkey": destination_coldkey_ss58, - "hotkey": hotkey_ss58, - "origin_netuid": origin_netuid, - "destination_netuid": destination_netuid, - "alpha_amount": amount.rao, - }, + logging.info( + f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" ) - success, message = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + return response - if success: - if not wait_for_finalization and not wait_for_inclusion: - return True - - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - - # Get updated stakes - origin_stake, dest_stake = await _get_stake_in_origin_and_dest( - subtensor=subtensor, - origin_hotkey_ss58=hotkey_ss58, - destination_hotkey_ss58=hotkey_ss58, - origin_coldkey_ss58=wallet.coldkeypub.ss58_address, - destination_coldkey_ss58=destination_coldkey_ss58, - origin_netuid=origin_netuid, - destination_netuid=destination_netuid, - ) - logging.info( - f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" - ) - logging.info( - f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" - ) - - return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {message}") - return False - - except Exception as e: - logging.error(f":cross_mark: [red]Failed[/red]: {str(e)}") - return False + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") + return response async def swap_stake_extrinsic( @@ -166,7 +162,7 @@ async def swap_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Swaps stake from one subnet to another for a given hotkey in the Bittensor network. @@ -189,7 +185,7 @@ async def swap_stake_extrinsic( Returns: - success (bool): True if the swap was successful. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount.set_unit(netuid=origin_netuid) @@ -203,102 +199,98 @@ async def swap_stake_extrinsic( origin_netuid=origin_netuid, destination_netuid=destination_netuid, ) + if stake_in_origin < amount: - logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " - f"Stake: {stake_in_origin}, amount: {amount}" + message = f"Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}." + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) + + call_params = { + "hotkey": hotkey_ss58, + "origin_netuid": origin_netuid, + "destination_netuid": destination_netuid, + "alpha_amount": amount.rao, + } + + if safe_swapping: + origin_pool, destination_pool = await asyncio.gather( + subtensor.subnet(netuid=origin_netuid), + subtensor.subnet(netuid=destination_netuid), ) - return False + swap_rate_ratio = origin_pool.price.rao / destination_pool.price.rao + swap_rate_ratio_with_tolerance = swap_rate_ratio * (1 + rate_tolerance) - try: - call_params = { - "hotkey": hotkey_ss58, - "origin_netuid": origin_netuid, - "destination_netuid": destination_netuid, - "alpha_amount": amount.rao, - } - - if safe_swapping: - origin_pool, destination_pool = await asyncio.gather( - subtensor.subnet(netuid=origin_netuid), - subtensor.subnet(netuid=destination_netuid), - ) - swap_rate_ratio = origin_pool.price.rao / destination_pool.price.rao - swap_rate_ratio_with_tolerance = swap_rate_ratio * (1 + rate_tolerance) - - logging.info( - f"Swapping stake with safety for hotkey [blue]{hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " - f"[green]{destination_netuid}[/green]\n" - f"Current price ratio: [green]{swap_rate_ratio:.4f}[/green], " - f"Ratio with tolerance: [green]{swap_rate_ratio_with_tolerance:.4f}[/green]" - ) - call_params.update( - { - "limit_price": swap_rate_ratio_with_tolerance, - "allow_partial": allow_partial_stake, - } - ) - call_function = "swap_stake_limit" - else: - logging.info( - f"Swapping stake for hotkey [blue]{hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " - f"[green]{destination_netuid}[/green]" - ) - call_function = "swap_stake" - - call = await subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params, + logging.info( + f"Swapping stake with safety for hotkey [blue]{hotkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " + f"[green]{destination_netuid}[/green]\n" + f"Current price ratio: [green]{swap_rate_ratio:.4f}[/green], " + f"Ratio with tolerance: [green]{swap_rate_ratio_with_tolerance:.4f}[/green]" + ) + call_params.update( + { + "limit_price": swap_rate_ratio_with_tolerance, + "allow_partial": allow_partial_stake, + } + ) + call_function = "swap_stake_limit" + else: + logging.info( + f"Swapping stake for hotkey [blue]{hotkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " + f"[green]{destination_netuid}[/green]" ) + call_function = "swap_stake" + + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=call_params, + ) - success, err_msg = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) + + if response.success: + if not wait_for_finalization and not wait_for_inclusion: + return response + + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + + # Get updated stakes + origin_stake, dest_stake = await _get_stake_in_origin_and_dest( + subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + ) + logging.info( + f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" + ) + logging.info( + f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" ) - if success: - if not wait_for_finalization and not wait_for_inclusion: - return True - - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - - # Get updated stakes - origin_stake, dest_stake = await _get_stake_in_origin_and_dest( - subtensor, - origin_hotkey_ss58=hotkey_ss58, - destination_hotkey_ss58=hotkey_ss58, - origin_coldkey_ss58=wallet.coldkeypub.ss58_address, - destination_coldkey_ss58=wallet.coldkeypub.ss58_address, - origin_netuid=origin_netuid, - destination_netuid=destination_netuid, - ) - logging.info( - f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" - ) - logging.info( - f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" - ) - - return True - else: - if safe_swapping and "Custom error: 8" in err_msg: - logging.error( - ":cross_mark: [red]Failed[/red]: Price ratio exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") - return False - - except Exception as e: - logging.error(f":cross_mark: [red]Failed[/red]: {str(e)}") - return False + return response + + if safe_swapping and "Custom error: 8" in response.message: + logging.error( + ":cross_mark: [red]Failed[/red]: Price ratio exceeded tolerance limit. Either increase price tolerance or enable partial staking." + ) + else: + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") + + return response async def move_stake_extrinsic( @@ -314,7 +306,7 @@ async def move_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Moves stake from one hotkey to another within subnets in the Bittensor network. @@ -335,13 +327,14 @@ async def move_stake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: True if the move was successful. Otherwise, False. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not amount and not move_all_stake: - logging.error( - ":cross_mark: [red]Failed[/red]: Please specify an `amount` or `move_all_stake` argument to move stake." + message = ( + "Please specify an `amount` or `move_all_stake` argument to move stake." ) - return False + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( @@ -357,69 +350,63 @@ async def move_stake_extrinsic( amount = stake_in_origin elif stake_in_origin < amount: - logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey_ss58}. " - f"Stake: {stake_in_origin}, amount: {amount}" - ) - return False + message = f"Insufficient stake in origin hotkey: {origin_hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}." + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) amount.set_unit(netuid=origin_netuid) - try: + logging.info( + f"Moving stake from hotkey [blue]{origin_hotkey_ss58}[/blue] to hotkey [blue]{destination_hotkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" + ) + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="move_stake", + call_params={ + "origin_hotkey": origin_hotkey_ss58, + "origin_netuid": origin_netuid, + "destination_hotkey": destination_hotkey_ss58, + "destination_netuid": destination_netuid, + "alpha_amount": amount.rao, + }, + ) + + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) + + if response.success: + if not wait_for_finalization and not wait_for_inclusion: + return response + + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + + # Get updated stakes + origin_stake, dest_stake = await _get_stake_in_origin_and_dest( + subtensor=subtensor, + origin_hotkey_ss58=origin_hotkey_ss58, + destination_hotkey_ss58=destination_hotkey_ss58, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + ) logging.info( - f"Moving stake from hotkey [blue]{origin_hotkey_ss58}[/blue] to hotkey [blue]{destination_hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " - f"[yellow]{destination_netuid}[/yellow]" + f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" ) - call = await subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="move_stake", - call_params={ - "origin_hotkey": origin_hotkey_ss58, - "origin_netuid": origin_netuid, - "destination_hotkey": destination_hotkey_ss58, - "destination_netuid": destination_netuid, - "alpha_amount": amount.rao, - }, + logging.info( + f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" ) - success, message = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + return response - if success: - if not wait_for_finalization and not wait_for_inclusion: - return True - - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - - # Get updated stakes - origin_stake, dest_stake = await _get_stake_in_origin_and_dest( - subtensor=subtensor, - origin_hotkey_ss58=origin_hotkey_ss58, - destination_hotkey_ss58=destination_hotkey_ss58, - origin_coldkey_ss58=wallet.coldkeypub.ss58_address, - destination_coldkey_ss58=wallet.coldkeypub.ss58_address, - origin_netuid=origin_netuid, - destination_netuid=destination_netuid, - ) - logging.info( - f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" - ) - logging.info( - f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" - ) - - return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {message}") - return False - - except Exception as e: - logging.error(f":cross_mark: [red]Failed[/red]: {str(e)}") - return False + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") + return response diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 2e67368be5..f3a29b1d42 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -10,7 +10,8 @@ from typing import Optional, Union, TYPE_CHECKING from bittensor.core.extrinsics.asyncex.utils import get_extrinsic_fee -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name from bittensor.utils.btlogging import logging from bittensor.utils.registration import log_no_torch_error, create_pow_async, torch @@ -27,7 +28,7 @@ async def burned_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """Registers the wallet to chain by recycling TAO. Parameters: @@ -42,18 +43,24 @@ async def burned_register_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: True if the extrinsic was successful. Otherwise, False. + ExtrinsicResponse: The result object of the extrinsic execution. """ block_hash = await subtensor.substrate.get_chain_head() if not await subtensor.subnet_exists(netuid, block_hash=block_hash): logging.error( f":cross_mark: [red]Failed error:[/red] subnet [blue]{netuid}[/blue] does not exist." ) - return False + return ExtrinsicResponse( + False, + f"Subnet #{netuid} does not exist", + extrinsic_function=get_function_name(), + ) if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue][magenta] ...[/magenta]" @@ -71,12 +78,15 @@ async def burned_register_extrinsic( ) if not neuron.is_null: - logging.info(":white_heavy_check_mark: [green]Already Registered[/green]") + message = "Already registered." + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") logging.info(f"\t\tuid: [blue]{neuron.uid}[/blue]") logging.info(f"\t\tnetuid: [blue]{neuron.netuid}[/blue]") logging.info(f"\t\thotkey: [blue]{neuron.hotkey}[/blue]") logging.info(f"\t\tcoldkey: [blue]{neuron.coldkey}[/blue]") - return True + return ExtrinsicResponse( + message=message, extrinsic_function=get_function_name() + ) logging.debug(":satellite: [magenta]Recycling TAO for Registration...[/magenta]") @@ -95,19 +105,20 @@ async def burned_register_extrinsic( logging.info( f"The registration fee for SN #[blue]{netuid}[/blue] is [blue]{fee}[/blue]." ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if not success: - logging.error(f":cross_mark: [red]Failed error:[/red] {message}") + if not response.success: + logging.error(f":cross_mark: [red]Failed error:[/red] {response.message}") await asyncio.sleep(0.5) - return False + return response # TODO: It is worth deleting everything below and simply returning the result without additional verification. This # should be the responsibility of the user. We will also reduce the number of calls to the chain. @@ -125,12 +136,16 @@ async def burned_register_extrinsic( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address ) if is_registered: - logging.info(":white_heavy_check_mark: [green]Registered[/green]") - return True + message = "Registered." + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") + return response # neuron not found, try again - logging.error(":cross_mark: [red]Unknown error. Neuron not found.[/red]") - return False + message = "Unknown error. Neuron not found." + logging.error(f":cross_mark: [red]{message}[/red]") + response.success = False + response.message = message + return response async def register_subnet_extrinsic( @@ -140,7 +155,7 @@ async def register_subnet_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor blockchain asynchronously. @@ -155,16 +170,15 @@ async def register_subnet_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address) burn_cost = await subtensor.get_subnet_burn_cost() if burn_cost > balance: - logging.error( - f"Insufficient balance {balance} to register subnet. Current burn cost is {burn_cost} TAO" - ) - return False + message = f"Insufficient balance {balance} to register subnet. Current burn cost is {burn_cost} TAO." + logging.error(message) + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -175,26 +189,27 @@ async def register_subnet_extrinsic( }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True + return response - if success: + if response.success: logging.success( ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) - return True + return response - logging.error(f"Failed to register subnet: {message}") - return False + logging.error(f"Failed to register subnet: {response.message}") + return response async def register_extrinsic( @@ -213,7 +228,7 @@ async def register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. Registration is a critical step for a neuron to become an active participant in the network, enabling it to stake, @@ -239,15 +254,14 @@ async def register_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ block_hash = await subtensor.substrate.get_chain_head() logging.debug("[magenta]Checking subnet status... [/magenta]") if not await subtensor.subnet_exists(netuid, block_hash=block_hash): - logging.error( - f":cross_mark: [red]Failed error:[/red] subnet [blue]{netuid}[/blue] does not exist." - ) - return False + message = f"Subnet #{netuid} does not exist." + logging.error(f":cross_mark: [red]Failed error:[/red] {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) logging.info( f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue] [magenta]...[/magenta]" @@ -257,12 +271,13 @@ async def register_extrinsic( ) if not neuron.is_null: - logging.info(":white_heavy_check_mark: [green]Already Registered[/green]") + message = "Already registered." + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") logging.info(f"\t\tuid: [blue]{neuron.uid}[/blue]") logging.info(f"\t\tnetuid: [blue]{neuron.netuid}[/blue]") logging.info(f"\t\thotkey: [blue]{neuron.hotkey}[/blue]") logging.info(f"\t\tcoldkey: [blue]{neuron.coldkey}[/blue]") - return True + return ExtrinsicResponse(True, message, extrinsic_function=get_function_name()) logging.debug( f"Registration hotkey: {wallet.hotkey.ss58_address}, Public coldkey: " @@ -271,7 +286,9 @@ async def register_extrinsic( if not torch: log_no_torch_error() - return False + return ExtrinsicResponse( + False, "Torch is not installed.", extrinsic_function=get_function_name() + ) # Attempt rolling registration. attempts = 1 @@ -283,7 +300,9 @@ async def register_extrinsic( # Solve latest POW. if cuda: if not torch.cuda.is_available(): - return False + return ExtrinsicResponse( + False, "CUDA not available.", extrinsic_function=get_function_name() + ) pow_result = await create_pow_async( subtensor=subtensor, @@ -316,10 +335,11 @@ async def register_extrinsic( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address ) if is_registered: - logging.error( - f":white_heavy_check_mark: [green]Already registered on netuid:[/green] [blue]{netuid}[/blue]" + message = f"Already registered on netuid: {netuid}" + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") + return ExtrinsicResponse( + True, message, extrinsic_function=get_function_name() ) - return True # pow successful, proceed to submit pow to chain for registration else: @@ -338,45 +358,45 @@ async def register_extrinsic( "coldkey": wallet.coldkeypub.ss58_address, }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if not success: + if not response.success: # Look error here # https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs - if "HotKeyAlreadyRegisteredInSubNet" in message: + if "HotKeyAlreadyRegisteredInSubNet" in response.message: logging.info( f":white_heavy_check_mark: [green]Already Registered on subnet:[/green] " f"[blue]{netuid}[/blue]." ) - return True - logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return response await asyncio.sleep(0.5) # Successful registration, final check for neuron and pubkey - if success: + if response.success: logging.info(":satellite: Checking Registration status...") is_registered = await subtensor.is_hotkey_registered( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address ) if is_registered: logging.success( - ":white_heavy_check_mark: [green]Registered[/green]" + ":white_heavy_check_mark: [green]Registered.[/green]" ) - return True - else: - # neuron not found, try again - logging.error( - ":cross_mark: [red]Unknown error. Neuron not found.[/red]" - ) - continue + return response + + # neuron not found, try again + logging.error( + ":cross_mark: [red]Unknown error. Neuron not found.[/red]" + ) + continue else: # Exited loop because pow is no longer valid. logging.error("[red]POW is stale.[/red]") @@ -391,8 +411,11 @@ async def register_extrinsic( ) else: # Failed to register after max attempts. - logging.error("[red]No more attempts.[/red]") - return False + message = "No more attempts." + logging.error(f"[red]{message}[/red]") + return ExtrinsicResponse( + False, message, extrinsic_function=get_function_name() + ) async def set_subnet_identity_extrinsic( @@ -411,7 +434,7 @@ async def set_subnet_identity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Set the identity information for a given subnet. @@ -435,14 +458,14 @@ async def set_subnet_identity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -461,25 +484,26 @@ async def set_subnet_identity_extrinsic( }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True, message + return response - if success: + if response.success: logging.success( f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" ) - return True, f"Identities for subnet {netuid} are set." + return response - logging.error( - f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" - ) - return False, f"Failed to set identity for subnet {netuid}: {message}" + message = f"Failed to set identity for subnet #{netuid}" + logging.error(f":cross_mark: {message}: {response.message}") + response.message = message + return response diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index bb318cdb90..c6e76546ac 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -1,7 +1,8 @@ import asyncio from typing import Optional, TYPE_CHECKING -from bittensor.utils import u16_normalized_float, unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import u16_normalized_float, unlock_key, get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -41,7 +42,7 @@ async def root_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Registers the neuron to the root network. @@ -56,8 +57,14 @@ async def root_register_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + netuid = 0 logging.info( f"Registering on netuid [blue]{netuid}[/blue] on network: [blue]{subtensor.network}[/blue]" @@ -80,15 +87,9 @@ async def root_register_extrinsic( current_recycle = Balance.from_rao(int(recycle_call)) if balance < current_recycle: - logging.error( - f"[red]Insufficient balance {balance} to register neuron. " - f"Current recycle is {current_recycle} TAO[/red]." - ) - return False - - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + message = f"Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO" + logging.error(f"[red]{message}[/red].") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) logging.debug( f"Checking if hotkey ([blue]{wallet.hotkey_str}[/blue]) is registered on root." @@ -97,10 +98,11 @@ async def root_register_extrinsic( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address ) if is_registered: - logging.error( - ":white_heavy_check_mark: [green]Already registered on root network.[/green]" + message = "Already registered on root network." + logging.error(f":white_heavy_check_mark: [green]{message}[/green]") + return ExtrinsicResponse( + message=message, extrinsic_function=get_function_name() ) - return True logging.info(":satellite: [magenta]Registering to root network...[/magenta]") call = await subtensor.substrate.compose_call( @@ -108,7 +110,7 @@ async def root_register_extrinsic( call_function="root_register", call_params={"hotkey": wallet.hotkey.ss58_address}, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -117,24 +119,25 @@ async def root_register_extrinsic( raise_error=raise_error, ) - if not success: - logging.error(f":cross_mark: [red]Failed error:[/red] {message}") + if not response.success: + logging.error(f":cross_mark: [red]Failed error:[/red] {response.message}") await asyncio.sleep(0.5) - return False + return response # Successful registration, final check for neuron and pubkey - else: - uid = await subtensor.substrate.query( - module="SubtensorModule", - storage_function="Uids", - params=[netuid, wallet.hotkey.ss58_address], + uid = await subtensor.substrate.query( + module="SubtensorModule", + storage_function="Uids", + params=[netuid, wallet.hotkey.ss58_address], + ) + if uid is not None: + response.data = {"uid": uid} + logging.info( + f":white_heavy_check_mark: [green]Registered with UID: {uid}[/green]." ) - if uid is not None: - logging.info( - f":white_heavy_check_mark: [green]Registered with UID[/green] [blue]{uid}[/blue]." - ) - return True - else: - # neuron not found, try again - logging.error(":cross_mark: [red]Unknown error. Neuron not found.[/red]") - return False + return response + + # neuron not found, try again + message = "Unknown error. Neuron not found." + logging.error(f":cross_mark: [red]{message}[/red]") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index 6a511718d1..9ab8b078b9 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -4,7 +4,9 @@ from bittensor.core.errors import MetadataError from bittensor.core.settings import version_as_int from bittensor.core.types import AxonServeCallParams +from bittensor.core.types import ExtrinsicResponse from bittensor.utils import ( + get_function_name, networking as net, unlock_key, Certificate, @@ -17,56 +19,6 @@ from bittensor_wallet import Wallet -async def do_serve_axon( - subtensor: "AsyncSubtensor", - wallet: "Wallet", - call_params: "AxonServeCallParams", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, - period: Optional[int] = None, -) -> tuple[bool, str]: - """ - Internal method to submit a serve axon transaction to the Bittensor blockchain. This method creates and submits a - transaction, enabling a neuron's ``Axon`` to serve requests on the network. - - Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Subtensor instance object. - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron. - call_params (bittensor.core.types.AxonServeCallParams): Parameters required for the serve axon call. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. - - Returns: - tuple[bool, str]: A tuple containing a success flag and an optional error message. - - This function is crucial for initializing and announcing a neuron's ``Axon`` service on the network, enhancing the - decentralized computation capabilities of Bittensor. - """ - - if call_params.certificate is None: - call_function = "serve_axon" - else: - call_function = "serve_axon_tls" - - call = await subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params.dict(), - ) - success, message = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - sign_with="hotkey", - period=period, - ) - return success, message - - async def serve_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", @@ -81,7 +33,7 @@ async def serve_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Subscribes a Bittensor endpoint to the subtensor chain. @@ -103,12 +55,14 @@ async def serve_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Decrypt hotkey if not (unlock := unlock_key(wallet, "hotkey")).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) params = AxonServeCallParams( **{ @@ -131,10 +85,9 @@ async def serve_extrinsic( ) neuron_up_to_date = not neuron.is_null and params == neuron if neuron_up_to_date: - logging.debug( - f"Axon already served on: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue]" - ) - return True + message = f"Axon already served on: AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})" + logging.debug(f"[blue]{message}[/blue]") + return ExtrinsicResponse(True, message, extrinsic_function=get_function_name()) logging.debug( f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " @@ -151,7 +104,7 @@ async def serve_extrinsic( call_function=call_function, call_params=params.dict(), ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -159,17 +112,18 @@ async def serve_extrinsic( sign_with="hotkey", period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: + if response.success: logging.debug( f"Axon served with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] on " f"[green]{subtensor.network}:{netuid}[/green]" ) - return True + return response - logging.error(f"Failed: {message}") - return False + logging.error(f"Failed: {response.message}") + return response async def serve_axon_extrinsic( @@ -181,7 +135,7 @@ async def serve_axon_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Serves the axon to the network. @@ -199,11 +153,14 @@ async def serve_axon_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(axon.wallet, "hotkey")).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + external_port = axon.external_port # ---- Get external ip ---- @@ -223,7 +180,7 @@ async def serve_axon_extrinsic( external_ip = axon.external_ip # ---- Subscribe to chain ---- - serve_success = await serve_extrinsic( + response = await serve_extrinsic( subtensor=subtensor, wallet=axon.wallet, ip=external_ip, @@ -236,10 +193,10 @@ async def serve_axon_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - return serve_success + return response -async def publish_metadata( +async def publish_metadata_extrinsic( subtensor: "AsyncSubtensor", wallet: "Wallet", netuid: int, @@ -250,7 +207,7 @@ async def publish_metadata( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -272,16 +229,17 @@ async def publish_metadata( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates - failure. + failure. """ - if not (unlock := unlock_key(wallet, "hotkey")).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) fields = [{f"{data_type}": data}] if reset_bonds: @@ -297,7 +255,7 @@ async def publish_metadata( }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, sign_with="hotkey", @@ -305,11 +263,12 @@ async def publish_metadata( wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: - return True - raise MetadataError(message) + if response.success: + return response + raise MetadataError(response.message) async def get_metadata( diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index c06c58cd78..1a97097c27 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -2,9 +2,9 @@ from typing import Optional, Sequence, TYPE_CHECKING from async_substrate_interface.errors import SubstrateRequestException - +from bittensor.core.types import ExtrinsicResponse from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key, format_error_message, get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -26,7 +26,7 @@ async def add_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. Staking is a fundamental process in the Bittensor network that enables neurons to participate actively and earn @@ -49,7 +49,7 @@ async def add_stake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. @@ -58,7 +58,9 @@ async def add_stake_extrinsic( # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -86,11 +88,14 @@ async def add_stake_extrinsic( # Check enough to stake. if amount > old_balance: - logging.error(":cross_mark: [red]Not enough stake:[/red]") + message = "Not enough stake" + logging.error(f":cross_mark: [red]{message}:[/red]") logging.error(f"\t\tbalance:{old_balance}") logging.error(f"\t\tamount: {amount}") logging.error(f"\t\twallet: {wallet.name}") - return False + return ExtrinsicResponse( + False, f"{message}.", extrinsic_function=get_function_name() + ) call_params = { "hotkey": hotkey_ss58, @@ -137,7 +142,7 @@ async def add_stake_extrinsic( call_function=call_function, call_params=call_params, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -147,11 +152,12 @@ async def add_stake_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: # If we successfully staked. + if response.success: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True + return response logging.success(":white_heavy_check_mark: [green]Finalized[/green]") @@ -178,15 +184,15 @@ async def add_stake_extrinsic( logging.info( f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" ) - return True + return response - if safe_staking and "Custom error: 8" in message: + if safe_staking and "Custom error: 8" in response.message: logging.error( ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.error(f":cross_mark: [red]Failed: {response.message}.[/red]") + return response async def add_stake_multiple_extrinsic( @@ -199,7 +205,7 @@ async def add_stake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey on subnet with corresponding netuid. @@ -218,12 +224,14 @@ async def add_stake_multiple_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) assert all( [ @@ -234,7 +242,9 @@ async def add_stake_multiple_extrinsic( ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." if len(hotkey_ss58s) == 0: - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." @@ -249,7 +259,9 @@ async def add_stake_multiple_extrinsic( if sum(amount.tao for amount in new_amounts) == 0: # Staking 0 tao - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -289,6 +301,7 @@ async def add_stake_multiple_extrinsic( ] successful_stakes = 0 + response = ExtrinsicResponse(False, "", extrinsic_function=get_function_name()) for idx, (hotkey_ss58, amount, old_stake, netuid) in enumerate( zip(hotkey_ss58s, new_amounts, old_stakes, netuids) ): @@ -314,7 +327,7 @@ async def add_stake_multiple_extrinsic( "netuid": netuid, }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -324,10 +337,11 @@ async def add_stake_multiple_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) # If we successfully staked. - if success: + if response.success: if not wait_for_finalization and not wait_for_inclusion: old_balance -= amount successful_stakes += 1 @@ -357,7 +371,7 @@ async def add_stake_multiple_extrinsic( old_balance = new_balance successful_stakes += 1 else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + logging.error(f":cross_mark: [red]Failed: {response.message}.[/red]") continue except SubstrateRequestException as error: @@ -374,6 +388,6 @@ async def add_stake_multiple_extrinsic( logging.info( f"Balance: [blue]{initial_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) - return True + return response - return False + return response diff --git a/bittensor/core/extrinsics/asyncex/start_call.py b/bittensor/core/extrinsics/asyncex/start_call.py index 3a2c64937a..d2a8747694 100644 --- a/bittensor/core/extrinsics/asyncex/start_call.py +++ b/bittensor/core/extrinsics/asyncex/start_call.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -16,7 +17,7 @@ async def start_call_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). @@ -33,13 +34,13 @@ async def start_call_extrinsic( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) async with subtensor.substrate as substrate: start_call = await substrate.compose_call( @@ -48,19 +49,14 @@ async def start_call_extrinsic( call_params={"netuid": netuid}, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=start_call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if not wait_for_finalization and not wait_for_inclusion: - return True, message - - if success: - return True, "Success with `start_call` response." - - return True, message + return response diff --git a/bittensor/core/extrinsics/asyncex/take.py b/bittensor/core/extrinsics/asyncex/take.py index 3840821c08..a60272461a 100644 --- a/bittensor/core/extrinsics/asyncex/take.py +++ b/bittensor/core/extrinsics/asyncex/take.py @@ -2,7 +2,9 @@ from bittensor_wallet.bittensor_wallet import Wallet -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name +from bittensor.utils.btlogging import logging if TYPE_CHECKING: from bittensor.core.async_subtensor import AsyncSubtensor @@ -17,7 +19,7 @@ async def increase_take_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Sets the delegate 'take' percentage for a neuron identified by its hotkey. Parameters: @@ -39,9 +41,11 @@ async def increase_take_extrinsic( """ unlock = unlock_key(wallet, raise_error=raise_error) - if not unlock.success: - return False, unlock.message + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -59,6 +63,7 @@ async def increase_take_extrinsic( wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -71,7 +76,7 @@ async def decrease_take_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. @@ -93,9 +98,11 @@ async def decrease_take_extrinsic( - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet, raise_error=raise_error) - if not unlock.success: - return False, unlock.message + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = await subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -113,4 +120,5 @@ async def decrease_take_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + calling_function=get_function_name(), ) diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index 3e3d6d7193..ce7639b9d8 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -1,12 +1,13 @@ import asyncio from typing import TYPE_CHECKING, Optional - +from bittensor.core.types import ExtrinsicResponse from bittensor.core.settings import NETWORK_EXPLORER_MAP from bittensor.utils import ( get_explorer_url_for_network, get_transfer_fn_params, is_valid_bittensor_address_or_public_key, unlock_key, + get_function_name, ) from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -27,7 +28,7 @@ async def transfer_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """Transfers funds from this wallet to the destination public key address. Parameters: @@ -47,22 +48,23 @@ async def transfer_extrinsic( Returns: bool: True if the subnet registration was successful, False otherwise. """ + # Unlock wallet coldkey. + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + if amount is None and not transfer_all: - logging.error("If not transferring all, `amount` must be specified.") - return False + message = "If not transferring all, `amount` must be specified." + logging.error(message) + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) # Validate destination address. if not is_valid_bittensor_address_or_public_key(destination): - logging.error( - f":cross_mark: [red]Invalid destination SS58 address[/red]: {destination}" - ) - return False - - logging.info(f"Initiating transfer on network: {subtensor.network}") - # Unlock wallet coldkey. - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + message = f"Invalid destination SS58 address: {destination}" + logging.error(f":cross_mark: [red]{message}[/red].") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) # Check balance. logging.info( @@ -87,14 +89,18 @@ async def transfer_extrinsic( # Check if we have enough balance. if transfer_all is True: if (account_balance - fee) < existential_deposit: - logging.error("Not enough balance to transfer") - return False + message = "Not enough balance to transfer." + logging.error(message) + return ExtrinsicResponse( + False, message, extrinsic_function=get_function_name() + ) elif account_balance < (amount + fee + existential_deposit): + message = "Not enough balance." logging.error(":cross_mark: [red]Not enough balance[/red]") logging.error(f"\t\tBalance:\t[blue]{account_balance}[/blue]") logging.error(f"\t\tAmount:\t[blue]{amount}[/blue]") logging.error(f"\t\tFor fee:\t[blue]{fee}[/blue]") - return False + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) logging.info(":satellite: [magenta]Transferring... bool: +) -> ExtrinsicResponse: """ Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -47,12 +47,14 @@ async def unstake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -72,118 +74,110 @@ async def unstake_extrinsic( # Check enough to unstake. if amount > old_stake: - logging.error( - f":cross_mark: [red]Not enough stake[/red]: [green]{old_stake}[/green] to unstake: " - f"[blue]{amount}[/blue] from hotkey: [yellow]{wallet.hotkey_str}[/yellow]" - ) - return False - - try: - call_params = { - "hotkey": hotkey_ss58, - "netuid": netuid, - "amount_unstaked": amount.rao, - } - if safe_unstaking: - pool = await subtensor.subnet(netuid=netuid) - base_price = pool.price.tao - - if pool.netuid == 0: - price_with_tolerance = base_price - else: - price_with_tolerance = base_price * (1 - rate_tolerance) - - logging_info = ( - f":satellite: [magenta]Safe Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " - f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " - f"price limit: [green]{price_with_tolerance}[/green], " - f"original price: [green]{base_price}[/green], " - f"with partial unstake: [green]{allow_partial_stake}[/green] " - f"on [blue]{subtensor.network}[/blue]" - ) + message = f"Not enough stake: {old_stake} to unstake: {amount} from hotkey: {wallet.hotkey_str}" + logging.error(f":cross_mark: [red]{message}[/red]") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) - limit_price = Balance.from_tao(price_with_tolerance).rao - call_params.update( - { - "limit_price": limit_price, - "allow_partial": allow_partial_stake, - } - ) - call_function = "remove_stake_limit" - else: - logging_info = ( - f":satellite: [magenta]Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " - f"on [blue]{subtensor.network}[/blue]" - ) - call_function = "remove_stake" + call_params = { + "hotkey": hotkey_ss58, + "netuid": netuid, + "amount_unstaked": amount.rao, + } + if safe_unstaking: + pool = await subtensor.subnet(netuid=netuid) + base_price = pool.price.tao - call = await subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params, + if pool.netuid == 0: + price_with_tolerance = base_price + else: + price_with_tolerance = base_price * (1 - rate_tolerance) + + logging_info = ( + f":satellite: [magenta]Safe Unstaking from:[/magenta] " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " + f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " + f"price limit: [green]{price_with_tolerance}[/green], " + f"original price: [green]{base_price}[/green], " + f"with partial unstake: [green]{allow_partial_stake}[/green] " + f"on [blue]{subtensor.network}[/blue]" ) - fee = await get_extrinsic_fee( - subtensor=subtensor, call=call, keypair=wallet.coldkeypub, netuid=netuid + + limit_price = Balance.from_tao(price_with_tolerance).rao + call_params.update( + { + "limit_price": limit_price, + "allow_partial": allow_partial_stake, + } ) - logging.info(f"{logging_info} for fee [blue]{fee}[/blue][magenta]...[/magenta]") - success, message = await subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - sign_with="coldkey", - use_nonce=True, - period=period, - raise_error=raise_error, + call_function = "remove_stake_limit" + else: + logging_info = ( + f":satellite: [magenta]Unstaking from:[/magenta] " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " + f"on [blue]{subtensor.network}[/blue]" ) + call_function = "remove_stake" - if success: # If we successfully unstaked. - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=call_params, + ) + fee = await get_extrinsic_fee( + subtensor=subtensor, call=call, keypair=wallet.coldkeypub, netuid=netuid + ) + logging.info(f"{logging_info} for fee [blue]{fee}[/blue][magenta]...[/magenta]") + response = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + sign_with="coldkey", + use_nonce=True, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + if response.success: # If we successfully unstaked. + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return response - logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " - f"[magenta]...[/magenta]" - ) - new_block_hash = await subtensor.substrate.get_chain_head() - new_balance, new_stake = await asyncio.gather( - subtensor.get_balance( - wallet.coldkeypub.ss58_address, block_hash=new_block_hash - ), - subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=netuid, - block_hash=new_block_hash, - ), - ) - logging.info( - f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" - ) - logging.info( - f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" - ) - return True + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - if safe_unstaking and "Custom error: 8" in message: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.info( + f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " + f"[magenta]...[/magenta]" + ) + new_block_hash = await subtensor.substrate.get_chain_head() + new_balance, new_stake = await asyncio.gather( + subtensor.get_balance( + wallet.coldkeypub.ss58_address, block_hash=new_block_hash + ), + subtensor.get_stake( + coldkey_ss58=wallet.coldkeypub.ss58_address, + hotkey_ss58=hotkey_ss58, + netuid=netuid, + block_hash=new_block_hash, + ), + ) + logging.info( + f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" + ) + logging.info( + f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" + ) + return response - except SubstrateRequestException as error: + if safe_unstaking and "Custom error: 8" in response.message: logging.error( - f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]" + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) - return False + else: + logging.error(f":cross_mark: [red]Failed: {response.message}.[/red]") + return response async def unstake_all_extrinsic( @@ -196,7 +190,7 @@ async def unstake_all_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. Parameters: @@ -214,14 +208,13 @@ async def unstake_all_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call_params = { "hotkey": hotkey, @@ -251,6 +244,7 @@ async def unstake_all_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -265,7 +259,7 @@ async def unstake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -284,12 +278,14 @@ async def unstake_multiple_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Unlock coldkey. if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) # or amounts or unstake_all (no both) if amounts and unstake_all: @@ -307,7 +303,9 @@ async def unstake_multiple_extrinsic( amounts = [amount.set_unit(netuid) for amount, netuid in zip(amounts, netuids)] if sum(amount.tao for amount in amounts) == 0: # Staking 0 tao - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) assert all( [ @@ -318,7 +316,9 @@ async def unstake_multiple_extrinsic( ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." if len(hotkey_ss58s) == 0: - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." @@ -351,6 +351,9 @@ async def unstake_multiple_extrinsic( ) successful_unstakes = 0 + response = ExtrinsicResponse( + False, "Failed", extrinsic_function=get_function_name() + ) for idx, (hotkey_ss58, amount, old_stake, netuid) in enumerate( zip(hotkey_ss58s, amounts, old_stakes, netuids) ): @@ -392,7 +395,7 @@ async def unstake_multiple_extrinsic( f"[blue]{netuid}[/blue] for fee [blue]{fee}[/blue]" ) - staking_response, err_msg = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -402,9 +405,10 @@ async def unstake_multiple_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if staking_response is True: # If we successfully unstaked. + if response.success: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: @@ -429,14 +433,16 @@ async def unstake_multiple_extrinsic( ) successful_unstakes += 1 else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {response.message}.[/red]") continue except SubstrateRequestException as error: logging.error( f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]" ) - return False + if raise_error: + raise error + return response if successful_unstakes != 0: logging.info( @@ -450,6 +456,6 @@ async def unstake_multiple_extrinsic( logging.info( f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) - return True + return response - return False + return response diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 53aa650f2d..49db1361b8 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -1,12 +1,12 @@ """This module provides async functionality for working with weights in the Bittensor network.""" from typing import Union, TYPE_CHECKING, Optional - +from bittensor.core.types import ExtrinsicResponse import numpy as np from numpy.typing import NDArray from bittensor.core.settings import version_as_int -from bittensor.utils import get_function_name +from bittensor.utils import get_function_name, unlock_key from bittensor.utils.btlogging import logging from bittensor.utils.weight_utils import ( convert_and_normalize_weights_and_uids, @@ -28,7 +28,7 @@ async def commit_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. This function is a wrapper around the `do_commit_weights` method. @@ -46,13 +46,17 @@ async def commit_weights_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, `False` otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper error handling and user interaction when required. """ + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + call = await subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="commit_weights", @@ -61,7 +65,7 @@ async def commit_weights_extrinsic( "commit_hash": commit_hash, }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -71,13 +75,14 @@ async def commit_weights_extrinsic( nonce_key="hotkey", sign_with="hotkey", raise_error=raise_error, + calling_function=get_function_name(), ) - if success: - logging.info(message) + if response.success: + logging.info(response.message) else: - logging.error(f"{get_function_name}: {message}") - return success, message + logging.error(f"{get_function_name}: {response.message}") + return response async def reveal_weights_extrinsic( @@ -92,7 +97,7 @@ async def reveal_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. This function is a wrapper around the `_do_reveal_weights` method. @@ -113,13 +118,17 @@ async def reveal_weights_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, `False` otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. This function provides a user-friendly interface for revealing weights on the Bittensor blockchain, ensuring proper - error handling and user interaction when required. + error handling and user interaction when required. """ + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + call = await subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="reveal_weights", @@ -131,7 +140,7 @@ async def reveal_weights_extrinsic( "version_key": version_key, }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -141,13 +150,14 @@ async def reveal_weights_extrinsic( nonce_key="hotkey", use_nonce=True, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: - logging.info(message) + if response.success: + logging.info(response.message) else: - logging.error(f"{get_function_name}: {message}") - return success, message + logging.error(f"{get_function_name}: {response.message}") + return response async def set_weights_extrinsic( @@ -161,7 +171,7 @@ async def set_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Sets the given weights and values on chain for a given wallet hotkey account. @@ -180,10 +190,14 @@ async def set_weights_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, `False` otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. """ + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + # Convert types. uids, weights = convert_uids_and_weights(uids, weights) @@ -206,7 +220,7 @@ async def set_weights_extrinsic( "version_key": version_key, }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -216,11 +230,12 @@ async def set_weights_extrinsic( nonce_key="hotkey", sign_with="hotkey", raise_error=raise_error, + calling_function=get_function_name(), ) - if success: + if response.success: logging.info("Successfully set weights and Finalized.") else: - logging.error(f"{get_function_name}: {message}") + logging.error(f"{get_function_name}: {response.message}") - return success, message + return response diff --git a/bittensor/core/extrinsics/children.py b/bittensor/core/extrinsics/children.py index 5f7c1a9d84..9ffe791cfe 100644 --- a/bittensor/core/extrinsics/children.py +++ b/bittensor/core/extrinsics/children.py @@ -1,5 +1,8 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import float_to_u64, unlock_key + +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import float_to_u64, unlock_key, get_function_name +from bittensor.utils.btlogging import logging if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -16,7 +19,7 @@ def set_children_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -): +) -> "ExtrinsicResponse": """ Allows a coldkey to set children-keys. @@ -34,9 +37,7 @@ def set_children_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: DuplicateChild: There are duplicates in the list of children. @@ -52,9 +53,11 @@ def set_children_extrinsic( bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. """ unlock = unlock_key(wallet, raise_error=raise_error) - if not unlock.success: - return False, unlock.message + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -72,22 +75,23 @@ def set_children_extrinsic( }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True, message + return response - if success: - return True, "Success with `set_children_extrinsic` response." + if response.success: + return response - return True, message + return response def root_set_pending_childkey_cooldown_extrinsic( @@ -98,7 +102,7 @@ def root_set_pending_childkey_cooldown_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Allows a root coldkey to set children-keys. @@ -114,14 +118,14 @@ def root_set_pending_childkey_cooldown_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ unlock = unlock_key(wallet) - if not unlock.success: - return False, unlock.message + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -135,22 +139,20 @@ def root_set_pending_childkey_cooldown_extrinsic( call_params={"call": call}, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=sudo_call, wallet=wallet, period=period, raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True, message + return response - if success: - return ( - True, - "Success with `root_set_pending_childkey_cooldown_extrinsic` response.", - ) + if response.success: + return response - return True, message + return response diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index d0997605b4..22899a312d 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -1,7 +1,8 @@ """This module provides sync functionality for commit reveal in the Bittensor network.""" from typing import Union, TYPE_CHECKING, Optional - +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import get_function_name, unlock_key import numpy as np from bittensor_drand import get_encrypted_commit from numpy.typing import NDArray @@ -29,7 +30,7 @@ def commit_reveal_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Commits and reveals weights for a given subtensor and wallet with provided uids and weights. @@ -50,10 +51,14 @@ def commit_reveal_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + uids, weights = convert_and_normalize_weights_and_uids(uids, weights) current_block = subtensor.get_current_block() @@ -91,7 +96,7 @@ def commit_reveal_extrinsic( "commit_reveal_version": commit_reveal_version, }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -99,13 +104,15 @@ def commit_reveal_extrinsic( sign_with="hotkey", period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if not success: - logging.error(message) - return False, message + if response.success: + logging.success( + f"[green]Finalized![/green] Weights committed with reveal round [blue]{reveal_round}[/blue]." + ) + response.data = {"reveal_round": reveal_round} + return response - logging.success( - f"[green]Finalized![/green] Weights committed with reveal round [blue]{reveal_round}[/blue]." - ) - return True, f"reveal_round:{reveal_round}" + logging.error(response.message) + return response diff --git a/bittensor/core/extrinsics/liquidity.py b/bittensor/core/extrinsics/liquidity.py index dbb974082a..56c1f6d13e 100644 --- a/bittensor/core/extrinsics/liquidity.py +++ b/bittensor/core/extrinsics/liquidity.py @@ -1,6 +1,7 @@ from typing import Optional, TYPE_CHECKING -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging from bittensor.utils.liquidity import price_to_tick @@ -22,7 +23,7 @@ def add_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -42,16 +43,16 @@ def add_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) tick_low = price_to_tick(price_low.tao) tick_high = price_to_tick(price_high.tao) @@ -76,6 +77,7 @@ def add_liquidity_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -90,7 +92,7 @@ def modify_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. Parameters: @@ -108,16 +110,16 @@ def modify_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Modifying is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="Swap", @@ -138,6 +140,7 @@ def modify_liquidity_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -151,7 +154,7 @@ def remove_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. Parameters: @@ -168,16 +171,16 @@ def remove_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity_extrinsic` to enable/disable user liquidity. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="Swap", @@ -197,6 +200,7 @@ def remove_liquidity_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -209,7 +213,7 @@ def toggle_user_liquidity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. Parameters: @@ -225,13 +229,13 @@ def toggle_user_liquidity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="Swap", @@ -246,4 +250,5 @@ def toggle_user_liquidity_extrinsic( wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index b3a9d54e95..fc5fe29bf3 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -1,7 +1,9 @@ from typing import Optional, TYPE_CHECKING +from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging +from bittensor.utils import get_function_name if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -46,7 +48,7 @@ def transfer_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -66,7 +68,7 @@ def transfer_stake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success (bool): True if the transfer was successful. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount.set_unit(netuid=origin_netuid) @@ -82,71 +84,65 @@ def transfer_stake_extrinsic( destination_coldkey_ss58=destination_coldkey_ss58, ) if stake_in_origin < amount: - logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " - f"Stake: {stake_in_origin}, amount: {amount}" - ) - return False + message = f"Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}." + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) + + logging.info( + f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey [" + f"blue]{destination_coldkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " + f"[yellow]{destination_netuid}[/yellow]" + ) + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="transfer_stake", + call_params={ + "destination_coldkey": destination_coldkey_ss58, + "hotkey": hotkey_ss58, + "origin_netuid": origin_netuid, + "destination_netuid": destination_netuid, + "alpha_amount": amount.rao, + }, + ) + + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) - try: + if response.success: + if not wait_for_finalization and not wait_for_inclusion: + return response + + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + + # Get updated stakes + origin_stake, dest_stake = _get_stake_in_origin_and_dest( + subtensor=subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=destination_coldkey_ss58, + ) logging.info( - f"Transferring stake from coldkey [blue]{wallet.coldkeypub.ss58_address}[/blue] to coldkey [" - f"blue]{destination_coldkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid " - f"[yellow]{destination_netuid}[/yellow]" + f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" ) - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="transfer_stake", - call_params={ - "destination_coldkey": destination_coldkey_ss58, - "hotkey": hotkey_ss58, - "origin_netuid": origin_netuid, - "destination_netuid": destination_netuid, - "alpha_amount": amount.rao, - }, + logging.info( + f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" ) - success, message = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + return response - if success: - if not wait_for_finalization and not wait_for_inclusion: - return True - - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - - # Get updated stakes - origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor=subtensor, - origin_hotkey_ss58=hotkey_ss58, - destination_hotkey_ss58=hotkey_ss58, - origin_netuid=origin_netuid, - destination_netuid=destination_netuid, - origin_coldkey_ss58=wallet.coldkeypub.ss58_address, - destination_coldkey_ss58=destination_coldkey_ss58, - ) - logging.info( - f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" - ) - logging.info( - f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" - ) - - return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {message}") - return False - - except Exception as e: - logging.error(f":cross_mark: [red]Failed[/red]: {str(e)}") - return False + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") + return response def swap_stake_extrinsic( @@ -163,7 +159,7 @@ def swap_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -186,7 +182,7 @@ def swap_stake_extrinsic( Returns: - success (bool): True if the swap was successful. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount.set_unit(netuid=origin_netuid) @@ -201,100 +197,96 @@ def swap_stake_extrinsic( origin_coldkey_ss58=wallet.coldkeypub.ss58_address, destination_coldkey_ss58=wallet.coldkeypub.ss58_address, ) + if stake_in_origin < amount: - logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {hotkey_ss58}. " - f"Stake: {stake_in_origin}, amount: {amount}" + message = f"Insufficient stake in origin hotkey: {hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}." + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) + + call_params = { + "hotkey": hotkey_ss58, + "origin_netuid": origin_netuid, + "destination_netuid": destination_netuid, + "alpha_amount": amount.rao, + } + + if safe_swapping: + origin_pool = subtensor.subnet(netuid=origin_netuid) + destination_pool = subtensor.subnet(netuid=destination_netuid) + swap_rate_ratio = origin_pool.price.rao / destination_pool.price.rao + swap_rate_ratio_with_tolerance = swap_rate_ratio * (1 + rate_tolerance) + + logging.info( + f"Swapping stake with safety for hotkey [blue]{hotkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " + f"[green]{destination_netuid}[/green]\n" + f"Current price ratio: [green]{swap_rate_ratio:.4f}[/green], " + f"Ratio with tolerance: [green]{swap_rate_ratio_with_tolerance:.4f}[/green]" + ) + call_params.update( + { + "limit_price": swap_rate_ratio_with_tolerance, + "allow_partial": allow_partial_stake, + } ) - return False + call_function = "swap_stake_limit" + else: + logging.info( + f"Swapping stake for hotkey [blue]{hotkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " + f"[green]{destination_netuid}[/green]" + ) + call_function = "swap_stake" - try: - call_params = { - "hotkey": hotkey_ss58, - "origin_netuid": origin_netuid, - "destination_netuid": destination_netuid, - "alpha_amount": amount.rao, - } - - if safe_swapping: - origin_pool = subtensor.subnet(netuid=origin_netuid) - destination_pool = subtensor.subnet(netuid=destination_netuid) - swap_rate_ratio = origin_pool.price.rao / destination_pool.price.rao - swap_rate_ratio_with_tolerance = swap_rate_ratio * (1 + rate_tolerance) - - logging.info( - f"Swapping stake with safety for hotkey [blue]{hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " - f"[green]{destination_netuid}[/green]\n" - f"Current price ratio: [green]{swap_rate_ratio:.4f}[/green], " - f"Ratio with tolerance: [green]{swap_rate_ratio_with_tolerance:.4f}[/green]" - ) - call_params.update( - { - "limit_price": swap_rate_ratio_with_tolerance, - "allow_partial": allow_partial_stake, - } - ) - call_function = "swap_stake_limit" - else: - logging.info( - f"Swapping stake for hotkey [blue]{hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [green]{origin_netuid}[/green] to netuid " - f"[green]{destination_netuid}[/green]" - ) - call_function = "swap_stake" - - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params, + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=call_params, + ) + + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) + + if response.success: + if not wait_for_finalization and not wait_for_inclusion: + return response + + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + + # Get updated stakes + origin_stake, dest_stake = _get_stake_in_origin_and_dest( + subtensor=subtensor, + origin_hotkey_ss58=hotkey_ss58, + destination_hotkey_ss58=hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + ) + logging.info( + f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" ) + logging.info( + f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" + ) + + return response - success, err_msg = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, + if safe_swapping and "Custom error: 8" in response.message: + logging.error( + ":cross_mark: [red]Failed[/red]: Price ratio exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) + else: + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") - if success: - if not wait_for_finalization and not wait_for_inclusion: - return True - - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - - # Get updated stakes - origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor=subtensor, - origin_hotkey_ss58=hotkey_ss58, - destination_hotkey_ss58=hotkey_ss58, - origin_netuid=origin_netuid, - destination_netuid=destination_netuid, - origin_coldkey_ss58=wallet.coldkeypub.ss58_address, - destination_coldkey_ss58=wallet.coldkeypub.ss58_address, - ) - logging.info( - f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" - ) - logging.info( - f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" - ) - - return True - else: - if safe_swapping and "Custom error: 8" in err_msg: - logging.error( - ":cross_mark: [red]Failed[/red]: Price ratio exceeded tolerance limit. Either increase price tolerance or enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") - return False - - except Exception as e: - logging.error(f":cross_mark: [red]Failed[/red]: {str(e)}") - return False + return response def move_stake_extrinsic( @@ -310,7 +302,7 @@ def move_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Moves stake to a different hotkey and/or subnet while keeping the same coldkey owner. @@ -331,13 +323,14 @@ def move_stake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: True if the move was successful. Otherwise, False. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not amount and not move_all_stake: - logging.error( - ":cross_mark: [red]Failed[/red]: Please specify an `amount` or `move_all_stake` argument to move stake." + message = ( + "Please specify an `amount` or `move_all_stake` argument to move stake." ) - return False + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) # Check sufficient stake stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( @@ -353,68 +346,62 @@ def move_stake_extrinsic( amount = stake_in_origin elif stake_in_origin < amount: - logging.error( - f":cross_mark: [red]Failed[/red]: Insufficient stake in origin hotkey: {origin_hotkey_ss58}. " - f"Stake: {stake_in_origin}, amount: {amount}" - ) - return False + message = f"Insufficient stake in origin hotkey: {origin_hotkey_ss58}. Stake: {stake_in_origin}, amount: {amount}." + logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) amount.set_unit(netuid=origin_netuid) - try: + logging.info( + f"Moving stake from hotkey [blue]{origin_hotkey_ss58}[/blue] to hotkey [blue]{destination_hotkey_ss58}[/blue]\n" + f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" + ) + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="move_stake", + call_params={ + "origin_hotkey": origin_hotkey_ss58, + "origin_netuid": origin_netuid, + "destination_hotkey": destination_hotkey_ss58, + "destination_netuid": destination_netuid, + "alpha_amount": amount.rao, + }, + ) + + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) + + if response.success: + if not wait_for_finalization and not wait_for_inclusion: + return response + + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + + # Get updated stakes + origin_stake, dest_stake = _get_stake_in_origin_and_dest( + subtensor=subtensor, + origin_hotkey_ss58=origin_hotkey_ss58, + destination_hotkey_ss58=destination_hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, + origin_coldkey_ss58=wallet.coldkeypub.ss58_address, + destination_coldkey_ss58=wallet.coldkeypub.ss58_address, + ) logging.info( - f"Moving stake from hotkey [blue]{origin_hotkey_ss58}[/blue] to hotkey [blue]{destination_hotkey_ss58}[/blue]\n" - f"Amount: [green]{amount}[/green] from netuid [yellow]{origin_netuid}[/yellow] to netuid [yellow]{destination_netuid}[/yellow]" + f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" ) - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="move_stake", - call_params={ - "origin_hotkey": origin_hotkey_ss58, - "origin_netuid": origin_netuid, - "destination_hotkey": destination_hotkey_ss58, - "destination_netuid": destination_netuid, - "alpha_amount": amount.rao, - }, + logging.info( + f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" ) - success, message = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - raise_error=raise_error, - ) + return response - if success: - if not wait_for_finalization and not wait_for_inclusion: - return True - - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - - # Get updated stakes - origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor=subtensor, - origin_hotkey_ss58=origin_hotkey_ss58, - destination_hotkey_ss58=destination_hotkey_ss58, - origin_netuid=origin_netuid, - destination_netuid=destination_netuid, - origin_coldkey_ss58=wallet.coldkeypub.ss58_address, - destination_coldkey_ss58=wallet.coldkeypub.ss58_address, - ) - logging.info( - f"Origin Stake: [blue]{stake_in_origin}[/blue] :arrow_right: [green]{origin_stake}[/green]" - ) - logging.info( - f"Destination Stake: [blue]{stake_in_destination}[/blue] :arrow_right: [green]{dest_stake}[/green]" - ) - - return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {message}") - return False - - except Exception as e: - logging.error(f":cross_mark: [red]Failed[/red]: {str(e)}") - return False + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") + return response diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 6f667ff7b6..f710ef96e0 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -10,7 +10,8 @@ from typing import Optional, Union, TYPE_CHECKING from bittensor.core.extrinsics.utils import get_extrinsic_fee -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name from bittensor.utils.btlogging import logging from bittensor.utils.registration import create_pow, log_no_torch_error, torch @@ -27,7 +28,7 @@ def burned_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """Registers the wallet to chain by recycling TAO. Parameters: @@ -42,18 +43,24 @@ def burned_register_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: True if the extrinsic was successful. Otherwise, False. + ExtrinsicResponse: The result object of the extrinsic execution. """ block = subtensor.get_current_block() if not subtensor.subnet_exists(netuid, block=block): logging.error( f":cross_mark: [red]Failed error:[/red] subnet [blue]{netuid}[/blue] does not exist." ) - return False + return ExtrinsicResponse( + False, + f"Subnet #{netuid} does not exist", + extrinsic_function=get_function_name(), + ) if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue][magenta] ...[/magenta]" @@ -65,12 +72,15 @@ def burned_register_extrinsic( old_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address, block=block) if not neuron.is_null: - logging.info(":white_heavy_check_mark: [green]Already Registered[/green]") + message = "Already registered." + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") logging.info(f"\t\tuid: [blue]{neuron.uid}[/blue]") logging.info(f"\t\tnetuid: [blue]{neuron.netuid}[/blue]") logging.info(f"\t\thotkey: [blue]{neuron.hotkey}[/blue]") logging.info(f"\t\tcoldkey: [blue]{neuron.coldkey}[/blue]") - return True + return ExtrinsicResponse( + message=message, extrinsic_function=get_function_name() + ) recycle_amount = subtensor.recycle(netuid=netuid, block=block) logging.debug(":satellite: [magenta]Recycling TAO for Registration...[/magenta]") @@ -89,19 +99,20 @@ def burned_register_extrinsic( logging.info( f"The registration fee for SN #[blue]{netuid}[/blue] is [blue]{fee}[/blue]." ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if not success: - logging.error(f":cross_mark: [red]Failed error:[/red] {message}") + if not response.success: + logging.error(f":cross_mark: [red]Failed error:[/red] {response.message}") time.sleep(0.5) - return False + return response # TODO: It is worth deleting everything below and simply returning the result without additional verification. This # should be the responsibility of the user. We will also reduce the number of calls to the chain. @@ -117,12 +128,16 @@ def burned_register_extrinsic( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address, block=block ) if is_registered: - logging.info(":white_heavy_check_mark: [green]Registered[/green]") - return True + message = "Registered" + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") + return response # neuron not found, try again - logging.error(":cross_mark: [red]Unknown error. Neuron not found.[/red]") - return False + message = "Unknown error. Neuron not found." + logging.error(f":cross_mark: [red]{message}[/red]") + response.success = False + response.message = message + return response def register_subnet_extrinsic( @@ -132,7 +147,7 @@ def register_subnet_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor blockchain. @@ -147,16 +162,15 @@ def register_subnet_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ balance = subtensor.get_balance(wallet.coldkeypub.ss58_address) burn_cost = subtensor.get_subnet_burn_cost() if burn_cost > balance: - logging.error( - f"Insufficient balance {balance} to register subnet. Current burn cost is {burn_cost} TAO" - ) - return False + message = f"Insufficient balance {balance} to register subnet. Current burn cost is {burn_cost} TAO." + logging.error(message) + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -167,26 +181,27 @@ def register_subnet_extrinsic( }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True + return response - if success: + if response.success: logging.success( ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) - return True + return response - logging.error(f"Failed to register subnet: {message}") - return False + logging.error(f"Failed to register subnet: {response.message}") + return response def register_extrinsic( @@ -205,7 +220,7 @@ def register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. Parameters: @@ -228,16 +243,15 @@ def register_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ logging.debug("[magenta]Checking subnet status... [/magenta]") block = subtensor.get_current_block() if not subtensor.subnet_exists(netuid, block=block): - logging.error( - f":cross_mark: [red]Failed error:[/red] subnet [blue]{netuid}[/blue] does not exist." - ) - return False + message = f"Subnet #{netuid} does not exist." + logging.error(f":cross_mark: [red]Failed error:[/red] {message}") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) logging.info( f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue] [magenta]...[/magenta]" @@ -247,12 +261,13 @@ def register_extrinsic( ) if not neuron.is_null: - logging.info(":white_heavy_check_mark: [green]Already Registered[/green]") + message = "Already registered." + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") logging.info(f"\t\tuid: [blue]{neuron.uid}[/blue]") logging.info(f"\t\tnetuid: [blue]{neuron.netuid}[/blue]") logging.info(f"\t\thotkey: [blue]{neuron.hotkey}[/blue]") logging.info(f"\t\tcoldkey: [blue]{neuron.coldkey}[/blue]") - return True + return ExtrinsicResponse(True, message, extrinsic_function=get_function_name()) logging.debug( f"Registration hotkey: {wallet.hotkey.ss58_address}, Public coldkey: " @@ -261,7 +276,9 @@ def register_extrinsic( if not torch: log_no_torch_error() - return False + return ExtrinsicResponse( + False, "No torch installed.", extrinsic_function=get_function_name() + ) # Attempt rolling registration. attempts = 1 @@ -273,7 +290,9 @@ def register_extrinsic( # Solve latest POW. if cuda: if not torch.cuda.is_available(): - return False + return ExtrinsicResponse( + False, "CUDA not available.", extrinsic_function=get_function_name() + ) pow_result = create_pow( subtensor=subtensor, @@ -306,10 +325,11 @@ def register_extrinsic( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address ) if is_registered: - logging.error( - f":white_heavy_check_mark: [green]Already registered on netuid:[/green] [blue]{netuid}[/blue]" + message = f"Already registered on netuid: {netuid}" + logging.info(f":white_heavy_check_mark: [green]{message}[/green]") + return ExtrinsicResponse( + True, message, extrinsic_function=get_function_name() ) - return True # pow successful, proceed to submit pow to chain for registration else: @@ -329,48 +349,45 @@ def register_extrinsic( "coldkey": wallet.coldkeypub.ss58_address, }, ) - logging.debug( - ":satellite: [magenta]Sending POW Register Extrinsic...[/magenta]" - ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if not success: + if not response.success: # Look error here # https://github.com/opentensor/subtensor/blob/development/pallets/subtensor/src/errors.rs - if "HotKeyAlreadyRegisteredInSubNet" in message: + if "HotKeyAlreadyRegisteredInSubNet" in response.message: logging.info( f":white_heavy_check_mark: [green]Already Registered on subnet:[/green] " f"[blue]{netuid}[/blue]." ) - return True - logging.error(f":cross_mark: [red]Failed[/red]: {message}") + return response time.sleep(0.5) # Successful registration, final check for neuron and pubkey - if success: + if response.success: logging.info(":satellite: Checking Registration status...") is_registered = subtensor.is_hotkey_registered( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address ) if is_registered: logging.success( - ":white_heavy_check_mark: [green]Registered[/green]" + ":white_heavy_check_mark: [green]Registered.[/green]" ) - return True - else: + return response + # neuron not found, try again - logging.error( - ":cross_mark: [red]Unknown error. Neuron not found.[/red]" - ) - continue + logging.error( + ":cross_mark: [red]Unknown error. Neuron not found.[/red]" + ) + continue else: # Exited loop because pow is no longer valid. logging.error("[red]POW is stale.[/red]") @@ -385,8 +402,11 @@ def register_extrinsic( ) else: # Failed to register after max attempts. - logging.error("[red]No more attempts.[/red]") - return False + message = "No more attempts." + logging.error(f"[red]{message}[/red]") + return ExtrinsicResponse( + False, message, extrinsic_function=get_function_name() + ) def set_subnet_identity_extrinsic( @@ -405,7 +425,7 @@ def set_subnet_identity_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Set the identity information for a given subnet. @@ -429,14 +449,14 @@ def set_subnet_identity_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -455,25 +475,26 @@ def set_subnet_identity_extrinsic( }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) if not wait_for_finalization and not wait_for_inclusion: - return True, f"Identities for subnet {netuid} are sent to the chain." + return response - if success: + if response.success: logging.success( f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" ) - return True, f"Identities for subnet {netuid} are set." - else: - logging.error( - f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" - ) - return False, f"Failed to set identity for subnet {netuid}: {message}" + return response + + message = f"Failed to set identity for subnet #{netuid}" + logging.error(f":cross_mark: {message}: {response.message}") + response.message = message + return response diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 161ca53fd0..9117832efd 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -1,10 +1,8 @@ import time from typing import Optional, TYPE_CHECKING -from bittensor.utils import ( - u16_normalized_float, - unlock_key, -) +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import u16_normalized_float, unlock_key, get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -42,7 +40,7 @@ def root_register_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Registers the neuron to the root network. @@ -57,8 +55,14 @@ def root_register_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + netuid = 0 logging.info( f"Registering on netuid [blue]{netuid}[/blue] on network: [blue]{subtensor.network}[/blue]" @@ -79,15 +83,9 @@ def root_register_extrinsic( current_recycle = Balance.from_rao(int(recycle_call)) if balance < current_recycle: - logging.error( - f"[red]Insufficient balance {balance} to register neuron. " - f"Current recycle is {current_recycle} TAO[/red]." - ) - return False - - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + message = f"Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO" + logging.error(f"[red]{message}[/red].") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) logging.debug( f"Checking if hotkey ([blue]{wallet.hotkey_str}[/blue]) is registered on root." @@ -96,10 +94,11 @@ def root_register_extrinsic( netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address ) if is_registered: - logging.error( - ":white_heavy_check_mark: [green]Already registered on root network.[/green]" + message = "Already registered on root network." + logging.error(f":white_heavy_check_mark: [green]{message}[/green]") + return ExtrinsicResponse( + message=message, extrinsic_function=get_function_name() ) - return True logging.info(":satellite: [magenta]Registering to root network...[/magenta]") call = subtensor.substrate.compose_call( @@ -107,7 +106,7 @@ def root_register_extrinsic( call_function="root_register", call_params={"hotkey": wallet.hotkey.ss58_address}, ) - success, err_msg = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -116,24 +115,26 @@ def root_register_extrinsic( raise_error=raise_error, ) - if not success: - logging.error(f":cross_mark: [red]Failed error:[/red] {err_msg}") + if not response.success: + logging.error(f":cross_mark: [red]Failed error:[/red] {response.message}") time.sleep(0.5) - return False + return response # Successful registration, final check for neuron and pubkey - else: - uid = subtensor.substrate.query( - module="SubtensorModule", - storage_function="Uids", - params=[netuid, wallet.hotkey.ss58_address], + uid = subtensor.substrate.query( + module="SubtensorModule", + storage_function="Uids", + params=[netuid, wallet.hotkey.ss58_address], + ) + if uid is not None: + response.data = {"uid": uid} + logging.info( + f":white_heavy_check_mark: [green]Registered with UID: {uid}[/green]." ) - if uid is not None: - logging.info( - f":white_heavy_check_mark: [green]Registered with UID[/green] [blue]{uid}[/blue]." - ) - return True - else: - # neuron not found, try again - logging.error(":cross_mark: [red]Unknown error. Neuron not found.[/red]") - return False + return response + + # neuron not found + # neuron not found, try again + message = "Unknown error. Neuron not found." + logging.error(f":cross_mark: [red]{message}[/red]") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index dd62fa3669..3c916b6fb9 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -3,7 +3,9 @@ from bittensor.core.errors import MetadataError from bittensor.core.settings import version_as_int from bittensor.core.types import AxonServeCallParams +from bittensor.core.types import ExtrinsicResponse from bittensor.utils import ( + get_function_name, networking as net, unlock_key, Certificate, @@ -30,7 +32,7 @@ def serve_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Subscribes a Bittensor endpoint to the subtensor chain. @@ -52,12 +54,14 @@ def serve_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Decrypt hotkey if not (unlock := unlock_key(wallet, "hotkey")).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) params = AxonServeCallParams( **{ @@ -80,10 +84,9 @@ def serve_extrinsic( ) neuron_up_to_date = not neuron.is_null and params == neuron if neuron_up_to_date: - logging.debug( - f"Axon already served on: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue]" - ) - return True + message = f"Axon already served on: AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})" + logging.debug(f"[blue]{message}[/blue]") + return ExtrinsicResponse(True, message, extrinsic_function=get_function_name()) logging.debug( f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " @@ -101,7 +104,7 @@ def serve_extrinsic( call_params=params.dict(), ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -109,17 +112,18 @@ def serve_extrinsic( sign_with="hotkey", period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: + if response.success: logging.debug( f"Axon served with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] on " f"[green]{subtensor.network}:{netuid}[/green]" ) - return True + return response - logging.error(f"Failed: {message}") - return False + logging.error(f"Failed: {response.message}") + return response def serve_axon_extrinsic( @@ -131,7 +135,7 @@ def serve_axon_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Serves the axon to the network. @@ -149,11 +153,14 @@ def serve_axon_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(axon.wallet, "hotkey")).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + external_port = axon.external_port # ---- Get external ip ---- @@ -171,7 +178,7 @@ def serve_axon_extrinsic( external_ip = axon.external_ip # ---- Subscribe to chain ---- - serve_success = serve_extrinsic( + response = serve_extrinsic( subtensor=subtensor, wallet=axon.wallet, ip=external_ip, @@ -184,10 +191,10 @@ def serve_axon_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - return serve_success + return response -def publish_metadata( +def publish_metadata_extrinsic( subtensor: "Subtensor", wallet: "Wallet", netuid: int, @@ -198,7 +205,7 @@ def publish_metadata( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -220,16 +227,16 @@ def publish_metadata( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. - + ExtrinsicResponse: The result object of the extrinsic execution. Raises: MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates - failure. + failure. """ - if not (unlock := unlock_key(wallet, "hotkey")).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) fields = [{f"{data_type}": data}] if reset_bonds: @@ -244,7 +251,7 @@ def publish_metadata( }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, sign_with="hotkey", @@ -252,11 +259,12 @@ def publish_metadata( wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: - return True - raise MetadataError(message) + if response.success: + return response + raise MetadataError(response.message) def get_metadata( diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index eefe5ea65d..3bd061c928 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -1,9 +1,9 @@ from typing import Optional, TYPE_CHECKING, Sequence from async_substrate_interface.errors import SubstrateRequestException - +from bittensor.core.types import ExtrinsicResponse from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key, format_error_message, get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -25,7 +25,7 @@ def add_stake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. Staking is a fundamental process in the Bittensor network that enables neurons to participate actively and earn @@ -48,7 +48,7 @@ def add_stake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. @@ -57,7 +57,9 @@ def add_stake_extrinsic( # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -81,11 +83,14 @@ def add_stake_extrinsic( # Check enough to stake. if amount > old_balance: - logging.error(":cross_mark: [red]Not enough stake:[/red]") + message = "Not enough stake" + logging.error(f":cross_mark: [red]{message}:[/red]") logging.error(f"\t\tbalance:{old_balance}") logging.error(f"\t\tamount: {amount}") logging.error(f"\t\twallet: {wallet.name}") - return False + return ExtrinsicResponse( + False, f"{message}.", extrinsic_function=get_function_name() + ) call_params = { "hotkey": hotkey_ss58, @@ -133,7 +138,7 @@ def add_stake_extrinsic( call_params=call_params, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -143,12 +148,13 @@ def add_stake_extrinsic( nonce_key="coldkeypub", period=period, raise_error=raise_error, + calling_function=get_function_name(), ) # If we successfully staked. - if success: + if response.success: # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True + return response logging.success(":white_heavy_check_mark: [green]Finalized[/green]") @@ -172,15 +178,15 @@ def add_stake_extrinsic( logging.info( f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" ) - return True + return response - if safe_staking and "Custom error: 8" in message: + if safe_staking and "Custom error: 8" in response.message: logging.error( ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.error(f":cross_mark: [red]Failed: {response.message}.[/red]") + return response def add_stake_multiple_extrinsic( @@ -193,7 +199,7 @@ def add_stake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey on subnet with corresponding netuid. @@ -212,12 +218,14 @@ def add_stake_multiple_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) assert all( [ @@ -228,7 +236,9 @@ def add_stake_multiple_extrinsic( ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." if len(hotkey_ss58s) == 0: - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." @@ -243,7 +253,9 @@ def add_stake_multiple_extrinsic( if sum(amount.tao for amount in new_amounts) == 0: # Staking 0 tao - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -281,6 +293,7 @@ def add_stake_multiple_extrinsic( ] successful_stakes = 0 + response = ExtrinsicResponse(False, "", extrinsic_function=get_function_name()) for idx, (hotkey_ss58, amount, old_stake, netuid) in enumerate( zip(hotkey_ss58s, new_amounts, old_stakes, netuids) ): @@ -306,7 +319,7 @@ def add_stake_multiple_extrinsic( "netuid": netuid, }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -316,10 +329,11 @@ def add_stake_multiple_extrinsic( sign_with="coldkey", period=period, raise_error=raise_error, + calling_function=get_function_name(), ) # If we successfully staked. - if success: + if response.success: # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: old_balance -= amount @@ -347,7 +361,7 @@ def add_stake_multiple_extrinsic( old_balance = new_balance successful_stakes += 1 else: - logging.error(f":cross_mark: [red]Failed[/red]: {message}") + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") continue except SubstrateRequestException as error: @@ -364,6 +378,6 @@ def add_stake_multiple_extrinsic( logging.info( f"Balance: [blue]{initial_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) - return True + return response - return False + return response diff --git a/bittensor/core/extrinsics/start_call.py b/bittensor/core/extrinsics/start_call.py index 4a0b37e382..9c3026d07b 100644 --- a/bittensor/core/extrinsics/start_call.py +++ b/bittensor/core/extrinsics/start_call.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -16,7 +17,7 @@ def start_call_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). @@ -33,13 +34,13 @@ def start_call_extrinsic( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) start_call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -47,19 +48,14 @@ def start_call_extrinsic( call_params={"netuid": netuid}, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=start_call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if not wait_for_finalization and not wait_for_inclusion: - return True, message - - if success: - return True, "Success with `start_call` response." - - return True, message + return response diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 3effd7699b..40411f3740 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -2,7 +2,9 @@ from bittensor_wallet.bittensor_wallet import Wallet -from bittensor.utils import unlock_key +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import unlock_key, get_function_name +from bittensor.utils.btlogging import logging if TYPE_CHECKING: from bittensor.core.subtensor import Subtensor @@ -17,7 +19,7 @@ def increase_take_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Sets the delegate 'take' percentage for a neuron identified by its hotkey. Parameters: @@ -38,9 +40,11 @@ def increase_take_extrinsic( - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet, raise_error=raise_error) - if not unlock.success: - return False, unlock.message + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -58,6 +62,7 @@ def increase_take_extrinsic( wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) @@ -70,7 +75,7 @@ def decrease_take_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Sets the delegate `take` percentage for a neuron identified by its hotkey. @@ -92,9 +97,11 @@ def decrease_take_extrinsic( - False and an error message if the submission fails or the wallet cannot be unlocked. """ unlock = unlock_key(wallet, raise_error=raise_error) - if not unlock.success: - return False, unlock.message + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -112,4 +119,5 @@ def decrease_take_extrinsic( raise_error=raise_error, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + calling_function=get_function_name(), ) diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index 7d2a28ad9f..3769ec9f65 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -1,11 +1,12 @@ from typing import TYPE_CHECKING, Optional - +from bittensor.core.types import ExtrinsicResponse from bittensor.core.settings import NETWORK_EXPLORER_MAP from bittensor.utils import ( get_explorer_url_for_network, get_transfer_fn_params, is_valid_bittensor_address_or_public_key, unlock_key, + get_function_name, ) from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -26,7 +27,7 @@ def transfer_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """Transfers funds from this wallet to the destination public key address. Parameters: @@ -46,22 +47,23 @@ def transfer_extrinsic( Returns: bool: True if the subnet registration was successful, False otherwise. """ + # Unlock wallet coldkey. + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + if amount is None and not transfer_all: - logging.error("If not transferring all, `amount` must be specified.") - return False + message = "If not transferring all, `amount` must be specified." + logging.error(message) + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) # Validate destination address. if not is_valid_bittensor_address_or_public_key(destination): - logging.error( - f":cross_mark: [red]Invalid destination SS58 address[/red]: {destination}" - ) - return False - - logging.info(f"Initiating transfer on network: {subtensor.network}") - # Unlock wallet coldkey. - if not (unlock := unlock_key(wallet)).success: - logging.error(unlock.message) - return False + message = f"Invalid destination SS58 address: {destination}" + logging.error(f":cross_mark: [red]{message}[/red].") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) # Check balance. logging.info( @@ -84,14 +86,18 @@ def transfer_extrinsic( # Check if we have enough balance. if transfer_all is True: if (account_balance - fee) < existential_deposit: - logging.error("Not enough balance to transfer") - return False + message = "Not enough balance to transfer." + logging.error(message) + return ExtrinsicResponse( + False, message, extrinsic_function=get_function_name() + ) elif account_balance < (amount + fee + existential_deposit): + message = "Not enough balance." logging.error(":cross_mark: [red]Not enough balance[/red]") logging.error(f"\t\tBalance:\t[blue]{account_balance}[/blue]") logging.error(f"\t\tAmount:\t[blue]{amount}[/blue]") logging.error(f"\t\tFor fee:\t[blue]{fee}[/blue]") - return False + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) logging.info(":satellite: [magenta]Transferring...[/magenta]") @@ -103,16 +109,17 @@ def transfer_extrinsic( call_params=call_params, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: + if response.success: block_hash = subtensor.get_block_hash() logging.success(":white_heavy_check_mark: [green]Finalized[/green]") logging.info(f"[green]Block Hash:[/green] [blue]{block_hash}[/blue]") @@ -135,7 +142,7 @@ def transfer_extrinsic( logging.info( f"Balance: [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) - return True + return response - logging.error(f":cross_mark: [red]Failed[/red]: {message}") - return False + logging.error(f":cross_mark: [red]Failed[/red]: {response.message}") + return response diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index d9539a8268..8758a474d6 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -1,10 +1,10 @@ from typing import Optional, TYPE_CHECKING - +from bittensor.core.types import ExtrinsicResponse from async_substrate_interface.errors import SubstrateRequestException from bittensor.core.extrinsics.utils import get_extrinsic_fee from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key, format_error_message, get_function_name from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -26,7 +26,7 @@ def unstake_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -47,12 +47,14 @@ def unstake_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Decrypt keys, if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) logging.info( f":satellite: [magenta]Syncing with chain:[/magenta] [blue]{subtensor.network}[/blue] [magenta]...[/magenta]" @@ -71,119 +73,109 @@ def unstake_extrinsic( # Check enough to unstake. if amount > old_stake: - logging.error( - f":cross_mark: [red]Not enough stake[/red]: [green]{old_stake}[/green] to unstake: " - f"[blue]{amount}[/blue] from hotkey: [yellow]{wallet.hotkey_str}[/yellow]" - ) - return False + message = f"Not enough stake: {old_stake} to unstake: {amount} from hotkey: {wallet.hotkey_str}" + logging.error(f":cross_mark: [red]{message}[/red]") + return ExtrinsicResponse(False, message, extrinsic_function=get_function_name()) - try: - call_params = { - "hotkey": hotkey_ss58, - "netuid": netuid, - "amount_unstaked": amount.rao, - } + call_params = { + "hotkey": hotkey_ss58, + "netuid": netuid, + "amount_unstaked": amount.rao, + } - if safe_unstaking: - pool = subtensor.subnet(netuid=netuid) - base_price = pool.price.tao + if safe_unstaking: + pool = subtensor.subnet(netuid=netuid) + base_price = pool.price.tao - if pool.netuid == 0: - price_with_tolerance = base_price - else: - price_with_tolerance = base_price * (1 - rate_tolerance) - - logging_info = ( - f":satellite: [magenta]Safe Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " - f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " - f"price limit: [green]{price_with_tolerance}[/green], " - f"original price: [green]{base_price}[/green], " - f"with partial unstake: [green]{allow_partial_stake}[/green] " - f"on [blue]{subtensor.network}[/blue]" - ) - - limit_price = Balance.from_tao(price_with_tolerance).rao - call_params.update( - { - "limit_price": limit_price, - "allow_partial": allow_partial_stake, - } - ) - call_function = "remove_stake_limit" + if pool.netuid == 0: + price_with_tolerance = base_price else: - logging_info = ( - f":satellite: [magenta]Unstaking from:[/magenta] " - f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " - f"on [blue]{subtensor.network}[/blue]" - ) - call_function = "remove_stake" - - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function=call_function, - call_params=call_params, + price_with_tolerance = base_price * (1 - rate_tolerance) + + logging_info = ( + f":satellite: [magenta]Safe Unstaking from:[/magenta] " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green], " + f"tolerance percentage: [green]{rate_tolerance * 100}%[/green], " + f"price limit: [green]{price_with_tolerance}[/green], " + f"original price: [green]{base_price}[/green], " + f"with partial unstake: [green]{allow_partial_stake}[/green] " + f"on [blue]{subtensor.network}[/blue]" ) - fee = get_extrinsic_fee( - subtensor=subtensor, netuid=netuid, call=call, keypair=wallet.coldkeypub + + limit_price = Balance.from_tao(price_with_tolerance).rao + call_params.update( + { + "limit_price": limit_price, + "allow_partial": allow_partial_stake, + } ) - logging.info(f"{logging_info} for fee [blue]{fee}[/blue][magenta]...[/magenta]") - - success, message = subtensor.sign_and_send_extrinsic( - call=call, - wallet=wallet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - nonce_key="coldkeypub", - sign_with="coldkey", - use_nonce=True, - period=period, - raise_error=raise_error, + call_function = "remove_stake_limit" + else: + logging_info = ( + f":satellite: [magenta]Unstaking from:[/magenta] " + f"netuid: [green]{netuid}[/green], amount: [green]{amount}[/green] " + f"on [blue]{subtensor.network}[/blue]" ) + call_function = "remove_stake" - if success: # If we successfully unstaked. - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function=call_function, + call_params=call_params, + ) + fee = get_extrinsic_fee( + subtensor=subtensor, netuid=netuid, call=call, keypair=wallet.coldkeypub + ) + logging.info(f"{logging_info} for fee [blue]{fee}[/blue][magenta]...[/magenta]") + + response = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + nonce_key="coldkeypub", + sign_with="coldkey", + use_nonce=True, + period=period, + raise_error=raise_error, + calling_function=get_function_name(), + ) - logging.success(":white_heavy_check_mark: [green]Finalized[/green]") + if response.success: # If we successfully unstaked. + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return response - logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " - f"[magenta]...[/magenta]" - ) - block = subtensor.get_current_block() - new_balance = subtensor.get_balance( - wallet.coldkeypub.ss58_address, block=block - ) - new_stake = subtensor.get_stake( - coldkey_ss58=wallet.coldkeypub.ss58_address, - hotkey_ss58=hotkey_ss58, - netuid=netuid, - block=block, - ) - logging.info( - f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" - ) - logging.info( - f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" - ) - return True + logging.success(":white_heavy_check_mark: [green]Finalized[/green]") - if safe_unstaking and "Custom error: 8" in message: - logging.error( - ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or" - " enable partial staking." - ) - else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") - return False + logging.info( + f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " + f"[magenta]...[/magenta]" + ) + block = subtensor.get_current_block() + new_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address, block=block) + new_stake = subtensor.get_stake( + coldkey_ss58=wallet.coldkeypub.ss58_address, + hotkey_ss58=hotkey_ss58, + netuid=netuid, + block=block, + ) + logging.info( + f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" + ) + logging.info( + f"Stake: [blue]{old_stake}[/blue] :arrow_right: [green]{new_stake}[/green]" + ) + return response - except SubstrateRequestException as error: + if safe_unstaking and "Custom error: 8" in response.message: logging.error( - f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]" + ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or" + " enable partial staking." ) - return False + else: + logging.error(f":cross_mark: [red]Failed: {response.message}.[/red]") + return response def unstake_all_extrinsic( @@ -196,7 +188,7 @@ def unstake_all_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. Parameters: @@ -214,14 +206,13 @@ def unstake_all_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False, unlock.message + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call_params = { "hotkey": hotkey, @@ -240,7 +231,7 @@ def unstake_all_extrinsic( call_params=call_params, ) - success, message = subtensor.sign_and_send_extrinsic( + return subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -250,10 +241,9 @@ def unstake_all_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - return success, message - def unstake_multiple_extrinsic( subtensor: "Subtensor", @@ -266,7 +256,7 @@ def unstake_multiple_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, -) -> bool: +) -> ExtrinsicResponse: """ Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -285,12 +275,14 @@ def unstake_multiple_extrinsic( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ # Unlock coldkey. if not (unlock := unlock_key(wallet)).success: logging.error(unlock.message) - return False + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) # or amounts or unstake_all (no both) if amounts and unstake_all: @@ -308,7 +300,9 @@ def unstake_multiple_extrinsic( amounts = [amount.set_unit(netuid) for amount, netuid in zip(amounts, netuids)] if sum(amount.tao for amount in amounts) == 0: # Staking 0 tao - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) assert all( [ @@ -319,7 +313,9 @@ def unstake_multiple_extrinsic( ), "The `netuids`, `hotkey_ss58s` and `amounts` must be lists." if len(hotkey_ss58s) == 0: - return True + return ExtrinsicResponse( + True, "Success", extrinsic_function=get_function_name() + ) assert len(netuids) == len(hotkey_ss58s) == len(amounts), ( "The number of items in `netuids`, `hotkey_ss58s` and `amounts` must be the same." @@ -347,6 +343,9 @@ def unstake_multiple_extrinsic( ) successful_unstakes = 0 + response = ExtrinsicResponse( + False, "Failed", extrinsic_function=get_function_name() + ) for idx, (hotkey_ss58, amount, old_stake, netuid) in enumerate( zip(hotkey_ss58s, amounts, old_stakes, netuids) ): @@ -388,7 +387,7 @@ def unstake_multiple_extrinsic( f"[blue]{netuid}[/blue] for fee [blue]{fee}[/blue]" ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -398,9 +397,10 @@ def unstake_multiple_extrinsic( use_nonce=True, period=period, raise_error=raise_error, + calling_function=get_function_name(), ) - if success: # If we successfully unstaked. + if response.success: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: @@ -424,14 +424,16 @@ def unstake_multiple_extrinsic( ) successful_unstakes += 1 else: - logging.error(f":cross_mark: [red]Failed: {message}.[/red]") + logging.error(f":cross_mark: [red]Failed: {response.message}.[/red]") continue except SubstrateRequestException as error: logging.error( f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]" ) - return False + if raise_error: + raise error + return response if successful_unstakes != 0: logging.info( @@ -442,6 +444,6 @@ def unstake_multiple_extrinsic( logging.info( f"Balance: [blue]{old_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) - return True + return response - return False + return response diff --git a/bittensor/core/extrinsics/weights.py b/bittensor/core/extrinsics/weights.py index 0a3aa8e264..0378acb9fc 100644 --- a/bittensor/core/extrinsics/weights.py +++ b/bittensor/core/extrinsics/weights.py @@ -6,7 +6,8 @@ from numpy.typing import NDArray from bittensor.core.settings import version_as_int -from bittensor.utils import get_function_name +from bittensor.core.types import ExtrinsicResponse +from bittensor.utils import get_function_name, unlock_key from bittensor.utils.btlogging import logging from bittensor.utils.weight_utils import ( convert_and_normalize_weights_and_uids, @@ -28,7 +29,7 @@ def commit_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. This function is a wrapper around the `do_commit_weights` method. @@ -46,13 +47,16 @@ def commit_weights_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, `False` otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper error handling and user interaction when required. """ + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -62,7 +66,7 @@ def commit_weights_extrinsic( "commit_hash": commit_hash, }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -72,13 +76,14 @@ def commit_weights_extrinsic( sign_with="hotkey", nonce_key="hotkey", raise_error=raise_error, + calling_function=get_function_name(), ) - if success: - logging.info(message) + if response.success: + logging.info(response.message) else: - logging.error(f"{get_function_name()}: {message}") - return success, message + logging.error(f"{get_function_name()}: {response.message}") + return response def reveal_weights_extrinsic( @@ -93,7 +98,7 @@ def reveal_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. This function is a wrapper around the `_do_reveal_weights` method. @@ -114,13 +119,16 @@ def reveal_weights_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, `False` otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. This function provides a user-friendly interface for revealing weights on the Bittensor blockchain, ensuring proper error handling and user interaction when required. """ + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) call = subtensor.substrate.compose_call( call_module="SubtensorModule", @@ -134,7 +142,7 @@ def reveal_weights_extrinsic( }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -144,13 +152,14 @@ def reveal_weights_extrinsic( sign_with="hotkey", nonce_key="hotkey", raise_error=raise_error, + calling_function=get_function_name(), ) - if success: - logging.info(message) + if response.success: + logging.info(response.message) else: - logging.error(f"{get_function_name()}: {message}") - return success, message + logging.error(f"{get_function_name()}: {response.message}") + return response def set_weights_extrinsic( @@ -164,7 +173,7 @@ def set_weights_extrinsic( raise_error: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, str]: +) -> ExtrinsicResponse: """ Sets the given weights and values on a chain for a wallet hotkey account. @@ -183,10 +192,14 @@ def set_weights_extrinsic( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, `False` otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. """ + if not (unlock := unlock_key(wallet, unlock_type="hotkey")).success: + logging.error(unlock.message) + return ExtrinsicResponse( + False, unlock.message, extrinsic_function=get_function_name() + ) + # Convert types. uids, weights = convert_uids_and_weights(uids, weights) @@ -209,7 +222,7 @@ def set_weights_extrinsic( "version_key": version_key, }, ) - success, message = subtensor.sign_and_send_extrinsic( + response = subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -219,10 +232,11 @@ def set_weights_extrinsic( nonce_key="hotkey", sign_with="hotkey", raise_error=raise_error, + calling_function=get_function_name(), ) - if success: + if response.success: logging.info("Successfully set weights and Finalized.") else: - logging.error(f"{get_function_name}: {message}") - return success, message + logging.error(f"{get_function_name}: {response.message}") + return response diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 435498038c..7f885162a1 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -68,7 +68,7 @@ from bittensor.core.extrinsics.root import root_register_extrinsic from bittensor.core.extrinsics.serving import ( get_last_bonds_reset, - publish_metadata, + publish_metadata_extrinsic, get_metadata, serve_axon_extrinsic, ) @@ -94,7 +94,7 @@ SS58_FORMAT, TYPE_REGISTRY, ) -from bittensor.core.types import ParamWithTypes, SubtensorMixin +from bittensor.core.types import ExtrinsicResponse, ParamWithTypes, SubtensorMixin from bittensor.utils import ( Certificate, decode_hex_identity_dict, @@ -2735,60 +2735,6 @@ def recycle(self, netuid: int, block: Optional[int] = None) -> Optional[Balance] call = self.get_hyperparameter(param_name="Burn", netuid=netuid, block=block) return None if call is None else Balance.from_rao(int(call)) - def set_reveal_commitment( - self, - wallet, - netuid: int, - data: str, - blocks_until_reveal: int = 360, - block_time: Union[int, float] = 12, - period: Optional[int] = None, - raise_error: bool = False, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = True, - ) -> tuple[bool, int]: - """ - Commits arbitrary data to the Bittensor network by publishing metadata. - - Parameters: - wallet: The wallet associated with the neuron committing the data. - netuid: The unique identifier of the subnetwork. - data: The data to be committed to the network. - blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of - blocks in one epoch. - block_time: The number of seconds between each block. - period: The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You - can think of it as an expiration date for the transaction. - raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. - wait_for_inclusion: Whether to wait for the inclusion of the transaction. - wait_for_finalization: Whether to wait for the finalization of the transaction. - - Returns: - bool: `True` if the commitment was successful, `False` otherwise. - - Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically. - """ - - encrypted, reveal_round = get_encrypted_commitment( - data, blocks_until_reveal, block_time - ) - - # increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed - # and stored. - data_ = {"encrypted": encrypted, "reveal_round": reveal_round} - return publish_metadata( - subtensor=self, - wallet=wallet, - netuid=netuid, - data_type="TimelockEncrypted", - data=data_, - period=period, - raise_error=raise_error, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ), reveal_round - def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicInfo]: """ Retrieves the subnet information for a single subnet in the network. @@ -3042,29 +2988,32 @@ def sign_and_send_extrinsic( period: Optional[int] = None, nonce_key: str = "hotkey", raise_error: bool = False, - ) -> tuple[bool, str]: + calling_function: Optional[str] = None, + ) -> ExtrinsicResponse: """ Helper method to sign and submit an extrinsic call to chain. - Arguments: - call (scalecodec.types.GenericCall): a prepared Call object - wallet (bittensor_wallet.Wallet): the wallet whose coldkey will be used to sign the extrinsic - wait_for_inclusion (bool): whether to wait until the extrinsic call is included on the chain - wait_for_finalization (bool): whether to wait until the extrinsic call is finalized on the chain - sign_with (str): the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" - use_nonce (bool): unique identifier for the transaction related with hot/coldkey. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's - submitted. If the transaction is not included in a block within that number of blocks, it will expire - and be rejected. You can think of it as an expiration date for the transaction. + Parameters: + call: a prepared Call object + wallet: the wallet whose coldkey will be used to sign the extrinsic + wait_for_inclusion: whether to wait until the extrinsic call is included on the chain + wait_for_finalization: whether to wait until the extrinsic call is finalized on the chain + sign_with: the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" + use_nonce: unique identifier for the transaction related with hot/coldkey. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". raise_error: raises the relevant exception rather than returning `False` if unsuccessful. + calling_function: the name of the calling function. Returns: - (success, error message) + ExtrinsicResponse: The result object of the extrinsic execution. Raises: SubstrateRequestException: Substrate request exception. """ + extrinsic_response = ExtrinsicResponse(extrinsic_function=calling_function) possible_keys = ("coldkey", "hotkey", "coldkeypub") if sign_with not in possible_keys: raise AttributeError( @@ -3085,32 +3034,44 @@ def sign_and_send_extrinsic( if period is not None: extrinsic_data["era"] = {"period": period} - extrinsic = self.substrate.create_signed_extrinsic(**extrinsic_data) + extrinsic_response.extrinsic = self.substrate.create_signed_extrinsic( + **extrinsic_data + ) try: response = self.substrate.submit_extrinsic( - extrinsic, + extrinsic=extrinsic_response.extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - message = "Not waiting for finalization or inclusion." - logging.debug(f"{message}. Extrinsic: {extrinsic}") - return True, message + extrinsic_response.message = ( + "Not waiting for finalization or inclusion." + ) + return extrinsic_response if response.is_success: - return True, "" + extrinsic_response.message = "Success" + return extrinsic_response + + response_error_message = response.error_message if raise_error: - raise ChainError.from_error(response.error_message) + raise ChainError.from_error(response_error_message) - return False, format_error_message(response.error_message) + extrinsic_response.success = False + extrinsic_response.message = format_error_message(response_error_message) + extrinsic_response.error = response_error_message + return extrinsic_response - except SubstrateRequestException as e: + except SubstrateRequestException as error: if raise_error: raise - return False, format_error_message(e) + extrinsic_response.success = False + extrinsic_response.message = format_error_message(error) + extrinsic_response.error = error + return extrinsic_response # Extrinsics ======================================================================================================= @@ -3127,7 +3088,7 @@ def add_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Adds a stake from the specified wallet to the neuron identified by the SS58 address of its hotkey in specified subnet. Staking is a fundamental process in the Bittensor network that enables neurons to participate actively @@ -3153,7 +3114,7 @@ def add_stake( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: ``True`` if the staking is successful, ``False`` otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function enables neurons to increase their stake in the network, enhancing their influence and potential. When safe_staking is enabled, it provides protection against price fluctuations during the time stake is @@ -3187,7 +3148,7 @@ def add_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Adds liquidity to the specified price range. @@ -3206,9 +3167,7 @@ def add_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` method to enable/disable user liquidity. @@ -3237,7 +3196,7 @@ def add_stake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. This bulk operation allows for efficient staking across different neurons from a single wallet. @@ -3255,7 +3214,7 @@ def add_stake_multiple( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the staking is successful for all specified neurons, ``False`` otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function is essential for managing stakes across multiple neurons, reflecting the dynamic and collaborative nature of the Bittensor network. @@ -3280,7 +3239,7 @@ def burned_register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling TAO tokens, allowing them to be re-mined by performing work on the network. @@ -3296,7 +3255,7 @@ def burned_register( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the registration is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ if netuid == 0: @@ -3332,7 +3291,7 @@ def commit_weights( raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. This action serves as a commitment or snapshot of the neuron's current weight distribution. @@ -3353,16 +3312,15 @@ def commit_weights( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - tuple[bool, str]: - `True` if the weight commitment is successful, False otherwise. - `msg` is a string value describing the success or potential error. + ExtrinsicResponse: The result object of the extrinsic execution. This function allows neurons to create a tamper-proof record of their weight distribution at a specific point in time, enhancing transparency and accountability within the Bittensor network. """ retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to commit weights!" + response = ExtrinsicResponse( + False, "No attempt made. Perhaps it is too soon to commit weights!" + ) logging.info( f"Committing weights with params: " @@ -3380,9 +3338,9 @@ def commit_weights( version_key=version_key, ) - while retries < max_retries and success is False: + while retries < max_retries and response.success is False: try: - success, message = commit_weights_extrinsic( + response = commit_weights_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -3392,13 +3350,12 @@ def commit_weights( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - if success: - break - except Exception as e: - logging.error(f"Error committing weights: {e}") - retries += 1 + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error committing weights: {error}") + retries += 1 - return success, message + return response def modify_liquidity( self, @@ -3411,7 +3368,7 @@ def modify_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Modifies liquidity in liquidity position by adding or removing liquidity from it. Parameters: @@ -3428,9 +3385,7 @@ def modify_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Example: import bittensor as bt @@ -3487,7 +3442,7 @@ def move_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Moves stake to a different hotkey and/or subnet. @@ -3507,7 +3462,7 @@ def move_stake( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - success: True if the stake movement was successful. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount = check_and_convert_to_balance(amount) return move_stake_extrinsic( @@ -3541,7 +3496,7 @@ def register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Registers a neuron on the Bittensor subnet with provided netuid using the provided wallet. @@ -3568,7 +3523,7 @@ def register( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: ``True`` if the registration is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function facilitates the entry of new neurons into the network, supporting the decentralized growth and scalability of the Bittensor ecosystem. @@ -3598,7 +3553,7 @@ def register_subnet( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Registers a new subnetwork on the Bittensor network. @@ -3612,7 +3567,7 @@ def register_subnet( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: True if the subnet registration was successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ return register_subnet_extrinsic( subtensor=self, @@ -3633,7 +3588,7 @@ def remove_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Remove liquidity and credit balances back to wallet's hotkey stake. Parameters: @@ -3649,9 +3604,7 @@ def remove_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: - Adding is allowed even when user liquidity is enabled in specified subnet. Call `toggle_user_liquidity` @@ -3683,7 +3636,7 @@ def reveal_weights( raise_error: bool = True, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. This action serves as a revelation of the neuron's previously committed weight distribution. @@ -3704,21 +3657,20 @@ def reveal_weights( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. This function allows subnet validators to reveal their previously committed weight vector. See also: , """ retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to reveal weights!" + response = ExtrinsicResponse( + False, "No attempt made. Perhaps it is too soon to reveal weights!" + ) - while retries < max_retries and success is False: + while retries < max_retries and response.success is False: try: - success, message = reveal_weights_extrinsic( + response = reveal_weights_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -3731,13 +3683,12 @@ def reveal_weights( period=period, raise_error=raise_error, ) - if success: - break - except Exception as e: - logging.error(f"Error revealing weights: {e}") - retries += 1 + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error revealing weights: {error}") + retries += 1 - return success, message + return response def root_register( self, @@ -3746,7 +3697,7 @@ def root_register( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Register neuron by recycling some TAO. @@ -3760,7 +3711,7 @@ def root_register( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the registration is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. """ return root_register_extrinsic( @@ -3780,7 +3731,7 @@ def root_set_pending_childkey_cooldown( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Sets the pending childkey cooldown. Parameters: @@ -3794,9 +3745,7 @@ def root_set_pending_childkey_cooldown( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: This operation can only be successfully performed if your wallet has root privileges. """ @@ -3820,7 +3769,7 @@ def set_children( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Allows a coldkey to set children-keys. @@ -3837,9 +3786,7 @@ def set_children( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ return set_children_extrinsic( subtensor=self, @@ -3862,7 +3809,7 @@ def set_delegate_take( wait_for_finalization: bool = True, raise_error: bool = False, period: Optional[int] = None, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. @@ -3879,9 +3826,7 @@ def set_delegate_take( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Raises: DelegateTakeTooHigh: Delegate take is too high. @@ -3903,8 +3848,9 @@ def set_delegate_take( current_take_u16 = int(current_take * 0xFFFF) if current_take_u16 == take_u16: - logging.info(":white_heavy_check_mark: [green]Already Set[/green]") - return True, "" + message = f"The take for {hotkey_ss58} is already set to {take}" + logging.info(f":white_heavy_check_mark: [green]{message}[/green].") + return ExtrinsicResponse(True, message) logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") @@ -3914,7 +3860,7 @@ def set_delegate_take( else decrease_take_extrinsic ) - success, message = extrinsic_call( + response = extrinsic_call( subtensor=self, wallet=wallet, hotkey_ss58=hotkey_ss58, @@ -3925,10 +3871,10 @@ def set_delegate_take( wait_for_inclusion=wait_for_inclusion, ) - if success: + if response.success: logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") - return success, message + return response def set_subnet_identity( self, @@ -3939,7 +3885,7 @@ def set_subnet_identity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Sets the identity of a subnet for a specific wallet and network. @@ -3956,9 +3902,7 @@ def set_subnet_identity( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ return set_subnet_identity_extrinsic( subtensor=self, @@ -3992,7 +3936,7 @@ def set_weights( raise_error: bool = True, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Sets the interneuronal weights for the specified neuron. This process involves specifying the influence or trust a neuron places on other neurons in the network, which is a fundamental aspect of Bittensor's decentralized @@ -4016,9 +3960,7 @@ def set_weights( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. This function is crucial in shaping the network's collective intelligence, where each neuron's learning and contribution are influenced by the weights it sets towards others. @@ -4033,12 +3975,13 @@ def _blocks_weight_limit() -> bool: return bslu > wrl retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to commit weights!" + response = ExtrinsicResponse( + False, "No attempt made. Perhaps it is too soon to set weights!" + ) if ( uid := self.get_uid_for_hotkey_on_subnet(wallet.hotkey.ss58_address, netuid) ) is None: - return ( + return ExtrinsicResponse( False, f"Hotkey {wallet.hotkey.ss58_address} not registered in subnet {netuid}", ) @@ -4046,13 +3989,17 @@ def _blocks_weight_limit() -> bool: if self.commit_reveal_enabled(netuid=netuid): # go with `commit reveal v3` extrinsic - while retries < max_retries and success is False and _blocks_weight_limit(): + while ( + retries < max_retries + and response.success is False + and _blocks_weight_limit() + ): logging.info( f"Committing weights for subnet [blue]{netuid}[/blue]. " f"Attempt [blue]{retries + 1}[blue] of [green]{max_retries}[/green]." ) try: - success, message = commit_reveal_extrinsic( + response = commit_reveal_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -4066,21 +4013,27 @@ def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - except Exception as e: - logging.error(f"Error setting weights: {e}") + + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error setting weights: {error}") retries += 1 - return success, message + return response else: # go with classic `set_weights_extrinsic` - while retries < max_retries and success is False and _blocks_weight_limit(): + while ( + retries < max_retries + and response.success is False + and _blocks_weight_limit() + ): try: logging.info( f"Setting weights for subnet [blue]{netuid}[/blue]. " f"Attempt [blue]{retries + 1}[/blue] of [green]{max_retries}[/green]." ) - success, message = set_weights_extrinsic( + response = set_weights_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -4092,11 +4045,12 @@ def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - except Exception as e: - logging.error(f"Error setting weights: {e}") - retries += 1 + except Exception as error: + response.error = error if not response.error else response.error + logging.error(f"Error setting weights: {error}") + retries += 1 - return success, message + return response def serve_axon( self, @@ -4107,7 +4061,7 @@ def serve_axon( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. @@ -4126,7 +4080,7 @@ def serve_axon( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Returns: - bool: ``True`` if the Axon serve registration is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. By registering an Axon, the neuron becomes an active part of the network's distributed computing infrastructure, contributing to the collective intelligence of Bittensor. @@ -4151,7 +4105,7 @@ def set_commitment( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -4171,7 +4125,7 @@ def set_commitment( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - bool: `True` if the commitment was successful, `False` otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. Example: # Commit some data to subnet 1 @@ -4182,7 +4136,7 @@ def set_commitment( Note: See """ - return publish_metadata( + return publish_metadata_extrinsic( subtensor=self, wallet=wallet, netuid=netuid, @@ -4194,6 +4148,62 @@ def set_commitment( wait_for_finalization=wait_for_finalization, ) + def set_reveal_commitment( + self, + wallet, + netuid: int, + data: str, + blocks_until_reveal: int = 360, + block_time: Union[int, float] = 12, + period: Optional[int] = None, + raise_error: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + ) -> ExtrinsicResponse: + """ + Commits arbitrary data to the Bittensor network by publishing metadata. + + Parameters: + wallet: The wallet associated with the neuron committing the data. + netuid: The unique identifier of the subnetwork. + data: The data to be committed to the network. + blocks_until_reveal: The number of blocks from now after which the data will be revealed. Then number of + blocks in one epoch. + block_time: The number of seconds between each block. + period: The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You + can think of it as an expiration date for the transaction. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. + wait_for_finalization: Whether to wait for the finalization of the transaction. + + Returns: + ExtrinsicResponse: The result object of the extrinsic execution. + + Note: A commitment can be set once per subnet epoch and is reset at the next epoch in the chain automatically. + """ + + encrypted, reveal_round = get_encrypted_commitment( + data, blocks_until_reveal, block_time + ) + + # increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed + # and stored. + data_ = {"encrypted": encrypted, "reveal_round": reveal_round} + response = publish_metadata_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + data_type="TimelockEncrypted", + data=data_, + period=period, + raise_error=raise_error, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + response.data = data_ + return response + def start_call( self, wallet: "Wallet", @@ -4202,7 +4212,7 @@ def start_call( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a new subnet's emission mechanism). @@ -4218,9 +4228,7 @@ def start_call( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. """ return start_call_extrinsic( subtensor=self, @@ -4246,7 +4254,7 @@ def swap_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. Like subnet hopping - same owner, same hotkey, just changing which subnet the stake is in. @@ -4272,7 +4280,7 @@ def swap_stake( wait_for_finalization: Whether to wait for the finalization of the transaction. Returns: - success: True if the extrinsic was successful. + ExtrinsicResponse: The result object of the extrinsic execution. The price ratio for swap_stake in safe mode is calculated as: origin_subnet_price / destination_subnet_price When safe_staking is enabled, the swap will only execute if: @@ -4307,7 +4315,7 @@ def toggle_user_liquidity( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Allow to toggle user liquidity for specified subnet. Parameters: @@ -4322,9 +4330,7 @@ def toggle_user_liquidity( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - Tuple[bool, str]: - - True and a success message if the extrinsic is successfully submitted or processed. - - False and an error message if the submission fails or the wallet cannot be unlocked. + ExtrinsicResponse: The result object of the extrinsic execution. Note: The call can be executed successfully by the subnet owner only. """ @@ -4350,7 +4356,7 @@ def transfer( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - ) -> bool: + ) -> ExtrinsicResponse: """ Transfer token of amount to destination. @@ -4368,7 +4374,7 @@ def transfer( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - `True` if the transferring was successful, otherwise `False`. + ExtrinsicResponse: The result object of the extrinsic execution. """ if amount is not None: amount = check_and_convert_to_balance(amount) @@ -4397,7 +4403,7 @@ def transfer_stake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -4416,7 +4422,7 @@ def transfer_stake( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - success: True if the transfer was successful. + ExtrinsicResponse: The result object of the extrinsic execution. """ amount = check_and_convert_to_balance(amount) return transfer_stake_extrinsic( @@ -4446,7 +4452,7 @@ def unstake( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting individual neuron stakes within the Bittensor network. @@ -4471,7 +4477,7 @@ def unstake( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: ``True`` if the unstaking process is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function supports flexible stake management, allowing neurons to adjust their network participation and potential reward accruals. When safe_staking is enabled, it provides protection against price fluctuations @@ -4503,7 +4509,7 @@ def unstake_all( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> tuple[bool, str]: + ) -> ExtrinsicResponse: """Unstakes all TAO/Alpha associated with a hotkey from the specified subnets on the Bittensor network. Parameters: @@ -4520,10 +4526,7 @@ def unstake_all( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - tuple[bool, str]: - A tuple containing: - - `True` and a success message if the unstake operation succeeded; - - `False` and an error message otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. Example: # If you would like to unstake all stakes in all subnets safely: @@ -4590,7 +4593,7 @@ def unstake_multiple( raise_error: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, - ) -> bool: + ) -> ExtrinsicResponse: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts efficiently. This function is useful for managing the distribution of stakes across multiple neurons. @@ -4609,7 +4612,7 @@ def unstake_multiple( wait_for_finalization: Whether to wait for finalization of the extrinsic. Returns: - bool: ``True`` if the batch unstaking is successful, False otherwise. + ExtrinsicResponse: The result object of the extrinsic execution. This function allows for strategic reallocation or withdrawal of stakes, aligning with the dynamic stake management aspect of the Bittensor network. diff --git a/bittensor/core/types.py b/bittensor/core/types.py index d0c8b79cc9..aa158a6abf 100644 --- a/bittensor/core/types.py +++ b/bittensor/core/types.py @@ -1,13 +1,16 @@ -from abc import ABC import argparse -from typing import TypedDict, Optional +from abc import ABC +from dataclasses import dataclass +from typing import Any, Optional, TypedDict + +from scalecodec.types import GenericExtrinsic -from bittensor.utils import networking, Certificate -from bittensor.utils.btlogging import logging from bittensor.core import settings -from bittensor.core.config import Config from bittensor.core.chain_data import NeuronInfo, NeuronInfoLite +from bittensor.core.config import Config from bittensor.utils import determine_chain_endpoint_and_network +from bittensor.utils import networking, Certificate +from bittensor.utils.btlogging import logging class SubtensorMixin(ABC): @@ -262,3 +265,102 @@ class PrometheusServeCallParams(TypedDict): class ParamWithTypes(TypedDict): name: str # Name of the parameter. type: str # ScaleType string of the parameter. + + +@dataclass +class ExtrinsicResponse: + """ + A standardized response container for handling the extrinsic results submissions and related operations in the SDK. + + This class is designed to give developers a consistent way to represent the outcome of an extrinsic call — whether + it succeeded or failed — along with useful metadata for debugging, logging, or higher-level business logic. + + The object also implements tuple-like behavior: + * Iteration yields ``(success, message)``. + * Indexing is supported: ``response[0] -> success``, ``response[1] -> message``. + * ``len(response)`` returns 2. + + Attributes: + success: Indicates if the extrinsic execution was successful. + message: A status or informational message returned from the execution (e.g., "Successfully registered subnet"). + error: Captures the underlying exception if the extrinsic failed, otherwise `None`. + data: Arbitrary data returned from the extrinsic, such as decoded events, or extra context. + extrinsic_function: The name of the SDK extrinsic function that was executed (e.g. "register_subnet_extrinsic"). + extrinsic: The raw extrinsic object used in the call, if available. + + Example: + import bittensor as bt + + subtensor = bt.SubtensorApi("local") + wallet = bt.Wallet("alice") + + response = subtensor.subnets.register_subnet(alice_wallet) + print(response) + + ExtrinsicResponse: + success: True + message: Successfully registered subnet + error: None + extrinsic_function: register_subnet_extrinsic + extrinsic: {'account_id': '0xd43593c715fdd31c... + + success, message = response + print(success, message) + + True Successfully registered subnet + + print(response[0]) + True + print(response[1]) + 'Successfully registered subnet' + """ + + success: bool = True + message: str = None + error: Optional[Exception] = None + data: Optional[Any] = None + extrinsic_function: Optional[str] = None + extrinsic: Optional[GenericExtrinsic] = None + + def __iter__(self): + yield self.success + yield self.message + + def __str__(self): + return str( + f"{self.__class__.__name__}:" + f"\n\tsuccess: {self.success}" + f"\n\tmessage: {self.message}" + f"\n\terror: {self.error}" + f"\n\textrinsic_function: {self.extrinsic_function}" + f"\n\textrinsic: {self.extrinsic}" + ) + + def __repr__(self): + return repr((self.success, self.message)) + + def __eq__(self, other: Any) -> bool: + if isinstance(other, tuple): + return (self.success, self.message) == other + if isinstance(other, ExtrinsicResponse): + return ( + self.success == other.success + and self.message == other.message + and self.error == other.error + and self.extrinsic_function == other.extrinsic_function + and self.extrinsic == other.extrinsic + ) + return super().__eq__(other) + + def __getitem__(self, index: int) -> Any: + if index == 0: + return self.success + elif index == 1: + return self.message + else: + raise IndexError( + "ExtrinsicResponse only supports indices 0 (success) and 1 (message)." + ) + + def __len__(self): + return 2 diff --git a/migration.md b/migration.md index 3e4b0a7a1a..55d14e1a8f 100644 --- a/migration.md +++ b/migration.md @@ -42,11 +42,12 @@ ``` -2. Unify extrinsic return values by introducing an ExtrinsicResponse class. Extrinsics currently return either a boolean or a tuple. +2. ✅ Unify extrinsic return values by introducing an ExtrinsicResponse class. Extrinsics currently return either a boolean or a tuple. Purpose: - - Ease of processing - - This class should contain success, message, and optionally data and logs. (to save all logs during the extrinsic) + - Ease of processing. + - This class should contain success, message, and optionally data. + - Opportunity to expand the content of the extrinsic's response at any time upon community request or based on new technical requirements any time. 3. ✅ Set `wait_for_inclusion` and `wait_for_finalization` to `True` by default in extrinsics and their related calls. Then we will guarantee the correct/expected extrinsic call response is consistent with the chain response. If the user changes those values, then it is the user's responsibility. 4. ✅ Make the internal logic of extrinsics the same. There are extrinsics that are slightly different in implementation. @@ -112,8 +113,8 @@ rename this variable in documentation. ## New features 1. Add `bittensor.utils.hex_to_ss58` function. SDK still doesn't have it. (Probably inner import `from scalecodec import ss58_encode, ss58_decode`) -2. Implement Crowdloan logic. -3. “Implement Sub-subnets / Metagraph Changes?” (implementation unsure) Maciej Kula idea, requires mode details. +2. Implement Crowdloan logic. Issue: https://github.com/opentensor/bittensor/issues/3017 +3. Implement Sub-subnets logic. Subtensor PR https://github.com/opentensor/subtensor/pull/1984 ## Testing 1. When running tests via Docker, ensure no lingering processes occupy required ports before launch. @@ -197,6 +198,9 @@ wait_for_finalization: bool = False, - [x] alias `subtensor.set_commitment` removed - [x] `subtensor.comit` renamed to `subtensor.set_commitment` - [x] `.publish_metadata`, `subtensor.set_commitment` and `subtenor.set_reveal_commitment` + - Changes in `.publish_metadata` and subtensor's related calls: + - `publish_metadata` renamed to `publish_metadata_extrinsic` + - The response `subtensor.set_reveal_commitment` contains the field `date={"encrypted": encrypted, "reveal_round": reveal_round}` - [x] `.add_stake_extrinsic` and `subtensor.add_stake` - Changes in `.add_stake_extrinsic` and `subtensor.add_stake`: - parameter `old_balance` removed from async version @@ -223,4 +227,10 @@ wait_for_finalization: bool = False, - parameter `amounts` is now required (no Optional anymore) - [x] `.commit_weights_extrinsic` and `subtensor.commit_weights` - [x] `.reveal_weights_extrinsic` and `subtensor.reveal_weights` -- [x] `.set_weights_extrinsic` and `subtensor.set_weights` \ No newline at end of file +- [x] `.set_weights_extrinsic` and `subtensor.set_weights` + +All extrinsics and related subtensor calls now return an object of class `bittensor.core.types.ExtrinsicResponse` +Additional changes in extrinsics: + - `commit_reveal_extrinsic` and `subtensor.set_weights` (with `commit_reveal_enabled=True`), the response is the usual `ExtrinsicResponse` (`success` and `message` unchanged), plus field `data` holds the `{"reveal_round": reveal_round}`. + - in positive case, all extrinsics return `response.message = "Success"` + - `root_register_extrinsic`, `subtensor.burned_register` (with netuid=0) and `subtensor.root_register` has response `ExtrinsicResponse`. In successful case `.data` holds the `{"uid": uid}` where is `uid` is uid of registered neuron. diff --git a/pyproject.toml b/pyproject.toml index c29ce7a344..41c1b1b178 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "bittensor" version = "9.10.1" -description = "Bittensor" +description = "Bittensor SDK" readme = "README.md" authors = [ {name = "bittensor.com"} @@ -36,7 +36,7 @@ dependencies = [ "uvicorn", "bittensor-drand>=1.0.0,<2.0.0", "bittensor-wallet>=4.0.0,<5.0", - "async-substrate-interface>=1.5.1" + "async-substrate-interface>=1.5.2" ] [project.optional-dependencies] @@ -66,7 +66,7 @@ torch = [ "torch>=1.13.1,<3.0" ] cli = [ - "bittensor-cli>=9.0.2" + "bittensor-cli>=9.10.2" ] diff --git a/tests/e2e_tests/test_commit_reveal.py b/tests/e2e_tests/test_commit_reveal.py index 0fc4637359..d6c284b533 100644 --- a/tests/e2e_tests/test_commit_reveal.py +++ b/tests/e2e_tests/test_commit_reveal.py @@ -150,7 +150,7 @@ async def test_commit_and_reveal_weights_cr4(local_chain, subtensor, alice_walle # commit_block is the block when weights were committed on the chain (transaction block) expected_commit_block = subtensor.block + 1 # Commit weights - success, message = subtensor.extrinsics.set_weights( + response = subtensor.extrinsics.set_weights( wallet=alice_wallet, netuid=alice_subnet_netuid, uids=weight_uids, @@ -162,11 +162,11 @@ async def test_commit_and_reveal_weights_cr4(local_chain, subtensor, alice_walle ) # Assert committing was a success - assert success is True, message - assert bool(re.match(r"reveal_round:\d+", message)) + assert response.success is True, response.message + assert response.data.get("reveal_round") is not None # Parse expected reveal_round - expected_reveal_round = int(message.split(":")[1]) + expected_reveal_round = response.data.get("reveal_round") logging.console.success( f"Successfully set weights: uids {weight_uids}, weights {weight_vals}, reveal_round: {expected_reveal_round}" ) @@ -373,7 +373,7 @@ async def test_commit_and_reveal_weights_cr4_async( ) # Commit weights - success, message = await async_subtensor.extrinsics.set_weights( + response = await async_subtensor.extrinsics.set_weights( wallet=alice_wallet, netuid=alice_subnet_netuid, uids=weight_uids, @@ -385,11 +385,11 @@ async def test_commit_and_reveal_weights_cr4_async( ) # Assert committing was a success - assert success is True, message - assert bool(re.match(r"reveal_round:\d+", message)) + assert response.success is True, response.message + assert response.data.get("reveal_round") is not None # Parse expected reveal_round - expected_reveal_round = int(message.split(":")[1]) + expected_reveal_round = response.data.get("reveal_round") logging.console.success( f"Successfully set weights: uids {weight_uids}, weights {weight_vals}, reveal_round: {expected_reveal_round}" ) diff --git a/tests/e2e_tests/test_commitment.py b/tests/e2e_tests/test_commitment.py index d31bf739ef..825f3a3625 100644 --- a/tests/e2e_tests/test_commitment.py +++ b/tests/e2e_tests/test_commitment.py @@ -33,7 +33,7 @@ def test_commitment(subtensor, alice_wallet, dave_wallet): assert subtensor.subnets.burned_register( wallet=alice_wallet, netuid=dave_subnet_netuid, - ) + ).success uid = subtensor.subnets.get_uid_for_hotkey_on_subnet( hotkey_ss58=alice_wallet.hotkey.ss58_address, @@ -109,10 +109,12 @@ async def test_commitment_async(async_subtensor, alice_wallet, dave_wallet): data="Hello World!", ) - assert await sub.subnets.burned_register( - wallet=alice_wallet, - netuid=dave_subnet_netuid, - ) + assert ( + await sub.subnets.burned_register( + wallet=alice_wallet, + netuid=dave_subnet_netuid, + ) + ).success uid = await sub.subnets.get_uid_for_hotkey_on_subnet( alice_wallet.hotkey.ss58_address, diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py index 04417f6615..6851fc6110 100644 --- a/tests/e2e_tests/test_delegate.py +++ b/tests/e2e_tests/test_delegate.py @@ -45,16 +45,16 @@ def test_identity(subtensor, alice_wallet, bob_wallet): identities = subtensor.delegates.get_delegate_identities() assert alice_wallet.coldkey.ss58_address not in identities - subtensor.extrinsics.root_register( - alice_wallet, + assert subtensor.extrinsics.root_register( + wallet=alice_wallet, wait_for_inclusion=True, wait_for_finalization=True, - ) + ).success identities = subtensor.delegates.get_delegate_identities() assert alice_wallet.coldkey.ss58_address not in identities - success, error = set_identity( + success, message = set_identity( subtensor=subtensor, wallet=alice_wallet, name="Alice", @@ -62,8 +62,8 @@ def test_identity(subtensor, alice_wallet, bob_wallet): github_repo="https://github.com/opentensor/bittensor", description="Local Chain", ) - assert error == "" - assert success is True + assert success is True, message + assert message == "Success" identity = subtensor.neurons.query_identity(alice_wallet.coldkeypub.ss58_address) assert identity == ChainIdentity( @@ -109,16 +109,18 @@ async def test_identity_async(async_subtensor, alice_wallet, bob_wallet): identities = await async_subtensor.delegates.get_delegate_identities() assert alice_wallet.coldkey.ss58_address not in identities - await async_subtensor.extrinsics.root_register( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert ( + await async_subtensor.extrinsics.root_register( + wallet=alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + ).success identities = await async_subtensor.delegates.get_delegate_identities() assert alice_wallet.coldkey.ss58_address not in identities - success, error = await async_set_identity( + success, message = await async_set_identity( subtensor=async_subtensor, wallet=alice_wallet, name="Alice", @@ -127,8 +129,8 @@ async def test_identity_async(async_subtensor, alice_wallet, bob_wallet): description="Local Chain", ) - assert error == "" - assert success is True + assert success is True, message + assert message == "Success" identity = await async_subtensor.neurons.query_identity( alice_wallet.coldkeypub.ss58_address @@ -171,26 +173,26 @@ def test_change_take(subtensor, alice_wallet, bob_wallet): logging.console.info("Testing [green]test_change_take[/green].") with pytest.raises(HotKeyAccountNotExists): subtensor.delegates.set_delegate_take( - alice_wallet, - alice_wallet.hotkey.ss58_address, - 0.1, + wallet=alice_wallet, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + take=0.1, raise_error=True, ) - subtensor.extrinsics.root_register( - alice_wallet, + assert subtensor.extrinsics.root_register( + wallet=alice_wallet, wait_for_inclusion=True, wait_for_finalization=True, - ) + ).success take = subtensor.delegates.get_delegate_take(alice_wallet.hotkey.ss58_address) assert take == DEFAULT_DELEGATE_TAKE with pytest.raises(NonAssociatedColdKey): subtensor.delegates.set_delegate_take( - bob_wallet, - alice_wallet.hotkey.ss58_address, - 0.1, + wallet=bob_wallet, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + take=0.1, raise_error=True, ) @@ -202,12 +204,12 @@ def test_change_take(subtensor, alice_wallet, bob_wallet): raise_error=True, ) - subtensor.delegates.set_delegate_take( + assert subtensor.delegates.set_delegate_take( alice_wallet, alice_wallet.hotkey.ss58_address, 0.1, raise_error=True, - ) + ).success take = subtensor.delegates.get_delegate_take(alice_wallet.hotkey.ss58_address) assert take == 0.09999237048905166 @@ -232,12 +234,12 @@ def test_change_take(subtensor, alice_wallet, bob_wallet): }, ) - subtensor.delegates.set_delegate_take( + assert subtensor.delegates.set_delegate_take( wallet=alice_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, take=0.15, raise_error=True, - ) + ).success take = subtensor.delegates.get_delegate_take(alice_wallet.hotkey.ss58_address) assert take == 0.14999618524452582 @@ -263,11 +265,13 @@ async def test_change_take_async(async_subtensor, alice_wallet, bob_wallet): raise_error=True, ) - await async_subtensor.extrinsics.root_register( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert ( + await async_subtensor.extrinsics.root_register( + wallet=alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + ).success take = await async_subtensor.delegates.get_delegate_take( alice_wallet.hotkey.ss58_address @@ -290,12 +294,14 @@ async def test_change_take_async(async_subtensor, alice_wallet, bob_wallet): raise_error=True, ) - await async_subtensor.delegates.set_delegate_take( - alice_wallet, - alice_wallet.hotkey.ss58_address, - 0.1, - raise_error=True, - ) + assert ( + await async_subtensor.delegates.set_delegate_take( + alice_wallet, + alice_wallet.hotkey.ss58_address, + 0.1, + raise_error=True, + ) + ).success take = await async_subtensor.delegates.get_delegate_take( alice_wallet.hotkey.ss58_address @@ -324,12 +330,14 @@ async def test_change_take_async(async_subtensor, alice_wallet, bob_wallet): }, ) - await async_subtensor.delegates.set_delegate_take( - wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - take=0.15, - raise_error=True, - ) + assert ( + await async_subtensor.delegates.set_delegate_take( + wallet=alice_wallet, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + take=0.15, + raise_error=True, + ) + ).success take = await async_subtensor.delegates.get_delegate_take( alice_wallet.hotkey.ss58_address @@ -368,16 +376,16 @@ def test_delegates(subtensor, alice_wallet, bob_wallet): subtensor.delegates.is_hotkey_delegate(bob_wallet.hotkey.ss58_address) is False ) - subtensor.extrinsics.root_register( - alice_wallet, + assert subtensor.extrinsics.root_register( + wallet=alice_wallet, wait_for_inclusion=True, wait_for_finalization=True, - ) - subtensor.extrinsics.root_register( - bob_wallet, + ).success + assert subtensor.extrinsics.root_register( + wallet=bob_wallet, wait_for_inclusion=True, wait_for_finalization=True, - ) + ).success assert ( subtensor.delegates.is_hotkey_delegate(alice_wallet.hotkey.ss58_address) is True @@ -450,12 +458,12 @@ def test_delegates(subtensor, alice_wallet, bob_wallet): is True ) - subtensor.staking.add_stake( + assert subtensor.staking.add_stake( wallet=bob_wallet, netuid=alice_subnet_netuid, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), - ) + ).success # let chain update validator_permits subtensor.wait_for_block(subtensor.block + set_tempo + 1) @@ -521,16 +529,20 @@ async def test_delegates_async(async_subtensor, alice_wallet, bob_wallet): is False ) - await async_subtensor.extrinsics.root_register( - alice_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - await async_subtensor.extrinsics.root_register( - bob_wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert ( + await async_subtensor.extrinsics.root_register( + wallet=alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + ).success + assert ( + await async_subtensor.extrinsics.root_register( + wallet=bob_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + ).success assert ( await async_subtensor.delegates.is_hotkey_delegate( @@ -615,12 +627,14 @@ async def test_delegates_async(async_subtensor, alice_wallet, bob_wallet): ) )[0] is True - await async_subtensor.staking.add_stake( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(10_000), - ) + assert ( + await async_subtensor.staking.add_stake( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(10_000), + ) + ).success # let chain update validator_permits await async_subtensor.wait_for_block(await async_subtensor.block + set_tempo + 1) @@ -670,23 +684,22 @@ def test_nominator_min_required_stake(subtensor, alice_wallet, bob_wallet, dave_ minimum_required_stake = subtensor.staking.get_minimum_required_stake() assert minimum_required_stake == Balance(0) - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - ) + ).success - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid, - ) + ).success - success = subtensor.staking.add_stake( + assert subtensor.staking.add_stake( wallet=dave_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=Balance.from_tao(1000), - ) - assert success is True + ).success stake = subtensor.staking.get_stake( coldkey_ss58=dave_wallet.coldkey.ss58_address, @@ -753,23 +766,28 @@ async def test_nominator_min_required_stake_async( minimum_required_stake = await async_subtensor.staking.get_minimum_required_stake() assert minimum_required_stake == Balance(0) - await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + ) + ).success - await async_subtensor.subnets.burned_register( - wallet=dave_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=dave_wallet, + netuid=alice_subnet_netuid, + ) + ).success - success = await async_subtensor.staking.add_stake( - wallet=dave_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=Balance.from_tao(1000), - ) - assert success is True + assert ( + await async_subtensor.staking.add_stake( + wallet=dave_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=Balance.from_tao(1000), + ) + ).success stake = await async_subtensor.staking.get_stake( coldkey_ss58=dave_wallet.coldkey.ss58_address, @@ -813,7 +831,7 @@ def test_get_vote_data(subtensor, alice_wallet): """ logging.console.info("Testing [green]test_get_vote_data[/green].") - assert subtensor.extrinsics.root_register(alice_wallet), ( + assert subtensor.extrinsics.root_register(alice_wallet).success, ( "Can not register Alice in root SN." ) @@ -825,7 +843,7 @@ def test_get_vote_data(subtensor, alice_wallet): assert proposals.records == [] - success, error = propose( + success, message = propose( subtensor=subtensor, wallet=alice_wallet, proposal=subtensor.substrate.compose_call( @@ -840,8 +858,8 @@ def test_get_vote_data(subtensor, alice_wallet): duration=1_000_000, ) - assert error == "" - assert success is True + assert success is True, message + assert message == "Success" proposals = subtensor.queries.query_map( module="Triumvirate", @@ -881,7 +899,7 @@ def test_get_vote_data(subtensor, alice_wallet): threshold=3, ) - success, error = vote( + success, message = vote( subtensor=subtensor, wallet=alice_wallet, hotkey=alice_wallet.hotkey.ss58_address, @@ -890,8 +908,8 @@ def test_get_vote_data(subtensor, alice_wallet): approve=True, ) - assert error == "" - assert success is True + assert success is True, message + assert message == "Success" proposal = subtensor.chain.get_vote_data( proposal_hash=proposal_hash, @@ -920,7 +938,7 @@ async def test_get_vote_data_async(async_subtensor, alice_wallet): """ logging.console.info("Testing [green]test_get_vote_data_async[/green].") - assert await async_subtensor.extrinsics.root_register(alice_wallet), ( + assert (await async_subtensor.extrinsics.root_register(alice_wallet)).success, ( "Can not register Alice in root SN." ) @@ -932,7 +950,7 @@ async def test_get_vote_data_async(async_subtensor, alice_wallet): assert proposals.records == [] - success, error = await async_propose( + success, message = await async_propose( subtensor=async_subtensor, wallet=alice_wallet, proposal=await async_subtensor.substrate.compose_call( @@ -947,8 +965,8 @@ async def test_get_vote_data_async(async_subtensor, alice_wallet): duration=1_000_000, ) - assert error == "" assert success is True + assert message == "Success" proposals = await async_subtensor.queries.query_map( module="Triumvirate", @@ -989,7 +1007,7 @@ async def test_get_vote_data_async(async_subtensor, alice_wallet): threshold=3, ) - success, error = await async_vote( + success, message = await async_vote( subtensor=async_subtensor, wallet=alice_wallet, hotkey=alice_wallet.hotkey.ss58_address, @@ -998,8 +1016,8 @@ async def test_get_vote_data_async(async_subtensor, alice_wallet): approve=True, ) - assert error == "" - assert success is True + assert success is True, message + assert message == "Success" proposal = await async_subtensor.chain.get_vote_data( proposal_hash=proposal_hash, diff --git a/tests/e2e_tests/test_dendrite.py b/tests/e2e_tests/test_dendrite.py index 73d646aadf..0aa7d9e6f2 100644 --- a/tests/e2e_tests/test_dendrite.py +++ b/tests/e2e_tests/test_dendrite.py @@ -61,7 +61,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): netuid=alice_subnet_netuid, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), - ) + ).success # set tempo to 10 block for non-fast-runtime assert sudo_set_admin_utils( substrate=subtensor.substrate, @@ -98,7 +98,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): assert status is True # Register Bob to the network - assert subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid), ( + assert subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid).success, ( "Unable to register Bob as a neuron" ) @@ -129,7 +129,7 @@ async def test_dendrite(subtensor, templates, alice_wallet, bob_wallet): netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=tao, - ) + ).success # Refresh metagraph metagraph = subtensor.metagraphs.metagraph(alice_subnet_netuid) @@ -203,14 +203,16 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall if not await async_subtensor.chain.is_fast_blocks(): # Make sure Alice is Top Validator (for non-fast-runtime only) - assert await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(5), - wait_for_inclusion=False, - wait_for_finalization=False, - ) + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(5), + wait_for_inclusion=False, + wait_for_finalization=False, + ) + ).success # set tempo to 10 block for non-fast-runtime assert await async_sudo_set_admin_utils( substrate=async_subtensor.substrate, @@ -247,9 +249,9 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall assert status is True # Register Bob to the network - assert await async_subtensor.subnets.burned_register( - bob_wallet, alice_subnet_netuid - ), "Unable to register Bob as a neuron" + assert ( + await async_subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid) + ).success, "Unable to register Bob as a neuron" metagraph = await async_subtensor.metagraphs.metagraph(alice_subnet_netuid) @@ -273,14 +275,16 @@ async def test_dendrite_async(async_subtensor, templates, alice_wallet, bob_wall await async_subtensor.subnets.subnet(alice_subnet_netuid) ).tao_to_alpha_with_slippage(tao) - assert await async_subtensor.staking.add_stake( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=tao, - wait_for_inclusion=False, - wait_for_finalization=False, - ) + assert ( + await async_subtensor.staking.add_stake( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=tao, + wait_for_inclusion=False, + wait_for_finalization=False, + ) + ).success # Refresh metagraph metagraph = await async_subtensor.metagraphs.metagraph(alice_subnet_netuid) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index f9fde7a37f..dfde2595c6 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -63,7 +63,7 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): assert subtensor.subnets.burned_register( wallet=alice_wallet, netuid=dave_subnet_netuid, - ) + ).success assert subtensor.wallets.does_hotkey_exist(hotkey) is True assert subtensor.wallets.get_hotkey_owner(hotkey) == coldkey @@ -118,10 +118,12 @@ async def test_hotkeys_async(async_subtensor, alice_wallet, dave_wallet): is False ) - assert await async_subtensor.subnets.burned_register( - wallet=alice_wallet, - netuid=dave_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=alice_wallet, + netuid=dave_subnet_netuid, + ) + ).success assert await async_subtensor.wallets.does_hotkey_exist(hotkey) is True assert await async_subtensor.wallets.get_hotkey_owner(hotkey) == coldkey @@ -159,11 +161,8 @@ def test_children(subtensor, alice_wallet, bob_wallet, dave_wallet): success, message = subtensor.extrinsics.root_set_pending_childkey_cooldown( wallet=alice_wallet, cooldown=ROOT_COOLDOWN ) - assert success, f"Call `root_set_pending_childkey_cooldown` failed: {message}" - assert ( - message - == "Success with `root_set_pending_childkey_cooldown_extrinsic` response." - ) + assert success is True, message + assert message == "Success" assert subtensor.subnets.register_subnet(dave_wallet) assert subtensor.subnets.subnet_exists(dave_subnet_netuid), ( @@ -224,13 +223,13 @@ def test_children(subtensor, alice_wallet, bob_wallet, dave_wallet): assert subtensor.subnets.burned_register( wallet=alice_wallet, netuid=dave_subnet_netuid, - ) + ).success logging.console.success(f"Alice registered on subnet {dave_subnet_netuid}") assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=dave_subnet_netuid, - ) + ).success logging.console.success(f"Bob registered on subnet {dave_subnet_netuid}") success, children, error = subtensor.wallets.get_children( @@ -321,9 +320,8 @@ def test_children(subtensor, alice_wallet, bob_wallet, dave_wallet): wait_for_inclusion=True, wait_for_finalization=True, ) - - assert message == "Success with `set_children_extrinsic` response." - assert success is True + assert success is True, message + assert message == "Success" set_children_block = subtensor.block @@ -406,7 +404,8 @@ def test_children(subtensor, alice_wallet, bob_wallet, dave_wallet): wait_for_inclusion=True, wait_for_finalization=True, ) - assert success, message + assert success is True, message + assert message == "Success" set_children_block = subtensor.block @@ -484,11 +483,8 @@ async def test_children_async(async_subtensor, alice_wallet, bob_wallet, dave_wa ) = await async_subtensor.extrinsics.root_set_pending_childkey_cooldown( wallet=alice_wallet, cooldown=ROOT_COOLDOWN ) - assert success, f"Call `root_set_pending_childkey_cooldown` failed: {message}" - assert ( - message - == "Success with `root_set_pending_childkey_cooldown_extrinsic` response." - ) + assert success is True, message + assert message == "Success" assert await async_subtensor.subnets.register_subnet(dave_wallet) assert await async_subtensor.subnets.subnet_exists(dave_subnet_netuid), ( @@ -549,16 +545,20 @@ async def test_children_async(async_subtensor, alice_wallet, bob_wallet, dave_wa raise_error=True, ) - assert await async_subtensor.subnets.burned_register( - wallet=alice_wallet, - netuid=dave_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=alice_wallet, + netuid=dave_subnet_netuid, + ) + ).success logging.console.success(f"Alice registered on subnet {dave_subnet_netuid}") - assert await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=dave_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=dave_subnet_netuid, + ) + ).success logging.console.success(f"Bob registered on subnet {dave_subnet_netuid}") success, children, error = await async_subtensor.wallets.get_children( @@ -649,8 +649,8 @@ async def test_children_async(async_subtensor, alice_wallet, bob_wallet, dave_wa wait_for_inclusion=True, wait_for_finalization=True, ) - assert message == "Success with `set_children_extrinsic` response." - assert success is True + assert success is True, message + assert message == "Success" logging.console.info(f"[orange]success: {success}, message: {message}[/orange]") set_children_block = await async_subtensor.chain.get_current_block() @@ -734,8 +734,8 @@ async def test_children_async(async_subtensor, alice_wallet, bob_wallet, dave_wa wait_for_inclusion=True, wait_for_finalization=True, ) - assert message == "Success with `set_children_extrinsic` response." - assert success is True + assert success is True, message + assert message == "Success" logging.console.info(f"[orange]success: {success}, message: {message}[/orange]") set_children_block = await async_subtensor.chain.get_current_block() diff --git a/tests/e2e_tests/test_incentive.py b/tests/e2e_tests/test_incentive.py index 0cee9beb8f..3f7358b160 100644 --- a/tests/e2e_tests/test_incentive.py +++ b/tests/e2e_tests/test_incentive.py @@ -57,7 +57,7 @@ async def test_incentive(subtensor, templates, alice_wallet, bob_wallet): assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) # Register Bob as a neuron on the subnet - assert subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid), ( + assert subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid).success, ( "Unable to register Bob as a neuron" ) @@ -232,9 +232,9 @@ async def test_incentive_async(async_subtensor, templates, alice_wallet, bob_wal ) # Register Bob as a neuron on the subnet - assert await async_subtensor.subnets.burned_register( - bob_wallet, alice_subnet_netuid - ), "Unable to register Bob as a neuron" + assert ( + await async_subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid) + ).success, "Unable to register Bob as a neuron" # Assert two neurons are in network assert ( diff --git a/tests/e2e_tests/test_liquid_alpha.py b/tests/e2e_tests/test_liquid_alpha.py index e31a852933..3fe87dffee 100644 --- a/tests/e2e_tests/test_liquid_alpha.py +++ b/tests/e2e_tests/test_liquid_alpha.py @@ -52,7 +52,7 @@ def test_liquid_alpha(subtensor, alice_wallet): assert wait_to_start_call(subtensor, alice_wallet, netuid) # Register a neuron (Alice) to the subnet - assert subtensor.subnets.burned_register(alice_wallet, netuid), ( + assert subtensor.subnets.burned_register(alice_wallet, netuid).success, ( "Unable to register Alice as a neuron" ) @@ -62,7 +62,7 @@ def test_liquid_alpha(subtensor, alice_wallet): netuid=netuid, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), - ), "Unable to stake to Alice neuron" + ).success, "Unable to stake to Alice neuron" # Assert liquid alpha is disabled assert ( @@ -234,17 +234,19 @@ async def test_liquid_alpha_async(async_subtensor, alice_wallet): assert await async_wait_to_start_call(async_subtensor, alice_wallet, netuid) # Register a neuron (Alice) to the subnet - assert await async_subtensor.subnets.burned_register(alice_wallet, netuid), ( - "Unable to register Alice as a neuron" - ) + assert ( + await async_subtensor.subnets.burned_register(alice_wallet, netuid) + ).success, "Unable to register Alice as a neuron" # Stake to become to top neuron after the first epoch - assert await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(10_000), - ), "Unable to stake to Alice neuron" + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(10_000), + ) + ).success, "Unable to stake to Alice neuron" # Assert liquid alpha is disabled assert ( diff --git a/tests/e2e_tests/test_liquidity.py b/tests/e2e_tests/test_liquidity.py index 2617a6259c..d180dc3759 100644 --- a/tests/e2e_tests/test_liquidity.py +++ b/tests/e2e_tests/test_liquidity.py @@ -73,7 +73,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): enable=True, ) assert success, message - assert message == "", "❌ Cannot enable user liquidity." + assert message == "Success", "❌ Cannot enable user liquidity." # Add steak to call add_liquidity assert subtensor.staking.add_stake( @@ -81,7 +81,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=Balance.from_tao(1), - ), "❌ Cannot cannot add stake to Alice from Alice." + ).success, "❌ Cannot cannot add stake to Alice from Alice." # wait for the next block to give the chain time to update the stake subtensor.wait_for_block() @@ -105,7 +105,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ Cannot add liquidity." + assert message == "Success", "❌ Cannot add liquidity." # Get liquidity liquidity_positions = subtensor.subnets.get_liquidity_list( @@ -138,7 +138,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ cannot modify liquidity position." + assert message == "Success", "❌ cannot modify liquidity position." liquidity_positions = subtensor.subnets.get_liquidity_list( wallet=alice_wallet, netuid=alice_subnet_netuid @@ -169,7 +169,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ cannot modify liquidity position." + assert message == "Success", "❌ cannot modify liquidity position." liquidity_positions = subtensor.subnets.get_liquidity_list( wallet=alice_wallet, netuid=alice_subnet_netuid @@ -196,7 +196,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=Balance.from_tao(1000), - ), "❌ Cannot add stake from Bob to Alice." + ).success, "❌ Cannot add stake from Bob to Alice." # wait for the next block to give the chain time to update the stake subtensor.wait_for_block() @@ -220,7 +220,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ Cannot add liquidity." + assert message == "Success", "❌ Cannot add liquidity." liquidity_positions = subtensor.subnets.get_liquidity_list( wallet=alice_wallet, netuid=alice_subnet_netuid @@ -263,7 +263,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): rate_tolerance=0.9, # keep high rate tolerance to avoid flaky behavior wait_for_inclusion=True, wait_for_finalization=True, - ) + ).success # Check that fees_alpha comes too after all unstake liquidity_position_first = subtensor.subnets.get_liquidity_list( @@ -284,7 +284,7 @@ async def test_liquidity(subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ Cannot remove liquidity." + assert message == "Success", "❌ Cannot remove liquidity." # Make sure all liquidity positions removed assert ( @@ -364,15 +364,17 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): enable=True, ) assert success, message - assert message == "", "❌ Cannot enable user liquidity." + assert message == "Success", "❌ Cannot enable user liquidity." # Add steak to call add_liquidity - assert await async_subtensor.staking.add_stake( - wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - amount=Balance.from_tao(1), - ), "❌ Cannot cannot add stake to Alice from Alice." + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + amount=Balance.from_tao(1), + ) + ).success, "❌ Cannot cannot add stake to Alice from Alice." # wait for the next block to give the chain time to update the stake await async_subtensor.wait_for_block() @@ -398,7 +400,7 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ Cannot add liquidity." + assert message == "Success", "❌ Cannot add liquidity." # Get liquidity liquidity_positions = await async_subtensor.subnets.get_liquidity_list( @@ -431,7 +433,7 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ cannot modify liquidity position." + assert message == "Success", "❌ cannot modify liquidity position." liquidity_positions = await async_subtensor.subnets.get_liquidity_list( wallet=alice_wallet, netuid=alice_subnet_netuid @@ -462,7 +464,7 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ cannot modify liquidity position." + assert message == "Success", "❌ cannot modify liquidity position." liquidity_positions = await async_subtensor.subnets.get_liquidity_list( wallet=alice_wallet, netuid=alice_subnet_netuid @@ -484,12 +486,14 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): ) # Add stake from Bob to Alice - assert await async_subtensor.staking.add_stake( - wallet=bob_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - amount=Balance.from_tao(1000), - ), "❌ Cannot add stake from Bob to Alice." + assert ( + await async_subtensor.staking.add_stake( + wallet=bob_wallet, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + amount=Balance.from_tao(1000), + ) + ).success, "❌ Cannot add stake from Bob to Alice." # wait for the next block to give the chain time to update the stake await async_subtensor.wait_for_block() @@ -515,7 +519,7 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ Cannot add liquidity." + assert message == "Success", "❌ Cannot add liquidity." liquidity_positions = await async_subtensor.subnets.get_liquidity_list( wallet=alice_wallet, netuid=alice_subnet_netuid @@ -551,14 +555,16 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): assert liquidity_position_first.fees_tao > Balance.from_tao(0) # Bob remove all stake from alice - assert await async_subtensor.extrinsics.unstake_all( - wallet=bob_wallet, - hotkey=alice_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - rate_tolerance=0.9, # keep high rate tolerance to avoid flaky behavior - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert ( + await async_subtensor.extrinsics.unstake_all( + wallet=bob_wallet, + hotkey=alice_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + rate_tolerance=0.9, # keep high rate tolerance to avoid flaky behavior + wait_for_inclusion=True, + wait_for_finalization=True, + ) + ).success # Check that fees_alpha comes too after all unstake liquidity_position_first = ( @@ -581,7 +587,7 @@ async def test_liquidity_async(async_subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) assert success, message - assert message == "", "❌ Cannot remove liquidity." + assert message == "Success", "❌ Cannot remove liquidity." # Make sure all liquidity positions removed assert ( diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index d59b944162..fcd02ec7cf 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -75,7 +75,7 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): assert len(metagraph.uids) == 1, "Metagraph doesn't have exactly 1 neuron" logging.console.info("Register Bob to the subnet") - assert subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid), ( + assert subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid).success, ( "Unable to register Bob as a neuron" ) @@ -115,9 +115,9 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): metagraph_pre_dave = subtensor.metagraphs.metagraph(netuid=alice_subnet_netuid) logging.console.info("Register Dave as a neuron") - assert subtensor.subnets.burned_register(dave_wallet, alice_subnet_netuid), ( - "Unable to register Dave as a neuron" - ) + assert subtensor.subnets.burned_register( + dave_wallet, alice_subnet_netuid + ).success, "Unable to register Dave as a neuron" metagraph.sync(subtensor=subtensor._subtensor) @@ -145,7 +145,7 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=tao, - ), "Failed to add stake for Bob" + ).success, "Failed to add stake for Bob" logging.console.info("Assert stake is added after updating metagraph") metagraph.sync(subtensor=subtensor._subtensor) @@ -238,9 +238,11 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w assert len(metagraph.uids) == 1, "Metagraph doesn't have exactly 1 neuron" logging.console.info("Register Bob to the subnet") - assert await async_subtensor.subnets.burned_register( - bob_wallet, alice_subnet_netuid - ), "Unable to register Bob as a neuron" + assert ( + await async_subtensor.subnets.burned_register( + bob_wallet, alice_subnet_netuid + ) + ).success, "Unable to register Bob as a neuron" logging.console.info("Refresh the metagraph") await metagraph.sync(subtensor=async_subtensor._subtensor) @@ -282,9 +284,11 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w ) logging.console.info("Register Dave as a neuron") - assert await async_subtensor.subnets.burned_register( - dave_wallet, alice_subnet_netuid - ), "Unable to register Dave as a neuron" + assert ( + await async_subtensor.subnets.burned_register( + dave_wallet, alice_subnet_netuid + ) + ).success, "Unable to register Dave as a neuron" await metagraph.sync(subtensor=async_subtensor._subtensor) @@ -309,12 +313,14 @@ async def test_metagraph_async(async_subtensor, alice_wallet, bob_wallet, dave_w alpha, _ = ( await async_subtensor.subnets.subnet(alice_subnet_netuid) ).tao_to_alpha_with_slippage(tao) - assert await async_subtensor.staking.add_stake( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=tao, - ), "Failed to add stake for Bob" + assert ( + await async_subtensor.staking.add_stake( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=tao, + ) + ).success, "Failed to add stake for Bob" logging.console.info("Assert stake is added after updating metagraph") await metagraph.sync(subtensor=async_subtensor._subtensor) @@ -558,7 +564,7 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): assert subtensor.subnets.burned_register( bob_wallet, netuid=alice_subnet_netuid, - ) + ).success metagraph_info = subtensor.metagraphs.get_metagraph_info(netuid=alice_subnet_netuid) @@ -813,10 +819,12 @@ async def test_metagraph_info_async(async_subtensor, alice_wallet, bob_wallet): async_subtensor, alice_wallet, alice_subnet_netuid ) - assert await async_subtensor.subnets.burned_register( - bob_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + bob_wallet, + netuid=alice_subnet_netuid, + ) + ).success metagraph_info = await async_subtensor.metagraphs.get_metagraph_info( netuid=alice_subnet_netuid @@ -992,7 +1000,7 @@ def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - ) + ).success fields = [ SelectiveMetagraphIndex.Name, @@ -1225,10 +1233,12 @@ async def test_metagraph_info_with_indexes_async( async_subtensor, alice_wallet, alice_subnet_netuid ) - assert await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + ) + ).success fields = [ SelectiveMetagraphIndex.Name, diff --git a/tests/e2e_tests/test_neuron_certificate.py b/tests/e2e_tests/test_neuron_certificate.py index 0a1748d8eb..880a8b8662 100644 --- a/tests/e2e_tests/test_neuron_certificate.py +++ b/tests/e2e_tests/test_neuron_certificate.py @@ -25,7 +25,7 @@ async def test_neuron_certificate(subtensor, alice_wallet): assert subtensor.subnets.subnet_exists(netuid), "Subnet wasn't created successfully" # Register Alice as a neuron on the subnet - assert subtensor.subnets.burned_register(alice_wallet, netuid), ( + assert subtensor.subnets.burned_register(alice_wallet, netuid).success, ( "Unable to register Alice as a neuron" ) @@ -79,9 +79,9 @@ async def test_neuron_certificate_async(async_subtensor, alice_wallet): ) # Register Alice as a neuron on the subnet - assert await async_subtensor.subnets.burned_register(alice_wallet, netuid), ( - "Unable to register Alice as a neuron" - ) + assert ( + await async_subtensor.subnets.burned_register(alice_wallet, netuid) + ).success, "Unable to register Alice as a neuron" # Serve Alice's axon with a certificate axon = Axon(wallet=alice_wallet) diff --git a/tests/e2e_tests/test_reveal_commitments.py b/tests/e2e_tests/test_reveal_commitments.py index 9ad12eaa76..7d7807cd2c 100644 --- a/tests/e2e_tests/test_reveal_commitments.py +++ b/tests/e2e_tests/test_reveal_commitments.py @@ -48,7 +48,7 @@ def test_set_reveal_commitment(subtensor, alice_wallet, bob_wallet): assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - ), "Bob's neuron was not register." + ).success, "Bob's neuron was not register." # Verify subnet 2 created successfully assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -59,27 +59,27 @@ def test_set_reveal_commitment(subtensor, alice_wallet, bob_wallet): message_alice = f"This is test message with time {time.time()} from Alice." response = subtensor.commitments.set_reveal_commitment( - alice_wallet, - alice_subnet_netuid, - message_alice, - BLOCKS_UNTIL_REVEAL, - BLOCK_TIME, + wallet=alice_wallet, + netuid=alice_subnet_netuid, + data=message_alice, + blocks_until_reveal=BLOCKS_UNTIL_REVEAL, + block_time=BLOCK_TIME, ) - assert response[0] is True + assert response.success is True # Set commitment from Bob's hotkey message_bob = f"This is test message with time {time.time()} from Bob." response = subtensor.commitments.set_reveal_commitment( - bob_wallet, - alice_subnet_netuid, - message_bob, - BLOCKS_UNTIL_REVEAL, + wallet=bob_wallet, + netuid=alice_subnet_netuid, + data=message_bob, + blocks_until_reveal=BLOCKS_UNTIL_REVEAL, block_time=BLOCK_TIME, ) - assert response[0] is True + assert response.success is True - target_reveal_round = response[1] + target_reveal_round = response.data.get("reveal_round") # Sometimes the chain doesn't update the repository right away and the commit doesn't appear in the expected # `last_drand_round`. In this case need to wait a bit. @@ -124,7 +124,7 @@ def test_set_reveal_commitment(subtensor, alice_wallet, bob_wallet): @pytest.mark.asyncio -async def test_set_reveal_commitment(async_subtensor, alice_wallet, bob_wallet): +async def test_set_reveal_commitment_async(async_subtensor, alice_wallet, bob_wallet): """ Tests the set/reveal commitments with TLE (time-locked encrypted commitments) mechanism. @@ -162,9 +162,9 @@ async def test_set_reveal_commitment(async_subtensor, alice_wallet, bob_wallet): ) # Register Bob's neuron - assert await async_subtensor.subnets.burned_register( - bob_wallet, alice_subnet_netuid - ), "Bob's neuron was not register." + assert ( + await async_subtensor.subnets.burned_register(bob_wallet, alice_subnet_netuid) + ).success, "Bob's neuron was not register." # Verify subnet 2 created successfully assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -181,7 +181,7 @@ async def test_set_reveal_commitment(async_subtensor, alice_wallet, bob_wallet): BLOCKS_UNTIL_REVEAL, BLOCK_TIME, ) - assert response[0] is True + assert response.success is True # Set commitment from Bob's hotkey message_bob = f"This is test message with time {time.time()} from Bob." @@ -193,9 +193,9 @@ async def test_set_reveal_commitment(async_subtensor, alice_wallet, bob_wallet): BLOCKS_UNTIL_REVEAL, block_time=BLOCK_TIME, ) - assert response[0] is True + assert response.success is True - target_reveal_round = response[1] + target_reveal_round = response.data.get("reveal_round") # Sometimes the chain doesn't update the repository right away and the commit doesn't appear in the expected # `last_drand_round`. In this case need to wait a bit. diff --git a/tests/e2e_tests/test_root_set_weights.py b/tests/e2e_tests/test_root_set_weights.py index 7d18dccfcf..dc906a86ef 100644 --- a/tests/e2e_tests/test_root_set_weights.py +++ b/tests/e2e_tests/test_root_set_weights.py @@ -68,7 +68,7 @@ async def test_root_reg_hyperparams(subtensor, templates, alice_wallet, bob_wall default_tempo = 10 if subtensor.chain.is_fast_blocks() else 360 # Register Alice in root network (0) - assert subtensor.extrinsics.root_register(alice_wallet) + assert subtensor.extrinsics.root_register(alice_wallet).success # Assert Alice is successfully registered to root alice_root_neuron = subtensor.neurons.neurons(netuid=0)[0] @@ -92,7 +92,7 @@ async def test_root_reg_hyperparams(subtensor, templates, alice_wallet, bob_wall hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), period=16, - ), "Unable to stake from Bob to Alice" + ).success, "Unable to stake from Bob to Alice" async with templates.validator(alice_wallet, netuid): await asyncio.sleep(5) # Wait a bit for chain to process data @@ -188,7 +188,7 @@ async def test_root_reg_hyperparams_async( default_tempo = 10 if await async_subtensor.chain.is_fast_blocks() else 360 # Register Alice in root network (0) - assert await async_subtensor.extrinsics.root_register(alice_wallet) + assert (await async_subtensor.extrinsics.root_register(alice_wallet)).success # Assert Alice is successfully registered to root alice_root_neuron = (await async_subtensor.neurons.neurons(netuid=0))[0] @@ -209,13 +209,15 @@ async def test_root_reg_hyperparams_async( ) assert await async_subtensor.subnets.tempo(netuid=netuid) == default_tempo - assert await async_subtensor.staking.add_stake( - wallet=bob_wallet, - netuid=netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(1), - period=16, - ), "Unable to stake from Bob to Alice" + assert ( + await async_subtensor.staking.add_stake( + wallet=bob_wallet, + netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(1), + period=16, + ) + ).success, "Unable to stake from Bob to Alice" async with templates.validator(alice_wallet, netuid): await asyncio.sleep(5) # Wait a bit for chain to process data diff --git a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py index de52ad4550..228cfdd744 100644 --- a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py +++ b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py @@ -40,7 +40,7 @@ def test_set_subnet_identity_extrinsic_happy_pass(subtensor, alice_wallet): wallet=alice_wallet, netuid=netuid, subnet_identity=subnet_identity, - )[0] + ).success is True ), "Set subnet identity failed" @@ -96,7 +96,7 @@ async def test_set_subnet_identity_extrinsic_happy_pass_async( netuid=netuid, subnet_identity=subnet_identity, ) - )[0] is True, "Set subnet identity failed" + ).success is True, "Set subnet identity failed" # Check SubnetIdentity of the subnet assert ( @@ -112,14 +112,6 @@ def test_set_subnet_identity_extrinsic_failed(subtensor, alice_wallet, bob_walle Test case for verifying the behavior of the `set_subnet_identity_extrinsic` function in the scenario where the result of the function is expected to fail. It ensures proper handling and validation when attempting to set the subnet identity under specific conditions. - - Args: - subtensor: The instance of the subtensor class under test. - alice_wallet: A mock or test wallet associated with Alice, used for creating a subnet. - bob_wallet: A mock or test wallet associated with Bob, used for setting the subnet identity. - - Decorators: - @pytest.mark.asyncio: Marks this test as an asynchronous test. """ logging.console.info( "Testing [blue]test_set_subnet_identity_extrinsic_failed[/blue]" @@ -156,7 +148,7 @@ def test_set_subnet_identity_extrinsic_failed(subtensor, alice_wallet, bob_walle wallet=bob_wallet, netuid=netuid, subnet_identity=subnet_identity, - )[0] + ).success is False ), "Set subnet identity failed" @@ -173,14 +165,6 @@ async def test_set_subnet_identity_extrinsic_failed_async( Async test case for verifying the behavior of the `set_subnet_identity_extrinsic` function in the scenario where the result of the function is expected to fail. It ensures proper handling and validation when attempting to set the subnet identity under specific conditions. - - Args: - subtensor: The instance of the subtensor class under test. - alice_wallet: A mock or test wallet associated with Alice, used for creating a subnet. - bob_wallet: A mock or test wallet associated with Bob, used for setting the subnet identity. - - Decorators: - @pytest.mark.asyncio: Marks this test as an asynchronous test. """ logging.console.info( "Testing [blue]test_set_subnet_identity_extrinsic_failed[/blue]" @@ -222,7 +206,7 @@ async def test_set_subnet_identity_extrinsic_failed_async( netuid=netuid, subnet_identity=subnet_identity, ) - )[0] is False, "Set subnet identity failed" + ).success is False, "Set subnet identity failed" logging.console.success( "✅ Passed [blue]test_set_subnet_identity_extrinsic_failed[/blue]" diff --git a/tests/e2e_tests/test_set_weights.py b/tests/e2e_tests/test_set_weights.py index ea754b68c8..cde2be58bc 100644 --- a/tests/e2e_tests/test_set_weights.py +++ b/tests/e2e_tests/test_set_weights.py @@ -86,7 +86,7 @@ def test_set_weights_uses_next_nonce(subtensor, alice_wallet): netuid=netuid, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(10_000), - ) + ).success # Set weight hyperparameters per subnet for netuid in netuids: @@ -245,12 +245,14 @@ async def test_set_weights_uses_next_nonce_async(async_subtensor, alice_wallet): # Stake to become to top neuron after the first epoch for netuid in netuids: - assert await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(10_000), - ) + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(10_000), + ) + ).success # Set weight hyperparameters per subnet for netuid in netuids: diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index b3847bb507..46009e1e1c 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -29,7 +29,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert subtensor.subnets.register_subnet(alice_wallet) + assert subtensor.subnets.register_subnet(alice_wallet).success # Verify subnet created successfully assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -38,15 +38,15 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=alice_wallet, netuid=alice_subnet_netuid, - ) + ).success logging.console.success(f"Alice is registered in subnet {alice_subnet_netuid}") - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - ) + ).success logging.console.success(f"Bob is registered in subnet {alice_subnet_netuid}") stake = subtensor.staking.get_stake( @@ -57,15 +57,13 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): assert stake == Balance(0).set_unit(alice_subnet_netuid) - success = subtensor.staking.add_stake( + assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, amount=Balance.from_tao(1), period=16, - ) - - assert success is True + ).success stake_alice = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -171,7 +169,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): logging.console.info(f"Alice stake before unstake: {stake}") # unstale all to check in later - success = subtensor.staking.unstake( + response = subtensor.staking.unstake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -179,7 +177,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): period=16, ) - assert success is True + assert response.success is True stake = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -205,7 +203,7 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 # Register root as Alice - the subnet owner and validator - assert await async_subtensor.subnets.register_subnet(alice_wallet) + assert (await async_subtensor.subnets.register_subnet(alice_wallet)).success # Verify subnet created successfully assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( @@ -216,15 +214,19 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) async_subtensor, alice_wallet, alice_subnet_netuid ) - await async_subtensor.subnets.burned_register( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + ) + ).success logging.console.success(f"Alice is registered in subnet {alice_subnet_netuid}") - await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + ) + ).success logging.console.success(f"Bob is registered in subnet {alice_subnet_netuid}") stake = await async_subtensor.staking.get_stake( @@ -235,15 +237,15 @@ async def test_single_operation_async(async_subtensor, alice_wallet, bob_wallet) assert stake == Balance(0).set_unit(alice_subnet_netuid) - success = await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=Balance.from_tao(1), - period=16, - ) - - assert success is True + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=Balance.from_tao(1), + period=16, + ) + ).success stake_alice = await async_subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -388,17 +390,17 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): ] for _ in netuids: - subtensor.subnets.register_subnet(alice_wallet) + assert subtensor.subnets.register_subnet(alice_wallet).success # make sure we passed start_call limit for both subnets for netuid in netuids: assert wait_to_start_call(subtensor, alice_wallet, netuid) for netuid in netuids: - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=netuid, - ) + ).success for netuid in netuids: stake = subtensor.staking.get_stake( @@ -427,14 +429,12 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): alice_balance = balances[alice_wallet.coldkey.ss58_address] - success = subtensor.staking.add_stake_multiple( + assert subtensor.staking.add_stake_multiple( wallet=alice_wallet, hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], netuids=netuids, amounts=[Balance.from_tao(10_000) for _ in netuids], - ) - - assert success is True + ).success stakes = [ subtensor.staking.get_stake( @@ -492,7 +492,7 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): amounts=[Balance.from_tao(100) for _ in netuids], ) - assert success is True + assert success.success is True for netuid, old_stake in zip(netuids, stakes): stake = subtensor.staking.get_stake( @@ -533,17 +533,19 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet) ] for _ in netuids: - await async_subtensor.subnets.register_subnet(alice_wallet) + assert (await async_subtensor.subnets.register_subnet(alice_wallet)).success # make sure we passed start_call limit for both subnets for netuid in netuids: assert await async_wait_to_start_call(async_subtensor, alice_wallet, netuid) for netuid in netuids: - await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=netuid, + ) + ).success for netuid in netuids: stake = await async_subtensor.staking.get_stake( @@ -572,14 +574,14 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet) alice_balance = balances[alice_wallet.coldkey.ss58_address] - success = await async_subtensor.staking.add_stake_multiple( - alice_wallet, - hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], - netuids=netuids, - amounts=[Balance.from_tao(10_000) for _ in netuids], - ) - - assert success is True + assert ( + await async_subtensor.staking.add_stake_multiple( + alice_wallet, + hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], + netuids=netuids, + amounts=[Balance.from_tao(10_000) for _ in netuids], + ) + ).success stakes = [ await async_subtensor.staking.get_stake( @@ -637,7 +639,7 @@ async def test_batch_operations_async(async_subtensor, alice_wallet, bob_wallet) amounts=[Balance.from_tao(100) for _ in netuids], ) - assert success is True + assert success.success is True for netuid, old_stake in zip(netuids, stakes): stake = await async_subtensor.staking.get_stake( @@ -700,12 +702,12 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) - subtensor.extrinsics.burned_register( + assert subtensor.extrinsics.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, wait_for_inclusion=True, wait_for_finalization=True, - ) + ).success initial_stake = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -719,16 +721,18 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) stake_amount = Balance.from_tao(100) # 1. Strict params - should fail - success = subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=stake_amount, - safe_staking=True, - rate_tolerance=0.005, # 0.5% - allow_partial_stake=False, - ) - assert success is False + assert ( + subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=stake_amount, + safe_staking=True, + rate_tolerance=0.005, # 0.5% + allow_partial_stake=False, + ).success + is False + ), "Staking should fail." current_stake = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -741,7 +745,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) logging.console.info(f"[orange]Current stake: {current_stake}[orange]") # 2. Partial allowed - should succeed partially - success = subtensor.staking.add_stake( + assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -749,8 +753,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) safe_staking=True, rate_tolerance=0.005, # 0.5% allow_partial_stake=True, - ) - assert success is True + ).success partial_stake = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -766,7 +769,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) # 3. Higher threshold - should succeed fully amount = Balance.from_tao(100) - success = subtensor.staking.add_stake( + assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -774,8 +777,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) safe_staking=True, rate_tolerance=0.22, # 22% allow_partial_stake=False, - ) - assert success is True + ).success full_stake = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -785,7 +787,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) # Test Unstaking Scenarios # 1. Strict params - should fail - success = subtensor.staking.unstake( + response = subtensor.staking.unstake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -794,7 +796,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) - assert success is False, "Unstake should fail." + assert response.success is False current_stake = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -810,7 +812,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) ) # 2. Partial allowed - should succeed partially - success = subtensor.staking.unstake( + response = subtensor.staking.unstake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -819,7 +821,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) rate_tolerance=0.005, # 0.5% allow_partial_stake=True, ) - assert success is True + assert response.success is True partial_unstake = subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -832,7 +834,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) ) # 3. Higher threshold - should succeed fully - success = subtensor.staking.unstake( + response = subtensor.staking.unstake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -841,7 +843,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet, eve_wallet) rate_tolerance=0.3, # 30% allow_partial_stake=False, ) - assert success is True, "Unstake should succeed" + assert response.success is True, "Unstake should succeed" logging.console.success("✅ Test [green]test_safe_staking_scenarios[/green] passed") @@ -890,12 +892,14 @@ async def test_safe_staking_scenarios_async( async_subtensor, alice_wallet, alice_subnet_netuid ) - await async_subtensor.extrinsics.burned_register( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) + assert ( + await async_subtensor.extrinsics.burned_register( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + ).success initial_stake = await async_subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -909,16 +913,17 @@ async def test_safe_staking_scenarios_async( stake_amount = Balance.from_tao(100) # 1. Strict params - should fail - success = await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=stake_amount, - safe_staking=True, - rate_tolerance=0.005, # 0.5% - allow_partial_stake=False, - ) - assert success is False + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=stake_amount, + safe_staking=True, + rate_tolerance=0.005, # 0.5% + allow_partial_stake=False, + ) + ).success is False, "Staking should fail." current_stake = await async_subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -931,16 +936,17 @@ async def test_safe_staking_scenarios_async( logging.console.info(f"[orange]Current stake: {current_stake}[orange]") # 2. Partial allowed - should succeed partially - success = await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=stake_amount, - safe_staking=True, - rate_tolerance=0.005, # 0.5% - allow_partial_stake=True, - ) - assert success is True + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=stake_amount, + safe_staking=True, + rate_tolerance=0.005, # 0.5% + allow_partial_stake=True, + ) + ).success partial_stake = await async_subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -956,16 +962,17 @@ async def test_safe_staking_scenarios_async( # 3. Higher threshold - should succeed fully amount = Balance.from_tao(100) - success = await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=bob_wallet.hotkey.ss58_address, - amount=amount, - safe_staking=True, - rate_tolerance=0.22, # 22% - allow_partial_stake=False, - ) - assert success is True + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=bob_wallet.hotkey.ss58_address, + amount=amount, + safe_staking=True, + rate_tolerance=0.22, # 22% + allow_partial_stake=False, + ) + ).success full_stake = await async_subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -975,7 +982,7 @@ async def test_safe_staking_scenarios_async( # Test Unstaking Scenarios # 1. Strict params - should fail - success = await async_subtensor.staking.unstake( + response = await async_subtensor.staking.unstake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -984,7 +991,7 @@ async def test_safe_staking_scenarios_async( rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) - assert success is False, "Unstake should fail." + assert response.success is False, "Unstake should fail." current_stake = await async_subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -1000,7 +1007,7 @@ async def test_safe_staking_scenarios_async( ) # 2. Partial allowed - should succeed partially - success = await async_subtensor.staking.unstake( + response = await async_subtensor.staking.unstake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -1009,7 +1016,7 @@ async def test_safe_staking_scenarios_async( rate_tolerance=0.005, # 0.5% allow_partial_stake=True, ) - assert success is True + assert response.success is True partial_unstake = await async_subtensor.staking.get_stake( coldkey_ss58=alice_wallet.coldkey.ss58_address, @@ -1022,7 +1029,7 @@ async def test_safe_staking_scenarios_async( ) # 3. Higher threshold - should succeed fully - success = await async_subtensor.staking.unstake( + response = await async_subtensor.staking.unstake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=bob_wallet.hotkey.ss58_address, @@ -1031,7 +1038,7 @@ async def test_safe_staking_scenarios_async( rate_tolerance=0.3, # 30% allow_partial_stake=False, ) - assert success is True, "Unstake should succeed" + assert response.success is True, "Unstake should succeed" logging.console.success( "✅ Test [green]test_safe_staking_scenarios_async[/green] passed" ) @@ -1049,12 +1056,12 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): # Create new subnet (netuid 2) and register Alice origin_netuid = 2 - assert subtensor.subnets.register_subnet(bob_wallet) + assert subtensor.subnets.register_subnet(bob_wallet).success assert subtensor.subnets.subnet_exists(origin_netuid), ( "Subnet wasn't created successfully" ) dest_netuid = 3 - assert subtensor.subnets.register_subnet(bob_wallet) + assert subtensor.subnets.register_subnet(bob_wallet).success assert subtensor.subnets.subnet_exists(dest_netuid), ( "Subnet wasn't created successfully" ) @@ -1064,24 +1071,23 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): assert wait_to_start_call(subtensor, bob_wallet, dest_netuid) # Register Alice on both subnets - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=alice_wallet, netuid=origin_netuid, - ) - subtensor.subnets.burned_register( + ).success + assert subtensor.subnets.burned_register( wallet=alice_wallet, netuid=dest_netuid, - ) + ).success # Add initial stake to swap from initial_stake_amount = Balance.from_tao(10_000) - success = subtensor.staking.add_stake( + assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=origin_netuid, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=initial_stake_amount, - ) - assert success is True + ).success origin_stake = subtensor.staking.get_stake( alice_wallet.coldkey.ss58_address, @@ -1094,7 +1100,7 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): stake_swap_amount = Balance.from_tao(10_000) # 1. Try swap with strict threshold and big amount- should fail - success = subtensor.staking.swap_stake( + response = subtensor.staking.swap_stake( wallet=alice_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=origin_netuid, @@ -1106,7 +1112,7 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) - assert success is False + assert response.success is False # Verify no stake was moved dest_stake = subtensor.staking.get_stake( @@ -1120,7 +1126,7 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): # 2. Try swap with higher threshold and less amount - should succeed stake_swap_amount = Balance.from_tao(100) - success = subtensor.staking.swap_stake( + response = subtensor.staking.swap_stake( wallet=alice_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=origin_netuid, @@ -1132,7 +1138,7 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): rate_tolerance=0.3, # 30% allow_partial_stake=True, ) - assert success is True + assert response.success is True # Verify stake was moved dest_stake = subtensor.staking.get_stake( @@ -1163,12 +1169,12 @@ async def test_safe_swap_stake_scenarios_async( # Create new subnet (netuid 2) and register Alice origin_netuid = 2 - assert await async_subtensor.subnets.register_subnet(bob_wallet) + assert (await async_subtensor.subnets.register_subnet(bob_wallet)).success assert await async_subtensor.subnets.subnet_exists(origin_netuid), ( "Subnet wasn't created successfully" ) dest_netuid = 3 - assert await async_subtensor.subnets.register_subnet(bob_wallet) + assert (await async_subtensor.subnets.register_subnet(bob_wallet)).success assert await async_subtensor.subnets.subnet_exists(dest_netuid), ( "Subnet wasn't created successfully" ) @@ -1178,24 +1184,29 @@ async def test_safe_swap_stake_scenarios_async( assert await async_wait_to_start_call(async_subtensor, bob_wallet, dest_netuid) # Register Alice on both subnets - await async_subtensor.subnets.burned_register( - wallet=alice_wallet, - netuid=origin_netuid, - ) - await async_subtensor.subnets.burned_register( - wallet=alice_wallet, - netuid=dest_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=alice_wallet, + netuid=origin_netuid, + ) + ).success + assert ( + await async_subtensor.subnets.burned_register( + wallet=alice_wallet, + netuid=dest_netuid, + ) + ).success # Add initial stake to swap from initial_stake_amount = Balance.from_tao(10_000) - success = await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=origin_netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=initial_stake_amount, - ) - assert success is True + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=origin_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=initial_stake_amount, + ) + ).success origin_stake = await async_subtensor.staking.get_stake( alice_wallet.coldkey.ss58_address, @@ -1208,7 +1219,7 @@ async def test_safe_swap_stake_scenarios_async( stake_swap_amount = Balance.from_tao(10_000) # 1. Try swap with strict threshold and big amount- should fail - success = await async_subtensor.staking.swap_stake( + response = await async_subtensor.staking.swap_stake( wallet=alice_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=origin_netuid, @@ -1220,7 +1231,7 @@ async def test_safe_swap_stake_scenarios_async( rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) - assert success is False + assert response.success is False # Verify no stake was moved dest_stake = await async_subtensor.staking.get_stake( @@ -1234,7 +1245,7 @@ async def test_safe_swap_stake_scenarios_async( # 2. Try swap with higher threshold and less amount - should succeed stake_swap_amount = Balance.from_tao(100) - success = await async_subtensor.staking.swap_stake( + response = await async_subtensor.staking.swap_stake( wallet=alice_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=origin_netuid, @@ -1246,7 +1257,7 @@ async def test_safe_swap_stake_scenarios_async( rate_tolerance=0.3, # 30% allow_partial_stake=True, ) - assert success is True + assert response.success is True # Verify stake was moved dest_stake = await async_subtensor.staking.get_stake( @@ -1272,7 +1283,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): logging.console.info("Testing [blue]test_move_stake[/blue]") alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet) + assert subtensor.subnets.register_subnet(alice_wallet).success assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1284,7 +1295,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): netuid=alice_subnet_netuid, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1_000), - ) + ).success stakes = subtensor.staking.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) @@ -1302,24 +1313,24 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): ] bob_subnet_netuid = subtensor.subnets.get_total_subnets() # 3 - subtensor.subnets.register_subnet(bob_wallet) + assert subtensor.subnets.register_subnet(bob_wallet).success assert subtensor.subnets.subnet_exists(bob_subnet_netuid), ( "Subnet wasn't created successfully" ) assert wait_to_start_call(subtensor, bob_wallet, bob_subnet_netuid) - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid, - ) + ).success - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid, - ) + ).success - assert subtensor.staking.move_stake( + response = subtensor.staking.move_stake( wallet=alice_wallet, origin_hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=alice_subnet_netuid, @@ -1329,6 +1340,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): wait_for_finalization=True, wait_for_inclusion=True, ) + assert response.success is True stakes = subtensor.staking.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) @@ -1381,7 +1393,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): hotkey_ss58=dave_wallet.hotkey.ss58_address, amount=Balance.from_tao(1000), allow_partial_stake=True, - ) + ).success dave_stake = subtensor.staking.get_stake( coldkey_ss58=dave_wallet.coldkey.ss58_address, @@ -1395,7 +1407,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): subtensor.block + subtensor.subnets.tempo(netuid=bob_subnet_netuid) ) - assert subtensor.staking.move_stake( + response = subtensor.staking.move_stake( wallet=dave_wallet, origin_hotkey_ss58=dave_wallet.hotkey.ss58_address, origin_netuid=bob_subnet_netuid, @@ -1405,6 +1417,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): wait_for_finalization=True, move_all_stake=True, ) + assert response.success is True dave_stake = subtensor.staking.get_stake( coldkey_ss58=dave_wallet.coldkey.ss58_address, @@ -1429,7 +1442,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ logging.console.info("Testing [blue]test_move_stake_async[/blue]") alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet) + assert (await async_subtensor.subnets.register_subnet(alice_wallet)).success assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1438,12 +1451,14 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ async_subtensor, alice_wallet, alice_subnet_netuid ) - assert await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(1_000), - ) + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(1_000), + ) + ).success stakes = await async_subtensor.staking.get_stake_for_coldkey( alice_wallet.coldkey.ss58_address @@ -1463,7 +1478,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ ] bob_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 3 - await async_subtensor.subnets.register_subnet(bob_wallet) + assert (await async_subtensor.subnets.register_subnet(bob_wallet)).success assert await async_subtensor.subnets.subnet_exists(bob_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1472,17 +1487,21 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ async_subtensor, bob_wallet, bob_subnet_netuid ) - await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=alice_subnet_netuid, + ) + ).success - await async_subtensor.subnets.burned_register( - wallet=dave_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=dave_wallet, + netuid=alice_subnet_netuid, + ) + ).success - assert await async_subtensor.staking.move_stake( + response = await async_subtensor.staking.move_stake( wallet=alice_wallet, origin_hotkey_ss58=alice_wallet.hotkey.ss58_address, origin_netuid=alice_subnet_netuid, @@ -1492,6 +1511,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ wait_for_finalization=True, wait_for_inclusion=True, ) + assert response.success is True stakes = await async_subtensor.staking.get_stake_for_coldkey( alice_wallet.coldkey.ss58_address @@ -1540,13 +1560,15 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ ) logging.console.info(f"[orange]Dave stake before adding: {dave_stake}[orange]") - assert await async_subtensor.staking.add_stake( - wallet=dave_wallet, - hotkey_ss58=dave_wallet.hotkey.ss58_address, - netuid=bob_subnet_netuid, - amount=Balance.from_tao(1000), - allow_partial_stake=True, - ) + assert ( + await async_subtensor.staking.add_stake( + wallet=dave_wallet, + hotkey_ss58=dave_wallet.hotkey.ss58_address, + netuid=bob_subnet_netuid, + amount=Balance.from_tao(1000), + allow_partial_stake=True, + ) + ).success dave_stake = await async_subtensor.staking.get_stake( coldkey_ss58=dave_wallet.coldkey.ss58_address, @@ -1561,7 +1583,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ # let chain to process the transaction await async_subtensor.wait_for_block(block_ + tampo_) - assert await async_subtensor.staking.move_stake( + response = await async_subtensor.staking.move_stake( wallet=dave_wallet, origin_hotkey_ss58=dave_wallet.hotkey.ss58_address, origin_netuid=bob_subnet_netuid, @@ -1571,6 +1593,7 @@ async def test_move_stake_async(async_subtensor, alice_wallet, bob_wallet, dave_ wait_for_finalization=True, move_all_stake=True, ) + assert response.success is True dave_stake = await async_subtensor.staking.get_stake( coldkey_ss58=dave_wallet.coldkey.ss58_address, @@ -1594,24 +1617,24 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): alice_subnet_netuid = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet) + assert subtensor.subnets.register_subnet(alice_wallet).success assert subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=alice_wallet, netuid=alice_subnet_netuid, - ) + ).success assert subtensor.staking.add_stake( wallet=alice_wallet, netuid=alice_subnet_netuid, hotkey_ss58=alice_wallet.hotkey.ss58_address, amount=Balance.from_tao(1_000), - ) + ).success alice_stakes = subtensor.staking.get_stake_for_coldkey( alice_wallet.coldkey.ss58_address @@ -1639,16 +1662,16 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): assert bob_stakes == [] dave_subnet_netuid = subtensor.subnets.get_total_subnets() # 3 - subtensor.subnets.register_subnet(dave_wallet) + assert subtensor.subnets.register_subnet(dave_wallet).success assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) - subtensor.subnets.burned_register( + assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=dave_subnet_netuid, - ) + ).success - assert subtensor.staking.transfer_stake( + response = subtensor.staking.transfer_stake( alice_wallet, destination_coldkey_ss58=bob_wallet.coldkey.ss58_address, hotkey_ss58=alice_wallet.hotkey.ss58_address, @@ -1658,6 +1681,7 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): wait_for_inclusion=True, wait_for_finalization=True, ) + assert response.success is True alice_stakes = subtensor.staking.get_stake_for_coldkey( alice_wallet.coldkey.ss58_address @@ -1721,7 +1745,7 @@ async def test_transfer_stake_async( alice_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet) + assert (await async_subtensor.subnets.register_subnet(alice_wallet)).success assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid), ( "Subnet wasn't created successfully" ) @@ -1730,17 +1754,21 @@ async def test_transfer_stake_async( async_subtensor, alice_wallet, alice_subnet_netuid ) - await async_subtensor.subnets.burned_register( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + ) + ).success - assert await async_subtensor.staking.add_stake( - wallet=alice_wallet, - netuid=alice_subnet_netuid, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(1_000), - ) + assert ( + await async_subtensor.staking.add_stake( + wallet=alice_wallet, + netuid=alice_subnet_netuid, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(1_000), + ) + ).success alice_stakes = await async_subtensor.staking.get_stake_for_coldkey( alice_wallet.coldkey.ss58_address @@ -1768,18 +1796,20 @@ async def test_transfer_stake_async( assert bob_stakes == [] dave_subnet_netuid = await async_subtensor.subnets.get_total_subnets() # 3 - await async_subtensor.subnets.register_subnet(dave_wallet) + assert (await async_subtensor.subnets.register_subnet(dave_wallet)).success assert await async_wait_to_start_call( async_subtensor, dave_wallet, dave_subnet_netuid ) - await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=dave_subnet_netuid, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=dave_subnet_netuid, + ) + ).success - assert await async_subtensor.staking.transfer_stake( + response = await async_subtensor.staking.transfer_stake( alice_wallet, destination_coldkey_ss58=bob_wallet.coldkey.ss58_address, hotkey_ss58=alice_wallet.hotkey.ss58_address, @@ -1789,6 +1819,7 @@ async def test_transfer_stake_async( wait_for_inclusion=True, wait_for_finalization=True, ) + assert response.success is True alice_stakes = await async_subtensor.staking.get_stake_for_coldkey( alice_wallet.coldkey.ss58_address @@ -1858,7 +1889,7 @@ def test_unstaking_with_limit( # Register first SN alice_subnet_netuid_2 = subtensor.subnets.get_total_subnets() # 2 - assert subtensor.subnets.register_subnet(alice_wallet) + assert subtensor.subnets.register_subnet(alice_wallet).success assert subtensor.subnets.subnet_exists(alice_subnet_netuid_2), ( "Subnet wasn't created successfully" ) @@ -1869,16 +1900,16 @@ def test_unstaking_with_limit( assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid_2, - ) + ).success assert subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid_2, - ) + ).success # Register second SN alice_subnet_netuid_3 = subtensor.subnets.get_total_subnets() # 3 - assert subtensor.subnets.register_subnet(alice_wallet) + assert subtensor.subnets.register_subnet(alice_wallet).success assert subtensor.subnets.subnet_exists(alice_subnet_netuid_3), ( "Subnet wasn't created successfully" ) @@ -1889,12 +1920,12 @@ def test_unstaking_with_limit( assert subtensor.subnets.burned_register( wallet=bob_wallet, netuid=alice_subnet_netuid_3, - ) + ).success assert subtensor.subnets.burned_register( wallet=dave_wallet, netuid=alice_subnet_netuid_3, - ) + ).success # Check Bob's stakes are empty. assert ( @@ -1910,14 +1941,15 @@ def test_unstaking_with_limit( netuid=alice_subnet_netuid_2, amount=Balance.from_tao(10000), period=16, - ), f"Cant add stake to dave in SN {alice_subnet_netuid_2}" + ).success, f"Cant add stake to dave in SN {alice_subnet_netuid_2}" + assert subtensor.staking.add_stake( wallet=bob_wallet, hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid_3, amount=Balance.from_tao(15000), period=16, - ), f"Cant add stake to dave in SN {alice_subnet_netuid_3}" + ).success, f"Cant add stake to dave in SN {alice_subnet_netuid_3}" # Check that both stakes are presented in result bob_stakes = subtensor.staking.get_stake_info_for_coldkey( @@ -1944,7 +1976,7 @@ def test_unstaking_with_limit( hotkey=si.hotkey_ss58, netuid=si.netuid, rate_tolerance=rate_tolerance, - )[0] + ).success # Make sure both unstake were successful. bob_stakes = subtensor.staking.get_stake_info_for_coldkey( @@ -1970,7 +2002,7 @@ async def test_unstaking_with_limit_async( # Register first SN alice_subnet_netuid_2 = await async_subtensor.subnets.get_total_subnets() # 2 - assert await async_subtensor.subnets.register_subnet(alice_wallet) + assert (await async_subtensor.subnets.register_subnet(alice_wallet)).success assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid_2), ( "Subnet wasn't created successfully" ) @@ -1980,19 +2012,23 @@ async def test_unstaking_with_limit_async( ) # Register Bob and Dave in SN2 - assert await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=alice_subnet_netuid_2, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=alice_subnet_netuid_2, + ) + ).success - assert await async_subtensor.subnets.burned_register( - wallet=dave_wallet, - netuid=alice_subnet_netuid_2, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=dave_wallet, + netuid=alice_subnet_netuid_2, + ) + ).success # Register second SN alice_subnet_netuid_3 = await async_subtensor.subnets.get_total_subnets() # 3 - assert await async_subtensor.subnets.register_subnet(alice_wallet) + assert (await async_subtensor.subnets.register_subnet(alice_wallet)).success assert await async_subtensor.subnets.subnet_exists(alice_subnet_netuid_3), ( "Subnet wasn't created successfully" ) @@ -2000,15 +2036,19 @@ async def test_unstaking_with_limit_async( await async_wait_to_start_call(async_subtensor, alice_wallet, alice_subnet_netuid_3) # Register Bob and Dave in SN3 - assert await async_subtensor.subnets.burned_register( - wallet=bob_wallet, - netuid=alice_subnet_netuid_3, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=bob_wallet, + netuid=alice_subnet_netuid_3, + ) + ).success - assert await async_subtensor.subnets.burned_register( - wallet=dave_wallet, - netuid=alice_subnet_netuid_3, - ) + assert ( + await async_subtensor.subnets.burned_register( + wallet=dave_wallet, + netuid=alice_subnet_netuid_3, + ) + ).success # Check Bob's stakes are empty. assert ( @@ -2020,20 +2060,25 @@ async def test_unstaking_with_limit_async( # Bob stakes to Dave in both SNs - assert await async_subtensor.staking.add_stake( - wallet=bob_wallet, - netuid=alice_subnet_netuid_2, - hotkey_ss58=dave_wallet.hotkey.ss58_address, - amount=Balance.from_tao(10000), - period=16, - ), f"Cant add stake to dave in SN {alice_subnet_netuid_2}" - assert await async_subtensor.staking.add_stake( - wallet=bob_wallet, - netuid=alice_subnet_netuid_3, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - amount=Balance.from_tao(15000), - period=16, - ), f"Cant add stake to dave in SN {alice_subnet_netuid_3}" + assert ( + await async_subtensor.staking.add_stake( + wallet=bob_wallet, + netuid=alice_subnet_netuid_2, + hotkey_ss58=dave_wallet.hotkey.ss58_address, + amount=Balance.from_tao(10000), + period=16, + ) + ).success, f"Cant add stake to dave in SN {alice_subnet_netuid_2}" + + assert ( + await async_subtensor.staking.add_stake( + wallet=bob_wallet, + netuid=alice_subnet_netuid_3, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + amount=Balance.from_tao(15000), + period=16, + ) + ).success, f"Cant add stake to dave in SN {alice_subnet_netuid_3}" # Check that both stakes are presented in result bob_stakes = await async_subtensor.staking.get_stake_info_for_coldkey( @@ -2062,7 +2107,7 @@ async def test_unstaking_with_limit_async( hotkey=si.hotkey_ss58, rate_tolerance=rate_tolerance, ) - )[0] + ).success # Make sure both unstake were successful. bob_stakes = await async_subtensor.staking.get_stake_info_for_coldkey( diff --git a/tests/e2e_tests/test_subtensor_functions.py b/tests/e2e_tests/test_subtensor_functions.py index 669b929d3f..a7ce1b9de8 100644 --- a/tests/e2e_tests/test_subtensor_functions.py +++ b/tests/e2e_tests/test_subtensor_functions.py @@ -142,7 +142,7 @@ async def test_subtensor_extrinsics(subtensor, templates, alice_wallet, bob_wall assert wait_to_start_call(subtensor, alice_wallet, netuid) # Register Bob to the subnet - assert subtensor.subnets.burned_register(bob_wallet, netuid), ( + assert subtensor.subnets.burned_register(bob_wallet, netuid).success, ( "Unable to register Bob as a neuron" ) @@ -335,9 +335,9 @@ async def test_subtensor_extrinsics_async( assert await async_wait_to_start_call(async_subtensor, alice_wallet, netuid) # Register Bob to the subnet - assert await async_subtensor.subnets.burned_register(bob_wallet, netuid), ( - "Unable to register Bob as a neuron" - ) + assert ( + await async_subtensor.subnets.burned_register(bob_wallet, netuid) + ).success, "Unable to register Bob as a neuron" # Verify Bob's UID on netuid 2 is 1 assert ( diff --git a/tests/e2e_tests/test_transfer.py b/tests/e2e_tests/test_transfer.py index 89613f6553..cfa3eede93 100644 --- a/tests/e2e_tests/test_transfer.py +++ b/tests/e2e_tests/test_transfer.py @@ -44,7 +44,7 @@ def test_transfer(subtensor, alice_wallet): amount=transfer_value, wait_for_finalization=True, wait_for_inclusion=True, - ) + ).success # Account details after transfer balance_after = subtensor.wallets.get_balance(alice_wallet.coldkeypub.ss58_address) @@ -85,13 +85,15 @@ async def test_transfer_async(async_subtensor, alice_wallet): ) # Transfer Tao - assert await async_subtensor.extrinsics.transfer( - wallet=alice_wallet, - destination=dest_coldkey, - amount=transfer_value, - wait_for_finalization=True, - wait_for_inclusion=True, - ) + assert ( + await async_subtensor.extrinsics.transfer( + wallet=alice_wallet, + destination=dest_coldkey, + amount=transfer_value, + wait_for_finalization=True, + wait_for_inclusion=True, + ) + ).success # Account details after transfer balance_after = await async_subtensor.wallets.get_balance( alice_wallet.coldkeypub.ss58_address @@ -121,7 +123,7 @@ def test_transfer_all(subtensor, alice_wallet): amount=Balance.from_tao(2.0), wait_for_finalization=True, wait_for_inclusion=True, - ) + ).success # Account details before transfer existential_deposit = subtensor.chain.get_existential_deposit() assert subtensor.extrinsics.transfer( @@ -132,7 +134,7 @@ def test_transfer_all(subtensor, alice_wallet): wait_for_finalization=True, wait_for_inclusion=True, keep_alive=True, - ) + ).success balance_after = subtensor.wallets.get_balance( dummy_account_1.coldkeypub.ss58_address ) @@ -145,7 +147,7 @@ def test_transfer_all(subtensor, alice_wallet): wait_for_inclusion=True, wait_for_finalization=True, keep_alive=False, - ) + ).success balance_after = subtensor.wallets.get_balance( dummy_account_2.coldkeypub.ss58_address ) @@ -165,37 +167,43 @@ async def test_transfer_all_async(async_subtensor, alice_wallet): dummy_account_2.create_new_coldkey(use_password=False, overwrite=True) # fund the first dummy account - assert await async_subtensor.extrinsics.transfer( - wallet=alice_wallet, - destination=dummy_account_1.coldkeypub.ss58_address, - amount=Balance.from_tao(2.0), - wait_for_finalization=True, - wait_for_inclusion=True, - ) + assert ( + await async_subtensor.extrinsics.transfer( + wallet=alice_wallet, + destination=dummy_account_1.coldkeypub.ss58_address, + amount=Balance.from_tao(2.0), + wait_for_finalization=True, + wait_for_inclusion=True, + ) + ).success # Account details before transfer existential_deposit = await async_subtensor.chain.get_existential_deposit() - assert await async_subtensor.extrinsics.transfer( - wallet=dummy_account_1, - destination=dummy_account_2.coldkeypub.ss58_address, - amount=None, - transfer_all=True, - wait_for_finalization=True, - wait_for_inclusion=True, - keep_alive=True, - ) + assert ( + await async_subtensor.extrinsics.transfer( + wallet=dummy_account_1, + destination=dummy_account_2.coldkeypub.ss58_address, + amount=None, + transfer_all=True, + wait_for_finalization=True, + wait_for_inclusion=True, + keep_alive=True, + ) + ).success balance_after = await async_subtensor.wallets.get_balance( dummy_account_1.coldkeypub.ss58_address ) assert balance_after == existential_deposit - assert await async_subtensor.extrinsics.transfer( - wallet=dummy_account_2, - destination=alice_wallet.coldkeypub.ss58_address, - amount=None, - transfer_all=True, - wait_for_inclusion=True, - wait_for_finalization=True, - keep_alive=False, - ) + assert ( + await async_subtensor.extrinsics.transfer( + wallet=dummy_account_2, + destination=alice_wallet.coldkeypub.ss58_address, + amount=None, + transfer_all=True, + wait_for_inclusion=True, + wait_for_finalization=True, + keep_alive=False, + ) + ).success balance_after = await async_subtensor.wallets.get_balance( dummy_account_2.coldkeypub.ss58_address ) diff --git a/tests/unit_tests/extrinsics/asyncex/test_children.py b/tests/unit_tests/extrinsics/asyncex/test_children.py index 48c0940170..b89b4dc0ec 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_children.py +++ b/tests/unit_tests/extrinsics/asyncex/test_children.py @@ -1,6 +1,7 @@ import pytest from bittensor.core.extrinsics.asyncex import children +from bittensor.core.types import ExtrinsicResponse @pytest.mark.asyncio @@ -19,7 +20,9 @@ async def test_set_children_extrinsic(subtensor, mocker, fake_wallet): substrate = subtensor.substrate.__aenter__.return_value substrate.compose_call = mocker.AsyncMock() mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) # Call @@ -54,6 +57,7 @@ async def test_set_children_extrinsic(subtensor, mocker, fake_wallet): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + calling_function="set_children_extrinsic", ) assert success is True @@ -71,7 +75,9 @@ async def test_root_set_pending_childkey_cooldown_extrinsic( substrate = subtensor.substrate.__aenter__.return_value substrate.compose_call = mocker.AsyncMock() mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) # Call @@ -90,6 +96,7 @@ async def test_root_set_pending_childkey_cooldown_extrinsic( raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + calling_function="root_set_pending_childkey_cooldown_extrinsic", ) assert success is True assert "Success" in message diff --git a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py index 7e46e436be..4c52b6b457 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py @@ -1,9 +1,10 @@ -from bittensor.core import async_subtensor as subtensor_module -from bittensor.core.chain_data import SubnetHyperparameters -from bittensor.core.extrinsics.asyncex import commit_reveal as async_commit_reveal +import numpy as np import pytest import torch -import numpy as np + +from bittensor.core.chain_data import SubnetHyperparameters +from bittensor.core.extrinsics.asyncex import commit_reveal as async_commit_reveal +from bittensor.core.types import ExtrinsicResponse @pytest.fixture @@ -73,7 +74,9 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) mock_block = mocker.patch.object( subtensor.substrate, @@ -99,7 +102,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( # Asserts assert success is True - assert message == "reveal_round:1" + assert message == "Success" mocked_convert_weights_and_uids_for_emit.assert_called_once_with( fake_uids, fake_weights ) @@ -122,6 +125,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( sign_with="hotkey", period=None, raise_error=False, + calling_function="commit_reveal_extrinsic", ) @@ -145,7 +149,9 @@ async def test_commit_reveal_v3_extrinsic_success_with_numpy( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) mocker.patch.object(subtensor.substrate, "get_block_number", return_value=1) mocker.patch.object( @@ -167,7 +173,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_numpy( # Asserts assert success is True - assert message == "reveal_round:0" + assert message == "Success" mock_convert.assert_called_once_with(fake_uids, fake_weights) mock_encode_drand.assert_called_once() mocked_sign_and_send_extrinsic.assert_awaited_once_with( @@ -178,6 +184,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_numpy( sign_with="hotkey", period=None, raise_error=False, + calling_function="commit_reveal_extrinsic", ) @@ -206,7 +213,9 @@ async def test_commit_reveal_v3_extrinsic_response_false( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, "Failed") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(False, "Failed"), ) mocker.patch.object(subtensor.substrate, "get_block_number", return_value=1) mocker.patch.object( @@ -237,6 +246,7 @@ async def test_commit_reveal_v3_extrinsic_response_false( sign_with="hotkey", period=None, raise_error=False, + calling_function="commit_reveal_extrinsic", ) @@ -255,14 +265,11 @@ async def test_commit_reveal_v3_extrinsic_exception(mocker, subtensor, fake_wall ) # Call - success, message = await async_commit_reveal.commit_reveal_extrinsic( - subtensor=subtensor, - wallet=fake_wallet, - netuid=fake_netuid, - uids=fake_uids, - weights=fake_weights, - ) - - # Asserts - assert success is False - assert "Test Error" in message + with pytest.raises(Exception): + await async_commit_reveal.commit_reveal_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + ) diff --git a/tests/unit_tests/extrinsics/asyncex/test_liquidity.py b/tests/unit_tests/extrinsics/asyncex/test_liquidity.py index c572a518a9..430c8b522f 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_liquidity.py +++ b/tests/unit_tests/extrinsics/asyncex/test_liquidity.py @@ -47,6 +47,7 @@ async def test_add_liquidity_extrinsic(subtensor, fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="add_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -92,6 +93,7 @@ async def test_modify_liquidity_extrinsic(subtensor, fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="modify_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -134,6 +136,7 @@ async def test_remove_liquidity_extrinsic(subtensor, fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="remove_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -174,5 +177,6 @@ async def test_toggle_user_liquidity_extrinsic(subtensor, fake_wallet, mocker): wait_for_finalization=True, period=None, raise_error=False, + calling_function="toggle_user_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index 03f00aab82..21f3c72338 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -1,4 +1,5 @@ import pytest +from bittensor.core.types import ExtrinsicResponse from bittensor.core.extrinsics.asyncex import registration as async_registration @@ -27,7 +28,7 @@ async def test_register_extrinsic_success(subtensor, fake_wallet, mocker): ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, None) + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) mocked_is_hotkey_registered = mocker.patch.object( subtensor, "is_hotkey_registered", return_value=True @@ -60,11 +61,13 @@ async def test_register_extrinsic_success(subtensor, fake_wallet, mocker): wait_for_finalization=True, period=None, raise_error=False, + calling_function="register_extrinsic", ) mocked_is_hotkey_registered.assert_called_once_with( netuid=1, hotkey_ss58="hotkey_ss58" ) - assert result is True + + assert result[0] @pytest.mark.asyncio @@ -92,7 +95,7 @@ async def test_register_extrinsic_success_with_cuda(subtensor, fake_wallet, mock ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, None) + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) mocked_is_hotkey_registered = mocker.patch.object( subtensor, "is_hotkey_registered", return_value=True @@ -126,11 +129,12 @@ async def test_register_extrinsic_success_with_cuda(subtensor, fake_wallet, mock wait_for_finalization=True, period=None, raise_error=False, + calling_function="register_extrinsic", ) mocked_is_hotkey_registered.assert_called_once_with( netuid=1, hotkey_ss58="hotkey_ss58" ) - assert result is True + assert result[0] @pytest.mark.asyncio @@ -170,13 +174,18 @@ async def test_register_extrinsic_failed_with_cuda(subtensor, fake_wallet, mocke netuid=1, block_hash=subtensor.substrate.get_chain_head.return_value, ) - assert result is False + assert result == ExtrinsicResponse( + False, + "CUDA not available.", + extrinsic_function="register_extrinsic", + ) @pytest.mark.asyncio async def test_register_extrinsic_subnet_not_exists(subtensor, fake_wallet, mocker): """Tests registration when subnet does not exist.""" # Preps + netuid = 14 mocked_subnet_exists = mocker.patch.object( subtensor, "subnet_exists", return_value=False ) @@ -185,21 +194,26 @@ async def test_register_extrinsic_subnet_not_exists(subtensor, fake_wallet, mock result = await async_registration.register_extrinsic( subtensor=subtensor, wallet=fake_wallet, - netuid=1, + netuid=netuid, ) # Asserts mocked_subnet_exists.assert_called_once_with( - 1, + netuid, block_hash=subtensor.substrate.get_chain_head.return_value, ) - assert result is False + assert result == ExtrinsicResponse( + False, + f"Subnet #{netuid} does not exist.", + extrinsic_function="register_extrinsic", + ) @pytest.mark.asyncio async def test_register_extrinsic_already_registered(subtensor, fake_wallet, mocker): """Tests registration when the key is already registered.""" # Preps + netuid = 14 mocked_get_neuron = mocker.patch.object( subtensor, "get_neuron_for_pubkey_and_subnet", @@ -207,19 +221,20 @@ async def test_register_extrinsic_already_registered(subtensor, fake_wallet, moc ) # Call - result = await async_registration.register_extrinsic( + success, message = await async_registration.register_extrinsic( subtensor=subtensor, wallet=fake_wallet, - netuid=1, + netuid=netuid, ) # Asserts mocked_get_neuron.assert_called_once_with( hotkey_ss58=fake_wallet.hotkey.ss58_address, - netuid=1, + netuid=netuid, block_hash=subtensor.substrate.get_chain_head.return_value, ) - assert result is True + assert success is True + assert message == f"Already registered." @pytest.mark.asyncio @@ -252,7 +267,9 @@ async def is_stale_side_effect(*_, **__): ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, "Test Error") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(False, "Test Error"), ) # Call @@ -284,8 +301,10 @@ async def is_stale_side_effect(*_, **__): wait_for_finalization=True, period=None, raise_error=False, + calling_function="register_extrinsic", ) - assert result is False + assert result[0] is False + assert result[1] == "No more attempts." @pytest.mark.asyncio @@ -305,9 +324,7 @@ async def test_set_subnet_identity_extrinsic_is_success(subtensor, fake_wallet, mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, - "sign_and_send_extrinsic", - return_value=[True, ""], + subtensor, "sign_and_send_extrinsic" ) # Call @@ -349,9 +366,10 @@ async def test_set_subnet_identity_extrinsic_is_success(subtensor, fake_wallet, wait_for_finalization=True, period=None, raise_error=False, + calling_function="set_subnet_identity_extrinsic", ) - assert result == (True, "Identities for subnet 123 are set.") + assert result == mocked_sign_and_send_extrinsic.return_value @pytest.mark.asyncio @@ -374,7 +392,6 @@ async def test_set_subnet_identity_extrinsic_is_failed(subtensor, fake_wallet, m mocked_sign_and_send_extrinsic = mocker.patch.object( subtensor, "sign_and_send_extrinsic", - return_value=[False, fake_error_message], ) # Call @@ -418,9 +435,7 @@ async def test_set_subnet_identity_extrinsic_is_failed(subtensor, fake_wallet, m wait_for_finalization=True, period=None, raise_error=False, + calling_function="set_subnet_identity_extrinsic", ) - assert result == ( - False, - f"Failed to set identity for subnet {netuid}: {fake_error_message}", - ) + assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/asyncex/test_root.py b/tests/unit_tests/extrinsics/asyncex/test_root.py index 70acf9857c..329d051c15 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_root.py +++ b/tests/unit_tests/extrinsics/asyncex/test_root.py @@ -1,8 +1,7 @@ import pytest -from bittensor.core.errors import SubstrateRequestException from bittensor.core.extrinsics.asyncex import root as async_root - +from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -57,7 +56,7 @@ async def test_root_register_extrinsic_success(subtensor, fake_wallet, mocker): mocked_sign_and_send_extrinsic = mocker.patch.object( subtensor, "sign_and_send_extrinsic", - return_value=(True, ""), + return_value=ExtrinsicResponse(True, "Success"), ) mocked_query = mocker.patch.object( subtensor.substrate, @@ -95,7 +94,8 @@ async def test_root_register_extrinsic_success(subtensor, fake_wallet, mocker): storage_function="Uids", params=[0, "fake_hotkey_address"], ) - assert result is True + assert result.success is True + assert result.message == "Success" @pytest.mark.asyncio @@ -122,7 +122,7 @@ async def test_root_register_extrinsic_insufficient_balance( wait_for_finalization=True, ) - assert result is False + assert result.success is False subtensor.get_balance.assert_called_once_with( fake_wallet.coldkeypub.ss58_address, @@ -161,7 +161,7 @@ async def test_root_register_extrinsic_unlock_failed(subtensor, fake_wallet, moc # Asserts mocked_unlock_key.assert_called_once_with(fake_wallet) - assert result is False + assert result.success is False @pytest.mark.asyncio @@ -206,7 +206,7 @@ async def test_root_register_extrinsic_already_registered( mocked_is_hotkey_registered.assert_called_once_with( netuid=0, hotkey_ss58="fake_hotkey_address" ) - assert result is True + assert result.success is True @pytest.mark.asyncio @@ -241,7 +241,7 @@ async def test_root_register_extrinsic_transaction_failed( mocked_sign_and_send_extrinsic = mocker.patch.object( subtensor, "sign_and_send_extrinsic", - return_value=(False, "Transaction failed"), + return_value=ExtrinsicResponse(False, "Transaction failed"), ) # Call @@ -259,7 +259,7 @@ async def test_root_register_extrinsic_transaction_failed( ) mocked_compose_call.assert_called_once() mocked_sign_and_send_extrinsic.assert_called_once() - assert result is False + assert result.success is False @pytest.mark.asyncio @@ -292,7 +292,7 @@ async def test_root_register_extrinsic_uid_not_found(subtensor, fake_wallet, moc mocked_sign_and_send_extrinsic = mocker.patch.object( subtensor, "sign_and_send_extrinsic", - return_value=(True, ""), + return_value=ExtrinsicResponse(True, ""), ) mocked_query = mocker.patch.object( subtensor.substrate, @@ -320,4 +320,4 @@ async def test_root_register_extrinsic_uid_not_found(subtensor, fake_wallet, moc storage_function="Uids", params=[0, "fake_hotkey_address"], ) - assert result is False + assert result.success is False diff --git a/tests/unit_tests/extrinsics/asyncex/test_start_call.py b/tests/unit_tests/extrinsics/asyncex/test_start_call.py index 68507acbd2..4f9bdddbd9 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_start_call.py +++ b/tests/unit_tests/extrinsics/asyncex/test_start_call.py @@ -13,7 +13,7 @@ async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): substrate = subtensor.substrate.__aenter__.return_value substrate.compose_call = mocker.AsyncMock() mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") ) # Call @@ -37,6 +37,7 @@ async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): wait_for_finalization=False, period=None, raise_error=False, + calling_function="start_call_extrinsic", ) assert success is True diff --git a/tests/unit_tests/extrinsics/asyncex/test_transfer.py b/tests/unit_tests/extrinsics/asyncex/test_transfer.py index 3992fa33bb..df0a82a56d 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_transfer.py +++ b/tests/unit_tests/extrinsics/asyncex/test_transfer.py @@ -1,5 +1,7 @@ import pytest + from bittensor.core.extrinsics.asyncex import transfer as async_transfer +from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -37,7 +39,7 @@ async def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) # Call @@ -69,8 +71,9 @@ async def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): wait_for_finalization=True, period=None, raise_error=False, + calling_function="transfer_extrinsic", ) - assert result is True + assert result.success is True @pytest.mark.asyncio @@ -109,7 +112,7 @@ async def test_transfer_extrinsic_call_successful_with_failed_response( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(False, "") ) # Call @@ -142,8 +145,9 @@ async def test_transfer_extrinsic_call_successful_with_failed_response( wait_for_finalization=True, period=None, raise_error=False, + calling_function="transfer_extrinsic", ) - assert result is False + assert result.success is False @pytest.mark.asyncio @@ -199,7 +203,7 @@ async def test_transfer_extrinsic_insufficient_balance(subtensor, fake_wallet, m mocked_get_existential_deposit.assert_called_once_with( block_hash=mocked_get_chain_head.return_value ) - assert result is False + assert result.success is False @pytest.mark.asyncio @@ -230,7 +234,7 @@ async def test_transfer_extrinsic_invalid_destination(subtensor, fake_wallet, mo # Asserts mocked_is_valid_address.assert_called_once_with(fake_destination) - assert result is False + assert result.success is False @pytest.mark.asyncio @@ -241,12 +245,6 @@ async def test_transfer_extrinsic_unlock_key_false(subtensor, fake_wallet, mocke fake_destination = "invalid_address" fake_amount = Balance(15) - mocked_is_valid_address = mocker.patch.object( - async_transfer, - "is_valid_bittensor_address_or_public_key", - return_value=True, - ) - mocked_unlock_key = mocker.patch.object( async_transfer, "unlock_key", @@ -266,9 +264,8 @@ async def test_transfer_extrinsic_unlock_key_false(subtensor, fake_wallet, mocke ) # Asserts - mocked_is_valid_address.assert_called_once_with(fake_destination) mocked_unlock_key.assert_called_once_with(fake_wallet) - assert result is False + assert result.success is False @pytest.mark.asyncio @@ -307,7 +304,7 @@ async def test_transfer_extrinsic_keep_alive_false_and_transfer_all_true( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) # Call @@ -332,4 +329,4 @@ async def test_transfer_extrinsic_keep_alive_false_and_transfer_all_true( ) assert mocked_compose_call.call_count == 0 assert mocked_sign_and_send_extrinsic.call_count == 0 - assert result is False + assert result.success is False diff --git a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py index 2f70f9bc68..2124f2377a 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_unstaking.py +++ b/tests/unit_tests/extrinsics/asyncex/test_unstaking.py @@ -1,6 +1,7 @@ import pytest from bittensor.core.extrinsics.asyncex import unstaking +from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -14,7 +15,7 @@ async def test_unstake_extrinsic(fake_wallet, mocker): **{ "get_hotkey_owner.return_value": "hotkey_owner", "get_stake_for_coldkey_and_hotkey.return_value": Balance(10.0), - "sign_and_send_extrinsic.return_value": (True, ""), + "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), "get_stake.return_value": Balance(10.0), "substrate": fake_substrate, } @@ -39,7 +40,7 @@ async def test_unstake_extrinsic(fake_wallet, mocker): ) # Asserts - assert result is True + assert result.success is True fake_subtensor.substrate.compose_call.assert_awaited_once_with( call_module="SubtensorModule", @@ -60,6 +61,7 @@ async def test_unstake_extrinsic(fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="unstake_extrinsic", ) @@ -107,6 +109,7 @@ async def test_unstake_all_extrinsic(fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="unstake_all_extrinsic", ) @@ -121,7 +124,7 @@ async def test_unstake_multiple_extrinsic(fake_wallet, mocker): **{ "get_hotkey_owner.return_value": "hotkey_owner", "get_stake_for_coldkey_and_hotkey.return_value": [Balance(10.0)], - "sign_and_send_extrinsic.return_value": (True, ""), + "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), "tx_rate_limit.return_value": 0, "substrate": fake_substrate, } @@ -148,7 +151,7 @@ async def test_unstake_multiple_extrinsic(fake_wallet, mocker): ) # Asserts - assert result is True + assert result.success is True assert fake_subtensor.substrate.compose_call.call_count == 1 assert fake_subtensor.sign_and_send_extrinsic.call_count == 1 @@ -180,4 +183,5 @@ async def test_unstake_multiple_extrinsic(fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="unstake_multiple_extrinsic", ) diff --git a/tests/unit_tests/extrinsics/asyncex/test_weights.py b/tests/unit_tests/extrinsics/asyncex/test_weights.py index dc1bbdc8f0..73ad9a38f2 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_weights.py +++ b/tests/unit_tests/extrinsics/asyncex/test_weights.py @@ -2,6 +2,7 @@ from bittensor.core import async_subtensor from bittensor.core.extrinsics.asyncex import weights as async_weights from bittensor.core.settings import version_as_int +from bittensor.core.types import ExtrinsicResponse @pytest.mark.asyncio @@ -27,7 +28,7 @@ async def test_set_weights_extrinsic_success_with_finalization( mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) # Call @@ -66,6 +67,7 @@ async def test_set_weights_extrinsic_success_with_finalization( nonce_key="hotkey", sign_with="hotkey", raise_error=False, + calling_function="set_weights_extrinsic", ) assert result is True assert message == "" @@ -94,7 +96,9 @@ async def test_set_weights_extrinsic_no_waiting(subtensor, fake_wallet, mocker): mocked_sign_and_send_extrinsic = mocker.patch.object( subtensor, "sign_and_send_extrinsic", - return_value=(True, "Not waiting for finalization or inclusion."), + return_value=ExtrinsicResponse( + True, "Not waiting for finalization or inclusion." + ), ) # Call @@ -133,6 +137,7 @@ async def test_set_weights_extrinsic_no_waiting(subtensor, fake_wallet, mocker): nonce_key="hotkey", sign_with="hotkey", raise_error=False, + calling_function="set_weights_extrinsic", ) assert result is True assert message == "Not waiting for finalization or inclusion." @@ -159,7 +164,9 @@ async def test_set_weights_extrinsic_failure(subtensor, fake_wallet, mocker): mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, "Test error message") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(False, "Test error message"), ) # Call @@ -198,6 +205,7 @@ async def test_set_weights_extrinsic_failure(subtensor, fake_wallet, mocker): nonce_key="hotkey", sign_with="hotkey", raise_error=False, + calling_function="set_weights_extrinsic", ) assert result is False assert message == "Test error message" @@ -249,7 +257,7 @@ async def test_commit_weights_extrinsic_success(subtensor, fake_wallet, mocker): mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) # Call @@ -278,6 +286,7 @@ async def test_commit_weights_extrinsic_success(subtensor, fake_wallet, mocker): raise_error=False, nonce_key="hotkey", sign_with="hotkey", + calling_function="commit_weights_extrinsic", ) assert result is True assert message == "" @@ -292,7 +301,9 @@ async def test_commit_weights_extrinsic_failure(subtensor, fake_wallet, mocker): mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, "Commit failed.") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(False, "Commit failed."), ) # Call @@ -321,6 +332,7 @@ async def test_commit_weights_extrinsic_failure(subtensor, fake_wallet, mocker): raise_error=False, nonce_key="hotkey", sign_with="hotkey", + calling_function="commit_weights_extrinsic", ) assert result is False assert message == "Commit failed." diff --git a/tests/unit_tests/extrinsics/test_children.py b/tests/unit_tests/extrinsics/test_children.py index 12bb96f09b..1b0710abb5 100644 --- a/tests/unit_tests/extrinsics/test_children.py +++ b/tests/unit_tests/extrinsics/test_children.py @@ -1,4 +1,5 @@ from bittensor.core.extrinsics import children +from bittensor.core.types import ExtrinsicResponse def test_set_children_extrinsic(subtensor, mocker, fake_wallet): @@ -15,7 +16,9 @@ def test_set_children_extrinsic(subtensor, mocker, fake_wallet): subtensor.substrate.compose_call = mocker.Mock() mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) # Call @@ -50,6 +53,7 @@ def test_set_children_extrinsic(subtensor, mocker, fake_wallet): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + calling_function="set_children_extrinsic", ) assert success is True @@ -63,7 +67,9 @@ def test_root_set_pending_childkey_cooldown_extrinsic(subtensor, mocker, fake_wa subtensor.substrate.compose_call = mocker.Mock() mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) # Call @@ -82,6 +88,7 @@ def test_root_set_pending_childkey_cooldown_extrinsic(subtensor, mocker, fake_wa raise_error=False, wait_for_inclusion=True, wait_for_finalization=False, + calling_function="root_set_pending_childkey_cooldown_extrinsic", ) assert success is True assert "Success" in message diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index 92dc926f51..c3ddc17383 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -5,6 +5,7 @@ from bittensor.core import subtensor as subtensor_module from bittensor.core.chain_data import SubnetHyperparameters from bittensor.core.extrinsics import commit_reveal +from bittensor.core.types import ExtrinsicResponse @pytest.fixture @@ -74,7 +75,9 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) mock_block = mocker.patch.object(subtensor, "get_current_block", return_value=1) mock_hyperparams = mocker.patch.object( @@ -97,7 +100,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( # Asserts assert success is True - assert message == "reveal_round:1" + assert message == "Success" mocked_convert_weights_and_uids_for_emit.assert_called_once_with( fake_uids, fake_weights ) @@ -120,6 +123,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( sign_with="hotkey", period=None, raise_error=False, + calling_function="commit_reveal_extrinsic", ) @@ -142,7 +146,9 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, "Success"), ) mocker.patch.object(subtensor, "get_current_block", return_value=1) mocker.patch.object( @@ -164,7 +170,7 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy( # Asserts assert success is True - assert message == "reveal_round:0" + assert message == "Success" mock_convert.assert_called_once_with(fake_uids, fake_weights) mock_encode_drand.assert_called_once() mocked_sign_and_send_extrinsic.assert_called_once_with( @@ -175,6 +181,7 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy( sign_with="hotkey", period=None, raise_error=False, + calling_function="commit_reveal_extrinsic", ) @@ -202,7 +209,9 @@ def test_commit_reveal_v3_extrinsic_response_false( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, "Failed") + subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(False, "Failed"), ) mocker.patch.object(subtensor, "get_current_block", return_value=1) mocker.patch.object( @@ -233,6 +242,7 @@ def test_commit_reveal_v3_extrinsic_response_false( sign_with="hotkey", period=None, raise_error=False, + calling_function="commit_reveal_extrinsic", ) diff --git a/tests/unit_tests/extrinsics/test_commit_weights.py b/tests/unit_tests/extrinsics/test_commit_weights.py index 1757cf6e33..c54ba78fa8 100644 --- a/tests/unit_tests/extrinsics/test_commit_weights.py +++ b/tests/unit_tests/extrinsics/test_commit_weights.py @@ -4,13 +4,14 @@ commit_weights_extrinsic, reveal_weights_extrinsic, ) +from bittensor.core.types import ExtrinsicResponse @pytest.mark.parametrize( "sign_and_send_return", [ - (True, "Success"), - (False, "Failure"), + ExtrinsicResponse(True, "Success"), + ExtrinsicResponse(False, "Failure"), ], ids=["success", "failure"], ) @@ -52,6 +53,7 @@ def test_commit_weights_extrinsic(subtensor, fake_wallet, mocker, sign_and_send_ raise_error=False, sign_with="hotkey", use_nonce=True, + calling_function="commit_weights_extrinsic", ) assert result == sign_and_send_return @@ -59,8 +61,8 @@ def test_commit_weights_extrinsic(subtensor, fake_wallet, mocker, sign_and_send_ @pytest.mark.parametrize( "sign_and_send_return", [ - (True, "Success"), - (False, "Failure"), + ExtrinsicResponse(True, "Success"), + ExtrinsicResponse(False, "Failure"), ], ids=["success", "failure"], ) @@ -115,5 +117,6 @@ def test_reveal_weights_extrinsic(subtensor, fake_wallet, mocker, sign_and_send_ raise_error=False, sign_with="hotkey", use_nonce=True, + calling_function="reveal_weights_extrinsic", ) assert result == sign_and_send_return diff --git a/tests/unit_tests/extrinsics/test_liquidity.py b/tests/unit_tests/extrinsics/test_liquidity.py index ce60b34bd3..eda7580d34 100644 --- a/tests/unit_tests/extrinsics/test_liquidity.py +++ b/tests/unit_tests/extrinsics/test_liquidity.py @@ -45,6 +45,7 @@ def test_add_liquidity_extrinsic(subtensor, fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="add_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -89,6 +90,7 @@ def test_modify_liquidity_extrinsic(subtensor, fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="modify_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -130,6 +132,7 @@ def test_remove_liquidity_extrinsic(subtensor, fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="remove_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value @@ -169,5 +172,6 @@ def test_toggle_user_liquidity_extrinsic(subtensor, fake_wallet, mocker): wait_for_finalization=True, period=None, raise_error=False, + calling_function="toggle_user_liquidity_extrinsic", ) assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/test_registration.py b/tests/unit_tests/extrinsics/test_registration.py index 796c3d45f0..286491bd55 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -1,6 +1,6 @@ import pytest from bittensor_wallet import Wallet - +from bittensor.core.types import ExtrinsicResponse from bittensor.core.extrinsics import registration from bittensor.core.subtensor import Subtensor from bittensor.utils.registration import POWSolution @@ -45,11 +45,18 @@ def mock_new_wallet(mocker): @pytest.mark.parametrize( - "subnet_exists, neuron_is_null, cuda_available, expected_result, test_id", + "subnet_exists, neuron_is_null, cuda_available, expected_result, test_id, expected_message", [ - (False, True, True, False, "subnet-does-not-exist"), - (True, False, True, True, "neuron-already-registered"), - (True, True, False, False, "cuda-unavailable"), + ( + False, + True, + True, + False, + "subnet-does-not-exist", + "Subnet #123 does not exist.", + ), + (True, False, True, True, "neuron-already-registered", "Already registered."), + (True, True, False, False, "cuda-unavailable", "CUDA not available."), ], ) def test_register_extrinsic_without_pow( @@ -61,6 +68,7 @@ def test_register_extrinsic_without_pow( expected_result, test_id, mocker, + expected_message, ): # Arrange mocker.patch.object(mock_subtensor, "subnet_exists", return_value=subnet_exists) @@ -93,18 +101,26 @@ def test_register_extrinsic_without_pow( ) # Assert - assert result == expected_result, f"Test failed for test_id: {test_id}" + assert result == ExtrinsicResponse( + expected_result, expected_message, extrinsic_function="register_extrinsic" + ), f"Test failed for test_id: {test_id}" @pytest.mark.parametrize( - "pow_success, pow_stale, registration_success, cuda, hotkey_registered, expected_result, test_id", + "pow_success, pow_stale, registration_success, cuda, hotkey_registered, expected_result", [ - (True, False, True, False, False, True, "successful-with-valid-pow"), - (True, False, True, True, False, True, "successful-with-valid-cuda-pow"), + (True, False, True, False, False, True), + (True, False, True, True, False, True), # Pow failed but key was registered already - (False, False, False, False, True, True, "hotkey-registered"), + (False, False, False, False, True, True), # Pow was a success but registration failed with error 'key already registered' - (True, False, False, False, False, True, "registration-fail-key-registered"), + (True, False, False, False, False, False), + ], + ids=[ + "successful-with-valid-pow", + "successful-with-valid-cuda-pow", + "hotkey-registered", + "registration-fail-key-registered", ], ) def test_register_extrinsic_with_pow( @@ -117,7 +133,6 @@ def test_register_extrinsic_with_pow( cuda, hotkey_registered, expected_result, - test_id, mocker, ): # Arrange @@ -132,7 +147,9 @@ def test_register_extrinsic_with_pow( mocker.patch.object( mock_subtensor, "sign_and_send_extrinsic", - return_value=(registration_success, "HotKeyAlreadyRegisteredInSubNet"), + return_value=ExtrinsicResponse( + registration_success, "HotKeyAlreadyRegisteredInSubNet" + ), ) mocker.patch("torch.cuda.is_available", return_value=cuda) @@ -162,7 +179,7 @@ def test_register_extrinsic_with_pow( ) # Assert - assert result == expected_result, f"Test failed for test_id: {test_id}." + assert result[0] is expected_result @pytest.mark.parametrize( @@ -203,7 +220,7 @@ def test_burned_register_extrinsic( mocker.patch.object( mock_subtensor, "sign_and_send_extrinsic", - return_value=(recycle_success, "Mock error message"), + return_value=ExtrinsicResponse(recycle_success, "Mock error message"), ) mocker.patch.object( mock_subtensor, "is_hotkey_registered", return_value=is_registered @@ -214,7 +231,7 @@ def test_burned_register_extrinsic( subtensor=mock_subtensor, wallet=mock_wallet, netuid=123 ) # Assert - assert result == expected_result, f"Test failed for test_id: {test_id}" + assert result.success == expected_result, f"Test failed for test_id: {test_id}" def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, mocker): @@ -232,7 +249,7 @@ def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, m mocked_compose_call = mocker.patch.object(mock_subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") + mock_subtensor, "sign_and_send_extrinsic" ) # Call @@ -274,9 +291,10 @@ def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, m wait_for_finalization=True, period=None, raise_error=False, + calling_function="set_subnet_identity_extrinsic", ) - assert result == (True, "Identities for subnet 123 are set.") + assert result == mocked_sign_and_send_extrinsic.return_value def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mocker): @@ -298,7 +316,6 @@ def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mo mocked_sign_and_send_extrinsic = mocker.patch.object( mock_subtensor, "sign_and_send_extrinsic", - return_value=(False, fake_error_message), ) # Call @@ -340,9 +357,7 @@ def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mo wait_for_finalization=True, period=None, raise_error=False, + calling_function="set_subnet_identity_extrinsic", ) - assert result == ( - False, - f"Failed to set identity for subnet {netuid}: {fake_error_message}", - ) + assert result == mocked_sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/test_root.py b/tests/unit_tests/extrinsics/test_root.py index 4a4c11e8e8..193be0f3e1 100644 --- a/tests/unit_tests/extrinsics/test_root.py +++ b/tests/unit_tests/extrinsics/test_root.py @@ -1,6 +1,7 @@ import pytest -from bittensor.core.subtensor import Subtensor from bittensor.core.extrinsics import root +from bittensor.core.subtensor import Subtensor +from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -69,7 +70,7 @@ def test_root_register_extrinsic( mocked_sign_and_send_extrinsic = mocker.patch.object( mock_subtensor, "sign_and_send_extrinsic", - return_value=(registration_success, "Error registering"), + return_value=ExtrinsicResponse(registration_success, "Error registering"), ) mocker.patch.object( mock_subtensor.substrate, @@ -90,7 +91,7 @@ def test_root_register_extrinsic( wait_for_finalization=wait_for_finalization, ) # Assert - assert result == expected_result + assert result.success == expected_result if not hotkey_registered[0]: mock_subtensor.substrate.compose_call.assert_called_once_with( @@ -119,7 +120,7 @@ def test_root_register_extrinsic_insufficient_balance( return_value=Balance(0), ) - success = root.root_register_extrinsic( + success, _ = root.root_register_extrinsic( subtensor=mock_subtensor, wallet=mock_wallet, ) diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index 3964df1cd2..0af11ae913 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -1,25 +1,8 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. -# -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - from unittest.mock import MagicMock, patch import pytest from bittensor_wallet import Wallet - +from bittensor.core.types import ExtrinsicResponse from bittensor.core.axon import Axon from bittensor.core.subtensor import Subtensor from bittensor.core.extrinsics import serving @@ -112,7 +95,9 @@ def test_serve_extrinsic_happy_path( ): # Prep mocker.patch.object( - mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "") + mock_subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, ""), ) # Call result = serving.serve_extrinsic( @@ -129,7 +114,7 @@ def test_serve_extrinsic_happy_path( ) # Assert - assert result == expected, f"Test ID: {test_id}" + assert result.success == expected, f"Test ID: {test_id}" # Various edge cases @@ -168,7 +153,9 @@ def test_serve_extrinsic_edge_cases( ): # Prep mocker.patch.object( - mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "") + mock_subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(True, ""), ) # Call @@ -186,7 +173,7 @@ def test_serve_extrinsic_edge_cases( ) # Assert - assert result == expected, f"Test ID: {test_id}" + assert result.success == expected, f"Test ID: {test_id}" # Various error cases @@ -225,7 +212,9 @@ def test_serve_extrinsic_error_cases( ): # Prep mocker.patch.object( - mock_subtensor, "sign_and_send_extrinsic", return_value=(False, "") + mock_subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(False, ""), ) # Call result = serving.serve_extrinsic( @@ -242,7 +231,7 @@ def test_serve_extrinsic_error_cases( ) # Assert - assert result == expected_error_message, f"Test ID: {test_id}" + assert result.success == expected_error_message, f"Test ID: {test_id}" @pytest.mark.parametrize( @@ -296,7 +285,9 @@ def test_serve_axon_extrinsic( else MagicMock(return_value="192.168.1.1"), ): mocker.patch.object( - mock_subtensor, "sign_and_send_extrinsic", return_value=(serve_success, "") + mock_subtensor, + "sign_and_send_extrinsic", + return_value=ExtrinsicResponse(serve_success, ""), ) # Calls @@ -319,11 +310,11 @@ def test_serve_axon_extrinsic( ) # Assert - assert result == expected_result, f"Test ID: {test_id}" + assert result.success == expected_result, f"Test ID: {test_id}" @pytest.mark.parametrize( - "wait_for_inclusion, wait_for_finalization, net_uid, type_u, data, response_success, expected_result, test_id", + "wait_for_inclusion, wait_for_finalization, net_uid, type_u, data, response_success, test_id", [ ( True, @@ -331,8 +322,7 @@ def test_serve_axon_extrinsic( 1, "Sha256", b"mock_bytes_data", - (True, ""), - True, + ExtrinsicResponse(True, ""), "happy-path-wait", ), ( @@ -341,8 +331,7 @@ def test_serve_axon_extrinsic( 1, "Sha256", b"mock_bytes_data", - (True, ""), - True, + ExtrinsicResponse(True, ""), "happy-path-no-wait", ), ], @@ -357,7 +346,6 @@ def test_publish_metadata( type_u, data, response_success, - expected_result, test_id, ): # Arrange @@ -368,7 +356,7 @@ def test_publish_metadata( ) as mocked_sign_and_send_extrinsic, ): # Act - result = serving.publish_metadata( + result = serving.publish_metadata_extrinsic( subtensor=mock_subtensor, wallet=mock_wallet, netuid=net_uid, @@ -378,7 +366,7 @@ def test_publish_metadata( wait_for_finalization=wait_for_finalization, ) # Assert - assert result == expected_result, f"Test ID: {test_id}" + assert result.success is True, f"Test ID: {test_id}" mocked_sign_and_send_extrinsic.assert_called_once_with( call=mock_subtensor.substrate.compose_call.return_value, wallet=mock_wallet, @@ -387,4 +375,5 @@ def test_publish_metadata( wait_for_finalization=wait_for_finalization, period=None, raise_error=False, + calling_function="publish_metadata_extrinsic", ) diff --git a/tests/unit_tests/extrinsics/test_set_weights.py b/tests/unit_tests/extrinsics/test_set_weights.py index 5690571ef9..382a36d5c7 100644 --- a/tests/unit_tests/extrinsics/test_set_weights.py +++ b/tests/unit_tests/extrinsics/test_set_weights.py @@ -5,6 +5,7 @@ from bittensor.core.extrinsics.weights import ( set_weights_extrinsic, ) +from bittensor.core.types import ExtrinsicResponse from bittensor.core.subtensor import Subtensor @@ -74,7 +75,7 @@ def test_set_weights_extrinsic( patch.object( mock_subtensor, "sign_and_send_extrinsic", - return_value=(expected_success, expected_message), + return_value=ExtrinsicResponse(expected_success, expected_message), ), ): result, message = set_weights_extrinsic( diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index 9ba6fe71a6..00e3b71ad1 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -1,5 +1,6 @@ from bittensor.core.extrinsics import staking from bittensor.utils.balance import Balance +from bittensor.core.types import ExtrinsicResponse def test_add_stake_extrinsic(mocker): @@ -10,7 +11,7 @@ def test_add_stake_extrinsic(mocker): "get_balance.return_value": Balance(10), "get_existential_deposit.return_value": Balance(1), "get_hotkey_owner.return_value": "hotkey_owner", - "sign_and_send_extrinsic.return_value": (True, ""), + "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, "Success"), } ) fake_wallet_ = mocker.Mock( @@ -36,7 +37,7 @@ def test_add_stake_extrinsic(mocker): ) # Asserts - assert result is True + assert result.success is True fake_subtensor.substrate.compose_call.assert_called_once_with( call_module="SubtensorModule", @@ -53,6 +54,7 @@ def test_add_stake_extrinsic(mocker): use_nonce=True, period=None, raise_error=False, + calling_function="add_stake_extrinsic", ) @@ -62,7 +64,7 @@ def test_add_stake_multiple_extrinsic(mocker): fake_subtensor = mocker.Mock( **{ "get_balance.return_value": Balance(10.0), - "sign_and_send_extrinsic.return_value": (True, ""), + "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), "substrate.query_multi.return_value": [ ( mocker.Mock( @@ -110,7 +112,7 @@ def test_add_stake_multiple_extrinsic(mocker): ) # Asserts - assert result is True + assert result.success is True assert fake_subtensor.substrate.compose_call.call_count == 2 assert fake_subtensor.sign_and_send_extrinsic.call_count == 2 @@ -142,4 +144,5 @@ def test_add_stake_multiple_extrinsic(mocker): raise_error=False, wait_for_inclusion=True, wait_for_finalization=True, + calling_function="add_stake_multiple_extrinsic", ) diff --git a/tests/unit_tests/extrinsics/test_start_call.py b/tests/unit_tests/extrinsics/test_start_call.py index 03373fa955..dd00bf2028 100644 --- a/tests/unit_tests/extrinsics/test_start_call.py +++ b/tests/unit_tests/extrinsics/test_start_call.py @@ -11,7 +11,7 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): subtensor.substrate.compose_call = mocker.Mock() mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") ) # Call @@ -35,6 +35,7 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): wait_for_finalization=False, period=None, raise_error=False, + calling_function="start_call_extrinsic", ) assert success is True diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index b885977de8..b56c3dbc90 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -1,6 +1,7 @@ import pytest from bittensor.core.extrinsics import transfer from bittensor.utils.balance import Balance +from bittensor.core.types import ExtrinsicResponse def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): @@ -36,7 +37,7 @@ def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) # Call @@ -68,8 +69,9 @@ def test_transfer_extrinsic_success(subtensor, fake_wallet, mocker): wait_for_finalization=True, period=None, raise_error=False, + calling_function="transfer_extrinsic", ) - assert result is True + assert result.success is True def test_transfer_extrinsic_call_successful_with_failed_response( @@ -107,7 +109,7 @@ def test_transfer_extrinsic_call_successful_with_failed_response( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(False, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(False, "") ) # Call @@ -139,8 +141,9 @@ def test_transfer_extrinsic_call_successful_with_failed_response( wait_for_finalization=True, period=None, raise_error=False, + calling_function="transfer_extrinsic", ) - assert result is False + assert result.success is False def test_transfer_extrinsic_insufficient_balance(subtensor, fake_wallet, mocker): @@ -194,7 +197,7 @@ def test_transfer_extrinsic_insufficient_balance(subtensor, fake_wallet, mocker) mocked_get_existential_deposit.assert_called_once_with( block=subtensor.substrate.get_block_number.return_value ) - assert result is False + assert result.success is False def test_transfer_extrinsic_invalid_destination(subtensor, fake_wallet, mocker): @@ -224,7 +227,7 @@ def test_transfer_extrinsic_invalid_destination(subtensor, fake_wallet, mocker): # Asserts mocked_is_valid_address.assert_called_once_with(fake_destination) - assert result is False + assert result.success is False def test_transfer_extrinsic_unlock_key_false(subtensor, fake_wallet, mocker): @@ -234,12 +237,6 @@ def test_transfer_extrinsic_unlock_key_false(subtensor, fake_wallet, mocker): fake_destination = "invalid_address" fake_amount = Balance(15) - mocked_is_valid_address = mocker.patch.object( - transfer, - "is_valid_bittensor_address_or_public_key", - return_value=True, - ) - mocked_unlock_key = mocker.patch.object( transfer, "unlock_key", @@ -259,9 +256,8 @@ def test_transfer_extrinsic_unlock_key_false(subtensor, fake_wallet, mocker): ) # Asserts - mocked_is_valid_address.assert_called_once_with(fake_destination) mocked_unlock_key.assert_called_once_with(fake_wallet) - assert result is False + assert result.success is False def test_transfer_extrinsic_keep_alive_false_and_transfer_all_true( @@ -299,7 +295,7 @@ def test_transfer_extrinsic_keep_alive_false_and_transfer_all_true( ) mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") mocked_sign_and_send_extrinsic = mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "") + subtensor, "sign_and_send_extrinsic", return_value=ExtrinsicResponse(True, "") ) # Call @@ -319,4 +315,4 @@ def test_transfer_extrinsic_keep_alive_false_and_transfer_all_true( mocked_unlock_key.assert_called_once_with(fake_wallet) assert mocked_compose_call.call_count == 0 assert mocked_sign_and_send_extrinsic.call_count == 0 - assert result is False + assert result.success is False diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index f4719cdbb2..5ed2da82fd 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -1,4 +1,5 @@ from bittensor.core.extrinsics import unstaking +from bittensor.core.types import ExtrinsicResponse from bittensor.utils.balance import Balance @@ -11,7 +12,7 @@ def test_unstake_extrinsic(fake_wallet, mocker): **{ "get_hotkey_owner.return_value": "hotkey_owner", "get_stake_for_coldkey_and_hotkey.return_value": Balance(10.0), - "sign_and_send_extrinsic.return_value": (True, ""), + "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), "get_stake.return_value": Balance(10.0), "substrate": fake_substrate, } @@ -35,7 +36,7 @@ def test_unstake_extrinsic(fake_wallet, mocker): ) # Asserts - assert result is True + assert result.success is True fake_subtensor.substrate.compose_call.assert_called_once_with( call_module="SubtensorModule", @@ -56,6 +57,7 @@ def test_unstake_extrinsic(fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="unstake_extrinsic", ) @@ -64,7 +66,7 @@ def test_unstake_all_extrinsic(fake_wallet, mocker): fake_subtensor = mocker.Mock( **{ "subnet.return_value": mocker.Mock(price=100), - "sign_and_send_extrinsic.return_value": (True, ""), + "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), } ) @@ -102,6 +104,7 @@ def test_unstake_all_extrinsic(fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="unstake_all_extrinsic", ) @@ -115,7 +118,7 @@ def test_unstake_multiple_extrinsic(fake_wallet, mocker): **{ "get_hotkey_owner.return_value": "hotkey_owner", "get_stake_for_coldkey_and_hotkey.return_value": [Balance(10.0)], - "sign_and_send_extrinsic.return_value": (True, ""), + "sign_and_send_extrinsic.return_value": ExtrinsicResponse(True, ""), "tx_rate_limit.return_value": 0, "substrate": fake_substrate, } @@ -142,7 +145,7 @@ def test_unstake_multiple_extrinsic(fake_wallet, mocker): ) # Asserts - assert result is True + assert result.success is True assert fake_subtensor.substrate.compose_call.call_count == 1 assert fake_subtensor.sign_and_send_extrinsic.call_count == 1 @@ -174,4 +177,5 @@ def test_unstake_multiple_extrinsic(fake_wallet, mocker): use_nonce=True, period=None, raise_error=False, + calling_function="unstake_multiple_extrinsic", ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 28decac35a..1b246c4eb9 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -15,6 +15,7 @@ StakeInfo, SelectiveMetagraphIndex, ) +from bittensor.core.types import ExtrinsicResponse from bittensor.utils import U64_MAX from bittensor.utils.balance import Balance from tests.helpers.helpers import assert_submit_signed_extrinsic @@ -180,7 +181,7 @@ async def test_burned_register(mock_substrate, subtensor, fake_wallet, mocker): return_value=Balance(1), ) - success = await subtensor.burned_register( + success, _ = await subtensor.burned_register( fake_wallet, netuid=1, ) @@ -223,7 +224,7 @@ async def test_burned_register_on_root(mock_substrate, subtensor, fake_wallet, m return_value=False, ) - success = await subtensor.burned_register( + success, _ = await subtensor.burned_register( fake_wallet, netuid=0, ) @@ -1730,11 +1731,11 @@ async def fake_is_success(): call=fake_call, keypair=fake_wallet.coldkey ) mocked_submit_extrinsic.assert_called_once_with( - fake_extrinsic, + extrinsic=fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True, ) - assert result == (True, "") + assert result == (True, "Success") @pytest.mark.asyncio @@ -1781,7 +1782,7 @@ async def fake_error_message(): call=fake_call, keypair=fake_wallet.coldkey ) mocked_submit_extrinsic.assert_called_once_with( - fake_extrinsic, + extrinsic=fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True, ) @@ -1818,7 +1819,7 @@ async def test_sign_and_send_extrinsic_success_without_inclusion_finalization( ) mocked_submit_extrinsic.assert_awaited_once() mocked_submit_extrinsic.assert_called_once_with( - fake_extrinsic, + extrinsic=fake_extrinsic, wait_for_inclusion=False, wait_for_finalization=False, ) @@ -2686,11 +2687,13 @@ async def test_set_children(subtensor, fake_wallet, mocker): async def test_set_delegate_take_equal(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18) - await subtensor.set_delegate_take( - fake_wallet, - fake_wallet.hotkey.ss58_address, - 0.18, - ) + assert ( + await subtensor.set_delegate_take( + fake_wallet, + fake_wallet.hotkey.ss58_address, + 0.18, + ) + ).success subtensor.substrate.submit_extrinsic.assert_not_called() @@ -2704,11 +2707,13 @@ async def test_set_delegate_take_increase( ) mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18) - await subtensor.set_delegate_take( - fake_wallet, - fake_wallet.hotkey.ss58_address, - 0.2, - ) + assert ( + await subtensor.set_delegate_take( + fake_wallet, + fake_wallet.hotkey.ss58_address, + 0.2, + ) + ).success assert_submit_signed_extrinsic( mock_substrate, @@ -2733,11 +2738,13 @@ async def test_set_delegate_take_decrease( ) mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18) - await subtensor.set_delegate_take( - fake_wallet, - fake_wallet.hotkey.ss58_address, - 0.1, - ) + assert ( + await subtensor.set_delegate_take( + fake_wallet, + fake_wallet.hotkey.ss58_address, + 0.1, + ) + ).success assert_submit_signed_extrinsic( mock_substrate, @@ -2773,7 +2780,9 @@ async def test_set_weights_success(subtensor, fake_wallet, mocker): mocked_weights_rate_limit = mocker.AsyncMock(return_value=1) subtensor.weights_rate_limit = mocked_weights_rate_limit - mocked_set_weights_extrinsic = mocker.AsyncMock(return_value=(True, "Success")) + mocked_set_weights_extrinsic = mocker.AsyncMock( + return_value=ExtrinsicResponse(True, "Success") + ) mocker.patch.object( async_subtensor, "set_weights_extrinsic", mocked_set_weights_extrinsic ) @@ -2871,7 +2880,9 @@ async def test_commit_weights_success(subtensor, fake_wallet, mocker): async_subtensor, "generate_weight_hash", mocked_generate_weight_hash ) - mocked_commit_weights_extrinsic = mocker.AsyncMock(return_value=(True, "Success")) + mocked_commit_weights_extrinsic = mocker.AsyncMock( + return_value=ExtrinsicResponse(True, "Success") + ) mocker.patch.object( async_subtensor, "commit_weights_extrinsic", mocked_commit_weights_extrinsic ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index a8882e9897..556f91fa29 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2,7 +2,7 @@ import unittest.mock as mock import datetime from unittest.mock import MagicMock - +from bittensor.core.types import ExtrinsicResponse import pytest from bittensor_wallet import Wallet from async_substrate_interface import sync_substrate @@ -25,6 +25,7 @@ determine_chain_endpoint_and_network, ) from bittensor.utils.balance import Balance +from bittensor.core.types import ExtrinsicResponse U16_MAX = 65535 U64_MAX = 18446744073709551615 @@ -1157,7 +1158,7 @@ def test_set_weights(subtensor, mocker, fake_wallet): fake_wait_for_finalization = False fake_max_retries = 5 - expected_result = (True, None) + expected_result = ExtrinsicResponse(True, None) mocked_get_uid_for_hotkey_on_subnet = mocker.MagicMock() subtensor.get_uid_for_hotkey_on_subnet = mocked_get_uid_for_hotkey_on_subnet @@ -1260,7 +1261,9 @@ def test_commit(subtensor, fake_wallet, mocker): # Preps fake_netuid = 1 fake_data = "some data to network" - mocked_publish_metadata = mocker.patch.object(subtensor_module, "publish_metadata") + mocked_publish_metadata = mocker.patch.object( + subtensor_module, "publish_metadata_extrinsic" + ) # Call result = subtensor.set_commitment(fake_wallet, fake_netuid, fake_data) @@ -1837,7 +1840,7 @@ def test_commit_weights(subtensor, fake_wallet, mocker): wait_for_finalization = False max_retries = 5 - expected_result = (True, None) + expected_result = ExtrinsicResponse(True, None) mocked_generate_weight_hash = mocker.patch.object( subtensor_module, "generate_weight_hash", return_value=expected_result ) @@ -1888,7 +1891,7 @@ def test_reveal_weights(subtensor, fake_wallet, mocker): uids = [1, 2, 3, 4] weights = [0.1, 0.2, 0.3, 0.4] salt = [4, 2, 2, 1] - expected_result = (True, None) + expected_result = ExtrinsicResponse(True, None) mocked_extrinsic = mocker.patch.object( subtensor_module, "reveal_weights_extrinsic", return_value=expected_result ) @@ -1929,11 +1932,13 @@ def test_reveal_weights_false(subtensor, fake_wallet, mocker): weights = [0.1, 0.2, 0.3, 0.4] salt = [4, 2, 2, 1] - expected_result = ( + expected_result = ExtrinsicResponse( False, "No attempt made. Perhaps it is too soon to reveal weights!", ) - mocked_extrinsic = mocker.patch.object(subtensor_module, "reveal_weights_extrinsic") + mocked_extrinsic = mocker.patch.object( + subtensor_module, "reveal_weights_extrinsic", return_value=expected_result + ) # Call result = subtensor.reveal_weights( @@ -1947,7 +1952,7 @@ def test_reveal_weights_false(subtensor, fake_wallet, mocker): ) # Assertion - assert result == expected_result + assert result == mocked_extrinsic.return_value assert mocked_extrinsic.call_count == 5 @@ -3055,7 +3060,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): mocked_commit_reveal_v3_extrinsic = mocker.patch.object( subtensor_module, "commit_reveal_extrinsic" ) - mocked_commit_reveal_v3_extrinsic.return_value = ( + mocked_commit_reveal_v3_extrinsic.return_value = ExtrinsicResponse( True, "Weights committed successfully", ) diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index 01a365bd31..54ceba0d69 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -177,7 +177,7 @@ def test_burned_register(mock_substrate, subtensor, fake_wallet, mocker): ) mocker.patch.object(subtensor, "get_balance") - success = subtensor.burned_register( + success, _ = subtensor.burned_register( fake_wallet, netuid=1, ) @@ -216,7 +216,7 @@ def test_burned_register_on_root(mock_substrate, subtensor, fake_wallet, mocker) return_value=False, ) - success = subtensor.burned_register( + success, _ = subtensor.burned_register( fake_wallet, netuid=0, ) @@ -241,7 +241,7 @@ def test_burned_register_on_root(mock_substrate, subtensor, fake_wallet, mocker) ) -def test_get_all_commitments(mock_substrate, subtensor, mocker): +def test_get_all_commitments(mock_substrate, subtensor): mock_substrate.query_map.return_value = [ ( (tuple(bytearray(32)),), @@ -582,7 +582,7 @@ def test_get_neuron_certificate(mock_substrate, subtensor): ) -def test_get_stake_for_coldkey(mock_substrate, subtensor, mocker): +def test_get_stake_for_coldkey(mock_substrate, subtensor): mock_substrate.runtime_call.return_value.value = [ { "coldkey": tuple(bytearray(32)), @@ -688,14 +688,14 @@ def test_last_drand_round(mock_substrate, subtensor): @pytest.mark.parametrize( - "wait", + "wait,resp_message", ( - True, - False, + [True, "Success"], + [False, "Not waiting for finalization or inclusion."], ), ) -def test_move_stake(mock_substrate, subtensor, fake_wallet, wait): - success = subtensor.move_stake( +def test_move_stake(mock_substrate, subtensor, fake_wallet, wait, resp_message): + success, message = subtensor.move_stake( wallet=fake_wallet, origin_hotkey_ss58="origin_hotkey", origin_netuid=1, @@ -707,6 +707,7 @@ def test_move_stake(mock_substrate, subtensor, fake_wallet, wait): ) assert success is True + assert message == resp_message assert_submit_signed_extrinsic( mock_substrate, @@ -728,7 +729,7 @@ def test_move_stake(mock_substrate, subtensor, fake_wallet, wait): def test_move_stake_insufficient_stake(mock_substrate, subtensor, fake_wallet, mocker): mocker.patch.object(subtensor, "get_stake", return_value=Balance(0)) - success = subtensor.move_stake( + success, message = subtensor.move_stake( fake_wallet, origin_hotkey_ss58="origin_hotkey", origin_netuid=1, @@ -738,6 +739,7 @@ def test_move_stake_insufficient_stake(mock_substrate, subtensor, fake_wallet, m ) assert success is False + assert "Insufficient stake in origin hotkey" in message mock_substrate.submit_extrinsic.assert_not_called() @@ -748,7 +750,7 @@ def test_move_stake_error(mock_substrate, subtensor, fake_wallet, mocker): is_success=False, ) - success = subtensor.move_stake( + success, message = subtensor.move_stake( fake_wallet, origin_hotkey_ss58="origin_hotkey", origin_netuid=1, @@ -758,6 +760,10 @@ def test_move_stake_error(mock_substrate, subtensor, fake_wallet, mocker): ) assert success is False + assert ( + message + == "Subtensor returned `UnknownError(UnknownType)` error. This means: `Unknown Description`." + ) assert_submit_signed_extrinsic( mock_substrate, @@ -779,16 +785,16 @@ def test_move_stake_error(mock_substrate, subtensor, fake_wallet, mocker): def test_move_stake_exception(mock_substrate, subtensor, fake_wallet): mock_substrate.submit_extrinsic.side_effect = RuntimeError - success = subtensor.move_stake( - fake_wallet, - origin_hotkey_ss58="origin_hotkey", - origin_netuid=1, - destination_hotkey_ss58="destination_hotkey", - destination_netuid=2, - amount=Balance(1), - ) - - assert success is False + with pytest.raises(RuntimeError) as exc: + subtensor.move_stake( + fake_wallet, + origin_hotkey_ss58="origin_hotkey", + origin_netuid=1, + destination_hotkey_ss58="destination_hotkey", + destination_netuid=2, + amount=Balance(1), + raise_error=True, + ) assert_submit_signed_extrinsic( mock_substrate, @@ -917,7 +923,7 @@ def test_neurons_lite(mock_substrate, subtensor, mock_neuron_info): ) -def test_set_children(mock_substrate, subtensor, fake_wallet, mocker): +def test_set_children(mock_substrate, subtensor, fake_wallet): subtensor.set_children( fake_wallet, fake_wallet.hotkey.ss58_address, @@ -953,11 +959,11 @@ def test_set_children(mock_substrate, subtensor, fake_wallet, mocker): def test_set_delegate_take_equal(mock_substrate, subtensor, fake_wallet, mocker): mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18) - subtensor.set_delegate_take( + assert subtensor.set_delegate_take( fake_wallet, fake_wallet.hotkey.ss58_address, 0.18, - ) + ).success mock_substrate.submit_extrinsic.assert_not_called() @@ -965,11 +971,11 @@ def test_set_delegate_take_equal(mock_substrate, subtensor, fake_wallet, mocker) def test_set_delegate_take_increase(mock_substrate, subtensor, fake_wallet, mocker): mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18) - subtensor.set_delegate_take( + assert subtensor.set_delegate_take( fake_wallet, fake_wallet.hotkey.ss58_address, 0.2, - ) + ).success assert_submit_signed_extrinsic( mock_substrate, @@ -988,11 +994,11 @@ def test_set_delegate_take_increase(mock_substrate, subtensor, fake_wallet, mock def test_set_delegate_take_decrease(mock_substrate, subtensor, fake_wallet, mocker): mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18) - subtensor.set_delegate_take( + assert subtensor.set_delegate_take( fake_wallet, fake_wallet.hotkey.ss58_address, 0.1, - ) + ).success assert_submit_signed_extrinsic( mock_substrate, @@ -1064,7 +1070,7 @@ def test_swap_stake(mock_substrate, subtensor, fake_wallet, mocker): return_value=fake_wallet.coldkeypub.ss58_address, ) - result = subtensor.swap_stake( + success, message = subtensor.swap_stake( fake_wallet, fake_wallet.hotkey.ss58_address, origin_netuid=1, @@ -1072,7 +1078,8 @@ def test_swap_stake(mock_substrate, subtensor, fake_wallet, mocker): amount=Balance(999), ) - assert result is True + assert success is True + assert message == "Success" assert_submit_signed_extrinsic( mock_substrate, @@ -1152,12 +1159,10 @@ def test_register(mock_substrate, subtensor, fake_wallet, mocker): return_value=NeuronInfo.get_null_neuron(), ) - result = subtensor.register( - fake_wallet, + assert subtensor.register( + wallet=fake_wallet, netuid=1, - ) - - assert result is True + ).success subtensor.get_neuron_for_pubkey_and_subnet.assert_called_once_with( hotkey_ss58=fake_wallet.hotkey.ss58_address, @@ -1208,7 +1213,7 @@ def test_register_subnet(mock_substrate, subtensor, fake_wallet, mocker, success result = subtensor.register_subnet(fake_wallet) - assert result is success + assert result.success is success assert_submit_signed_extrinsic( substrate=mock_substrate, @@ -1228,7 +1233,7 @@ def test_register_subnet_insufficient_funds( mocker.patch.object(subtensor, "get_balance", return_value=Balance(0)) mocker.patch.object(subtensor, "get_subnet_burn_cost", return_value=Balance(10)) - success = subtensor.register_subnet(fake_wallet) + success, _ = subtensor.register_subnet(fake_wallet) assert success is False @@ -1244,7 +1249,7 @@ def test_root_register(mock_substrate, subtensor, fake_wallet, mocker): subtensor, "is_hotkey_registered_on_subnet", autospec=True, return_value=False ) - success = subtensor.root_register(fake_wallet) + success, _ = subtensor.root_register(fake_wallet) assert success is True @@ -1281,7 +1286,7 @@ def test_root_register_is_already_registered( subtensor, "is_hotkey_registered_on_subnet", autospec=True, return_value=True ) - success = subtensor.root_register(fake_wallet) + success, _ = subtensor.root_register(fake_wallet) assert success is True @@ -1343,13 +1348,15 @@ def test_sign_and_send_extrinsic_raises_error( @pytest.mark.parametrize( - "wait", + "wait,response_message", ( - True, - False, + [True, "Success"], + [False, "Not waiting for finalization or inclusion."], ), ) -def test_transfer_stake(mock_substrate, subtensor, fake_wallet, mocker, wait): +def test_transfer_stake( + mock_substrate, subtensor, fake_wallet, mocker, wait, response_message +): mocker.patch.object( subtensor, "get_hotkey_owner", @@ -1357,7 +1364,7 @@ def test_transfer_stake(mock_substrate, subtensor, fake_wallet, mocker, wait): return_value=fake_wallet.coldkeypub.ss58_address, ) - success = subtensor.transfer_stake( + success, message = subtensor.transfer_stake( fake_wallet, "dest", "hotkey_ss58", @@ -1369,6 +1376,7 @@ def test_transfer_stake(mock_substrate, subtensor, fake_wallet, mocker, wait): ) assert success is True + assert message == response_message assert_submit_signed_extrinsic( mock_substrate, @@ -1387,57 +1395,6 @@ def test_transfer_stake(mock_substrate, subtensor, fake_wallet, mocker, wait): ) -@pytest.mark.parametrize( - "side_effect", - ( - ( - unittest.mock.Mock( - error_message="ERROR", - is_success=False, - ), - ), - RuntimeError, - ), -) -def test_transfer_stake_error( - mock_substrate, subtensor, fake_wallet, mocker, side_effect -): - mocker.patch.object( - subtensor, - "get_hotkey_owner", - autospec=True, - return_value=fake_wallet.coldkeypub.ss58_address, - ) - mock_substrate.submit_extrinsic.return_value = side_effect - - success = subtensor.transfer_stake( - fake_wallet, - "dest", - "hotkey_ss58", - origin_netuid=1, - destination_netuid=1, - amount=Balance(1), - ) - - assert success is False - - assert_submit_signed_extrinsic( - mock_substrate, - fake_wallet.coldkey, - call_module="SubtensorModule", - call_function="transfer_stake", - call_params={ - "destination_coldkey": "dest", - "hotkey": "hotkey_ss58", - "origin_netuid": 1, - "destination_netuid": 1, - "alpha_amount": 1, - }, - wait_for_finalization=True, - wait_for_inclusion=True, - ) - - def test_transfer_stake_insufficient_stake( mock_substrate, subtensor, fake_wallet, mocker ): @@ -1453,7 +1410,7 @@ def test_transfer_stake_insufficient_stake( "get_stake", return_value=Balance(0), ): - success = subtensor.transfer_stake( + success, message = subtensor.transfer_stake( fake_wallet, "dest", "hotkey_ss58", @@ -1463,6 +1420,7 @@ def test_transfer_stake_insufficient_stake( ) assert success is False + assert "Insufficient stake in origin hotkey" in message mock_substrate.submit_extrinsic.assert_not_called()