diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 3f63ad35cb..c9b05ceef2 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -98,7 +98,7 @@ check_and_convert_to_balance, ) from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import generate_weight_hash +from bittensor.utils.weight_utils import generate_weight_hash, convert_uids_and_weights if TYPE_CHECKING: from async_substrate_interface.types import ScaleObj @@ -653,7 +653,9 @@ async def bonds( return b_map - async def commit(self, wallet: "Wallet", netuid: int, data: str) -> bool: + async def commit( + self, wallet: "Wallet", netuid: int, data: str, period: Optional[int] = None + ) -> bool: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -661,6 +663,12 @@ async def commit(self, wallet: "Wallet", netuid: int, data: str) -> bool: wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. + 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. + + Return: + bool: `True` if the commit was successful, `False` otherwise. """ return await publish_metadata( subtensor=self, @@ -2850,6 +2858,7 @@ async def set_reveal_commitment( data: str, blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, + period: Optional[int] = None, ) -> tuple[bool, int]: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -2859,8 +2868,11 @@ async def set_reveal_commitment( netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`. - Then amount of blocks in one epoch. + The number of blocks in one epoch. block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`. + 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: bool: `True` if the commitment was successful, `False` otherwise. @@ -2881,6 +2893,7 @@ async def set_reveal_commitment( netuid=netuid, data_type=f"TimelockEncrypted", data=data_, + period=period, ), reveal_round async def subnet( @@ -3038,14 +3051,14 @@ async def wait_for_block(self, block: Optional[int] = None): waits for the next block. Args: - block (Optional[int]): The block number to wait for. If None, waits for next block. + block (Optional[int]): The block number to wait for. If None, waits for the next block. Returns: bool: True if the target block was reached, False if timeout occurred. Example: await subtensor.wait_for_block() # Waits for next block - await subtensor.wait_for_block(block=1234) # Waits for specific block + await subtensor.wait_for_block(block=1234) # Waits for a specific block """ async def handler(block_data: dict): @@ -3054,6 +3067,7 @@ async def handler(block_data: dict): ) if block_data["header"]["number"] >= target_block: return True + return None current_block = await self.substrate.get_block() current_block_hash = current_block.get("header", {}).get("hash") @@ -3229,12 +3243,18 @@ async def sign_and_send_extrinsic( wait_for_finalization (bool): 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 period of the transaction as ERA part for transaction. Means how many blocks the transaction will be valid for. + 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. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". - raise_error: raises relevant exception rather than returning `False` if unsuccessful. + 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. Returns: (success, error message) + + Raises: + SubstrateRequestException: Substrate request exception. """ possible_keys = ("coldkey", "hotkey", "coldkeypub") if sign_with not in possible_keys: @@ -3264,7 +3284,9 @@ async def sign_and_send_extrinsic( ) # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "" + message = "Not waiting for finalization or inclusion." + logging.debug(f"{message}. Extrinsic: {extrinsic}") + return True, message if await response.is_success: return True, "" @@ -3293,6 +3315,7 @@ async def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to a neuron identified by the hotkey ``SS58`` address. @@ -3313,6 +3336,9 @@ async def add_stake( exceed the threshold. Default is False. rate_tolerance (float): The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. Default is 0.005. + 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: bool: ``True`` if the staking is successful, False otherwise. @@ -3333,6 +3359,7 @@ async def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) async def add_stake_multiple( @@ -3378,6 +3405,7 @@ async def burned_register( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling @@ -3390,6 +3418,9 @@ async def burned_register( `False`. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to `True`. + 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: bool: ``True`` if the registration is successful, False otherwise. @@ -3401,6 +3432,7 @@ async def burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) return await burned_register_extrinsic( @@ -3409,6 +3441,7 @@ async def burned_register( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def commit_weights( @@ -3422,6 +3455,7 @@ async def commit_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = 16, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -3434,15 +3468,19 @@ async def commit_weights( uids (np.ndarray): NumPy array of neuron UIDs for which weights are being committed. weights (np.ndarray): NumPy array of weight values corresponding to each UID. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version.``. + a Bittensor version.``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to commit weights. Default is ``5``. + 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]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, False otherwise. + `msg` is a string value describing the success or potential error. 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. @@ -3452,8 +3490,9 @@ async def commit_weights( message = "No attempt made. Perhaps it is too soon to commit weights!" logging.info( - f"Committing weights with params: netuid={netuid}, uids={uids}, weights={weights}, " - f"version_key={version_key}" + f"Committing weights with params: " + f"netuid=[blue]{netuid}[/blue], uids=[blue]{uids}[/blue], weights=[blue]{weights}[/blue], " + f"version_key=[blue]{version_key}[/blue]" ) # Generate the hash of the weights @@ -3475,12 +3514,12 @@ async def commit_weights( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error committing weights: {e}") - finally: retries += 1 return success, message @@ -3495,6 +3534,7 @@ async def move_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake to a different hotkey and/or subnet. @@ -3508,6 +3548,9 @@ async def move_stake( amount (Balance): Amount of stake to move. 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: success (bool): True if the stake movement was successful. @@ -3523,6 +3566,7 @@ async def move_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def register( @@ -3539,6 +3583,7 @@ async def register( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ): """ Registers a neuron on the Bittensor network using the provided wallet. @@ -3561,6 +3606,9 @@ async def register( num_processes (Optional[int]): The number of processes to use to register. Default to `None`. update_interval (Optional[int]): The number of nonces to solve between updates. Default to `None`. log_verbose (bool): If ``true``, the registration process will log more information. Default to `False`. + 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: bool: ``True`` if the registration is successful, False otherwise. @@ -3582,6 +3630,7 @@ async def register( dev_id=dev_id, output_in_place=output_in_place, log_verbose=log_verbose, + period=period, ) async def register_subnet( @@ -3589,6 +3638,7 @@ async def register_subnet( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor network. @@ -3599,6 +3649,9 @@ async def register_subnet( false if the extrinsic fails to enter the block within the timeout. Default is False. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true, or returns false if the extrinsic fails to be finalized within the timeout. Default is True. + 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: bool: True if the subnet registration was successful, False otherwise. @@ -3609,6 +3662,7 @@ async def register_subnet( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def reveal_weights( @@ -3622,6 +3676,7 @@ async def reveal_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -3634,11 +3689,14 @@ async def reveal_weights( weights (np.ndarray): NumPy array of weight values corresponding to each UID. salt (np.ndarray): NumPy array of salt values corresponding to the hash function. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version``. + the Bittensor version``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to reveal weights. Default is ``5``. + 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]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string @@ -3663,32 +3721,37 @@ async def reveal_weights( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error revealing weights: {e}") - finally: retries += 1 return success, message + # TODO: remove `block_hash` argument async def root_register( self, wallet: "Wallet", block_hash: Optional[str] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Register neuron by recycling some TAO. Arguments: wallet (bittensor_wallet.Wallet): Bittensor wallet instance. - block_hash (Optional[str]): The hash of the blockchain block for the query. + block_hash (Optional[str]): This argument will be removed in Bittensor v10 wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. + 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: `True` if registration was successful, otherwise `False`. @@ -3699,6 +3762,7 @@ async def root_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def root_set_weights( @@ -3709,9 +3773,10 @@ async def root_set_weights( version_key: int = 0, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ - Set weights for root network. + Set weights for the root network. Arguments: wallet (bittensor_wallet.Wallet): bittensor wallet instance. @@ -3722,12 +3787,14 @@ async def root_set_weights( ``False``. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to ``False``. + 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: `True` if the setting of weights is successful, `False` otherwise. """ - netuids_ = np.array(netuids, dtype=np.int64) - weights_ = np.array(weights, dtype=np.float32) + netuids_, weights_ = convert_uids_and_weights(netuids, weights) logging.info(f"Setting weights in network: [blue]{self.network}[/blue]") # Run the set weights operation. return await set_root_weights_extrinsic( @@ -3738,6 +3805,7 @@ async def root_set_weights( version_key=version_key, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) async def set_children( @@ -3749,9 +3817,10 @@ async def set_children( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ - Allows a coldkey to set children keys. + Allows a coldkey to set children-keys. Arguments: wallet (bittensor_wallet.Wallet): bittensor wallet instance. @@ -3760,7 +3829,10 @@ async def set_children( children (list[tuple[float, str]]): A list of children with their proportions. 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. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + 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 where the first element is a boolean indicating success or failure of the @@ -3807,6 +3879,7 @@ async def set_children( wait_for_inclusion, wait_for_finalization, raise_error=raise_error, + period=period, ) async def set_delegate_take( @@ -3817,6 +3890,7 @@ async def set_delegate_take( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. @@ -3828,7 +3902,10 @@ async def set_delegate_take( take (float): Percentage reward for the delegate. 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. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + 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 where the first element is a boolean indicating success or failure of the @@ -3838,8 +3915,8 @@ async def set_delegate_take( DelegateTakeTooHigh: Delegate take is too high. DelegateTakeTooLow: Delegate take is too low. DelegateTxRateLimitExceeded: A transactor exceeded the rate limit for delegate transaction. - HotKeyAccountNotExists: The hotkey does not exists. - NonAssociatedColdKey: Request to stake, unstake or subscribe is made by a coldkey that is not associated with the hotkey account. + HotKeyAccountNotExists: The hotkey does not exist. + NonAssociatedColdKey: Request to stake, unstake, or subscribe is made by a coldkey that is not associated with the hotkey account. bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. @@ -3868,6 +3945,7 @@ async def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, + period=period, ) else: success, error = await decrease_take_extrinsic( @@ -3878,6 +3956,7 @@ async def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, + period=period, ) if success: @@ -3892,6 +3971,7 @@ async def set_subnet_identity( subnet_identity: SubnetIdentity, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the identity of a subnet for a specific wallet and network. @@ -3903,6 +3983,9 @@ async def set_subnet_identity( repository, contact, URL, discord, description, and any additional metadata. wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. + 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 where the first element is a boolean indicating success or failure of the @@ -3921,6 +4004,7 @@ async def set_subnet_identity( additional=subnet_identity.additional, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def set_weights( @@ -3934,7 +4018,7 @@ async def set_weights( wait_for_finalization: bool = False, max_retries: int = 5, block_time: float = 12.0, - period: int = 5, + period: Optional[int] = 8, ): """ Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or @@ -3949,13 +4033,15 @@ async def set_weights( weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The corresponding weights to be set for each UID. version_key (int): Version key for compatibility with the network. Default is int representation of - Bittensor version. + the Bittensor version. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to set weights. Default is ``5``. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + block_time (float): The number of seconds for block duration. Default is 12.0 seconds. + 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. Default is 16. Returns: tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string @@ -3974,6 +4060,7 @@ async def _blocks_weight_limit() -> bool: retries = 0 success = False + message = "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 @@ -3986,7 +4073,7 @@ async def _blocks_weight_limit() -> bool: if (await self.commit_reveal_enabled(netuid=netuid)) is True: # go with `commit reveal v3` extrinsic - message = "No attempt made. Perhaps it is too soon to commit weights!" + while ( retries < max_retries and success is False @@ -4005,12 +4092,13 @@ async def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, block_time=block_time, + period=period, ) retries += 1 return success, message else: # go with classic `set weights extrinsic` - message = "No attempt made. Perhaps it is too soon to set weights!" + while ( retries < max_retries and success is False @@ -4019,7 +4107,7 @@ async def _blocks_weight_limit() -> bool: try: logging.info( f"Setting weights for subnet #[blue]{netuid}[/blue]. " - f"Attempt [blue]{retries + 1} of {max_retries}[/blue]." + f"Attempt [blue]{retries + 1}[/blue] of [green]{max_retries}[/green]." ) success, message = await set_weights_extrinsic( subtensor=self, @@ -4034,7 +4122,6 @@ async def _blocks_weight_limit() -> bool: ) except Exception as e: logging.error(f"Error setting weights: {e}") - finally: retries += 1 return success, message @@ -4046,6 +4133,7 @@ async def serve_axon( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. This function is used to @@ -4059,6 +4147,9 @@ async def serve_axon( ``True``. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + 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: bool: ``True`` if the Axon serve registration is successful, False otherwise. @@ -4073,6 +4164,7 @@ async def serve_axon( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period, ) async def start_call( @@ -4081,6 +4173,7 @@ async def start_call( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -4091,6 +4184,9 @@ async def start_call( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + 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]: @@ -4103,6 +4199,7 @@ async def start_call( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def swap_stake( @@ -4117,6 +4214,7 @@ async def swap_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -4139,6 +4237,9 @@ async def swap_stake( rate_tolerance (float): The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. Default is 0.005. + 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: success (bool): True if the extrinsic was successful. @@ -4163,6 +4264,49 @@ async def swap_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, + ) + + async def transfer( + self, + wallet: "Wallet", + dest: str, + amount: Balance, + transfer_all: bool = False, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + keep_alive: bool = True, + period: Optional[int] = None, + ) -> bool: + """ + Transfer token of amount to destination. + + Arguments: + wallet (bittensor_wallet.Wallet): Source wallet for the transfer. + dest (str): Destination address for the transfer. + amount (float): Number of tokens to transfer. + transfer_all (bool): Flag to transfer all tokens. Default is ``False``. + wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. + wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is + ``False``. + keep_alive (bool): Flag to keep the connection alive. Default is ``True``. + 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: + `True` if the transferring was successful, otherwise `False`. + """ + amount = check_and_convert_to_balance(amount) + return await transfer_extrinsic( + subtensor=self, + wallet=wallet, + dest=dest, + amount=amount, + transfer_all=transfer_all, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + keep_alive=keep_alive, + period=period, ) async def transfer_stake( @@ -4175,6 +4319,7 @@ async def transfer_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -4188,6 +4333,9 @@ async def transfer_stake( amount (Balance): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + 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: success (bool): True if the transfer was successful. @@ -4203,44 +4351,7 @@ async def transfer_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - ) - - async def transfer( - self, - wallet: "Wallet", - dest: str, - amount: Balance, - transfer_all: bool = False, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = False, - keep_alive: bool = True, - ) -> bool: - """ - Transfer token of amount to destination. - - Arguments: - wallet (bittensor_wallet.Wallet): Source wallet for the transfer. - dest (str): Destination address for the transfer. - amount (float): Amount of tokens to transfer. - transfer_all (bool): Flag to transfer all tokens. Default is ``False``. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - keep_alive (bool): Flag to keep the connection alive. Default is ``True``. - - Returns: - `True` if the transferring was successful, otherwise `False`. - """ - amount = check_and_convert_to_balance(amount) - return await transfer_extrinsic( - subtensor=self, - wallet=wallet, - dest=dest, - amount=amount, - transfer_all=transfer_all, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - keep_alive=keep_alive, + period=period, ) async def unstake( @@ -4254,6 +4365,7 @@ async def unstake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting @@ -4274,6 +4386,9 @@ async def unstake( exceed the threshold. Default is False. rate_tolerance (float): The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. Default is 0.005. + 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: bool: ``True`` if the unstaking process is successful, False otherwise. @@ -4293,6 +4408,7 @@ async def unstake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) async def unstake_multiple( @@ -4303,6 +4419,7 @@ async def unstake_multiple( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts @@ -4317,6 +4434,9 @@ async def unstake_multiple( unstakes all available stakes. 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: bool: ``True`` if the batch unstaking is successful, False otherwise. @@ -4332,6 +4452,7 @@ async def unstake_multiple( amounts=amounts, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index 70c12fe0fb..9c65ae8ea1 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -8,7 +8,7 @@ from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -24,23 +24,26 @@ async def _do_commit_reveal_v3( reveal_round: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[str]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ - Executes the commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or - finalization. + Executes commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or finalization. Arguments: subtensor: An instance of the AsyncSubtensor class. wallet: Wallet An instance of the Wallet class containing the user's keypair. netuid: int The network unique identifier. - commit bytes The commit data in bytes format. + commit: bytes The commit data in bytes format. reveal_round: int The round number for the reveal phase. wait_for_inclusion: bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. wait_for_finalization: bool, optional Flag indicating whether to wait for the extrinsic to be finalized. + 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: - A tuple where the first element is a boolean indicating success or failure, and the second element is an - optional string containing error message if any. + A tuple where the first element is a boolean indicating success or failure, and the second element is a + string containing an error message if any. """ logging.info( f"Committing weights hash [blue]{commit.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " @@ -57,7 +60,12 @@ async def _do_commit_reveal_v3( }, ) return await subtensor.sign_and_send_extrinsic( - call, wallet, wait_for_inclusion, wait_for_finalization, sign_with="hotkey" + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period, ) @@ -70,10 +78,11 @@ async def commit_reveal_v3_extrinsic( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - block_time: float = 12.0, + block_time: Union[int, float] = 12.0, + period: Optional[int] = None, ) -> tuple[bool, str]: """ - Commits and reveals weights for given subtensor and wallet with provided uids and weights. + Commits and reveals weights for a given subtensor and wallet with provided uids and weights. Arguments: subtensor: The AsyncSubtensor instance. @@ -84,21 +93,17 @@ async def commit_reveal_v3_extrinsic( version_key: The version key to use for committing and revealing. Default is version_as_int. wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. + block_time (float): The number of seconds for block duration. Default is 12.0 seconds. + 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 where the first element is a boolean indicating success or failure, and the second element is a message associated with the result """ try: - # Convert uids and weights - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - - # Reformat and normalize. - uids, weights = convert_weights_and_uids_for_emit(uids, weights) + 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( @@ -127,6 +132,7 @@ async def commit_reveal_v3_extrinsic( reveal_round=reveal_round, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success is not True: diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 6ab0b13028..8cfd5a2604 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -1,5 +1,5 @@ import asyncio -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -18,6 +18,7 @@ async def _get_stake_in_origin_and_dest( origin_netuid: int, destination_netuid: int, ) -> tuple[Balance, Balance]: + """Gets the current stake balances for both origin and destination addresses in their respective subnets.""" block_hash = await subtensor.substrate.get_chain_head() stake_in_origin, stake_in_destination = await asyncio.gather( subtensor.get_stake( @@ -46,6 +47,7 @@ async def transfer_stake_extrinsic( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one coldkey to another in the Bittensor network. @@ -60,6 +62,9 @@ async def transfer_stake_extrinsic( amount (Balance): The amount of stake to transfer as a `Balance` object. wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to `True`. wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to `False`. + 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: bool: True if the transfer was successful, False otherwise. @@ -77,7 +82,7 @@ async def transfer_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -116,6 +121,7 @@ async def transfer_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -126,7 +132,7 @@ async def transfer_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -163,6 +169,7 @@ async def swap_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Swaps stake from one subnet to another for a given hotkey in the Bittensor network. @@ -178,7 +185,10 @@ async def swap_stake_extrinsic( wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to False. safe_staking (bool): If true, enables price safety checks to protect against price impact. allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. - rate_tolerance (float): Maximum allowed increase in price ratio (0.005 = 0.5%). + rate_tolerance (float): Maximum allowed increase in a price ratio (0.005 = 0.5%). + 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: bool: True if the swap was successful, False otherwise. @@ -195,7 +205,7 @@ async def swap_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -259,6 +269,7 @@ async def swap_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -309,6 +320,7 @@ async def move_stake_extrinsic( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake from one hotkey to another within subnets in the Bittensor network. @@ -323,6 +335,9 @@ async def move_stake_extrinsic( amount (Balance): The amount of stake to move as a `Balance` object. wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to True. wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to False. + 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: bool: True if the move was successful, False otherwise. @@ -331,7 +346,7 @@ async def move_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -369,6 +384,7 @@ async def move_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -379,7 +395,7 @@ async def move_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index cfdd78e6f5..8758169869 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -10,7 +10,7 @@ import asyncio from typing import Optional, Union, TYPE_CHECKING -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key from bittensor.utils.btlogging import logging from bittensor.utils.registration import log_no_torch_error, create_pow_async, torch @@ -26,6 +26,7 @@ async def _do_burned_register( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Performs a burned register extrinsic call to the Subtensor chain. @@ -38,6 +39,9 @@ async def _do_burned_register( wallet (bittensor_wallet.Wallet): The wallet to be registered. wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False. wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True. + 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, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error @@ -58,6 +62,7 @@ async def _do_burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -67,6 +72,7 @@ async def burned_register_extrinsic( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """Registers the wallet to chain by recycling TAO. @@ -78,6 +84,9 @@ async def burned_register_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -98,7 +107,7 @@ async def burned_register_extrinsic( f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue][magenta] ...[/magenta]" ) - # We could do this as_completed because we don't actually need old_balance and recycle + # We could do this as_completed because we don't need old_balance and recycle # if neuron is null, but the complexity isn't worth it considering the small performance # gains we'd hypothetically receive in this situation neuron, old_balance, recycle_amount = await asyncio.gather( @@ -126,6 +135,7 @@ async def burned_register_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not success: @@ -162,6 +172,7 @@ async def _do_pow_register( pow_result: "POWSolution", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, Optional[str]]: """Sends a (POW) register extrinsic to the chain. @@ -172,6 +183,9 @@ async def _do_pow_register( pow_result (POWSolution): The PoW result to register. wait_for_inclusion (bool): If ``True``, waits for the extrinsic to be included in a block. Default to `False`. wait_for_finalization (bool): If ``True``, waits for the extrinsic to be finalized. Default to `True`. + 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: success (bool): ``True`` if the extrinsic was included in a block. @@ -196,6 +210,7 @@ async def _do_pow_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -213,6 +228,7 @@ async def register_extrinsic( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ) -> bool: """Registers the wallet to the chain. @@ -233,6 +249,9 @@ async def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. + 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: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -321,7 +340,7 @@ async def register_extrinsic( # pow successful, proceed to submit pow to chain for registration else: logging.info(":satellite: [magenta]Submitting POW...[/magenta]") - # check if pow result is still valid + # check if a pow result is still valid while not await pow_result.is_stale_async(subtensor=subtensor): result: tuple[bool, Optional[str]] = await _do_pow_register( subtensor=subtensor, @@ -330,6 +349,7 @@ async def register_extrinsic( pow_result=pow_result, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) success, err_msg = result @@ -386,6 +406,7 @@ async def register_subnet_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor blockchain asynchronously. @@ -395,6 +416,9 @@ async def register_subnet_extrinsic( wallet (Wallet): The wallet to be used for subnet registration. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. + 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: bool: True if the subnet registration was successful, False otherwise. @@ -417,29 +441,25 @@ async def register_subnet_extrinsic( }, ) - extrinsic = await subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.coldkey - ) - - response = await subtensor.substrate.submit_extrinsic( - extrinsic, + 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, ) if not wait_for_finalization and not wait_for_inclusion: return True - if not await response.is_success: - logging.error( - f"Failed to register subnet: {format_error_message(await response.error_message)}" + if success: + logging.success( + ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) - return False + return True - logging.success( - ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" - ) - return True + logging.error(f"Failed to register subnet: {message}") + return False async def set_subnet_identity_extrinsic( @@ -455,6 +475,7 @@ async def set_subnet_identity_extrinsic( additional: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Set the identity information for a given subnet. @@ -472,6 +493,9 @@ async def set_subnet_identity_extrinsic( additional (str): Any additional metadata or information related to the subnet. wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). + 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 where the first element indicates success or failure (True/False), and the second @@ -498,15 +522,16 @@ async def set_subnet_identity_extrinsic( }, ) - success, error_message = await subtensor.sign_and_send_extrinsic( + 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, ) if not wait_for_finalization and not wait_for_inclusion: - return True, f"Identities for subnet {netuid} are sent to the chain." + return True, message if success: logging.success( @@ -515,6 +540,6 @@ async def set_subnet_identity_extrinsic( return True, f"Identities for subnet {netuid} are set." logging.error( - f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {error_message}" + f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" ) - return False, f"Failed to set identity for subnet {netuid}: {error_message}" + return False, f"Failed to set identity for subnet {netuid}: {message}" diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index 6c30f631eb..79c5417e4f 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -1,7 +1,6 @@ import asyncio -from typing import Union, TYPE_CHECKING +from typing import Optional, Union, TYPE_CHECKING -from bittensor_wallet import Wallet import numpy as np from numpy.typing import NDArray @@ -12,9 +11,11 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, + convert_uids_and_weights, ) if TYPE_CHECKING: + from bittensor_wallet import Wallet from bittensor.core.async_subtensor import AsyncSubtensor @@ -47,8 +48,9 @@ async def root_register_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: - """Registers the wallet to root network. + """Registers the wallet to the root network. Arguments: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The AsyncSubtensor object @@ -57,6 +59,9 @@ async def root_register_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, @@ -112,15 +117,16 @@ async def root_register_extrinsic( call_function="root_register", call_params={"hotkey": wallet.hotkey.ss58_address}, ) - success, err_msg = await subtensor.sign_and_send_extrinsic( - call, + 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, ) if not success: - logging.error(f":cross_mark: [red]Failed error:[/red] {err_msg}") + logging.error(f":cross_mark: [red]Failed error:[/red] {message}") await asyncio.sleep(0.5) return False @@ -151,7 +157,7 @@ async def _do_set_root_weights( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: """ Sets the root weights on the Subnet for the given wallet hotkey account. @@ -171,7 +177,9 @@ async def _do_set_root_weights( False. wait_for_finalization (bool, optional): If True, waits for the extrinsic to be finalized on the chain. Defaults to False. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + 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: Returns a tuple containing a boolean indicating success and a message describing the result of the @@ -189,30 +197,23 @@ async def _do_set_root_weights( }, ) - next_nonce = await subtensor.substrate.get_account_next_index( - wallet.hotkey.ss58_address - ) - - # Period dictates how long the extrinsic will stay as part of waiting pool - extrinsic = await subtensor.substrate.create_signed_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call=call, - keypair=wallet.coldkey, - era={"period": period}, - nonce=next_nonce, - ) - response = await subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message - if await response.is_success: + if success: return True, "Successfully set weights." - return False, format_error_message(await response.error_message) + return False, message async def set_root_weights_extrinsic( @@ -223,20 +224,24 @@ async def set_root_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on a chain for a wallet hotkey account. Arguments: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The AsyncSubtensor object wallet (bittensor_wallet.Wallet): Bittensor wallet object. netuids (Union[NDArray[np.int64], list[int]]): The `netuid` of the subnet to set weights for. - weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be `float` s and must correspond + weights (Union[NDArray[np.float32], list[Float]]): Weights to set. These must be `Float`s and must correspond to the passed `netuid` s. version_key (int): The version key of the validator. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ` True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -254,13 +259,10 @@ async def set_root_weights_extrinsic( logging.error(unlock.message) return False - # First convert types. - if isinstance(netuids, list): - netuids = np.array(netuids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) + # Convert types. + netuids, weights = convert_uids_and_weights(netuids, weights) - logging.debug("Fetching weight limits") + logging.debug("[magenta]Fetching weight limits ...[/magenta]") min_allowed_weights, max_weight_limit = await _get_limits(subtensor) # Get non zero values. @@ -274,7 +276,7 @@ async def set_root_weights_extrinsic( ) # Normalize the weights to max value. - logging.info("Normalizing weights") + logging.info("[magenta]Normalizing weights ...[/magenta]") formatted_weights = normalize_max_weight(x=weights, limit=max_weight_limit) logging.info( f"Raw weights -> Normalized weights: [blue]{weights}[/blue] -> [green]{formatted_weights}[/green]" @@ -292,11 +294,9 @@ async def set_root_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - if not wait_for_finalization and not wait_for_inclusion: - return True - if success is True: logging.info(":white_heavy_check_mark: [green]Finalized[/green]") return True diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index 558b58cae5..c37c612191 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -3,14 +3,13 @@ from bittensor.core.errors import MetadataError from bittensor.core.settings import version_as_int +from bittensor.core.types import AxonServeCallParams from bittensor.utils import ( - format_error_message, networking as net, unlock_key, Certificate, ) from bittensor.utils.btlogging import logging -from bittensor.core.types import AxonServeCallParams if TYPE_CHECKING: from bittensor.core.axon import Axon @@ -24,7 +23,8 @@ async def do_serve_axon( call_params: "AxonServeCallParams", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, -) -> tuple[bool, Optional[dict]]: + 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. @@ -35,9 +35,12 @@ async def do_serve_axon( 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, Optional[str]]: A tuple containing a success flag and an optional error message. + 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. @@ -53,21 +56,14 @@ async def do_serve_axon( call_function=call_function, call_params=call_params.dict(), ) - extrinsic = await subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = await subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + 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, ) - if wait_for_inclusion or wait_for_finalization: - if await response.is_success: - return True, None - - return False, await response.error_message - - return True, None + return success, message async def serve_extrinsic( @@ -82,6 +78,7 @@ async def serve_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization=True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """Subscribes a Bittensor endpoint to the subtensor chain. @@ -100,6 +97,9 @@ async def serve_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -132,33 +132,33 @@ 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: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) " + f"Axon already served on: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue]" ) return True logging.debug( - f"Serving axon with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) -> {subtensor.network}:{netuid}" + f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " + f"[green]{subtensor.network}:{netuid}[/green]" ) - success, error_message = await do_serve_axon( + success, message = do_serve_axon( subtensor=subtensor, wallet=wallet, call_params=params, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) - if wait_for_inclusion or wait_for_finalization: - if success is True: - logging.debug( - f"Axon served with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) on {subtensor.network}:{netuid} " - ) - return True - else: - logging.error(f"Failed: {format_error_message(error_message)}") - return False - else: + if 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 + logging.error(f"Failed: {message}") + return False + async def serve_axon_extrinsic( subtensor: "AsyncSubtensor", @@ -167,6 +167,7 @@ async def serve_axon_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """Serves the axon to the network. @@ -180,6 +181,9 @@ async def serve_axon_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -217,6 +221,7 @@ async def serve_axon_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period, ) return serve_success @@ -229,6 +234,7 @@ async def publish_metadata( data: Union[bytes, dict], wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -246,12 +252,15 @@ async def publish_metadata( block before returning. Defaults to ``False``. wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized on the chain before returning. Defaults to ``True``. + 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: bool: ``True`` if the metadata was successfully published (and finalized if specified). ``False`` otherwise. Raises: - MetadataError: If there is an error in submitting the extrinsic or if the response from the blockchain indicates + MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates failure. """ @@ -269,21 +278,17 @@ async def publish_metadata( }, ) - extrinsic = await substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = await substrate.submit_extrinsic( - extrinsic=extrinsic, + 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, ) - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True - if await response.is_success: + if success: return True - raise MetadataError(format_error_message(await response.error_message)) + raise MetadataError(message) async def get_metadata( diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index ddaa1bd240..41076f0178 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -1,11 +1,12 @@ import asyncio from typing import Optional, Sequence, TYPE_CHECKING -from bittensor.core.errors import StakeError, NotRegisteredError -from bittensor.utils import unlock_key +from async_substrate_interface.errors import SubstrateRequestException + +from bittensor.core.extrinsics.utils import get_old_stakes +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging -from bittensor.core.extrinsics.utils import get_old_stakes if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -24,6 +25,7 @@ async def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to passed hotkey `uid`. @@ -42,10 +44,16 @@ async def add_stake_extrinsic( safe_staking: If set, uses safe staking logic allow_partial_stake: If set, allows partial stake rate_tolerance: The rate tolerance for safe staking + 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. Returns: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the response is `True`. + + Raises: + SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. """ # Decrypt keys, @@ -147,13 +155,14 @@ async def add_stake_extrinsic( call_params=call_params, ) staking_response, err_msg = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + 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, ) if staking_response is True: # If we successfully staked. # We only wait here if we expect finalization. @@ -163,8 +172,8 @@ async def add_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " - "[magenta]...[/magenta]" + f":satellite: [magenta]Checking Balance on:[/magenta] " + f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) new_block_hash = await subtensor.substrate.get_chain_head() new_balance, new_stake = await asyncio.gather( @@ -195,16 +204,11 @@ async def add_stake_extrinsic( logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") return False - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - ":cross_mark: [red]Hotkey: {} is not registered.[/red]".format( - wallet.hotkey_str - ) + f":cross_mark: [red]Add Stake Error: {format_error_message(error)}[/red]" ) return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - return False async def add_stake_multiple_extrinsic( @@ -216,8 +220,9 @@ async def add_stake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: - """Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + """Adds a stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. Arguments: subtensor: The initialized SubtensorInterface object. @@ -230,6 +235,9 @@ async def add_stake_multiple_extrinsic( if the extrinsic fails to enter the block within the timeout. wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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. Returns: success: `True` if extrinsic was finalized or included in the block. `True` if any wallet was staked. If we did @@ -341,17 +349,18 @@ async def add_stake_multiple_extrinsic( "netuid": netuid, }, ) - staking_response, err_msg = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + 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, ) - if staking_response is True: # If we successfully staked. + if success is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: @@ -391,17 +400,14 @@ async def add_stake_multiple_extrinsic( break else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") continue - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - f":cross_mark: [red]Hotkey: {hotkey_ss58} is not registered.[/red]" + f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]" ) continue - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - continue if successful_stakes != 0: logging.info( diff --git a/bittensor/core/extrinsics/asyncex/start_call.py b/bittensor/core/extrinsics/asyncex/start_call.py index bc0089ddb9..63f6fbc3c1 100644 --- a/bittensor/core/extrinsics/asyncex/start_call.py +++ b/bittensor/core/extrinsics/asyncex/start_call.py @@ -1,6 +1,6 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -14,6 +14,7 @@ async def start_call_extrinsic( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -25,6 +26,9 @@ async def start_call_extrinsic( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + 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. Returns: Tuple[bool, str]: @@ -41,21 +45,19 @@ async def start_call_extrinsic( call_function="start_call", call_params={"netuid": netuid}, ) - signed_ext = await substrate.create_signed_extrinsic( - call=start_call, - keypair=wallet.coldkey, - ) - response = await substrate.submit_extrinsic( - extrinsic=signed_ext, + success, message = 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, ) if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message - if await response.is_success: + if success: return True, "Success with `start_call` response." - return False, format_error_message(await response.error_message) + return True, message diff --git a/bittensor/core/extrinsics/asyncex/take.py b/bittensor/core/extrinsics/asyncex/take.py index 6a51239bc0..543d4e72da 100644 --- a/bittensor/core/extrinsics/asyncex/take.py +++ b/bittensor/core/extrinsics/asyncex/take.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor_wallet.bittensor_wallet import Wallet @@ -16,7 +16,26 @@ async def increase_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + 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. + + Returns: + tuple[bool, str]: Success flag and status message. + """ + unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -32,10 +51,11 @@ async def increase_take_extrinsic( ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, raise_error=raise_error, ) @@ -48,7 +68,25 @@ async def decrease_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + 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. + + Returns: + tuple[bool, str]: Success flag and status message. + """ unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -64,9 +102,10 @@ async def decrease_take_extrinsic( ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, raise_error=raise_error, ) diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index 1347c44260..a1c781310c 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -1,9 +1,8 @@ import asyncio -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.core.settings import NETWORK_EXPLORER_MAP from bittensor.utils import ( - format_error_message, get_explorer_url_for_network, is_valid_bittensor_address_or_public_key, unlock_key, @@ -23,6 +22,7 @@ async def _do_transfer( amount: "Balance", wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str, str]: """ Makes transfer from wallet to destination public key address. @@ -34,8 +34,11 @@ async def _do_transfer( amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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: success, block hash, formatted error message @@ -45,24 +48,25 @@ async def _do_transfer( call_function="transfer_allow_death", call_params={"dest": destination, "value": amount.rao}, ) - extrinsic = await subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.coldkey - ) - response = await subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + + 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, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "", "Success, extrinsic submitted without waiting." + return True, "", message # Otherwise continue with finalization. - if await response.is_success: - block_hash_ = response.block_hash + if success: + block_hash_ = await subtensor.get_block_hash() return True, block_hash_, "Success with response." - return False, "", format_error_message(await response.error_message) + return False, "", message async def transfer_extrinsic( @@ -74,6 +78,7 @@ async def transfer_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = False, keep_alive: bool = True, + period: Optional[int] = None, ) -> bool: """Transfers funds from this wallet to the destination public key address. @@ -85,9 +90,12 @@ async def transfer_extrinsic( transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. keep_alive (bool): If set, keeps the account alive by keeping the balance above the existential deposit. + 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: success (bool): Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for @@ -100,6 +108,7 @@ async def transfer_extrinsic( 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: @@ -148,6 +157,7 @@ async def transfer_extrinsic( amount=amount, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) if success: @@ -173,6 +183,6 @@ async def transfer_extrinsic( f"Balance: [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") - return False + + logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + return False diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 54fb43c79d..8125b603e9 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -1,11 +1,12 @@ import asyncio from typing import Optional, TYPE_CHECKING -from bittensor.core.errors import StakeError, NotRegisteredError -from bittensor.utils import unlock_key +from async_substrate_interface.errors import SubstrateRequestException + +from bittensor.core.extrinsics.utils import get_old_stakes +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging -from bittensor.core.extrinsics.utils import get_old_stakes if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -23,6 +24,7 @@ async def unstake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -40,6 +42,9 @@ async def unstake_extrinsic( safe_staking: If true, enables price safety checks allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%) + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -133,17 +138,18 @@ async def unstake_extrinsic( call_function=call_function, call_params=call_params, ) - staking_response, err_msg = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + 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, ) - if staking_response is True: # If we successfully unstaked. + if success is True: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -174,22 +180,19 @@ async def unstake_extrinsic( ) return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_staking 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: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") return False - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - f":cross_mark: [red]Hotkey: {wallet.hotkey_str} is not registered.[/red]" + f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]" ) return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - return False async def unstake_multiple_extrinsic( @@ -200,6 +203,7 @@ async def unstake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -213,6 +217,9 @@ async def unstake_multiple_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. Flag is ``True`` if any @@ -310,13 +317,14 @@ async def unstake_multiple_extrinsic( ) staking_response, err_msg = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + 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, ) if staking_response is True: # If we successfully unstaked. @@ -347,14 +355,11 @@ async def unstake_multiple_extrinsic( logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") continue - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - f":cross_mark: [red]Hotkey[/red] [blue]{hotkey_ss58}[/blue] [red]is not registered.[/red]" + f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]" ) - continue - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - continue + return False if successful_unstakes != 0: logging.info( diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 6e07b90adb..cf993b37eb 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -5,10 +5,9 @@ import numpy as np from numpy.typing import NDArray -import bittensor.utils.weight_utils as weight_utils from bittensor.core.settings import version_as_int -from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -23,22 +22,27 @@ async def _do_commit_weights( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[str]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, committing the hash of a neuron's weights. This method constructs and submits the transaction, handling retries and blockchain communication. Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain - interaction. + subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain interaction. wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the weights. netuid (int): The unique identifier of the subnet. commit_hash (str): The hash of the neuron's weights to be committed. 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, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight commitment is securely recorded on the Bittensor blockchain, providing a verifiable record of the neuron's weight distribution at a specific point in time. @@ -52,11 +56,12 @@ async def _do_commit_weights( }, ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, + period=period, nonce_key="hotkey", sign_with="hotkey", ) @@ -69,6 +74,7 @@ async def commit_weights_extrinsic( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -82,15 +88,18 @@ async def commit_weights_extrinsic( commit_hash (str): The hash of the neuron's weights to be committed. 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]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper error handling and user interaction when required. """ - success, error_message = await _do_commit_weights( subtensor=subtensor, wallet=wallet, @@ -98,6 +107,7 @@ async def commit_weights_extrinsic( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -119,7 +129,8 @@ async def _do_reveal_weights( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[dict]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, revealing the weights for a specific subnet. This method constructs and submits the transaction, handling retries and blockchain communication. @@ -135,14 +146,18 @@ async def _do_reveal_weights( version_key (int): Version key for compatibility with the network. 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, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight revelation is securely recorded on the Bittensor blockchain, providing transparency and accountability for the neuron's weight distribution. """ - call = await subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="reveal_weights", @@ -155,11 +170,12 @@ async def _do_reveal_weights( }, ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, sign_with="hotkey", + period=period, nonce_key="hotkey", use_nonce=True, ) @@ -175,14 +191,14 @@ async def reveal_weights_extrinsic( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ 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. Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain - interaction. + subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain interaction. wallet (bittensor_wallet.Wallet): The wallet associated with the neuron revealing the weights. netuid (int): The unique identifier of the subnet. uids (list[int]): List of neuron UIDs for which weights are being revealed. @@ -191,15 +207,18 @@ async def reveal_weights_extrinsic( version_key (int): Version key for compatibility with the network. 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]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string value - describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for revealing weights on the Bittensor blockchain, ensuring proper error handling and user interaction when required. """ - success, error_message = await _do_reveal_weights( subtensor=subtensor, wallet=wallet, @@ -210,6 +229,7 @@ async def reveal_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -217,7 +237,6 @@ async def reveal_weights_extrinsic( logging.info(success_message) return True, success_message - error_message = format_error_message(error_message) logging.error(f"Failed to reveal weights: {error_message}") return False, error_message @@ -231,12 +250,11 @@ async def _do_set_weights( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, -) -> tuple[bool, Optional[str]]: # (success, error_message) + period: Optional[int] = None, +) -> tuple[bool, str]: # (success, error_message) """ - Internal method to send a transaction to the Bittensor blockchain, setting weights - for specified neurons. This method constructs and submits the transaction, handling - retries and blockchain communication. + Internal method to send a transaction to the Bittensor blockchain, setting weights for specified neurons. This + method constructs and submits the transaction, handling retries and blockchain communication. Args: subtensor (subtensor.core.async_subtensor.AsyncSubtensor): Async Subtensor instance. @@ -247,15 +265,18 @@ async def _do_set_weights( version_key (int, optional): Version key for compatibility with the network. wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + 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, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their trust in other neurons based on observed performance and contributions. """ - call = await subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="set_weights", @@ -266,17 +287,25 @@ async def _do_set_weights( "version_key": version_key, }, ) - return await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + 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, use_nonce=True, nonce_key="hotkey", sign_with="hotkey", ) + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True, "Not waiting for finalization or inclusion." + + if success: + return success, "Successfully set weights." + return success, message + async def set_weights_extrinsic( subtensor: "AsyncSubtensor", @@ -287,9 +316,9 @@ async def set_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on chain for a given wallet hotkey account. Args: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Bittensor subtensor object. @@ -303,28 +332,24 @@ async def set_weights_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + 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: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``True``. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. """ - # First convert types. - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - - # Reformat and normalize. - weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit( - uids, weights - ) + weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) logging.info( - f":satellite: [magenta]Setting weights on [/magenta][blue]{subtensor.network}[/blue] [magenta]...[/magenta]" + f":satellite: [magenta]Setting weights on [/magenta]" + f"[blue]{subtensor.network}[/blue] " + f"[magenta]...[/magenta]" ) try: - success, error_message = await _do_set_weights( + success, message = await _do_set_weights( subtensor=subtensor, wallet=wallet, netuid=netuid, @@ -337,15 +362,15 @@ async def set_weights_extrinsic( ) if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message if success is True: message = "Successfully set weights and Finalized." logging.success(f":white_heavy_check_mark: [green]{message}[/green]") return True, message - logging.error(f"[red]Failed[/red] set weights. Error: {error_message}") - return False, error_message + logging.error(f"[red]Failed[/red] set weights. Error: {message}") + return False, message except Exception as error: logging.error(f":cross_mark: [red]Failed[/red] set weights. Error: {error}") diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index be66a71ea3..c531986254 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -2,13 +2,13 @@ from typing import Union, TYPE_CHECKING, Optional -from bittensor_drand import get_encrypted_commit import numpy as np +from bittensor_drand import get_encrypted_commit from numpy.typing import NDArray from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -24,23 +24,27 @@ def _do_commit_reveal_v3( reveal_round: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[str]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ - Executes the commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or + Executes commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or finalization. Arguments: subtensor: An instance of the Subtensor class. wallet: Wallet An instance of the Wallet class containing the user's keypair. netuid: int The network unique identifier. - commit bytes The commit data in bytes format. + commit: bytes The commit data in bytes format. reveal_round: int The round number for the reveal phase. wait_for_inclusion: bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. wait_for_finalization: bool, optional Flag indicating whether to wait for the extrinsic to be finalized. + 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: - A tuple where the first element is a boolean indicating success or failure, and the second element is an - optional string containing error message if any. + A tuple where the first element is a boolean indicating success or failure, and the second element is a string + containing an error message if any. """ logging.info( f"Committing weights hash [blue]{commit.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " @@ -57,7 +61,12 @@ def _do_commit_reveal_v3( }, ) return subtensor.sign_and_send_extrinsic( - call, wallet, wait_for_inclusion, wait_for_finalization, sign_with="hotkey" + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period, ) @@ -70,10 +79,11 @@ def commit_reveal_v3_extrinsic( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - block_time: float = 12.0, + block_time: Union[int, float] = 12.0, + period: Optional[int] = None, ) -> tuple[bool, str]: """ - Commits and reveals weights for given subtensor and wallet with provided uids and weights. + Commits and reveals weights for a given subtensor and wallet with provided uids and weights. Arguments: subtensor: The Subtensor instance. @@ -84,21 +94,17 @@ def commit_reveal_v3_extrinsic( version_key: The version key to use for committing and revealing. Default is version_as_int. wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. + block_time (float): The number of seconds for block duration. Default is 12.0 seconds. + 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 where the first element is a boolean indicating success or failure, and the second element is a message associated with the result """ try: - # Convert uids and weights - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - - # Reformat and normalize. - uids, weights = convert_weights_and_uids_for_emit(uids, weights) + uids, weights = convert_and_normalize_weights_and_uids(uids, weights) current_block = subtensor.get_current_block() subnet_hyperparameters = subtensor.get_subnet_hyperparameters( @@ -127,6 +133,7 @@ def commit_reveal_v3_extrinsic( reveal_round=reveal_round, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success is not True: diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index 06e3295eb5..511d1364ef 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -2,7 +2,6 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -17,7 +16,8 @@ def _do_commit_weights( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[str]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, committing the hash of a neuron's weights. This method constructs and submits the transaction, handling retries and blockchain communication. @@ -29,9 +29,14 @@ def _do_commit_weights( commit_hash (str): The hash of the neuron's weights to be committed. 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, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight commitment is securely recorded on the Bittensor blockchain, providing a verifiable record of the neuron's weight distribution at a specific point in time. @@ -45,11 +50,12 @@ def _do_commit_weights( }, ) return subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, + period=period, sign_with="hotkey", nonce_key="hotkey", ) @@ -62,6 +68,7 @@ def commit_weights_extrinsic( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -74,10 +81,14 @@ def commit_weights_extrinsic( commit_hash (str): The hash of the neuron's weights to be committed. 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]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper error handling and user interaction when required. @@ -90,6 +101,7 @@ def commit_weights_extrinsic( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -111,7 +123,8 @@ def _do_reveal_weights( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[dict]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, revealing the weights for a specific subnet. This method constructs and submits the transaction, handling retries and blockchain communication. @@ -126,9 +139,14 @@ def _do_reveal_weights( version_key (int): Version key for compatibility with the network. 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, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight revelation is securely recorded on the Bittensor blockchain, providing transparency and accountability for the neuron's weight distribution. @@ -146,11 +164,12 @@ def _do_reveal_weights( }, ) return subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, + period=period, sign_with="hotkey", nonce_key="hotkey", ) @@ -166,6 +185,7 @@ def reveal_weights_extrinsic( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -181,10 +201,14 @@ def reveal_weights_extrinsic( version_key (int): Version key for compatibility with the network. 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]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string value - describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for revealing weights on the Bittensor blockchain, ensuring proper error handling and user interaction when required. @@ -200,6 +224,7 @@ def reveal_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -207,6 +232,6 @@ def reveal_weights_extrinsic( logging.info(success_message) return True, success_message - error_message = format_error_message(error_message) + error_message = error_message logging.error(f"Failed to reveal weights: {error_message}") return False, error_message diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index fdf9d406a8..3e7e49eefd 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -17,6 +17,7 @@ def _get_stake_in_origin_and_dest( origin_netuid: int, destination_netuid: int, ) -> tuple[Balance, Balance]: + """Gets the current stake balances for both origin and destination addresses in their respective subnets.""" block = subtensor.get_current_block() stake_in_origin = subtensor.get_stake( coldkey_ss58=origin_coldkey_ss58, @@ -43,6 +44,7 @@ def transfer_stake_extrinsic( amount: Optional[Balance] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -57,6 +59,9 @@ def transfer_stake_extrinsic( amount (Union[Balance, float, int]): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + 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: success (bool): True if the transfer was successful. @@ -113,6 +118,7 @@ def transfer_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -160,6 +166,7 @@ def swap_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -175,7 +182,10 @@ def swap_stake_extrinsic( wait_for_finalization (bool): If true, waits for finalization before returning. safe_staking (bool): If true, enables price safety checks to protect against price impact. allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. - rate_tolerance (float): Maximum allowed increase in price ratio (0.005 = 0.5%). + rate_tolerance (float): Maximum allowed increase in a price ratio (0.005 = 0.5%). + 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: success (bool): True if the swap was successful. @@ -193,7 +203,7 @@ def swap_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_netuid=origin_netuid, @@ -255,6 +265,7 @@ def swap_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -265,7 +276,7 @@ def swap_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_netuid=origin_netuid, @@ -305,6 +316,7 @@ def move_stake_extrinsic( amount: Optional[Balance] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake to a different hotkey and/or subnet while keeping the same coldkey owner. @@ -319,6 +331,9 @@ def move_stake_extrinsic( amount (Union[Balance, float]): Amount to move. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + 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: success (bool): True if the move was successful. @@ -328,7 +343,7 @@ def move_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_netuid=origin_netuid, @@ -364,6 +379,7 @@ def move_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -374,7 +390,7 @@ def move_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_netuid=origin_netuid, diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index c1feea3a30..9acd2f0f8d 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -25,6 +25,7 @@ def _do_burned_register( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Performs a burned register extrinsic call to the Subtensor chain. @@ -37,6 +38,9 @@ def _do_burned_register( wallet (bittensor_wallet.Wallet): The wallet to be registered. wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False. wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True. + 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, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error @@ -57,6 +61,7 @@ def _do_burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -66,6 +71,7 @@ def burned_register_extrinsic( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """Registers the wallet to chain by recycling TAO. @@ -77,6 +83,9 @@ def burned_register_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -120,6 +129,7 @@ def burned_register_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not success: @@ -154,6 +164,7 @@ def _do_pow_register( pow_result: "POWSolution", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, Optional[str]]: """Sends a (POW) register extrinsic to the chain. @@ -164,6 +175,9 @@ def _do_pow_register( pow_result (POWSolution): The PoW result to register. wait_for_inclusion (bool): If ``True``, waits for the extrinsic to be included in a block. Default to `False`. wait_for_finalization (bool): If ``True``, waits for the extrinsic to be finalized. Default to `True`. + 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: success (bool): ``True`` if the extrinsic was included in a block. @@ -188,6 +202,7 @@ def _do_pow_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -196,6 +211,7 @@ def register_subnet_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor blockchain. @@ -205,6 +221,9 @@ def register_subnet_extrinsic( wallet (Wallet): The wallet to be used for subnet registration. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. + 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: bool: True if the subnet registration was successful, False otherwise. @@ -232,16 +251,20 @@ def register_subnet_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) + if not wait_for_finalization and not wait_for_inclusion: + return True + if success: logging.success( ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) return True - else: - logging.error(f"Failed to register subnet: {message}") - return False + + logging.error(f"Failed to register subnet: {message}") + return False def register_extrinsic( @@ -258,6 +281,7 @@ def register_extrinsic( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ) -> bool: """Registers the wallet to the chain. @@ -277,6 +301,9 @@ def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. + 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: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -366,7 +393,7 @@ def register_extrinsic( # pow successful, proceed to submit pow to chain for registration else: logging.info(":satellite: [magenta]Submitting POW...[/magenta]") - # check if pow result is still valid + # check if a pow result is still valid while not pow_result.is_stale(subtensor=subtensor): result: tuple[bool, Optional[str]] = _do_pow_register( subtensor=subtensor, @@ -375,6 +402,7 @@ def register_extrinsic( pow_result=pow_result, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) success, err_msg = result @@ -439,6 +467,7 @@ def set_subnet_identity_extrinsic( additional: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Set the identity information for a given subnet. @@ -456,6 +485,9 @@ def set_subnet_identity_extrinsic( additional (str): Any additional metadata or information related to the subnet. wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). + 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 where the first element indicates success or failure (True/False), and the second @@ -487,6 +519,7 @@ def set_subnet_identity_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 43fd65a5a1..f59a1104d7 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -1,5 +1,5 @@ import time -from typing import Union, TYPE_CHECKING +from typing import Optional, Union, TYPE_CHECKING import numpy as np from numpy.typing import NDArray @@ -16,6 +16,7 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, + convert_uids_and_weights, ) if TYPE_CHECKING: @@ -50,8 +51,9 @@ def root_register_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: - """Registers the wallet to root network. + """Registers the wallet to the root network. Arguments: subtensor (bittensor.core.subtensor.Subtensor): The Subtensor object @@ -60,6 +62,9 @@ def root_register_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -78,7 +83,7 @@ def root_register_extrinsic( block=block, ) balance = subtensor.get_balance( - wallet.coldkeypub.ss58_address, + address=wallet.coldkeypub.ss58_address, block=block, ) @@ -114,10 +119,11 @@ def root_register_extrinsic( call_params={"hotkey": wallet.hotkey.ss58_address}, ) success, err_msg = subtensor.sign_and_send_extrinsic( - call, + call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not success: @@ -152,7 +158,7 @@ def _do_set_root_weights( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: """ Sets the root weights on the Subnet for the given wallet hotkey account. @@ -172,7 +178,9 @@ def _do_set_root_weights( False. wait_for_finalization (bool, optional): If True, waits for the extrinsic to be finalized on the chain. Defaults to False. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + 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: Returns a tuple containing a boolean indicating success and a message describing the result of the @@ -190,28 +198,23 @@ def _do_set_root_weights( }, ) - next_nonce = subtensor.substrate.get_account_next_index(wallet.hotkey.ss58_address) - - # Period dictates how long the extrinsic will stay as part of waiting pool - extrinsic = subtensor.substrate.create_signed_extrinsic( + success, message = subtensor.sign_and_send_extrinsic( call=call, - keypair=wallet.coldkey, - era={"period": period}, - nonce=next_nonce, - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True, "Not waiting for finalization or inclusion." - if response.is_success: + if success: return True, "Successfully set weights." - return False, format_error_message(response.error_message) + return False, message def set_root_weights_extrinsic( @@ -222,20 +225,24 @@ def set_root_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on chain for a given wallet hotkey account. Arguments: subtensor (bittensor.core.subtensor.Subtensor): The Subtensor object wallet (bittensor_wallet.Wallet): Bittensor wallet object. netuids (Union[NDArray[np.int64], list[int]]): The `netuid` of the subnet to set weights for. - weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be `float` s and must correspond + weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be floats and must correspond to the passed `netuid` s. version_key (int): The version key of the validator. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -253,13 +260,10 @@ def set_root_weights_extrinsic( logging.error(unlock.message) return False - # First convert types. - if isinstance(netuids, list): - netuids = np.array(netuids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) + # Convert types. + netuids, weights = convert_uids_and_weights(netuids, weights) - logging.debug("Fetching weight limits") + logging.debug("[magenta]Fetching weight limits ...[/magenta]") min_allowed_weights, max_weight_limit = _get_limits(subtensor) # Get non zero values. @@ -273,7 +277,7 @@ def set_root_weights_extrinsic( ) # Normalize the weights to max value. - logging.info("Normalizing weights") + logging.info("[magenta]Normalizing weights ...[/magenta]") formatted_weights = normalize_max_weight(x=weights, limit=max_weight_limit) logging.info( f"Raw weights -> Normalized weights: [blue]{weights}[/blue] -> [green]{formatted_weights}[/green]" @@ -283,7 +287,7 @@ def set_root_weights_extrinsic( logging.info(":satellite: [magenta]Setting root weights...[magenta]") weight_uids, weight_vals = convert_weights_and_uids_for_emit(netuids, weights) - success, error_message = _do_set_root_weights( + success, message = _do_set_root_weights( subtensor=subtensor, wallet=wallet, netuids=weight_uids, @@ -291,18 +295,15 @@ def set_root_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - if not wait_for_finalization and not wait_for_inclusion: - return True - if success is True: logging.info(":white_heavy_check_mark: [green]Finalized[/green]") return True - else: - fmt_err = error_message - logging.error(f":cross_mark: [red]Failed error:[/red] {fmt_err}") - return False + + logging.error(f":cross_mark: [red]Failed error:[/red] {message}") + return False except SubstrateRequestException as e: fmt_err = format_error_message(e) diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index aaed6ad38f..d004fd7dba 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -2,14 +2,13 @@ from bittensor.core.errors import MetadataError from bittensor.core.settings import version_as_int +from bittensor.core.types import AxonServeCallParams from bittensor.utils import ( - format_error_message, networking as net, unlock_key, Certificate, ) from bittensor.utils.btlogging import logging -from bittensor.core.types import AxonServeCallParams if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -23,7 +22,8 @@ def do_serve_axon( call_params: "AxonServeCallParams", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, -) -> tuple[bool, Optional[dict]]: + 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. @@ -34,6 +34,9 @@ def do_serve_axon( 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, Optional[str]]: A tuple containing a success flag and an optional error message. @@ -51,21 +54,15 @@ def do_serve_axon( call_function=call_function, call_params=call_params.dict(), ) - extrinsic = subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + + 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, ) - if wait_for_inclusion or wait_for_finalization: - if response.is_success: - return True, None - - return False, response.error_message - - return True, None + return success, message def serve_extrinsic( @@ -80,6 +77,7 @@ def serve_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization=True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """Subscribes a Bittensor endpoint to the subtensor chain. @@ -98,6 +96,9 @@ def serve_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -130,33 +131,33 @@ 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: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) " + f"Axon already served on: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue]" ) return True logging.debug( - f"Serving axon with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) -> {subtensor.network}:{netuid}" + f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " + f"[green]{subtensor.network}:{netuid}[/green]" ) - success, error_message = do_serve_axon( + success, message = do_serve_axon( subtensor=subtensor, wallet=wallet, call_params=params, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) - if wait_for_inclusion or wait_for_finalization: - if success is True: - logging.debug( - f"Axon served with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) on {subtensor.network}:{netuid} " - ) - return True - else: - logging.error(f"Failed: {format_error_message(error_message)}") - return False - else: + if 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 + logging.error(f"Failed: {message}") + return False + def serve_axon_extrinsic( subtensor: "Subtensor", @@ -165,6 +166,7 @@ def serve_axon_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional["Certificate"] = None, + period: Optional[int] = None, ) -> bool: """Serves the axon to the network. @@ -178,6 +180,9 @@ def serve_axon_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -213,6 +218,7 @@ def serve_axon_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period, ) return serve_success @@ -225,6 +231,7 @@ def publish_metadata( data: Union[bytes, dict], wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -242,12 +249,15 @@ def publish_metadata( block before returning. Defaults to ``False``. wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized on the chain before returning. Defaults to ``True``. + 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: bool: ``True`` if the metadata was successfully published (and finalized if specified). ``False`` otherwise. Raises: - MetadataError: If there is an error in submitting the extrinsic or if the response from the blockchain indicates + MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates failure. """ @@ -264,21 +274,17 @@ def publish_metadata( }, ) - extrinsic = subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + 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, ) - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True - if response.is_success: + if success: return True - raise MetadataError(format_error_message(response.error_message)) + raise MetadataError(message) def get_metadata( diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index 908fb2b2a9..039dbdf837 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -6,8 +6,11 @@ from numpy.typing import NDArray from bittensor.core.settings import version_as_int -from bittensor.utils import format_error_message, weight_utils from bittensor.utils.btlogging import logging +from bittensor.utils.weight_utils import ( + convert_and_normalize_weights_and_uids, + convert_uids_and_weights, +) if TYPE_CHECKING: from bittensor.core.subtensor import Subtensor @@ -24,8 +27,8 @@ def _do_set_weights( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, -) -> tuple[bool, Optional[str]]: # (success, error_message) + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, setting weights for specified neurons. This method constructs and submits the transaction, handling @@ -40,10 +43,12 @@ def _do_set_weights( version_key (int, optional): Version key for compatibility with the network. wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + 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, Optional[str]]: A tuple containing a success flag and an optional error message. + Tuple[bool, str]: A tuple containing a success flag and an optional error message. This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their trust in other neurons based on observed performance and contributions. @@ -59,27 +64,17 @@ def _do_set_weights( "version_key": version_key, }, ) - next_nonce = subtensor.substrate.get_account_next_index(wallet.hotkey.ss58_address) - # Period dictates how long the extrinsic will stay as part of waiting pool - extrinsic = subtensor.substrate.create_signed_extrinsic( + success, message = subtensor.sign_and_send_extrinsic( call=call, - keypair=wallet.hotkey, - era={"period": period}, - nonce=next_nonce, - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, + use_nonce=True, + nonce_key="hotkey", + sign_with="hotkey", ) - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." - - if response.is_success: - return True, "Successfully set weights." - - return False, format_error_message(response.error_message) + return success, message def set_weights_extrinsic( @@ -91,9 +86,9 @@ def set_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on a chain for a wallet hotkey account. Args: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Bittensor subtensor object. @@ -107,28 +102,27 @@ def set_weights_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is ``True``. """ - # First convert types. - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) + # Convert types. + uids, weights = convert_uids_and_weights(uids, weights) # Reformat and normalize. - weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit( - uids, weights - ) + weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) logging.info( - f":satellite: [magenta]Setting weights on [/magenta][blue]{subtensor.network}[/blue] [magenta]...[/magenta]" + f":satellite: [magenta]Setting weights on [/magenta]" + f"[blue]{subtensor.network}[/blue] " + f"[magenta]...[/magenta]" ) try: - success, error_message = _do_set_weights( + success, message = _do_set_weights( subtensor=subtensor, wallet=wallet, netuid=netuid, @@ -141,15 +135,15 @@ def set_weights_extrinsic( ) if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message if success is True: message = "Successfully set weights and Finalized." logging.success(f":white_heavy_check_mark: [green]{message}[/green]") return True, message - logging.error(f"[red]Failed[/red] set weights. Error: {error_message}") - return False, error_message + logging.error(f"[red]Failed[/red] set weights. Error: {message}") + return False, message except Exception as error: logging.error(f":cross_mark: [red]Failed[/red] set weights. Error: {error}") diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 3a011bdf9b..58f5ed0b71 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -1,8 +1,9 @@ from typing import Optional, TYPE_CHECKING, Sequence -from bittensor.core.errors import StakeError, NotRegisteredError +from async_substrate_interface.errors import SubstrateRequestException + from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -22,6 +23,7 @@ def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to passed hotkey `uid`. @@ -29,7 +31,7 @@ def add_stake_extrinsic( Arguments: subtensor: the Subtensor object to use wallet: Bittensor wallet object. - hotkey_ss58: The `ss58` address of the hotkey account to stake to defaults to the wallet's hotkey. + hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. netuid (Optional[int]): Subnet unique ID. amount: Amount to stake as Bittensor balance, `None` if staking all. wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns @@ -39,10 +41,16 @@ def add_stake_extrinsic( safe_staking (bool): If true, enables price safety checks allow_partial_stake (bool): If true, allows partial unstaking if price tolerance exceeded rate_tolerance (float): Maximum allowed price increase percentage (0.005 = 0.5%) + 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. Returns: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the response is `True`. + + Raises: + SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. """ # Decrypt keys, @@ -143,16 +151,17 @@ def add_stake_extrinsic( call_params=call_params, ) - staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, sign_with="coldkey", nonce_key="coldkeypub", + period=period, ) - if staking_response is True: # If we successfully staked. + if success is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -160,8 +169,8 @@ def add_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " - "[magenta]...[/magenta]" + f":satellite: [magenta]Checking Balance on:[/magenta] " + f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) new_block = subtensor.get_current_block() new_balance = subtensor.get_balance( @@ -181,25 +190,19 @@ def add_stake_extrinsic( ) return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_staking 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: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") return False - # TODO I don't think these are used. Maybe should just catch SubstrateRequestException? - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - ":cross_mark: [red]Hotkey: {} is not registered.[/red]".format( - wallet.hotkey_str - ) + f":cross_mark: [red]Add Stake Error: {format_error_message((error))}[/red]" ) return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - return False def add_stake_multiple_extrinsic( @@ -210,6 +213,7 @@ def add_stake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. @@ -217,11 +221,15 @@ def add_stake_multiple_extrinsic( subtensor: The initialized SubtensorInterface object. wallet: Bittensor wallet object for the coldkey. hotkey_ss58s: List of hotkeys to stake to. + netuids: List of netuids to stake to. amounts: List of amounts to stake. If `None`, stake all to the first hotkey. wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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. Returns: success: `True` if extrinsic was finalized or included in the block. `True` if any wallet was staked. If we did @@ -327,17 +335,18 @@ def add_stake_multiple_extrinsic( "netuid": netuid, }, ) - staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, nonce_key="coldkeypub", sign_with="coldkey", + period=period, ) - if staking_response is True: # If we successfully staked. + if success is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: @@ -374,17 +383,14 @@ def add_stake_multiple_extrinsic( break else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + logging.error(f":cross_mark: [red]Failed[/red]: {message}") continue - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - f":cross_mark: [red]Hotkey: {hotkey_ss58} is not registered.[/red]" + f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]" ) continue - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - continue if successful_stakes != 0: logging.info( diff --git a/bittensor/core/extrinsics/start_call.py b/bittensor/core/extrinsics/start_call.py index d3a1d423ce..2788bb88f1 100644 --- a/bittensor/core/extrinsics/start_call.py +++ b/bittensor/core/extrinsics/start_call.py @@ -1,6 +1,6 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -14,6 +14,7 @@ def start_call_extrinsic( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -25,6 +26,9 @@ def start_call_extrinsic( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + 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. Returns: Tuple[bool, str]: @@ -35,28 +39,24 @@ def start_call_extrinsic( logging.error(unlock.message) return False, unlock.message - with subtensor.substrate as substrate: - start_call = substrate.compose_call( - call_module="SubtensorModule", - call_function="start_call", - call_params={"netuid": netuid}, - ) + start_call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="start_call", + call_params={"netuid": netuid}, + ) - signed_ext = substrate.create_signed_extrinsic( - call=start_call, - keypair=wallet.coldkey, - ) + success, message = subtensor.sign_and_send_extrinsic( + call=start_call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + ) - response = substrate.submit_extrinsic( - extrinsic=signed_ext, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if not wait_for_finalization and not wait_for_inclusion: + return True, message - if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + if success: + return True, "Success with `start_call` response." - if response.is_success: - return True, "Success with `start_call` response." - - return False, format_error_message(response.error_message) + return True, message diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 1ac6e96040..968338d8a9 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor_wallet.bittensor_wallet import Wallet @@ -16,7 +16,25 @@ def increase_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + 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. + + Returns: + tuple[bool, str]: Success flag and status message. + """ unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -32,10 +50,11 @@ def increase_take_extrinsic( ) return subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, raise_error=raise_error, ) @@ -48,7 +67,25 @@ def decrease_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate `take` percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + 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]: Success flag and status message. + """ unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -64,9 +101,10 @@ def decrease_take_extrinsic( ) return subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, raise_error=raise_error, + wait_for_finalization=wait_for_finalization, + period=period, ) diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index badc4d7cf9..03624097d0 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -1,13 +1,12 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.core.settings import NETWORK_EXPLORER_MAP -from bittensor.utils.balance import Balance from bittensor.utils import ( is_valid_bittensor_address_or_public_key, unlock_key, get_explorer_url_for_network, - format_error_message, ) +from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -22,6 +21,7 @@ def _do_transfer( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str, str]: """ Makes transfer from wallet to destination public key address. @@ -33,8 +33,11 @@ def _do_transfer( amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + 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: success, block hash, formatted error message @@ -44,24 +47,25 @@ def _do_transfer( call_function="transfer_allow_death", call_params={"dest": destination, "value": amount.rao}, ) - extrinsic = subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.coldkey - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + + 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, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "", "Success, extrinsic submitted without waiting." + return True, "", message # Otherwise continue with finalization. - if response.is_success: - block_hash_ = response.block_hash + if success: + block_hash_ = subtensor.get_block_hash() return True, block_hash_, "Success with response." - return False, "", format_error_message(response.error_message) + return False, "", message def transfer_extrinsic( @@ -73,6 +77,7 @@ def transfer_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = False, keep_alive: bool = True, + period: Optional[int] = None, ) -> bool: """Transfers funds from this wallet to the destination public key address. @@ -84,21 +89,24 @@ def transfer_extrinsic( transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. keep_alive (bool): If set, keeps the account alive by keeping the balance above the existential deposit. + 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: success (bool): Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is `True`, regardless of its inclusion. """ - destination = dest # Validate destination address. - if not is_valid_bittensor_address_or_public_key(destination): + if not is_valid_bittensor_address_or_public_key(dest): logging.error( - f":cross_mark: [red]Invalid destination SS58 address[/red]: {destination}" + f":cross_mark: [red]Invalid destination SS58 address[/red]: {dest}" ) return False + logging.info(f"Initiating transfer on network: {subtensor.network}") # Unlock wallet coldkey. if not (unlock := unlock_key(wallet)).success: @@ -119,7 +127,7 @@ def transfer_extrinsic( else: existential_deposit = subtensor.get_existential_deposit(block=block) - fee = subtensor.get_transfer_fee(wallet=wallet, dest=destination, value=amount) + fee = subtensor.get_transfer_fee(wallet=wallet, dest=dest, value=amount) # Check if we have enough balance. if transfer_all is True: @@ -139,10 +147,11 @@ def transfer_extrinsic( success, block_hash, err_msg = _do_transfer( subtensor=subtensor, wallet=wallet, - destination=destination, + destination=dest, amount=amount, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) if success: @@ -168,6 +177,6 @@ def transfer_extrinsic( f"Balance: [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") - return False + + logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + return False diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index f49362a4db..34fe47d7ac 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -1,8 +1,9 @@ from typing import Optional, TYPE_CHECKING -from bittensor.core.errors import StakeError, NotRegisteredError +from async_substrate_interface.errors import SubstrateRequestException + from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -22,6 +23,7 @@ def unstake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -39,6 +41,9 @@ def unstake_extrinsic( safe_staking: If true, enables price safety checks allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%) + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -64,6 +69,7 @@ def unstake_extrinsic( block=block, ) + # Covert to bittensor.Balance if amount is None: # Unstake it all. logging.warning( @@ -131,17 +137,18 @@ def unstake_extrinsic( call_params=call_params, ) - staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + 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, ) - if staking_response is True: # If we successfully unstaked. + if success is True: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -170,22 +177,19 @@ def unstake_extrinsic( ) return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_staking 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: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") return False - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - f":cross_mark: [red]Hotkey: {wallet.hotkey_str} is not registered.[/red]" + f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]" ) return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - return False def unstake_multiple_extrinsic( @@ -196,6 +200,7 @@ def unstake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -209,6 +214,9 @@ def unstake_multiple_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + 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: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. Flag is ``True`` if any @@ -298,13 +306,14 @@ def unstake_multiple_extrinsic( }, ) staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + 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, ) if staking_response is True: # If we successfully unstaked. @@ -334,14 +343,11 @@ def unstake_multiple_extrinsic( logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") continue - except NotRegisteredError: + except SubstrateRequestException as error: logging.error( - f":cross_mark: [red]Hotkey[/red] [blue]{hotkey_ss58}[/blue] [red]is not registered.[/red]" + f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]" ) - continue - except StakeError as e: - logging.error(":cross_mark: [red]Stake Error: {}[/red]".format(e)) - continue + return False if successful_unstakes != 0: logging.info( diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 1000d58f28..7566aa9bc2 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -18,9 +18,9 @@ def get_old_stakes( """ Retrieve the previous staking balances for a wallet's hotkeys across given netuids. - This function searches through the provided staking data to find the stake amounts - for the specified hotkeys and netuids associated with the wallet's coldkey. If no match - is found for a particular hotkey and netuid combination, a default balance of zero is returned. + This function searches through the provided staking data to find the stake amounts for the specified hotkeys and + netuids associated with the wallet's coldkey. If no match is found for a particular hotkey and netuid combination, + a default balance of zero is returned. Args: wallet: The wallet containing the coldkey to compare with stake data. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index c868a98a3b..800790ea40 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -101,7 +101,7 @@ check_and_convert_to_balance, ) from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import generate_weight_hash +from bittensor.utils.weight_utils import generate_weight_hash, convert_uids_and_weights if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -452,7 +452,9 @@ def bonds( return b_map - def commit(self, wallet, netuid: int, data: str) -> bool: + def commit( + self, wallet, netuid: int, data: str, period: Optional[int] = None + ) -> bool: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -460,6 +462,12 @@ def commit(self, wallet, netuid: int, data: str) -> bool: wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. + 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: + bool: `True` if the commitment was successful, `False` otherwise. """ return publish_metadata( subtensor=self, @@ -467,6 +475,7 @@ def commit(self, wallet, netuid: int, data: str) -> bool: netuid=netuid, data_type=f"Raw{len(data)}", data=data.encode(), + period=period, ) # add explicit alias @@ -476,7 +485,7 @@ def commit_reveal_enabled( self, netuid: int, block: Optional[int] = None ) -> Optional[bool]: """ - Check if commit-reveal mechanism is enabled for a given network at a specific block. + Check if the commit-reveal mechanism is enabled for a given network at a specific block. Arguments: netuid: The network identifier for which to check the commit-reveal mechanism. @@ -1924,151 +1933,6 @@ def immunity_period( ) return None if call is None else int(call) - def set_children( - self, - wallet: "Wallet", - hotkey: str, - netuid: int, - children: list[tuple[float, str]], - wait_for_inclusion: bool = True, - wait_for_finalization: bool = True, - raise_error: bool = False, - ) -> tuple[bool, str]: - """ - Allows a coldkey to set children keys. - - Arguments: - wallet (bittensor_wallet.Wallet): bittensor wallet instance. - hotkey (str): The ``SS58`` address of the neuron's hotkey. - netuid (int): The netuid value. - children (list[tuple[float, str]]): A list of children with their proportions. - 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. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. - - Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. - - Raises: - DuplicateChild: There are duplicates in the list of children. - InvalidChild: Child is the hotkey. - NonAssociatedColdKey: The coldkey does not own the hotkey or the child is the same as the hotkey. - NotEnoughStakeToSetChildkeys: Parent key doesn't have minimum own stake. - ProportionOverflow: The sum of the proportions does exceed uint64. - RegistrationNotPermittedOnRootSubnet: Attempting to register a child on the root network. - SubNetworkDoesNotExist: Attempting to register to a non-existent network. - TooManyChildren: Too many children in request. - TxRateLimitExceeded: Hotkey hit the rate limit. - bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. - 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 - - call = self.substrate.compose_call( - call_module="SubtensorModule", - call_function="set_children", - call_params={ - "children": [ - ( - float_to_u64(proportion), - child_hotkey, - ) - for proportion, child_hotkey in children - ], - "hotkey": hotkey, - "netuid": netuid, - }, - ) - - return self.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, - raise_error=raise_error, - ) - - def set_delegate_take( - self, - wallet: "Wallet", - hotkey_ss58: str, - take: float, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = True, - raise_error: bool = False, - ) -> tuple[bool, str]: - """ - Sets the delegate 'take' percentage for a nueron identified by its hotkey. - The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. - - Arguments: - wallet (bittensor_wallet.Wallet): bittensor wallet instance. - hotkey_ss58 (str): The ``SS58`` address of the neuron's hotkey. - take (float): Percentage reward for the delegate. - 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. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. - - Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. - - Raises: - DelegateTakeTooHigh: Delegate take is too high. - DelegateTakeTooLow: Delegate take is too low. - DelegateTxRateLimitExceeded: A transactor exceeded the rate limit for delegate transaction. - HotKeyAccountNotExists: The hotkey does not exists. - NonAssociatedColdKey: Request to stake, unstake or subscribe is made by a coldkey that is not associated with the hotkey account. - bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. - bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. - - The delegate take is a critical parameter in the network's incentive structure, influencing the distribution of - rewards among neurons and their nominators. - """ - - # u16 representation of the take - take_u16 = int(take * 0xFFFF) - - current_take = self.get_delegate_take(hotkey_ss58) - 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, "" - - logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") - - if current_take_u16 < take_u16: - success, error = increase_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - ) - else: - success, error = decrease_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - ) - - if success: - logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") - - return success, error - def is_hotkey_delegate(self, hotkey_ss58: str, block: Optional[int] = None) -> bool: """ Determines whether a given hotkey (public key) is a delegate on the Bittensor network. This function checks if @@ -2368,6 +2232,7 @@ def set_reveal_commitment( data: str, blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, + period: Optional[int] = None, ) -> tuple[bool, int]: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -2376,10 +2241,12 @@ def set_reveal_commitment( wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. - blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`. - Then amount of blocks in one epoch. + blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to + `360`. Then number of blocks in one epoch. block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`. - + 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: bool: `True` if the commitment was successful, `False` otherwise. @@ -2399,6 +2266,7 @@ def set_reveal_commitment( netuid=netuid, data_type=f"TimelockEncrypted", data=data_, + period=period, ), reveal_round def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicInfo]: @@ -2502,14 +2370,14 @@ def wait_for_block(self, block: Optional[int] = None): waits for the next block. Args: - block (Optional[int]): The block number to wait for. If None, waits for next block. + block (Optional[int]): The block number to wait for. If None, waits for the next block. Returns: bool: True if the target block was reached, False if timeout occurred. Example: - >>> subtensor.wait_for_block() # Waits for next block - >>> subtensor.wait_for_block(block=1234) # Waits for specific block + >>> subtensor.wait_for_block() # Waits for the next block + >>> subtensor.wait_for_block(block=1234) # Waits for a specific block """ def handler(block_data: dict): @@ -2518,6 +2386,7 @@ def handler(block_data: dict): ) if block_data["header"]["number"] >= target_block: return True + return None current_block = self.substrate.get_block() current_block_hash = current_block.get("header", {}).get("hash") @@ -2653,14 +2522,19 @@ def sign_and_send_extrinsic( 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: 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 period of the transaction as ERA part for transaction. Means how many blocks the transaction will be valid for. + 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. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". - raise_error: raises relevant exception rather than returning `False` if unsuccessful. + raise_error: raises the relevant exception rather than returning `False` if unsuccessful. Returns: (success, error message) + + Raises: + SubstrateRequestException: Substrate request exception. """ possible_keys = ("coldkey", "hotkey", "coldkeypub") if sign_with not in possible_keys: @@ -2691,7 +2565,9 @@ def sign_and_send_extrinsic( ) # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "" + message = f"Not waiting for finalization or inclusion." + logging.debug(f"{message}. Extrinsic: {extrinsic}") + return True, message if response.is_success: return True, "" @@ -2720,6 +2596,7 @@ def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to a neuron identified by the hotkey ``SS58`` address. @@ -2740,6 +2617,9 @@ def add_stake( exceed the tolerance. Default is False. rate_tolerance (float): The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. Default is 0.005. + 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: bool: True if the staking is successful, False otherwise. @@ -2761,6 +2641,7 @@ def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) def add_stake_multiple( @@ -2771,6 +2652,7 @@ def add_stake_multiple( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. @@ -2783,6 +2665,9 @@ def add_stake_multiple( amounts (list[Balance]): Corresponding amounts of TAO to stake for each hotkey. 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: bool: ``True`` if the staking is successful for all specified neurons, False otherwise. @@ -2798,6 +2683,7 @@ def add_stake_multiple( amounts=amounts, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def burned_register( @@ -2806,6 +2692,7 @@ def burned_register( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling @@ -2818,6 +2705,9 @@ def burned_register( `False`. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to `True`. + 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: bool: ``True`` if the registration is successful, False otherwise. @@ -2829,6 +2719,7 @@ def burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) return burned_register_extrinsic( @@ -2837,6 +2728,7 @@ def burned_register( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def commit_weights( @@ -2850,6 +2742,7 @@ def commit_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = 16, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -2862,15 +2755,19 @@ def commit_weights( uids (np.ndarray): NumPy array of neuron UIDs for which weights are being committed. weights (np.ndarray): NumPy array of weight values corresponding to each UID. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version.``. + a Bittensor version.``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to commit weights. Default is ``5``. + 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]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, False otherwise. + `msg` is a string value describing the success or potential error. 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. @@ -2880,8 +2777,9 @@ def commit_weights( message = "No attempt made. Perhaps it is too soon to commit weights!" logging.info( - f"Committing weights with params: netuid={netuid}, uids={uids}, weights={weights}, " - f"version_key={version_key}" + f"Committing weights with params: " + f"netuid=[blue]{netuid}[/blue], uids=[blue]{uids}[/blue], weights=[blue]{weights}[/blue], " + f"version_key=[blue]{version_key}[/blue]" ) # Generate the hash of the weights @@ -2903,12 +2801,12 @@ def commit_weights( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error committing weights: {e}") - finally: retries += 1 return success, message @@ -2923,6 +2821,7 @@ def move_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake to a different hotkey and/or subnet. @@ -2936,6 +2835,9 @@ def move_stake( amount (Balance): Amount of stake to move. 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: success (bool): True if the stake movement was successful. @@ -2951,6 +2853,7 @@ def move_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def register( @@ -2967,6 +2870,7 @@ def register( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ) -> bool: """ Registers a neuron on the Bittensor network using the provided wallet. @@ -2989,6 +2893,9 @@ def register( num_processes (Optional[int]): The number of processes to use to register. Default to `None`. update_interval (Optional[int]): The number of nonces to solve between updates. Default to `None`. log_verbose (bool): If ``true``, the registration process will log more information. Default to `False`. + 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: bool: ``True`` if the registration is successful, False otherwise. @@ -3010,6 +2917,7 @@ def register( dev_id=dev_id, output_in_place=output_in_place, log_verbose=log_verbose, + period=period, ) def register_subnet( @@ -3017,6 +2925,7 @@ def register_subnet( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor network. @@ -3027,6 +2936,9 @@ def register_subnet( false if the extrinsic fails to enter the block within the timeout. Default is False. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true, or returns false if the extrinsic fails to be finalized within the timeout. Default is True. + 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: bool: True if the subnet registration was successful, False otherwise. @@ -3036,6 +2948,7 @@ def register_subnet( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def reveal_weights( @@ -3049,6 +2962,7 @@ def reveal_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = 16, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -3061,11 +2975,14 @@ def reveal_weights( weights (np.ndarray): NumPy array of weight values corresponding to each UID. salt (np.ndarray): NumPy array of salt values corresponding to the hash function. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version``. + the Bittensor version``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to reveal weights. Default is ``5``. + 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]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string @@ -3090,12 +3007,12 @@ def reveal_weights( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error revealing weights: {e}") - finally: retries += 1 return success, message @@ -3105,6 +3022,7 @@ def root_register( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Register neuron by recycling some TAO. @@ -3114,6 +3032,9 @@ def root_register( wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. + 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: `True` if registration was successful, otherwise `False`. @@ -3124,6 +3045,7 @@ def root_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def root_set_weights( @@ -3134,9 +3056,10 @@ def root_set_weights( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ - Set weights for root network. + Set weights for the root network. Arguments: wallet (bittensor_wallet.Wallet): bittensor wallet instance. @@ -3147,12 +3070,14 @@ def root_set_weights( ``False``. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to ``False``. + 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: `True` if the setting of weights is successful, `False` otherwise. """ - netuids_ = np.array(netuids, dtype=np.int64) - weights_ = np.array(weights, dtype=np.float32) + netuids_, weights_ = convert_uids_and_weights(netuids, weights) logging.info(f"Setting weights in network: [blue]{self.network}[/blue]") return set_root_weights_extrinsic( subtensor=self, @@ -3162,8 +3087,165 @@ def root_set_weights( version_key=version_key, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) + def set_children( + self, + wallet: "Wallet", + hotkey: str, + netuid: int, + children: list[tuple[float, str]], + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + raise_error: bool = False, + period: Optional[int] = None, + ) -> tuple[bool, str]: + """ + Allows a coldkey to set children-keys. + + Arguments: + wallet (bittensor_wallet.Wallet): bittensor wallet instance. + hotkey (str): The ``SS58`` address of the neuron's hotkey. + netuid (int): The netuid value. + children (list[tuple[float, str]]): A list of children with their proportions. + 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. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + 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 where the first element is a boolean indicating success or failure of the + operation, and the second element is a message providing additional information. + + Raises: + DuplicateChild: There are duplicates in the list of children. + InvalidChild: Child is the hotkey. + NonAssociatedColdKey: The coldkey does not own the hotkey or the child is the same as the hotkey. + NotEnoughStakeToSetChildkeys: Parent key doesn't have minimum own stake. + ProportionOverflow: The sum of the proportions does exceed uint64. + RegistrationNotPermittedOnRootSubnet: Attempting to register a child on the root network. + SubNetworkDoesNotExist: Attempting to register to a non-existent network. + TooManyChildren: Too many children in request. + TxRateLimitExceeded: Hotkey hit the rate limit. + bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. + 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 + + call = self.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_children", + call_params={ + "children": [ + ( + float_to_u64(proportion), + child_hotkey, + ) + for proportion, child_hotkey in children + ], + "hotkey": hotkey, + "netuid": netuid, + }, + ) + + return self.sign_and_send_extrinsic( + call, + wallet, + wait_for_inclusion, + wait_for_finalization, + raise_error=raise_error, + period=period, + ) + + def set_delegate_take( + self, + wallet: "Wallet", + hotkey_ss58: str, + take: float, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + raise_error: bool = False, + period: Optional[int] = None, + ) -> tuple[bool, str]: + """ + 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. + + Arguments: + wallet (bittensor_wallet.Wallet): bittensor wallet instance. + hotkey_ss58 (str): The ``SS58`` address of the neuron's hotkey. + take (float): Percentage reward for the delegate. + 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. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + 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 where the first element is a boolean indicating success or failure of the + operation, and the second element is a message providing additional information. + + Raises: + DelegateTakeTooHigh: Delegate take is too high. + DelegateTakeTooLow: Delegate take is too low. + DelegateTxRateLimitExceeded: A transactor exceeded the rate limit for delegate transaction. + HotKeyAccountNotExists: The hotkey does not exist. + NonAssociatedColdKey: Request to stake, unstake, or subscribe is made by a coldkey that is not associated with the hotkey account. + bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. + bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. + + The delegate take is a critical parameter in the network's incentive structure, influencing the distribution of + rewards among neurons and their nominators. + """ + + # u16 representation of the take + take_u16 = int(take * 0xFFFF) + + current_take = self.get_delegate_take(hotkey_ss58) + 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, "" + + logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") + + if current_take_u16 < take_u16: + success, error = increase_take_extrinsic( + self, + wallet, + hotkey_ss58, + take_u16, + wait_for_finalization=wait_for_finalization, + wait_for_inclusion=wait_for_inclusion, + raise_error=raise_error, + period=period, + ) + else: + success, error = decrease_take_extrinsic( + self, + wallet, + hotkey_ss58, + take_u16, + wait_for_finalization=wait_for_finalization, + wait_for_inclusion=wait_for_inclusion, + raise_error=raise_error, + period=period, + ) + + if success: + logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") + + return success, error + def set_subnet_identity( self, wallet: "Wallet", @@ -3171,6 +3253,7 @@ def set_subnet_identity( subnet_identity: SubnetIdentity, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the identity of a subnet for a specific wallet and network. @@ -3182,6 +3265,9 @@ def set_subnet_identity( repository, contact, URL, discord, description, and any additional metadata. wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. + 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 where the first element is a boolean indicating success or failure of the @@ -3200,6 +3286,7 @@ def set_subnet_identity( additional=subnet_identity.additional, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def set_weights( @@ -3213,7 +3300,7 @@ def set_weights( wait_for_finalization: bool = False, max_retries: int = 5, block_time: float = 12.0, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: """ Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or @@ -3221,27 +3308,26 @@ def set_weights( decentralized learning architecture. Arguments: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron setting the weights. - netuid (int): The unique identifier of the subnet. - uids (Union[NDArray[np.int64], torch.LongTensor, list]): The list of neuron UIDs that the weights are being - set for. - weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The corresponding weights to be set for each - UID. - version_key (int): Version key for compatibility with the network. Default is int representation of - Bittensor version. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - max_retries (int): The number of maximum attempts to set weights. Default is ``5``. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. - - Returns: - tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + wallet: The wallet associated with the neuron setting the weights. + netuid: The unique identifier of the subnet. + uids: The list of neuron UIDs that the weights are being set for. + weights: The corresponding weights to be set for each UID. + version_key: Version key for compatibility with the network. Default is int representation of a Bittensor version. + wait_for_inclusion: Waits for the transaction to be included in a block. Default is ``False``. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is ``False``. + max_retries: The number of maximum attempts to set weights. Default is ``5``. + block_time: The number of seconds for block duration. Default is 12.0 seconds. + 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. Default is 16. + + Returns: + tuple: + ``True`` if the setting of weights is successful, False otherwise. + `msg` is a string value describing the success or potential error. 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【81†source】. + contribution are influenced by the weights it sets towards others. """ def _blocks_weight_limit() -> bool: @@ -3265,7 +3351,8 @@ def _blocks_weight_limit() -> bool: while retries < max_retries and success is False and _blocks_weight_limit(): logging.info( - f"Committing weights for subnet #{netuid}. Attempt {retries + 1} of {max_retries}." + f"Committing weights for subnet [blue]{netuid}[/blue]. " + f"Attempt [blue]{retries + 1}[blue] of [green]{max_retries}[/green]." ) success, message = commit_reveal_v3_extrinsic( subtensor=self, @@ -3277,17 +3364,18 @@ def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, block_time=block_time, + period=period, ) retries += 1 return success, message else: - # go with classic `set weights extrinsic` + # go with classic `set_weights_extrinsic` while retries < max_retries and success is False and _blocks_weight_limit(): try: logging.info( - f"Setting weights for subnet #[blue]{netuid}[/blue]. " - f"Attempt [blue]{retries + 1} of {max_retries}[/blue]." + f"Setting weights for subnet [blue]{netuid}[/blue]. " + f"Attempt [blue]{retries + 1}[/blue] of [green]{max_retries}[/green]." ) success, message = set_weights_extrinsic( subtensor=self, @@ -3302,7 +3390,6 @@ def _blocks_weight_limit() -> bool: ) except Exception as e: logging.error(f"Error setting weights: {e}") - finally: retries += 1 return success, message @@ -3314,6 +3401,7 @@ def serve_axon( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. This function is used to @@ -3327,6 +3415,9 @@ def serve_axon( ``True``. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + 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: bool: ``True`` if the Axon serve registration is successful, False otherwise. @@ -3341,6 +3432,7 @@ def serve_axon( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period, ) def start_call( @@ -3349,6 +3441,7 @@ def start_call( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -3359,6 +3452,9 @@ def start_call( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + 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]: @@ -3371,6 +3467,7 @@ def start_call( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def swap_stake( @@ -3385,6 +3482,7 @@ def swap_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -3407,7 +3505,9 @@ def swap_stake( rate_tolerance (float): The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. Default is 0.005. - + 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: success (bool): True if the extrinsic was successful. @@ -3432,6 +3532,7 @@ def swap_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) def transfer( @@ -3443,6 +3544,7 @@ def transfer( wait_for_finalization: bool = False, transfer_all: bool = False, keep_alive: bool = True, + period: Optional[int] = None, ) -> bool: """ Transfer token of amount to destination. @@ -3450,12 +3552,15 @@ def transfer( Arguments: wallet (bittensor_wallet.Wallet): Source wallet for the transfer. dest (str): Destination address for the transfer. - amount (float): Amount of tokens to transfer. + amount (float): Amount of tao to transfer. transfer_all (bool): Flag to transfer all tokens. Default is ``False``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. keep_alive (bool): Flag to keep the connection alive. Default is ``True``. + 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: `True` if the transferring was successful, otherwise `False`. @@ -3470,6 +3575,7 @@ def transfer( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, keep_alive=keep_alive, + period=period, ) def transfer_stake( @@ -3482,6 +3588,7 @@ def transfer_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -3495,6 +3602,9 @@ def transfer_stake( amount (Union[Balance, float, int]): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + 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: success (bool): True if the transfer was successful. @@ -3510,6 +3620,7 @@ def transfer_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def unstake( @@ -3523,6 +3634,7 @@ def unstake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting @@ -3543,6 +3655,9 @@ def unstake( exceed the tolerance. Default is False. rate_tolerance (float): The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. Default is 0.005. + 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: bool: ``True`` if the unstaking process is successful, False otherwise. @@ -3563,6 +3678,7 @@ def unstake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) def unstake_multiple( @@ -3573,6 +3689,7 @@ def unstake_multiple( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts @@ -3587,6 +3704,9 @@ def unstake_multiple( unstakes all available stakes. 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: bool: ``True`` if the batch unstaking is successful, False otherwise. @@ -3602,4 +3722,5 @@ def unstake_multiple( amounts=amounts, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) diff --git a/bittensor/utils/weight_utils.py b/bittensor/utils/weight_utils.py index 5c98b1f383..2a7b7f3afd 100644 --- a/bittensor/utils/weight_utils.py +++ b/bittensor/utils/weight_utils.py @@ -440,3 +440,42 @@ def generate_weight_hash( commit_hash = "0x" + blake2b_hash.hexdigest() return commit_hash + + +def convert_uids_and_weights( + uids: Union[NDArray[np.int64], list], + weights: Union[NDArray[np.float32], list], +) -> tuple[np.ndarray, np.ndarray]: + """Converts netuids and weights to numpy arrays if they are not already. + + Arguments: + uids (Union[NDArray[np.int64], list]): The uint64 uids of destination neurons. + weights (Union[NDArray[np.float32], list]): The weights to set. These must be floated. + + Returns: + tuple[ndarray, ndarray]: Bytes converted netuids and weights. + """ + if isinstance(uids, list): + uids = np.array(uids, dtype=np.int64) + if isinstance(weights, list): + weights = np.array(weights, dtype=np.float32) + return uids, weights + + +def convert_and_normalize_weights_and_uids( + uids: Union[NDArray[np.int64], "torch.LongTensor", list], + weights: Union[NDArray[np.float32], "torch.FloatTensor", list], +) -> tuple[list[int], list[int]]: + """Converts weights and uids to numpy arrays if they are not already. + + Arguments: + uids (Union[NDArray[np.int64], torch.LongTensor, list]): The ``uint64`` uids of destination neurons. + weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights to set. These must be ``float`` s + and correspond to the passed ``uid`` s. + + Returns: + weight_uids, weight_vals: Bytes converted weights and uids + """ + + # Reformat and normalize and return + return convert_weights_and_uids_for_emit(*convert_uids_and_weights(uids, weights)) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 0170cbd302..cd5229d5ee 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -57,7 +57,11 @@ def read_output(): def local_chain(request): """Determines whether to run the localnet.sh script in a subprocess or a Docker container.""" args = request.param if hasattr(request, "param") else None - params = "" if args is None else f"{args}" + + # passed env variable to control node mod (non-/fast-blocks) + fast_blocks = "False" if (os.getenv("FAST_BLOCKS") == "0") is True else "True" + params = f"{fast_blocks}" if args is None else f"{fast_blocks} {args} " + if shutil.which("docker") and not os.getenv("USE_DOCKER") == "0": yield from docker_runner(params) else: @@ -91,7 +95,7 @@ def legacy_runner(params): logging.warning("LOCALNET_SH_PATH env variable is not set, e2e test skipped.") pytest.skip("LOCALNET_SH_PATH environment variable is not set.") - # Check if param is None, and handle it accordingly + # Check if param is None and handle it accordingly args = "" if params is None else f"{params}" # Compile commands to send to process diff --git a/tests/e2e_tests/test_axon.py b/tests/e2e_tests/test_axon.py index 0a139eb457..613027c691 100644 --- a/tests/e2e_tests/test_axon.py +++ b/tests/e2e_tests/test_axon.py @@ -13,7 +13,7 @@ async def test_axon(subtensor, templates, alice_wallet): Steps: 1. Register a subnet and register Alice 2. Check if metagraph.axon is updated and check axon attributes - 3. Run Alice as a miner on the subnet + 3. Run Alice as a miner on subnet 4. Check the metagraph again after running the miner and verify all attributes Raises: AssertionError: If any of the checks or verifications fail diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 106af50a96..4eff2ce052 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -22,7 +22,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle Steps: 1. Register a subnet through Alice 2. Register Alice's neuron and add stake - 3. Enable commit-reveal mechanism on the subnet + 3. Enable a commit-reveal mechanism on subnet 4. Lower weights rate limit 5. Change the tempo for subnet 1 5. Commit weights and ensure they are committed. diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 5015208a1b..d3dabf6152 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -19,14 +19,14 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa Steps: 1. Register a subnet through Alice - 2. Enable commit-reveal mechanism on the subnet + 2. Enable the commit-reveal mechanism on subnet 3. Lower the commit_reveal interval and rate limit 4. Commit weights and verify 5. Wait interval & reveal weights and verify Raises: AssertionError: If any of the checks or verifications fail """ - netuid = 2 + netuid = subtensor.get_total_subnets() # 2 print("Testing test_commit_and_reveal_weights") @@ -153,12 +153,12 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa @pytest.mark.asyncio async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wallet): """ - Tests that committing weights doesn't re-use a nonce in the transaction pool. + Tests that committing weights doesn't re-use nonce in the transaction pool. Steps: 1. Register a subnet through Alice 2. Register Alice's neuron and add stake - 3. Enable commit-reveal mechanism on the subnet + 3. Enable the commit-reveal mechanism on subnet 4. Lower the commit_reveal interval and rate limit 5. Commit weights three times 6. Assert that all commits succeeded @@ -166,7 +166,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall AssertionError: If any of the checks or verifications fail """ subnet_tempo = 50 - netuid = 2 + netuid = subtensor.get_total_subnets() # 2 # Wait for 2 tempos to pass as CR3 only reveals weights after 2 tempos subtensor.wait_for_block(subnet_tempo * 2 + 1) @@ -226,7 +226,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall # wait while weights_rate_limit changes applied. subtensor.wait_for_block(subnet_tempo + 1) - # create different commited data to avoid coming into pool black list with the error + # Create different commited data to avoid coming into the pool's blacklist with the error # Failed to commit weights: Subtensor returned `Custom type(1012)` error. This means: `Transaction is temporarily # banned`.Failed to commit weights: Subtensor returned `Custom type(1012)` error. This means: `Transaction is # temporarily banned`.` @@ -245,7 +245,7 @@ def get_weights_and_salt(counter: int): f"{subtensor.substrate.get_account_next_index(alice_wallet.hotkey.ss58_address)}[/orange]" ) - # 3 time doing call if nonce wasn't updated, then raise error + # 3 time doing call if nonce wasn't updated, then raise the error @retry.retry(exceptions=Exception, tries=3, delay=1) @execute_and_wait_for_next_nonce(subtensor=subtensor, wallet=alice_wallet) def send_commit(salt_, weight_uids_, weight_vals_): @@ -260,7 +260,7 @@ def send_commit(salt_, weight_uids_, weight_vals_): ) assert success is True, message - # send some amount of commit weights + # Send some number of commit weights AMOUNT_OF_COMMIT_WEIGHTS = 3 for call in range(AMOUNT_OF_COMMIT_WEIGHTS): weight_uids, weight_vals, salt = get_weights_and_salt(call) diff --git a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py index 60d91fa3d1..11c8d76d7e 100644 --- a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py +++ b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py @@ -10,7 +10,7 @@ async def test_set_subnet_identity_extrinsic_happy_pass(subtensor, alice_wallet) "[magenta]Testing `set_subnet_identity_extrinsic` with success result.[/magenta]" ) - netuid = 2 + netuid = subtensor.get_total_subnets() # 2 # Register a subnet, netuid 2 assert subtensor.register_subnet(alice_wallet), "Subnet wasn't created" diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index dddd9d1af1..9ab91351db 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -55,6 +55,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): amount=Balance.from_tao(1), wait_for_inclusion=True, wait_for_finalization=True, + period=16, ) assert success is True @@ -162,19 +163,20 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): } success = subtensor.unstake( - alice_wallet, - bob_wallet.hotkey.ss58_address, + wallet=alice_wallet, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=stake_bob, wait_for_inclusion=True, wait_for_finalization=True, + period=16, ) assert success is True stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) diff --git a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py index 7802ccf9bb..66873eba00 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py @@ -82,7 +82,7 @@ async def test_do_commit_reveal_v3_success(mocker, subtensor, fake_wallet): wait_for_inclusion=False, wait_for_finalization=False, ) - assert result == (True, "") + assert result == (True, "Not waiting for finalization or inclusion.") @pytest.mark.asyncio @@ -163,7 +163,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( mocked_weights = mocker.Mock() mocked_convert_weights_and_uids_for_emit = mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(mocked_uids, mocked_weights), ) mocked_get_subnet_reveal_period_epochs = mocker.patch.object( @@ -223,6 +223,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) @@ -238,7 +239,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_numpy( mock_convert = mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mock_encode_drand = mocker.patch.object( @@ -288,7 +289,7 @@ async def test_commit_reveal_v3_extrinsic_response_false( # Mocks mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mocker.patch.object( @@ -328,6 +329,7 @@ async def test_commit_reveal_v3_extrinsic_response_false( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) @@ -341,7 +343,7 @@ async def test_commit_reveal_v3_extrinsic_exception(mocker, subtensor, fake_wall mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", side_effect=Exception("Test Error"), ) diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index b53fd946e0..a9b3ac511f 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -16,18 +16,11 @@ async def test_do_pow_register_success(subtensor, fake_wallet, mocker): seal=b"fake_seal", ) - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - fake_response.is_success = mocker.AsyncMock(return_value=True)() - fake_response.process_events = mocker.AsyncMock() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) + mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock(return_value=(True, "")), ) # Call @@ -53,12 +46,12 @@ async def test_do_pow_register_success(subtensor, fake_wallet, mocker): "coldkey": "coldkey_ss58", }, ) - subtensor.substrate.create_signed_extrinsic.assert_awaited_once_with( - call=fake_call, - keypair=fake_wallet.coldkey, - ) - subtensor.substrate.submit_extrinsic.assert_awaited_once_with( - fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + period=None, ) assert result is True assert error_message == "" @@ -75,25 +68,9 @@ async def test_do_pow_register_failure(subtensor, fake_wallet, mocker): nonce=67890, seal=b"fake_seal", ) - fake_err_message = mocker.Mock(autospec=str) - - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - fake_response.is_success = mocker.AsyncMock(return_value=False)() - fake_response.process_events = mocker.AsyncMock() - fake_response.error_message = mocker.AsyncMock(return_value=fake_err_message)() - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) - mocked_format_error_message = mocker.patch.object( - async_subtensor, "format_error_message" - ) + mocker.patch.object(subtensor.substrate, "compose_call") + mocker.patch.object(subtensor, "sign_and_send_extrinsic") # Call result_error_message = await async_registration._do_pow_register( @@ -118,16 +95,15 @@ async def test_do_pow_register_failure(subtensor, fake_wallet, mocker): "coldkey": "coldkey_ss58", }, ) - subtensor.substrate.create_signed_extrinsic.assert_awaited_once_with( - call=fake_call, - keypair=fake_wallet.coldkey, - ) - subtensor.substrate.submit_extrinsic.assert_awaited_once_with( - fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + period=None, ) - mocked_format_error_message.assert_called_once_with(fake_err_message) - assert result_error_message == (False, mocked_format_error_message.return_value) + assert result_error_message == subtensor.sign_and_send_extrinsic.return_value @pytest.mark.asyncio @@ -142,20 +118,11 @@ async def test_do_pow_register_no_waiting(subtensor, fake_wallet, mocker): seal=b"fake_seal", ) - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) + mocker.patch.object(subtensor.substrate, "compose_call") + mocker.patch.object(subtensor, "sign_and_send_extrinsic") # Call - result, error_message = await async_registration._do_pow_register( + result = await async_registration._do_pow_register( subtensor=subtensor, netuid=1, wallet=fake_wallet, @@ -177,15 +144,15 @@ async def test_do_pow_register_no_waiting(subtensor, fake_wallet, mocker): "coldkey": "coldkey_ss58", }, ) - subtensor.substrate.create_signed_extrinsic.assert_awaited_once_with( - call=fake_call, - keypair=fake_wallet.coldkey, - ) - subtensor.substrate.submit_extrinsic.assert_awaited_once_with( - fake_extrinsic, wait_for_inclusion=False, wait_for_finalization=False + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=False, + wait_for_finalization=False, + period=None, ) - assert result is True - assert error_message == "" + + assert result == subtensor.sign_and_send_extrinsic.return_value @pytest.mark.asyncio @@ -450,6 +417,7 @@ async def is_stale_side_effect(*_, **__): pow_result=fake_pow_result, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result is False @@ -510,6 +478,7 @@ async def test_set_subnet_identity_extrinsic_is_success(subtensor, fake_wallet, wallet=fake_wallet, wait_for_inclusion=False, wait_for_finalization=True, + period=None, ) assert result == (True, "Identities for subnet 123 are set.") @@ -574,6 +543,7 @@ async def test_set_subnet_identity_extrinsic_is_failed(subtensor, fake_wallet, m wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result == ( diff --git a/tests/unit_tests/extrinsics/asyncex/test_root.py b/tests/unit_tests/extrinsics/asyncex/test_root.py index ecca5a9847..baa134ee92 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_root.py +++ b/tests/unit_tests/extrinsics/asyncex/test_root.py @@ -332,7 +332,7 @@ async def test_do_set_root_weights_success(subtensor, fake_wallet, mocker): fake_weights = [0.1, 0.2, 0.7] fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() + fake_extrinsic = True, "Successfully set weights." fake_response = mocker.Mock() fake_response.is_success = mocker.AsyncMock(return_value=True)() @@ -340,10 +340,7 @@ async def test_do_set_root_weights_success(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response + subtensor, "sign_and_send_extrinsic", return_value=fake_extrinsic ) # Call @@ -369,14 +366,13 @@ async def test_do_set_root_weights_success(subtensor, fake_wallet, mocker): "hotkey": "fake_hotkey_address", }, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic.assert_called_once_with( call=fake_call, - keypair=fake_wallet.coldkey, - era={"period": 5}, - nonce=subtensor.substrate.get_account_next_index.return_value, - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + use_nonce=True, + period=8, ) assert result is True assert message == "Successfully set weights." @@ -391,28 +387,10 @@ async def test_do_set_root_weights_failure(subtensor, fake_wallet, mocker): fake_weights = [0.1, 0.2, 0.7] fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - async def fake_is_success(): - return False - - fake_response.is_success = fake_is_success() - fake_response.process_events = mocker.AsyncMock() - fake_response.error_message = mocker.AsyncMock()() mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) - - mocked_format_error_message = mocker.patch.object( - async_root, - "format_error_message", - return_value="Transaction failed", + subtensor, "sign_and_send_extrinsic", return_value=(False, "Transaction failed") ) # Call @@ -428,7 +406,6 @@ async def fake_is_success(): # Asserts assert result is False - assert message == mocked_format_error_message.return_value @pytest.mark.asyncio @@ -441,14 +418,12 @@ async def test_do_set_root_weights_no_waiting(subtensor, fake_wallet, mocker): fake_call = mocker.AsyncMock() fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response + subtensor, + "sign_and_send_extrinsic", + return_value=(True, "Not waiting for finalization or inclusion."), ) # Call @@ -464,9 +439,13 @@ async def test_do_set_root_weights_no_waiting(subtensor, fake_wallet, mocker): # Asserts subtensor.substrate.compose_call.assert_called_once() - subtensor.substrate.create_signed_extrinsic.assert_called_once() - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=fake_extrinsic, wait_for_inclusion=False, wait_for_finalization=False + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=False, + wait_for_finalization=False, + use_nonce=True, + period=8, ) assert result is True assert message == "Not waiting for finalization or inclusion." @@ -674,5 +653,6 @@ async def test_set_root_weights_extrinsic_request_exception( version_key=0, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) mocked_format_error_message.assert_called_once() diff --git a/tests/unit_tests/extrinsics/asyncex/test_start_call.py b/tests/unit_tests/extrinsics/asyncex/test_start_call.py index 63083cd094..d99295195e 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_start_call.py +++ b/tests/unit_tests/extrinsics/asyncex/test_start_call.py @@ -5,18 +5,15 @@ @pytest.mark.asyncio async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): """Test that start_call_extrinsic correctly constructs and submits the extrinsic.""" - # Preps netuid = 123 wallet = fake_wallet wallet.name = "fake_wallet" wallet.coldkey = "fake_coldkey" - substrate = subtensor.substrate.__aenter__.return_value - substrate.compose_call.return_value = "mock_call" - substrate.create_signed_extrinsic.return_value = "signed_ext" - substrate.submit_extrinsic.return_value = mocker.MagicMock( - is_success=mocker.AsyncMock(return_value=True)(), error_message="" + substrate.compose_call = mocker.AsyncMock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") ) # Call @@ -33,15 +30,12 @@ async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): call_params={"netuid": netuid}, ) - substrate.create_signed_extrinsic.assert_awaited_once_with( - call="mock_call", - keypair=wallet.coldkey, - ) - - substrate.submit_extrinsic.assert_awaited_once_with( - extrinsic="signed_ext", + mocked_sign_and_send_extrinsic.assert_awaited_once_with( + call=substrate.compose_call.return_value, + wallet=wallet, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) 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 0fa70a8e75..77fcd72f9f 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_transfer.py +++ b/tests/unit_tests/extrinsics/asyncex/test_transfer.py @@ -9,22 +9,15 @@ async def test_do_transfer_success(subtensor, fake_wallet, mocker): # Preps fake_destination = "destination_address" fake_amount = mocker.Mock(autospec=Balance, rao=1000) + fake_block_hash = "fake_block_hash" - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - fake_response.is_success = mocker.AsyncMock(return_value=True)() - fake_response.process_events = mocker.AsyncMock() - fake_response.block_hash = "fake_block_hash" - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) + mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock(return_value=(True, "")), ) + mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) # Call success, block_hash, error_message = await async_transfer._do_transfer( @@ -37,18 +30,18 @@ async def test_do_transfer_success(subtensor, fake_wallet, mocker): ) # Asserts - subtensor.substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_awaited_once_with( call_module="Balances", call_function="transfer_allow_death", call_params={"dest": fake_destination, "value": fake_amount.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert success is True assert block_hash == "fake_block_hash" @@ -61,28 +54,15 @@ async def test_do_transfer_failure(subtensor, fake_wallet, mocker): # Preps fake_destination = "destination_address" fake_amount = mocker.Mock(autospec=Balance, rao=1000) + fake_block_hash = "fake_block_hash" - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - fake_response.is_success = mocker.AsyncMock(return_value=False)() - fake_response.process_events = mocker.AsyncMock() - fake_response.error_message = mocker.AsyncMock(return_value="Fake error message")() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) + mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) - - mocked_format_error_message = mocker.patch.object( - async_transfer, - "format_error_message", - return_value="Formatted error message", + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock(return_value=(False, "Formatted error message")), ) + mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) # Call success, block_hash, error_message = await async_transfer._do_transfer( @@ -95,22 +75,21 @@ async def test_do_transfer_failure(subtensor, fake_wallet, mocker): ) # Asserts - subtensor.substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_awaited_once_with( call_module="Balances", call_function="transfer_allow_death", call_params={"dest": fake_destination, "value": fake_amount.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert success is False assert block_hash == "" - mocked_format_error_message.assert_called_once_with("Fake error message") assert error_message == "Formatted error message" @@ -120,19 +99,17 @@ async def test_do_transfer_no_waiting(subtensor, fake_wallet, mocker): # Preps fake_destination = "destination_address" fake_amount = mocker.Mock(autospec=Balance, rao=1000) + fake_block_hash = "fake_block_hash" - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) + mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, - "submit_extrinsic", - return_value=mocker.Mock(), + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock( + return_value=(False, "Success, extrinsic submitted without waiting.") + ), ) + mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) # Call success, block_hash, error_message = await async_transfer._do_transfer( @@ -145,18 +122,18 @@ async def test_do_transfer_no_waiting(subtensor, fake_wallet, mocker): ) # Asserts - subtensor.substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_awaited_once_with( call_module="Balances", call_function="transfer_allow_death", call_params={"dest": fake_destination, "value": fake_amount.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=False, wait_for_finalization=False, + period=None, ) assert success is True assert block_hash == "" diff --git a/tests/unit_tests/extrinsics/asyncex/test_weights.py b/tests/unit_tests/extrinsics/asyncex/test_weights.py index c01490055d..226292b43c 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_weights.py +++ b/tests/unit_tests/extrinsics/asyncex/test_weights.py @@ -43,7 +43,7 @@ async def fake_is_success(): # Asserts assert result is True - assert message == "" + assert message == "Successfully set weights." @pytest.mark.asyncio @@ -131,7 +131,7 @@ async def test_do_set_weights_no_waiting(subtensor, fake_wallet, mocker): # Asserts assert result is True - assert message == "" + assert message == "Not waiting for finalization or inclusion." @pytest.mark.asyncio @@ -141,12 +141,16 @@ async def test_set_weights_extrinsic_success_with_finalization( """Tests set_weights_extrinsic when weights are successfully set with finalization.""" # Preps fake_netuid = 1 - fake_uids = [1, 2, 3] - fake_weights = [0.1, 0.2, 0.7] + fake_uids = mocker.Mock() + fake_weights = mocker.Mock() mocked_do_set_weights = mocker.patch.object( async_weights, "_do_set_weights", return_value=(True, "") ) + mocker_converter = mocker.patch.object( + async_weights, "convert_and_normalize_weights_and_uids" + ) + mocker_converter.return_value = (mocker.Mock(), mocker.Mock()) # Call result, message = await async_weights.set_weights_extrinsic( @@ -160,16 +164,18 @@ async def test_set_weights_extrinsic_success_with_finalization( ) # Asserts + mocker_converter.assert_called_once_with(fake_uids, fake_weights) + mocked_do_set_weights.assert_called_once_with( subtensor=subtensor, wallet=fake_wallet, netuid=fake_netuid, - uids=mocker.ANY, - vals=mocker.ANY, + uids=mocker_converter.return_value[0], + vals=mocker_converter.return_value[1], version_key=0, wait_for_finalization=True, wait_for_inclusion=True, - period=5, + period=8, ) assert result is True assert message == "Successfully set weights and Finalized." @@ -184,7 +190,9 @@ async def test_set_weights_extrinsic_no_waiting(subtensor, fake_wallet, mocker): fake_weights = [0.1, 0.2, 0.7] mocked_do_set_weights = mocker.patch.object( - async_weights, "_do_set_weights", return_value=(True, "") + async_weights, + "_do_set_weights", + return_value=(True, "Not waiting for finalization or inclusion."), ) # Call @@ -380,7 +388,7 @@ async def test_do_commit_weights_no_waiting(subtensor, fake_wallet, mocker): # Asserts assert result is True - assert message == "" + assert message == "Not waiting for finalization or inclusion." @pytest.mark.asyncio @@ -437,6 +445,7 @@ async def test_commit_weights_extrinsic_success(subtensor, fake_wallet, mocker): commit_hash=fake_commit_hash, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result is True assert message == "✅ [green]Successfully committed weights.[green]" @@ -471,6 +480,7 @@ async def test_commit_weights_extrinsic_failure(subtensor, fake_wallet, mocker): commit_hash=fake_commit_hash, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result is False assert message == "Commit failed." diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index 37b131e391..3891b97dae 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -48,11 +48,8 @@ def test_do_commit_reveal_v3_success(mocker, subtensor, fake_wallet): fake_reveal_round = 1 mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") - mocked_create_signed_extrinsic = mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic" - ) - mocked_submit_extrinsic = mocker.patch.object( - subtensor.substrate, "submit_extrinsic" + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") ) # Call @@ -74,14 +71,15 @@ def test_do_commit_reveal_v3_success(mocker, subtensor, fake_wallet): "reveal_round": fake_reveal_round, }, ) - mocked_create_signed_extrinsic.assert_called_once_with( - call=mocked_compose_call.return_value, keypair=fake_wallet.hotkey - ) - mocked_submit_extrinsic.assert_called_once_with( - mocked_create_signed_extrinsic.return_value, + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=False, wait_for_finalization=False, + sign_with="hotkey", + period=None, ) + assert result == (True, "") @@ -155,7 +153,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( mocked_weights = mocker.Mock() mocked_convert_weights_and_uids_for_emit = mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(mocked_uids, mocked_weights), ) mocker.patch.object(subtensor, "get_subnet_reveal_period_epochs") @@ -183,6 +181,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( weights=fake_weights, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) # Asserts @@ -209,6 +208,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) @@ -223,7 +223,7 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy( mock_convert = mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mock_encode_drand = mocker.patch.object( @@ -272,7 +272,7 @@ def test_commit_reveal_v3_extrinsic_response_false( # Mocks mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mocker.patch.object( @@ -312,6 +312,7 @@ def test_commit_reveal_v3_extrinsic_response_false( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) @@ -324,7 +325,7 @@ def test_commit_reveal_v3_extrinsic_exception(mocker, subtensor, fake_wallet): mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", side_effect=Exception("Test Error"), ) diff --git a/tests/unit_tests/extrinsics/test_commit_weights.py b/tests/unit_tests/extrinsics/test_commit_weights.py index 71771d2247..e27547528b 100644 --- a/tests/unit_tests/extrinsics/test_commit_weights.py +++ b/tests/unit_tests/extrinsics/test_commit_weights.py @@ -13,7 +13,8 @@ def test_do_commit_weights(subtensor, fake_wallet, mocker): wait_for_inclusion = True wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = None + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, "")) + mocker.patch.object(subtensor, "get_block_hash", return_value=1) mocked_format_error_message = mocker.Mock() mocker.patch( @@ -41,25 +42,18 @@ def test_do_commit_weights(subtensor, fake_wallet, mocker): }, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=None, + nonce_key="hotkey", + sign_with="hotkey", + use_nonce=True, ) - mocked_format_error_message.assert_called_once_with( - subtensor.substrate.submit_extrinsic.return_value.error_message, - ) - - assert result == ( - False, - mocked_format_error_message.return_value, - ) + assert result == (False, "") def test_do_reveal_weights(subtensor, fake_wallet, mocker): @@ -74,13 +68,7 @@ def test_do_reveal_weights(subtensor, fake_wallet, mocker): wait_for_inclusion = True wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = None - - mocked_format_error_message = mocker.Mock() - mocker.patch( - "bittensor.core.subtensor.format_error_message", - mocked_format_error_message, - ) + mocker.patch.object(subtensor, "sign_and_send_extrinsic") # Call result = _do_reveal_weights( @@ -108,23 +96,15 @@ def test_do_reveal_weights(subtensor, fake_wallet, mocker): }, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - nonce=subtensor.substrate.get_account_next_index.return_value, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=None, + nonce_key="hotkey", + sign_with="hotkey", + use_nonce=True, ) - mocked_format_error_message.assert_called_once_with( - subtensor.substrate.submit_extrinsic.return_value.error_message, - ) - - assert result == ( - False, - mocked_format_error_message.return_value, - ) + assert result == subtensor.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 b2d816d4dd..77c6a0d3aa 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -286,6 +286,7 @@ def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, m wallet=mock_wallet, wait_for_inclusion=False, wait_for_finalization=True, + period=None, ) assert result == (True, "Identities for subnet 123 are set.") @@ -347,6 +348,7 @@ def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mo wallet=mock_wallet, wait_for_inclusion=False, wait_for_finalization=True, + period=None, ) assert result == ( diff --git a/tests/unit_tests/extrinsics/test_root.py b/tests/unit_tests/extrinsics/test_root.py index a22d456a2e..8737b3f32c 100644 --- a/tests/unit_tests/extrinsics/test_root.py +++ b/tests/unit_tests/extrinsics/test_root.py @@ -99,10 +99,11 @@ def test_root_register_extrinsic( call_params={"hotkey": "fake_hotkey_address"}, ) mocked_sign_and_send_extrinsic.assert_called_once_with( - mock_subtensor.substrate.compose_call.return_value, + call=mock_subtensor.substrate.compose_call.return_value, wallet=mock_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=None, ) @@ -125,7 +126,7 @@ def test_root_register_extrinsic_insufficient_balance( assert success is False mock_subtensor.get_balance.assert_called_once_with( - mock_wallet.coldkeypub.ss58_address, + address=mock_wallet.coldkeypub.ss58_address, block=mock_subtensor.get_current_block.return_value, ) mock_subtensor.substrate.submit_extrinsic.assert_not_called() diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index 8fd01ef6ef..c1688fd3d4 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -321,7 +321,7 @@ def test_serve_axon_extrinsic( 1, "Sha256", b"mock_bytes_data", - True, + (True, ""), True, "happy-path-wait", ), @@ -331,7 +331,7 @@ def test_serve_axon_extrinsic( 1, "Sha256", b"mock_bytes_data", - True, + (True, ""), True, "happy-path-no-wait", ), @@ -353,15 +353,8 @@ def test_publish_metadata( # Arrange with ( patch.object(mock_subtensor.substrate, "compose_call"), - patch.object(mock_subtensor.substrate, "create_signed_extrinsic"), patch.object( - mock_subtensor.substrate, - "submit_extrinsic", - return_value=MagicMock( - is_success=response_success, - process_events=MagicMock(), - error_message="error", - ), + mock_subtensor, "sign_and_send_extrinsic", return_value=response_success ), ): # Act diff --git a/tests/unit_tests/extrinsics/test_set_weights.py b/tests/unit_tests/extrinsics/test_set_weights.py index d3ebfd71c8..1046385291 100644 --- a/tests/unit_tests/extrinsics/test_set_weights.py +++ b/tests/unit_tests/extrinsics/test_set_weights.py @@ -68,16 +68,16 @@ def test_set_weights_extrinsic( expected_success, expected_message, ): - uids_tensor = torch.tensor(uids, dtype=torch.int64) - weights_tensor = torch.tensor(weights, dtype=torch.float32) + # uids_tensor = torch.tensor(uids, dtype=torch.int64) + # weights_tensor = torch.tensor(weights, dtype=torch.float32) with ( patch( "bittensor.utils.weight_utils.convert_weights_and_uids_for_emit", - return_value=(uids_tensor, weights_tensor), + return_value=(uids, weights), ), patch( "bittensor.core.extrinsics.set_weights._do_set_weights", - return_value=(expected_success, "Mock error message"), + return_value=(expected_success, expected_message), ), ): result, message = set_weights_extrinsic( @@ -105,53 +105,10 @@ def test_do_set_weights_is_success(mock_subtensor, fake_wallet, mocker): fake_wait_for_finalization = True mock_subtensor.substrate.submit_extrinsic.return_value.is_success = True - - # Call - result = _do_set_weights( - subtensor=mock_subtensor, - wallet=fake_wallet, - uids=fake_uids, - vals=fake_vals, - netuid=fake_netuid, - version_key=version_as_int, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - mock_subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function="set_weights", - call_params={ - "dests": fake_uids, - "weights": fake_vals, - "netuid": fake_netuid, - "version_key": version_as_int, - }, + mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "") ) - mock_subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = mock_subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == mock_subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - assert kwargs["era"] == {"period": 5} - - assert result == (True, "Successfully set weights.") - - -def test_do_set_weights_is_not_success(mock_subtensor, fake_wallet, mocker): - """Unsuccessful _do_set_weights call.""" - # Prep - fake_uids = [1, 2, 3] - fake_vals = [4, 5, 6] - fake_netuid = 1 - fake_wait_for_inclusion = True - fake_wait_for_finalization = True - - mock_subtensor.substrate.submit_extrinsic.return_value.is_success = False - mocked_format_error_message = mocker.MagicMock() - subtensor_module.format_error_message = mocked_format_error_message - # Call result = _do_set_weights( subtensor=mock_subtensor, @@ -176,22 +133,17 @@ def test_do_set_weights_is_not_success(mock_subtensor, fake_wallet, mocker): }, ) - mock_subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = mock_subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == mock_subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - assert kwargs["era"] == {"period": 5} - - mock_subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=mock_subtensor.substrate.create_signed_extrinsic.return_value, + mock_subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=mock_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + nonce_key="hotkey", + sign_with="hotkey", + use_nonce=True, + period=None, ) - - assert result == ( - False, - "Subtensor returned `UnknownError(UnknownType)` error. This means: `Unknown Description`.", - ) + assert result == (True, "") def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): @@ -203,6 +155,12 @@ def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): fake_wait_for_inclusion = False fake_wait_for_finalization = False + mocker.patch.object( + mock_subtensor, + "sign_and_send_extrinsic", + return_value=(True, "Not waiting for finalization or inclusion."), + ) + # Call result = _do_set_weights( subtensor=mock_subtensor, @@ -227,15 +185,11 @@ def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): }, ) - mock_subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = mock_subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == mock_subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - assert kwargs["era"] == {"period": 5} - - mock_subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=mock_subtensor.substrate.create_signed_extrinsic.return_value, + mock_subtensor.sign_and_send_extrinsic( + call=mock_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None, ) assert result == (True, "Not waiting for finalization or inclusion.") diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index 899054006f..80ecb5c240 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -13,7 +13,7 @@ def test_add_stake_extrinsic(mocker): "sign_and_send_extrinsic.return_value": (True, ""), } ) - fake_wallet = mocker.Mock( + fake_wallet_ = mocker.Mock( **{ "coldkeypub.ss58_address": "hotkey_owner", } @@ -27,7 +27,7 @@ def test_add_stake_extrinsic(mocker): # Call result = staking.add_stake_extrinsic( subtensor=fake_subtensor, - wallet=fake_wallet, + wallet=fake_wallet_, hotkey_ss58=hotkey_ss58, netuid=fake_netuid, amount=amount, @@ -44,13 +44,14 @@ def test_add_stake_extrinsic(mocker): call_params={"hotkey": "hotkey", "amount_staked": 9, "netuid": 1}, ) fake_subtensor.sign_and_send_extrinsic.assert_called_once_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet_, + wait_for_inclusion=True, + wait_for_finalization=True, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=None, ) @@ -85,7 +86,7 @@ def test_add_stake_multiple_extrinsic(mocker): mocker.patch.object( staking, "get_old_stakes", return_value=[Balance(1.1), Balance(0.3)] ) - fake_wallet = mocker.Mock( + fake_wallet_ = mocker.Mock( **{ "coldkeypub.ss58_address": "hotkey_owner", } @@ -99,7 +100,7 @@ def test_add_stake_multiple_extrinsic(mocker): # Call result = staking.add_stake_multiple_extrinsic( subtensor=fake_subtensor, - wallet=fake_wallet, + wallet=fake_wallet_, hotkey_ss58s=hotkey_ss58s, netuids=netuids, amounts=amounts, @@ -131,11 +132,12 @@ def test_add_stake_multiple_extrinsic(mocker): }, ) fake_subtensor.sign_and_send_extrinsic.assert_called_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet_, + wait_for_inclusion=True, + wait_for_finalization=True, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=None, ) diff --git a/tests/unit_tests/extrinsics/test_start_call.py b/tests/unit_tests/extrinsics/test_start_call.py index ccc14581ee..ece0e6cf42 100644 --- a/tests/unit_tests/extrinsics/test_start_call.py +++ b/tests/unit_tests/extrinsics/test_start_call.py @@ -3,18 +3,15 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): """Test that start_call_extrinsic correctly constructs and submits the extrinsic.""" - # Preps netuid = 123 wallet = fake_wallet wallet.name = "fake_wallet" wallet.coldkey = "fake_coldkey" - substrate = subtensor.substrate.__enter__.return_value - substrate.compose_call.return_value = "mock_call" - substrate.create_signed_extrinsic.return_value = "signed_ext" - substrate.submit_extrinsic.return_value = mocker.MagicMock( - is_success=True, error_message="" + subtensor.substrate.compose_call = mocker.Mock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") ) # Call @@ -25,21 +22,18 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): ) # Assertions - substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_called_once_with( call_module="SubtensorModule", call_function="start_call", call_params={"netuid": netuid}, ) - substrate.create_signed_extrinsic.assert_called_once_with( - call="mock_call", - keypair=wallet.coldkey, - ) - - substrate.submit_extrinsic.assert_called_once_with( - extrinsic="signed_ext", + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=wallet, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert success is True diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index 9424352a55..fcd532e2d0 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -10,7 +10,8 @@ def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = True + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(True, "")) + mocker.patch.object(subtensor, "get_block_hash", return_value=1) # Call result = _do_transfer( @@ -28,20 +29,15 @@ def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): call_function="transfer_allow_death", call_params={"dest": fake_dest, "value": fake_transfer_balance.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None, ) # subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() - assert result == ( - True, - subtensor.substrate.submit_extrinsic.return_value.block_hash, - "Success with response.", - ) + assert result == (True, 1, "Success with response.") def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): @@ -52,13 +48,8 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = False - - mocked_format_error_message = mocker.Mock() - mocker.patch( - "bittensor.core.extrinsics.transfer.format_error_message", - mocked_format_error_message, - ) + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, "")) + mocker.patch.object(subtensor, "get_block_hash", return_value=1) # Call result = _do_transfer( @@ -76,23 +67,15 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): call_function="transfer_allow_death", call_params={"dest": fake_dest, "value": fake_transfer_balance.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - ) - mocked_format_error_message.assert_called_once_with( - subtensor.substrate.submit_extrinsic.return_value.error_message + period=None, ) - assert result == ( - False, - "", - mocked_format_error_message.return_value, - ) + assert result == (False, "", "") def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): @@ -103,6 +86,10 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): fake_wait_for_inclusion = False fake_wait_for_finalization = False + mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "msg") + ) + # Call result = _do_transfer( subtensor, @@ -119,12 +106,11 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): call_function="transfer_allow_death", call_params={"dest": fake_dest, "value": fake_transfer_balance.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None, ) - assert result == (True, "", "Success, extrinsic submitted without waiting.") + assert result == (True, "", "msg") diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index 224fe4b640..2fdf0cbe47 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -43,13 +43,14 @@ def test_unstake_extrinsic(fake_wallet, mocker): }, ) fake_subtensor.sign_and_send_extrinsic.assert_called_once_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, sign_with="coldkey", nonce_key="coldkeypub", use_nonce=True, + period=None, ) @@ -109,11 +110,12 @@ def test_unstake_multiple_extrinsic(fake_wallet, mocker): }, ) fake_subtensor.sign_and_send_extrinsic.assert_called_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, sign_with="coldkey", nonce_key="coldkeypub", use_nonce=True, + period=None, ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index bb6bb5b49f..b61df6dfaa 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -1821,7 +1821,7 @@ async def test_sign_and_send_extrinsic_success_without_inclusion_finalization( wait_for_inclusion=False, wait_for_finalization=False, ) - assert result == (True, "") + assert result == (True, "Not waiting for finalization or inclusion.") @pytest.mark.asyncio @@ -2518,6 +2518,7 @@ async def test_transfer_success(subtensor, fake_wallet, mocker): wait_for_inclusion=True, wait_for_finalization=False, keep_alive=True, + period=None, ) assert result == mocked_transfer_extrinsic.return_value @@ -2551,6 +2552,7 @@ async def test_register_success(subtensor, fake_wallet, mocker): wait_for_finalization=True, wait_for_inclusion=False, wallet=fake_wallet, + period=None, ) assert result == mocked_register_extrinsic.return_value @@ -2666,7 +2668,7 @@ async def test_set_delegate_take_decrease( @pytest.mark.asyncio async def test_set_weights_success(subtensor, fake_wallet, mocker): - """Tests set_weights with successful weight setting on the first try.""" + """Tests set_weights with the successful weight setting on the first try.""" # Preps fake_netuid = 1 fake_uids = [1, 2, 3] @@ -2715,7 +2717,7 @@ async def test_set_weights_success(subtensor, fake_wallet, mocker): wait_for_finalization=False, wait_for_inclusion=False, weights=fake_weights, - period=5, + period=8, ) mocked_weights_rate_limit.assert_called_once_with(fake_netuid) assert result is True @@ -2803,6 +2805,7 @@ async def test_root_set_weights_success(subtensor, fake_wallet, mocker): version_key=0, wait_for_finalization=True, wait_for_inclusion=True, + period=None, ) assert result == mocked_set_root_weights_extrinsic.return_value @@ -2853,6 +2856,7 @@ async def test_commit_weights_success(subtensor, fake_wallet, mocker): commit_hash="fake_commit_hash", wait_for_inclusion=False, wait_for_finalization=False, + period=16, ) assert result is True assert message == "Success" @@ -2955,6 +2959,7 @@ async def test_set_subnet_identity(mocker, subtensor, fake_wallet): additional=fake_subnet_identity.additional, wait_for_finalization=True, wait_for_inclusion=False, + period=None, ) assert result == mocked_extrinsic.return_value @@ -3060,6 +3065,7 @@ async def test_start_call(subtensor, mocker): netuid=netuid, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index ca6aad000d..17c858c8a5 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1203,7 +1203,7 @@ def test_set_weights(subtensor, mocker, fake_wallet): version_key=settings.version_as_int, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=5, + period=8, ) assert result == expected_result @@ -1234,6 +1234,7 @@ def test_serve_axon(subtensor, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, certificate=fake_certificate, + period=None, ) assert result == mocked_serve_axon_extrinsic.return_value @@ -1252,7 +1253,7 @@ def test_get_block_hash(subtensor, mocker): def test_commit(subtensor, fake_wallet, mocker): - """Test successful commit call.""" + """Test a successful commit call.""" # Preps fake_netuid = 1 fake_data = "some data to network" @@ -1268,6 +1269,7 @@ def test_commit(subtensor, fake_wallet, mocker): netuid=fake_netuid, data_type=f"Raw{len(fake_data)}", data=fake_data.encode(), + period=None, ) assert result is mocked_publish_metadata.return_value @@ -1325,6 +1327,7 @@ def test_transfer(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, keep_alive=True, + period=None, ) assert result == mocked_transfer_extrinsic.return_value @@ -1466,7 +1469,7 @@ def test_do_serve_axon_is_success( fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = True + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(True, "")) # Call result = do_serve_axon( @@ -1484,20 +1487,16 @@ def test_do_serve_axon_is_success( call_params=fake_call_params, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None, ) - # subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() assert result[0] is True - assert result[1] is None + assert result[1] is "" def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_params): @@ -1506,7 +1505,9 @@ def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_ fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = None + mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(False, None) + ) # Call result = do_serve_axon( @@ -1524,21 +1525,15 @@ def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_ call_params=fake_call_params, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None, ) - assert result == ( - False, - subtensor.substrate.submit_extrinsic.return_value.error_message, - ) + assert result == (False, None) def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params): @@ -1547,6 +1542,11 @@ def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params fake_wait_for_inclusion = False fake_wait_for_finalization = False + mocked_sign_and_send_extrinsic = mocker.Mock(return_value=(True, "")) + mocker.patch.object( + subtensor, "sign_and_send_extrinsic", new=mocked_sign_and_send_extrinsic + ) + # Call result = do_serve_axon( subtensor=subtensor, @@ -1563,17 +1563,14 @@ def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params call_params=fake_call_params, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + mocked_sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None, ) - assert result == (True, None) + assert result == (True, "") def test_immunity_period(subtensor, mocker): @@ -1963,6 +1960,7 @@ def test_commit_weights(subtensor, fake_wallet, mocker): commit_hash=mocked_generate_weight_hash.return_value, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=16, ) assert result == expected_result @@ -2002,6 +2000,7 @@ def test_reveal_weights(subtensor, fake_wallet, mocker): salt=salt, wait_for_inclusion=False, wait_for_finalization=False, + period=16, ) @@ -2832,6 +2831,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): safe_staking=False, allow_partial_stake=False, rate_tolerance=0.005, + period=None, ) assert result == mock_add_stake_extrinsic.return_value @@ -2871,6 +2871,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): safe_staking=True, allow_partial_stake=False, rate_tolerance=fake_rate_tolerance, + period=None, ) assert result == mock_add_stake_extrinsic.return_value @@ -2904,6 +2905,7 @@ def test_add_stake_multiple_success(mocker, fake_wallet, subtensor): amounts=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mock_add_stake_multiple_extrinsic.return_value @@ -2940,6 +2942,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): safe_staking=False, allow_partial_stake=False, rate_tolerance=0.005, + period=None, ) assert result == mock_unstake_extrinsic.return_value @@ -2976,6 +2979,7 @@ def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): safe_staking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, + period=None, ) assert result == mock_unstake_extrinsic.return_value @@ -3019,6 +3023,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): safe_staking=False, allow_partial_stake=False, rate_tolerance=0.005, + period=None, ) assert result == mock_swap_stake_extrinsic.return_value @@ -3063,6 +3068,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): safe_staking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, + period=None, ) assert result == mock_swap_stake_extrinsic.return_value @@ -3096,6 +3102,7 @@ def test_unstake_multiple_success(mocker, subtensor, fake_wallet): amounts=fake_amounts, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mock_unstake_multiple_extrinsic.return_value @@ -3144,6 +3151,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, block_time=12.0, + period=8, ) assert result == mocked_commit_reveal_v3_extrinsic.return_value @@ -3201,6 +3209,7 @@ def test_set_subnet_identity(mocker, subtensor, fake_wallet): additional=fake_subnet_identity.additional, wait_for_finalization=True, wait_for_inclusion=False, + period=None, ) assert result == mocked_extrinsic.return_value @@ -3390,6 +3399,7 @@ def test_start_call(subtensor, mocker): netuid=netuid, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index ec67747f97..45f65f24db 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -1291,59 +1291,6 @@ def test_root_register_is_already_registered( mock_substrate.submit_extrinsic.assert_not_called() -def test_root_set_weights(mock_substrate, subtensor, fake_wallet, mocker): - MIN_ALLOWED_WEIGHTS = 0 - MAX_WEIGHTS_LIMIT = 1 - - mock_substrate.query.return_value = 1 - mocker.patch.object( - subtensor, - "get_hyperparameter", - autospec=True, - side_effect=[ - MIN_ALLOWED_WEIGHTS, - MAX_WEIGHTS_LIMIT, - ], - ) - - subtensor.root_set_weights( - fake_wallet, - netuids=[1, 2], - weights=[0.5, 0.5], - ) - - subtensor.get_hyperparameter.assert_has_calls( - [ - mocker.call("MinAllowedWeights", netuid=0), - mocker.call("MaxWeightsLimit", netuid=0), - ] - ) - mock_substrate.query.assert_called_once_with( - "SubtensorModule", - "Uids", - [0, fake_wallet.hotkey.ss58_address], - ) - - assert_submit_signed_extrinsic( - mock_substrate, - fake_wallet.coldkey, - call_module="SubtensorModule", - call_function="set_root_weights", - call_params={ - "dests": [1, 2], - "weights": [65535, 65535], - "netuid": 0, - "version_key": 0, - "hotkey": fake_wallet.hotkey.ss58_address, - }, - era={ - "period": 5, - }, - nonce=mock_substrate.get_account_next_index.return_value, - wait_for_finalization=False, - ) - - def test_root_set_weights_no_uid(mock_substrate, subtensor, fake_wallet, mocker): mock_substrate.query.return_value = None