diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f732502c18..16a40a6608 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -44,6 +44,10 @@ swap_stake_extrinsic, move_stake_extrinsic, ) +from bittensor.core.extrinsics.asyncex.children import ( + root_set_pending_childkey_cooldown_extrinsic, + set_children_extrinsic, +) from bittensor.core.extrinsics.asyncex.registration import ( burned_register_extrinsic, register_extrinsic, @@ -84,13 +88,11 @@ from bittensor.utils import ( Certificate, decode_hex_identity_dict, - float_to_u64, format_error_message, is_valid_ss58_address, torch, u16_normalized_float, u64_normalized_float, - unlock_key, ) from bittensor.utils.balance import ( Balance, @@ -3878,6 +3880,41 @@ async def reveal_weights( return success, message + async def root_set_pending_childkey_cooldown( + self, + wallet: "Wallet", + cooldown: int, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + period: Optional[int] = None, + ) -> tuple[bool, str]: + """Sets the pending childkey cooldown. + + Arguments: + wallet: bittensor wallet instance. + cooldown: the number of blocks to setting pending childkey cooldown. + 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: + 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. + + Note: This operation can only be successfully performed if your wallet has root privileges. + """ + return await root_set_pending_childkey_cooldown_extrinsic( + subtensor=self, + wallet=wallet, + cooldown=cooldown, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + ) + # TODO: remove `block_hash` argument async def root_register( self, @@ -3998,33 +4035,14 @@ async def set_children( 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 = await 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 await self.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + return await set_children_extrinsic( + subtensor=self, + wallet=wallet, + hotkey=hotkey, + netuid=netuid, + children=children, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, raise_error=raise_error, period=period, ) diff --git a/bittensor/core/extrinsics/asyncex/children.py b/bittensor/core/extrinsics/asyncex/children.py new file mode 100644 index 0000000000..46853642fe --- /dev/null +++ b/bittensor/core/extrinsics/asyncex/children.py @@ -0,0 +1,139 @@ +from typing import TYPE_CHECKING, Optional +from bittensor.utils import float_to_u64, unlock_key + +if TYPE_CHECKING: + from bittensor_wallet import Wallet + from bittensor.core.async_subtensor import AsyncSubtensor + + +async def set_children_extrinsic( + subtensor: "AsyncSubtensor", + wallet: "Wallet", + hotkey: str, + netuid: int, + children: list[tuple[float, str]], + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + raise_error: bool = False, + period: Optional[int] = None, +): + """ + Allows a coldkey to set children-keys. + + Arguments: + subtensor: bittensor subtensor. + wallet: bittensor wallet instance. + hotkey: The ``SS58`` address of the neuron's hotkey. + netuid: The netuid value. + children: A list of children with their proportions. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + 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]: 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 + + async with subtensor.substrate as substrate: + call = await 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, + }, + ) + + success, message = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + raise_error=raise_error, + period=period, + ) + + if not wait_for_finalization and not wait_for_inclusion: + return True, message + + if success: + return True, "Success with `set_children_extrinsic` response." + + return True, message + + +async def root_set_pending_childkey_cooldown_extrinsic( + subtensor: "AsyncSubtensor", + wallet: "Wallet", + cooldown: int, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + period: Optional[int] = None, +) -> tuple[bool, str]: + """ + Allows a coldkey to set children-keys. + """ + unlock = unlock_key(wallet) + + if not unlock.success: + return False, unlock.message + + async with subtensor.substrate as substrate: + call = await substrate.compose_call( + call_module="SubtensorModule", + call_function="set_pending_childkey_cooldown", + call_params={"cooldown": cooldown}, + ) + + sudo_call = await substrate.compose_call( + call_module="Sudo", + call_function="sudo", + call_params={"call": call}, + ) + + success, message = await subtensor.sign_and_send_extrinsic( + call=sudo_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, message + + if success: + return ( + True, + "Success with `root_set_pending_childkey_cooldown_extrinsic` response.", + ) + + return True, message diff --git a/bittensor/core/extrinsics/children.py b/bittensor/core/extrinsics/children.py new file mode 100644 index 0000000000..dd91fbe97b --- /dev/null +++ b/bittensor/core/extrinsics/children.py @@ -0,0 +1,137 @@ +from typing import TYPE_CHECKING, Optional +from bittensor.utils import float_to_u64, unlock_key + +if TYPE_CHECKING: + from bittensor_wallet import Wallet + from bittensor.core.subtensor import Subtensor + + +def set_children_extrinsic( + subtensor: "Subtensor", + wallet: "Wallet", + hotkey: str, + netuid: int, + children: list[tuple[float, str]], + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + raise_error: bool = False, + period: Optional[int] = None, +): + """ + Allows a coldkey to set children-keys. + + Arguments: + subtensor: bittensor subtensor. + wallet: bittensor wallet instance. + hotkey: The ``SS58`` address of the neuron's hotkey. + netuid: The netuid value. + children: A list of children with their proportions. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + 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]: 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 = subtensor.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, + }, + ) + + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + raise_error=raise_error, + period=period, + ) + + if not wait_for_finalization and not wait_for_inclusion: + return True, message + + if success: + return True, "Success with `set_children_extrinsic` response." + + return True, message + + +def root_set_pending_childkey_cooldown_extrinsic( + subtensor: "Subtensor", + wallet: "Wallet", + cooldown: int, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + period: Optional[int] = None, +) -> tuple[bool, str]: + """ + Allows a coldkey to set children-keys. + """ + unlock = unlock_key(wallet) + + if not unlock.success: + return False, unlock.message + + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_pending_childkey_cooldown", + call_params={"cooldown": cooldown}, + ) + + sudo_call = subtensor.substrate.compose_call( + call_module="Sudo", + call_function="sudo", + call_params={"call": call}, + ) + + success, message = subtensor.sign_and_send_extrinsic( + call=sudo_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, message + + if success: + return ( + True, + "Success with `root_set_pending_childkey_cooldown_extrinsic` response.", + ) + + return True, message diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index b87f1f6de0..788130f4b1 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -36,6 +36,10 @@ ) from bittensor.core.config import Config from bittensor.core.errors import ChainError +from bittensor.core.extrinsics.children import ( + set_children_extrinsic, + root_set_pending_childkey_cooldown_extrinsic, +) from bittensor.core.extrinsics.commit_reveal import commit_reveal_v3_extrinsic from bittensor.core.extrinsics.commit_weights import ( commit_weights_extrinsic, @@ -86,13 +90,11 @@ from bittensor.utils import ( Certificate, decode_hex_identity_dict, - float_to_u64, format_error_message, is_valid_ss58_address, torch, u16_normalized_float, u64_normalized_float, - unlock_key, ) from bittensor.utils.balance import ( Balance, @@ -3150,6 +3152,41 @@ def root_register( period=period, ) + def root_set_pending_childkey_cooldown( + self, + wallet: "Wallet", + cooldown: int, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + period: Optional[int] = None, + ) -> tuple[bool, str]: + """Sets the pending childkey cooldown. + + Arguments: + wallet: bittensor wallet instance. + cooldown: the number of blocks to setting pending childkey cooldown. + 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: + 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. + + Note: This operation can only be successfully performed if your wallet has root privileges. + """ + return root_set_pending_childkey_cooldown_extrinsic( + subtensor=self, + wallet=wallet, + cooldown=cooldown, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + ) + def root_set_weights( self, wallet: "Wallet", @@ -3207,61 +3244,30 @@ def set_children( 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. + wallet: bittensor wallet instance. + hotkey: The ``SS58`` address of the neuron's hotkey. + netuid: The netuid value. + children: A list of children with their proportions. + wait_for_inclusion: Waits for the transaction to be included in a block. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. 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 + 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]: 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. + 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, + return set_children_extrinsic( + subtensor=self, + wallet=wallet, + hotkey=hotkey, + netuid=netuid, + children=children, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, raise_error=raise_error, period=period, ) diff --git a/bittensor/core/subtensor_api/extrinsics.py b/bittensor/core/subtensor_api/extrinsics.py index 0ff4439201..0096fe8c8c 100644 --- a/bittensor/core/subtensor_api/extrinsics.py +++ b/bittensor/core/subtensor_api/extrinsics.py @@ -17,6 +17,9 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.reveal_weights = subtensor.reveal_weights self.root_register = subtensor.root_register self.root_set_weights = subtensor.root_set_weights + self.root_set_pending_childkey_cooldown = ( + subtensor.root_set_pending_childkey_cooldown + ) self.set_children = subtensor.set_children self.set_subnet_identity = subtensor.set_subnet_identity self.set_weights = subtensor.set_weights diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py index 0ddd28bcc7..f0f2ded013 100644 --- a/bittensor/core/subtensor_api/utils.py +++ b/bittensor/core/subtensor_api/utils.py @@ -133,6 +133,9 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.register_subnet = subtensor._subtensor.register_subnet subtensor.reveal_weights = subtensor._subtensor.reveal_weights subtensor.root_register = subtensor._subtensor.root_register + subtensor.root_set_pending_childkey_cooldown = ( + subtensor._subtensor.root_set_pending_childkey_cooldown + ) subtensor.root_set_weights = subtensor._subtensor.root_set_weights subtensor.serve_axon = subtensor._subtensor.serve_axon subtensor.set_children = subtensor._subtensor.set_children diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index 84f612d6a2..5831e4bf27 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -17,6 +17,7 @@ SET_CHILDREN_RATE_LIMIT = 15 +ROOT_COOLDOWN = 15 # blocks def test_hotkeys(subtensor, alice_wallet, dave_wallet): @@ -77,6 +78,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w """ Tests: - Get default children (empty list) + - Call `root_set_pending_childkey_cooldown` extrinsic. - Update children list - Checking pending children - Checking cooldown period @@ -87,6 +89,16 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w dave_subnet_netuid = subtensor.get_total_subnets() # 2 set_tempo = 10 # affect to non-fast-blocks mode + # Set cooldown + success, message = subtensor.extrinsics.root_set_pending_childkey_cooldown( + wallet=alice_wallet, cooldown=ROOT_COOLDOWN + ) + assert success, f"Call `root_set_pending_childkey_cooldown` failed: {message}" + assert ( + message + == "Success with `root_set_pending_childkey_cooldown_extrinsic` response." + ) + assert subtensor.register_subnet(dave_wallet, True, True) assert subtensor.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." @@ -241,7 +253,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w raise_error=True, ) - assert error == "" + assert error == "Success with `set_children_extrinsic` response." assert success is True set_children_block = subtensor.get_current_block() diff --git a/tests/unit_tests/extrinsics/asyncex/test_children.py b/tests/unit_tests/extrinsics/asyncex/test_children.py new file mode 100644 index 0000000000..da107dfad4 --- /dev/null +++ b/tests/unit_tests/extrinsics/asyncex/test_children.py @@ -0,0 +1,94 @@ +import pytest + +from bittensor.core.extrinsics.asyncex import children + + +@pytest.mark.asyncio +async def test_set_children_extrinsic(subtensor, mocker, fake_wallet): + """Test that set_children_extrinsic correctly constructs and submits the extrinsic.""" + # Preps + hotkey = "fake hotkey" + netuid = 123 + fake_children = [ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ] + + substrate = subtensor.substrate.__aenter__.return_value + substrate.compose_call = mocker.AsyncMock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") + ) + + # Call + success, message = await children.set_children_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + hotkey=hotkey, + netuid=netuid, + children=fake_children, + ) + + # Asserts + substrate.compose_call.assert_awaited_once_with( + call_module="SubtensorModule", + call_function="set_children", + call_params={ + "children": [ + ( + 18446744073709551615, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ], + "hotkey": "fake hotkey", + "netuid": netuid, + }, + ) + + mocked_sign_and_send_extrinsic.assert_awaited_once_with( + call=substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=False, + period=None, + raise_error=False, + ) + + assert success is True + assert "Success" in message + + +@pytest.mark.asyncio +async def test_root_set_pending_childkey_cooldown_extrinsic( + subtensor, mocker, fake_wallet +): + """Verify root_set_pending_childkey_cooldown_extrinsic extrinsic.""" + # Preps + cooldown = 100 + + substrate = subtensor.substrate.__aenter__.return_value + substrate.compose_call = mocker.AsyncMock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") + ) + + # Call + success, message = await children.root_set_pending_childkey_cooldown_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + cooldown=cooldown, + ) + # Asserts + + substrate.compose_call.call_count == 2 + mocked_sign_and_send_extrinsic.assert_awaited_once_with( + call=substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=False, + period=None, + ) + assert success is True + assert "Success" in message diff --git a/tests/unit_tests/extrinsics/test_children.py b/tests/unit_tests/extrinsics/test_children.py new file mode 100644 index 0000000000..2af3517fff --- /dev/null +++ b/tests/unit_tests/extrinsics/test_children.py @@ -0,0 +1,86 @@ +from bittensor.core.extrinsics import children + + +def test_set_children_extrinsic(subtensor, mocker, fake_wallet): + """Test that set_children_extrinsic correctly constructs and submits the extrinsic.""" + # Preps + hotkey = "fake hotkey" + netuid = 123 + fake_children = [ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ] + + subtensor.substrate.compose_call = mocker.Mock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") + ) + + # Call + success, message = children.set_children_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + hotkey=hotkey, + netuid=netuid, + children=fake_children, + ) + + # Asserts + subtensor.substrate.compose_call.assert_called_once_with( + call_module="SubtensorModule", + call_function="set_children", + call_params={ + "children": [ + ( + 18446744073709551615, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ], + "hotkey": "fake hotkey", + "netuid": netuid, + }, + ) + + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=False, + period=None, + raise_error=False, + ) + + assert success is True + assert "Success" in message + + +def test_root_set_pending_childkey_cooldown_extrinsic(subtensor, mocker, fake_wallet): + """Verify root_set_pending_childkey_cooldown_extrinsic extrinsic.""" + # Preps + cooldown = 100 + + subtensor.substrate.compose_call = mocker.Mock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") + ) + + # Call + success, message = children.root_set_pending_childkey_cooldown_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + cooldown=cooldown, + ) + # Asserts + + subtensor.substrate.compose_call.call_count == 2 + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=False, + period=None, + ) + assert success is True + assert "Success" in message diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 12c6b6e123..97554b4468 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2642,41 +2642,41 @@ async def test_register_success(subtensor, fake_wallet, mocker): @pytest.mark.asyncio -async def test_set_children(mock_substrate, subtensor, fake_wallet, mocker): - mock_substrate.submit_extrinsic.return_value = mocker.Mock( - is_success=mocker.AsyncMock(return_value=True)(), +async def test_set_children(subtensor, fake_wallet, mocker): + """Tests set_children extrinsic calls properly.""" + # Preps + mocked_set_children_extrinsic = mocker.AsyncMock() + mocker.patch.object( + async_subtensor, "set_children_extrinsic", mocked_set_children_extrinsic ) + fake_children = [ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ] - await subtensor.set_children( + # Call + result = await subtensor.set_children( fake_wallet, fake_wallet.hotkey.ss58_address, netuid=1, - children=[ - ( - 1.0, - "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", - ), - ], + children=fake_children, ) - assert_submit_signed_extrinsic( - mock_substrate, - fake_wallet.coldkey, - call_module="SubtensorModule", - call_function="set_children", - call_params={ - "children": [ - ( - U64_MAX, - "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", - ) - ], - "hotkey": fake_wallet.hotkey.ss58_address, - "netuid": 1, - }, - wait_for_inclusion=True, + # Asserts + mocked_set_children_extrinsic.assert_awaited_once_with( + subtensor=subtensor, + wallet=fake_wallet, + hotkey=fake_wallet.hotkey.ss58_address, + netuid=1, + children=fake_children, wait_for_finalization=True, + wait_for_inclusion=True, + raise_error=False, + period=None, ) + assert result == mocked_set_children_extrinsic.return_value @pytest.mark.asyncio diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index bb73ec2f13..75e7a7950f 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3811,3 +3811,40 @@ def test_get_parents_no_parents(subtensor, mocker): params=[fake_hotkey, fake_netuid], ) assert result == [] + + +def test_set_children(subtensor, fake_wallet, mocker): + """Tests set_children extrinsic calls properly.""" + # Preps + mocked_set_children_extrinsic = mocker.Mock() + mocker.patch.object( + subtensor_module, "set_children_extrinsic", mocked_set_children_extrinsic + ) + fake_children = [ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ] + + # Call + result = subtensor.set_children( + fake_wallet, + fake_wallet.hotkey.ss58_address, + netuid=1, + children=fake_children, + ) + + # Asserts + mocked_set_children_extrinsic.assert_called_once_with( + subtensor=subtensor, + wallet=fake_wallet, + hotkey=fake_wallet.hotkey.ss58_address, + netuid=1, + children=fake_children, + wait_for_finalization=True, + wait_for_inclusion=True, + raise_error=False, + period=None, + ) + assert result == mocked_set_children_extrinsic.return_value