From de4287308df200c17e68d50be7675288c81ab94d Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:12:41 -0800 Subject: [PATCH 01/17] add sync extrinsic --- bittensor/core/extrinsics/registration.py | 75 +++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 8505eab15b..7c3f7f8ca8 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -424,3 +424,78 @@ def register_extrinsic( # Failed to register after max attempts. logging.error("[red]No more attempts.[/red]") return False + + +def set_subnet_identity_extrinsic( + subtensor: "Subtensor", + wallet: "Wallet", + netuid: int, + subnet_name: str, + github_repo: str, + subnet_contact: str, + subnet_url: str, + discord: str, + description: str, + additional: str, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, +) -> tuple[bool, str]: + """ + Set the identity information for a given subnet. + + Arguments: + subtensor (Subtensor): An instance of the Subtensor class to interact with the blockchain. + wallet (Wallet): A wallet instance used to sign and submit the extrinsic. + netuid (int): The unique ID for the subnet. + subnet_name (str): The name of the subnet to assign the identity information. + github_repo (str): URL of the GitHub repository related to the subnet. + subnet_contact (str): Subnet's contact information, e.g., email or contact link. + subnet_url (str): The URL of the subnet's primary web portal. + discord (str): Discord server or contact for the subnet. + description (str): A textual description of the subnet. + 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). + + Returns: + tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second + element contains a descriptive message. + """ + + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False, unlock.message + + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if success: + logging.success( + f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" + ) + return True, f"Identities for subnet {netuid} are set." + else: + logging.error( + f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" + ) + return False, f"Failed to set identity for subnet {netuid}: {message}" From e0b8f368adccb99a0d301156c130965fc540e894 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:13:06 -0800 Subject: [PATCH 02/17] add sync subtensor extrinsic method --- bittensor/core/subtensor.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 690af504b7..62f3836536 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -21,6 +21,7 @@ StakeInfo, SubnetHyperparameters, WeightCommitInfo, + SubnetIdentity, SubnetInfo, decode_account_id, ) @@ -60,6 +61,7 @@ unstake_extrinsic, unstake_multiple_extrinsic, ) +from bittensor.core.extrinsics.registration import set_subnet_identity_extrinsic from bittensor.core.metagraph import Metagraph from bittensor.core.settings import ( version_as_int, @@ -2526,6 +2528,44 @@ def root_set_weights( wait_for_inclusion=wait_for_inclusion, ) + def set_subnet_identity( + self, + wallet: "Wallet", + netuid: int, + subnet_identity: SubnetIdentity, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, + ) -> tuple[bool, str]: + """ + Sets the identity of a subnet for a specific wallet and network. + + Arguments: + wallet (Wallet): The wallet instance that will authorize the transaction. + netuid (int): The unique ID of the network on which the operation takes place. + subnet_identity (SubnetIdentity): The identity data of the subnet including attributes like name, GitHub + 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. + + 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. + """ + return set_subnet_identity_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_identity.subnet_name, + github_repo=subnet_identity.github_repo, + subnet_contact=subnet_identity.subnet_contact, + subnet_url=subnet_identity.subnet_url, + discord=subnet_identity.discord, + description=subnet_identity.description, + additional=subnet_identity.additional, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + def set_weights( self, wallet: "Wallet", From fc62baf673845a6a669d77a22b04fa8489020c78 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:13:32 -0800 Subject: [PATCH 03/17] update easy imports (add chain_data classes) --- bittensor/utils/easy_imports.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index e3f2bf0ef0..985aee1ed8 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -32,16 +32,27 @@ from bittensor.core.axon import Axon from bittensor.core.chain_data import ( # noqa: F401 AxonInfo, + ChainIdentity, + DelegateInfo, + DelegateInfoLite, + DynamicInfo, + IPInfo, + MetagraphInfo, + MetagraphInfoEmissions, + MetagraphInfoParams, + MetagraphInfoPool, NeuronInfo, NeuronInfoLite, PrometheusInfo, - DelegateInfo, - StakeInfo, - SubnetInfo, - SubnetHyperparameters, - IPInfo, ProposalCallData, ProposalVoteData, + ScheduledColdkeySwapInfo, + StakeInfo, + SubnetHyperparameters, + SubnetIdentity, + SubnetInfo, + SubnetState, + WeightCommitInfo, ) from bittensor.core.config import ( # noqa: F401 InvalidConfigFile, From 58c2bc7617a1b22daeba85fb7138d3ef2435d993 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:17:59 -0800 Subject: [PATCH 04/17] fix bugs with using `format_error_message` --- bittensor/core/extrinsics/asyncex/registration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 599f13ab5c..b12d626866 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -433,7 +433,7 @@ async def register_subnet_extrinsic( await response.process_events() if not await response.is_success: logging.error( - f"Failed to register subnet: {format_error_message(await response.error_message, subtensor.substrate)}" + f"Failed to register subnet: {format_error_message(await response.error_message)}" ) return False From 9f907b6a3b198ce573565d955eea2b571a3663c0 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:19:39 -0800 Subject: [PATCH 05/17] remove redundant call --- .../core/extrinsics/asyncex/registration.py | 76 ++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index b12d626866..9327bac447 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -430,7 +430,6 @@ async def register_subnet_extrinsic( if not wait_for_finalization and not wait_for_inclusion: return True - await response.process_events() if not await response.is_success: logging.error( f"Failed to register subnet: {format_error_message(await response.error_message)}" @@ -441,3 +440,78 @@ async def register_subnet_extrinsic( ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) return True + + +async def set_subnet_identity_extrinsic( + subtensor: "AsyncSubtensor", + wallet: "Wallet", + netuid: int, + subnet_name: str, + github_repo: str, + subnet_contact: str, + subnet_url: str, + discord: str, + description: str, + additional: str, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, +) -> tuple[bool, str]: + """ + Set the identity information for a given subnet. + + Arguments: + subtensor (Subtensor): An instance of the Subtensor class to interact with the blockchain. + wallet (Wallet): A wallet instance used to sign and submit the extrinsic. + netuid (int): The unique ID for the subnet. + subnet_name (str): The name of the subnet to assign the identity information. + github_repo (str): URL of the GitHub repository related to the subnet. + subnet_contact (str): Subnet's contact information, e.g., email or contact link. + subnet_url (str): The URL of the subnet's primary web portal. + discord (str): Discord server or contact for the subnet. + description (str): A textual description of the subnet. + 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). + + Returns: + tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second + element contains a descriptive message. + """ + + if not (unlock := unlock_key(wallet)).success: + logging.error(unlock.message) + return False, unlock.message + + call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + + success, message = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if success: + logging.success( + f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" + ) + return True, f"Identities for subnet {netuid} are set." + else: + logging.error( + f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" + ) + return False, f"Failed to set identity for subnet {netuid}: {message}" From 710503822de54c80b67c505cce78c064ff2e18a8 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:27:35 -0800 Subject: [PATCH 06/17] update sync extrinsic --- bittensor/core/extrinsics/registration.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index 7c3f7f8ca8..c1feea3a30 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -489,6 +489,9 @@ def set_subnet_identity_extrinsic( wait_for_finalization=wait_for_finalization, ) + if not wait_for_finalization and not wait_for_inclusion: + return True, f"Identities for subnet {netuid} are sent to the chain." + if success: logging.success( f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" From 6ee34ddeaa8919d42bf45ea6329e1e678117b31b Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:27:42 -0800 Subject: [PATCH 07/17] add async extrinsic --- bittensor/core/extrinsics/asyncex/registration.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 9327bac447..9cab3cd11a 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -498,20 +498,24 @@ async def set_subnet_identity_extrinsic( }, ) - success, message = await subtensor.sign_and_send_extrinsic( + response = await subtensor.substrate.submit_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) - if success: + if not wait_for_finalization and not wait_for_inclusion: + return True, f"Identities for subnet {netuid} are sent to the chain." + + if await response.is_success: logging.success( f":white_heavy_check_mark: [green]Identities for subnet[/green] [blue]{netuid}[/blue] [green]are set.[/green]" ) return True, f"Identities for subnet {netuid} are set." else: + error_message = await response.error_message logging.error( - f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" + f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {error_message}" ) - return False, f"Failed to set identity for subnet {netuid}: {message}" + return False, f"Failed to set identity for subnet {netuid}: {error_message}" From bc2a97d4bd7acb7b4b46df506e909076c144a06a Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:28:41 -0800 Subject: [PATCH 08/17] fix docsting's complaince in IDE --- bittensor/core/async_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 926c593ac0..24c3ee8fab 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2581,8 +2581,8 @@ async def wait_for_block(self, block: Optional[int] = None): 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() # Waits for next block + await subtensor.wait_for_block(block=1234) # Waits for specific block """ async def handler(block_data: dict): From 63a9a0f1114fcc7ce666f67a3bc29264232071d1 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:30:09 -0800 Subject: [PATCH 09/17] update import --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 62f3836536..f8ea2a7c44 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -41,6 +41,7 @@ burned_register_extrinsic, register_extrinsic, register_subnet_extrinsic, + set_subnet_identity_extrinsic ) from bittensor.core.extrinsics.root import ( root_register_extrinsic, @@ -61,7 +62,6 @@ unstake_extrinsic, unstake_multiple_extrinsic, ) -from bittensor.core.extrinsics.registration import set_subnet_identity_extrinsic from bittensor.core.metagraph import Metagraph from bittensor.core.settings import ( version_as_int, From 386b19cc946a55822e34450c3b9d3cf897419edd Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:30:59 -0800 Subject: [PATCH 10/17] update docstring --- bittensor/core/extrinsics/asyncex/registration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 9cab3cd11a..070b55e904 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -460,7 +460,7 @@ async def set_subnet_identity_extrinsic( Set the identity information for a given subnet. Arguments: - subtensor (Subtensor): An instance of the Subtensor class to interact with the blockchain. + subtensor (AsyncSubtensor): An instance of the Subtensor class to interact with the blockchain. wallet (Wallet): A wallet instance used to sign and submit the extrinsic. netuid (int): The unique ID for the subnet. subnet_name (str): The name of the subnet to assign the identity information. From 19f53f006ad5613a84557b2c04d684bb2be1120e Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:31:15 -0800 Subject: [PATCH 11/17] ruff --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f8ea2a7c44..15ce88b691 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -41,7 +41,7 @@ burned_register_extrinsic, register_extrinsic, register_subnet_extrinsic, - set_subnet_identity_extrinsic + set_subnet_identity_extrinsic, ) from bittensor.core.extrinsics.root import ( root_register_extrinsic, From 50db030baaa60c11ff6d6f1049b27e0dca4dcbdf Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:32:11 -0800 Subject: [PATCH 12/17] add async subtensor extrinsic method --- bittensor/core/async_subtensor.py | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 24c3ee8fab..e4c4a1c547 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -21,6 +21,7 @@ NeuronInfo, ProposalVoteData, SubnetHyperparameters, + SubnetIdentity, SubnetInfo, WeightCommitInfo, decode_account_id, @@ -34,6 +35,7 @@ burned_register_extrinsic, register_extrinsic, register_subnet_extrinsic, + set_subnet_identity_extrinsic, ) from bittensor.core.extrinsics.asyncex.move_stake import ( transfer_stake_extrinsic, @@ -3188,6 +3190,44 @@ async def root_set_weights( wait_for_inclusion=wait_for_inclusion, ) + async def set_subnet_identity( + self, + wallet: "Wallet", + netuid: int, + subnet_identity: SubnetIdentity, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = True, + ) -> tuple[bool, str]: + """ + Sets the identity of a subnet for a specific wallet and network. + + Arguments: + wallet (Wallet): The wallet instance that will authorize the transaction. + netuid (int): The unique ID of the network on which the operation takes place. + subnet_identity (SubnetIdentity): The identity data of the subnet including attributes like name, GitHub + 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. + + 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. + """ + return await set_subnet_identity_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_identity.subnet_name, + github_repo=subnet_identity.github_repo, + subnet_contact=subnet_identity.subnet_contact, + subnet_url=subnet_identity.subnet_url, + discord=subnet_identity.discord, + description=subnet_identity.description, + additional=subnet_identity.additional, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + async def set_weights( self, wallet: "Wallet", From 2b4714a0c2469ac2c3f5185ca5c9657fa266115c Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:39:38 -0800 Subject: [PATCH 13/17] add sync subtensor extrinsic unit test --- tests/unit_tests/test_subtensor.py | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 43414ca534..385bb45501 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3050,3 +3050,37 @@ def test_connection_limit(mocker): with pytest.raises(websockets.InvalidStatus): for i in range(2): Subtensor("test") + + +def test_set_subnet_identity(mocker, subtensor): + """Verify that subtensor method `set_subnet_identity` calls proper function with proper arguments.""" + # Preps + fake_wallet = mocker.Mock() + fake_netuid = 123 + fake_subnet_identity = mocker.MagicMock() + + mocked_extrinsic = mocker.patch.object( + subtensor_module, "set_subnet_identity_extrinsic" + ) + + # Call + result = subtensor.set_subnet_identity( + wallet=fake_wallet, netuid=fake_netuid, subnet_identity=fake_subnet_identity + ) + + # Asserts + mocked_extrinsic.assert_called_once_with( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + subnet_name=fake_subnet_identity.subnet_name, + github_repo=fake_subnet_identity.github_repo, + subnet_contact=fake_subnet_identity.subnet_contact, + subnet_url=fake_subnet_identity.subnet_url, + discord=fake_subnet_identity.discord, + description=fake_subnet_identity.description, + additional=fake_subnet_identity.additional, + wait_for_finalization=True, + wait_for_inclusion=False, + ) + assert result == mocked_extrinsic.return_value From 3aa0d78c57676d45bbb5121659431fa60bea04fb Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 17:41:41 -0800 Subject: [PATCH 14/17] add async subtensor extrinsic unit test --- tests/unit_tests/test_async_subtensor.py | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 6e88196906..10d1ba5e66 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2637,3 +2637,38 @@ async def test_get_all_subnets_info_success(mocker, subtensor): async_subtensor.SubnetInfo.list_from_dicts.assert_called_once_with( subtensor.query_runtime_api.return_value, ) + + +@pytest.mark.asyncio +async def test_set_subnet_identity(mocker, subtensor): + """Verify that subtensor method `set_subnet_identity` calls proper function with proper arguments.""" + # Preps + fake_wallet = mocker.Mock() + fake_netuid = 123 + fake_subnet_identity = mocker.MagicMock() + + mocked_extrinsic = mocker.patch.object( + async_subtensor, "set_subnet_identity_extrinsic" + ) + + # Call + result = await subtensor.set_subnet_identity( + wallet=fake_wallet, netuid=fake_netuid, subnet_identity=fake_subnet_identity + ) + + # Asserts + mocked_extrinsic.assert_awaited_once_with( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + subnet_name=fake_subnet_identity.subnet_name, + github_repo=fake_subnet_identity.github_repo, + subnet_contact=fake_subnet_identity.subnet_contact, + subnet_url=fake_subnet_identity.subnet_url, + discord=fake_subnet_identity.discord, + description=fake_subnet_identity.description, + additional=fake_subnet_identity.additional, + wait_for_finalization=True, + wait_for_inclusion=False, + ) + assert result == mocked_extrinsic.return_value From 70b653d144cd29560be5e08f0ad7c4f5fcf45cf2 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 18:20:43 -0800 Subject: [PATCH 15/17] add sync extrinsic unit test --- .../extrinsics/test_registration.py | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/tests/unit_tests/extrinsics/test_registration.py b/tests/unit_tests/extrinsics/test_registration.py index 18676619de..efe66714bf 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -222,3 +222,124 @@ def test_burned_register_extrinsic( ) # Assert assert result == expected_result, f"Test failed for test_id: {test_id}" + + +def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns the correct result.""" + # Preps + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + + mocked_compose_call = mocker.patch.object(mock_subtensor.substrate, "compose_call") + mocked_sign_and_send_extrinsic = mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "Success") + ) + + # Call + result = registration.set_subnet_identity_extrinsic( + subtensor=mock_subtensor, + wallet=mock_wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + ) + + # Asserts + mocked_compose_call.assert_called_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": mock_wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": "mock_subnet_name", + "github_repo": "mock_github_repo", + "subnet_contact": "mock_subnet_contact", + "subnet_url": "mock_subnet_url", + "discord": "mock_discord", + "description": "mock_description", + "additional": "mock_additional", + }, + ) + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, + wallet=mock_wallet, + wait_for_inclusion=False, + wait_for_finalization=True, + ) + + assert result == (True, "Identities for subnet 123 are set.") + + +def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns False with bad result.""" + # Preps + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + + fake_error_message = "error message" + + mocked_compose_call = mocker.patch.object(mock_subtensor.substrate, "compose_call") + mocked_sign_and_send_extrinsic = mocker.patch.object( + mock_subtensor, + "sign_and_send_extrinsic", + return_value=(False, fake_error_message), + ) + + # Call + result = registration.set_subnet_identity_extrinsic( + subtensor=mock_subtensor, + wallet=mock_wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + ) + + # Asserts + mocked_compose_call.assert_called_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": mock_wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": "mock_subnet_name", + "github_repo": "mock_github_repo", + "subnet_contact": "mock_subnet_contact", + "subnet_url": "mock_subnet_url", + "discord": "mock_discord", + "description": "mock_description", + "additional": "mock_additional", + }, + ) + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, + wallet=mock_wallet, + wait_for_inclusion=False, + wait_for_finalization=True, + ) + + assert result == ( + False, + f"Failed to set identity for subnet {netuid}: {fake_error_message}", + ) From 8086324dad803775e74be60af4fff42ac2cc8e7a Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 19:09:27 -0800 Subject: [PATCH 16/17] add async extrinsic unit test --- .../extrinsics/asyncex/test_registration.py | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index 82d9e6b561..5013469d1b 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -447,3 +447,134 @@ async def is_stale_side_effect(*_, **__): wait_for_finalization=True, ) assert result is False + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_is_success(subtensor, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns the correct result.""" + # Preps + wallet = mocker.MagicMock(autospec=Wallet) + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + + mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") + + fake_response = mocker.Mock() + fake_response.is_success = mocker.AsyncMock(return_value=True)() + mocked_submit_extrinsic = mocker.patch.object( + subtensor.substrate, "submit_extrinsic", return_value=fake_response + ) + + # Call + result = await async_registration.set_subnet_identity_extrinsic( + subtensor=subtensor, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + ) + + # Asserts + mocked_compose_call.assert_awaited_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + mocked_submit_extrinsic.assert_awaited_once_with( + call=mocked_compose_call.return_value, + wallet=wallet, + wait_for_inclusion=False, + wait_for_finalization=True, + ) + + assert result == (True, "Identities for subnet 123 are set.") + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_is_failed(subtensor, mocker): + """Verify that set_subnet_identity_extrinsic calls the correct functions and returns False with bad result.""" + # Preps + wallet = mocker.MagicMock(autospec=Wallet) + netuid = 123 + subnet_name = "mock_subnet_name" + github_repo = "mock_github_repo" + subnet_contact = "mock_subnet_contact" + subnet_url = "mock_subnet_url" + discord = "mock_discord" + description = "mock_description" + additional = "mock_additional" + fake_error_message = "error message" + + mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") + + fake_response = mocker.Mock() + fake_response.is_success = mocker.AsyncMock(return_value=False)() + fake_response.error_message = mocker.AsyncMock(return_value=fake_error_message)() + mocked_submit_extrinsic = mocker.patch.object( + subtensor.substrate, "submit_extrinsic", return_value=fake_response + ) + + # Call + result = await async_registration.set_subnet_identity_extrinsic( + subtensor=subtensor, + wallet=wallet, + netuid=netuid, + subnet_name=subnet_name, + github_repo=github_repo, + subnet_contact=subnet_contact, + subnet_url=subnet_url, + discord=discord, + description=description, + additional=additional, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # Asserts + mocked_compose_call.assert_awaited_once_with( + call_module="SubtensorModule", + call_function="set_subnet_identity", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + "subnet_name": subnet_name, + "github_repo": github_repo, + "subnet_contact": subnet_contact, + "subnet_url": subnet_url, + "discord": discord, + "description": description, + "additional": additional, + }, + ) + mocked_submit_extrinsic.assert_awaited_once_with( + call=mocked_compose_call.return_value, + wallet=wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert result == ( + False, + f"Failed to set identity for subnet {netuid}: {fake_error_message}", + ) From 1fab7b0215c8cd9b8ac22fd9039b5ffdd7272566 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 11 Feb 2025 20:03:24 -0800 Subject: [PATCH 17/17] add e2e test (happy pass, failed) --- .../test_set_subnet_identity_extrinsic.py | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tests/e2e_tests/test_set_subnet_identity_extrinsic.py diff --git a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py new file mode 100644 index 0000000000..e60cc69db3 --- /dev/null +++ b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py @@ -0,0 +1,104 @@ +import pytest + +from bittensor.core.chain_data import SubnetIdentity +from bittensor.utils.btlogging import logging + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_happy_pass(subtensor, alice_wallet): + logging.console.info( + "[magenta]Testing `set_subnet_identity_extrinsic` with success result.[/magenta]" + ) + + netuid = 2 + + # Register a subnet, netuid 2 + assert subtensor.register_subnet(alice_wallet), "Subnet wasn't created" + + # Verify subnet created successfully + assert subtensor.subnet_exists(netuid), "Subnet wasn't created successfully" + + # make sure subnet_identity is empty + assert ( + subtensor.subnet(netuid).subnet_identity is None + ), "Subnet identity should be None before set" + + # prepare SubnetIdentity for subnet + subnet_identity = SubnetIdentity( + subnet_name="e2e test subnet", + github_repo="e2e test repo", + subnet_contact="e2e test contact", + subnet_url="e2e test url", + discord="e2e test discord", + description="e2e test description", + additional="e2e test additional", + ) + + # set SubnetIdentity to subnet + assert ( + subtensor.set_subnet_identity( + wallet=alice_wallet, + netuid=netuid, + subnet_identity=subnet_identity, + )[0] + is True + ), "Set subnet identity failed" + + # check SubnetIdentity of the subnet + assert subtensor.subnet(netuid).subnet_identity == subnet_identity + + +@pytest.mark.asyncio +async def test_set_subnet_identity_extrinsic_failed( + subtensor, alice_wallet, bob_wallet +): + """ + Test case for verifying the behavior of the `set_subnet_identity_extrinsic` function in the + scenario where the result of the function is expected to fail. It ensures proper handling + and validation when attempting to set the subnet identity under specific conditions. + + Args: + subtensor: The instance of the subtensor class under test. + alice_wallet: A mock or test wallet associated with Alice, used for creating a subnet. + bob_wallet: A mock or test wallet associated with Bob, used for setting the subnet identity. + + Decorators: + @pytest.mark.asyncio: Marks this test as an asynchronous test. + """ + logging.console.info( + "[magenta]Testing `set_subnet_identity_extrinsic` with failed result.[/magenta]" + ) + + netuid = 2 + + # Register a subnet, netuid 2 + assert subtensor.register_subnet(alice_wallet), "Subnet wasn't created" + + # Verify subnet created successfully + assert subtensor.subnet_exists(netuid), "Subnet wasn't created successfully" + + # make sure subnet_identity is empty + assert ( + subtensor.subnet(netuid).subnet_identity is None + ), "Subnet identity should be None before set" + + # prepare SubnetIdentity for subnet + subnet_identity = SubnetIdentity( + subnet_name="e2e test subnet", + github_repo="e2e test repo", + subnet_contact="e2e test contact", + subnet_url="e2e test url", + discord="e2e test discord", + description="e2e test description", + additional="e2e test additional", + ) + + # set SubnetIdentity to subnet + assert ( + subtensor.set_subnet_identity( + wallet=bob_wallet, + netuid=netuid, + subnet_identity=subnet_identity, + )[0] + is False + ), "Set subnet identity failed"