From 3bc829ff0622917a0315a113fae3fda9645a5312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20=C5=BBy=C5=BAniewski?= Date: Fri, 14 Mar 2025 13:15:42 +0100 Subject: [PATCH] feat: set_children and get_pending_children methods --- bittensor/core/async_subtensor.py | 122 +++++++++++++ bittensor/core/errors.py | 118 ++++++++++--- bittensor/core/subtensor.py | 112 ++++++++++++ bittensor/utils/__init__.py | 12 ++ bittensor/utils/easy_imports.py | 8 + tests/e2e_tests/test_hotkeys.py | 181 ++++++++++++++++++-- tests/e2e_tests/utils/chain_interactions.py | 17 -- tests/unit_tests/test_async_subtensor.py | 73 ++++++++ tests/unit_tests/test_subtensor_extended.py | 70 +++++++- 9 files changed, 653 insertions(+), 60 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 546be4a9d0..c96923bf12 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -77,10 +77,12 @@ from bittensor.utils import ( Certificate, decode_hex_identity_dict, + float_to_u64, format_error_message, torch, u16_normalized_float, u64_normalized_float, + unlock_key, ) from bittensor.utils.balance import ( Balance, @@ -934,6 +936,57 @@ async def get_children( except SubstrateRequestException as e: return False, [], format_error_message(e) + async def get_children_pending( + self, + hotkey: str, + netuid: int, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, + ) -> tuple[ + list[tuple[float, str]], + int, + ]: + """ + This method retrieves the pending children of a given hotkey and netuid. + It queries the SubtensorModule's PendingChildKeys storage function. + + Arguments: + hotkey (str): The hotkey value. + netuid (int): The netuid value. + block (Optional[int]): The block number for which the children are to be retrieved. + block_hash (Optional[str]): The hash of the block to retrieve the subnet unique identifiers from. + reuse_block (bool): Whether to reuse the last-used block hash. + + Returns: + list[tuple[float, str]]: A list of children with their proportions. + int: The cool-down block number. + """ + + response = await self.substrate.query( + module="SubtensorModule", + storage_function="PendingChildKeys", + params=[netuid, hotkey], + block_hash=await self.determine_block_hash( + block, + block_hash, + reuse_block, + ), + reuse_block_hash=reuse_block, + ) + children, cooldown = response.value + + return ( + [ + ( + u64_normalized_float(proportion), + decode_account_id(child[0]), + ) + for proportion, child in children + ], + cooldown, + ) + async def get_commitment( self, netuid: int, @@ -3269,6 +3322,75 @@ async def root_set_weights( wait_for_inclusion=wait_for_inclusion, ) + async 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 = 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, + raise_error=raise_error, + ) + async def set_delegate_take( self, wallet: "Wallet", diff --git a/bittensor/core/errors.py b/bittensor/core/errors.py index 0625a098f9..d4b438bd2f 100644 --- a/bittensor/core/errors.py +++ b/bittensor/core/errors.py @@ -63,10 +63,6 @@ class ChainTransactionError(ChainError): """Error for any chain transaction related errors.""" -class ChainQueryError(ChainError): - """Error for any chain query related errors.""" - - class DelegateTakeTooHigh(ChainTransactionError): """ Delegate take is too high. @@ -79,9 +75,9 @@ class DelegateTakeTooLow(ChainTransactionError): """ -class DelegateTxRateLimitExceeded(ChainTransactionError): +class DuplicateChild(ChainTransactionError): """ - A transactor exceeded the rate limit for delegate transaction. + Duplicate child when setting children. """ @@ -91,50 +87,124 @@ class HotKeyAccountNotExists(ChainTransactionError): """ +class IdentityError(ChainTransactionError): + """ + Error raised when an identity transaction fails. + """ + + +class InvalidChild(ChainTransactionError): + """ + Attempting to set an invalid child for a hotkey on a network. + """ + + +class MetadataError(ChainTransactionError): + """ + Error raised when metadata commitment transaction fails. + """ + + +class NominationError(ChainTransactionError): + """ + Error raised when a nomination transaction fails. + """ + + class NonAssociatedColdKey(ChainTransactionError): """ Request to stake, unstake or subscribe is made by a coldkey that is not associated with the hotkey account. """ -class StakeError(ChainTransactionError): - """Error raised when a stake transaction fails.""" +class NotEnoughStakeToSetChildkeys(ChainTransactionError): + """ + The parent hotkey doesn't have enough own stake to set childkeys. + """ -class UnstakeError(ChainTransactionError): - """Error raised when an unstake transaction fails.""" +class NotRegisteredError(ChainTransactionError): + """ + Error raised when a neuron is not registered, and the transaction requires it to be. + """ -class IdentityError(ChainTransactionError): - """Error raised when an identity transaction fails.""" +class ProportionOverflow(ChainTransactionError): + """ + Proportion overflow when setting children. + """ -class NominationError(ChainTransactionError): - """Error raised when a nomination transaction fails.""" +class RegistrationError(ChainTransactionError): + """ + Error raised when a neuron registration transaction fails. + """ + + +class RegistrationNotPermittedOnRootSubnet(ChainTransactionError): + """ + Operation is not permitted on the root subnet. + """ + + +class StakeError(ChainTransactionError): + """ + Error raised when a stake transaction fails. + """ + + +class NotDelegateError(StakeError): + """ + Error raised when a hotkey you are trying to stake to is not a delegate. + """ + + +class SubNetworkDoesNotExist(ChainTransactionError): + """ + The subnet does not exist. + """ class TakeError(ChainTransactionError): - """Error raised when an increase / decrease take transaction fails.""" + """ + Error raised when an increase / decrease take transaction fails. + """ class TransferError(ChainTransactionError): - """Error raised when a transfer transaction fails.""" + """ + Error raised when a transfer transaction fails. + """ -class RegistrationError(ChainTransactionError): - """Error raised when a neuron registration transaction fails.""" +class TooManyChildren(ChainTransactionError): + """ + Too many children MAX 5. + """ -class NotRegisteredError(ChainTransactionError): - """Error raised when a neuron is not registered, and the transaction requires it to be.""" +class TxRateLimitExceeded(ChainTransactionError): + """ + Default transaction rate limit exceeded. + """ -class NotDelegateError(StakeError): - """Error raised when a hotkey you are trying to stake to is not a delegate.""" +class DelegateTxRateLimitExceeded(TxRateLimitExceeded): + """ + A transactor exceeded the rate limit for delegate transaction. + """ -class MetadataError(ChainTransactionError): - """Error raised when metadata commitment transaction fails.""" +class UnstakeError(ChainTransactionError): + """ + Error raised when an unstake transaction fails. + """ + + +class ChainQueryError(ChainError): + """ + Error for any chain query related errors. + """ class InvalidRequestNameError(Exception): diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 244080ff51..35cf416812 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -80,10 +80,12 @@ from bittensor.utils import ( Certificate, decode_hex_identity_dict, + float_to_u64, format_error_message, torch, u16_normalized_float, u64_normalized_float, + unlock_key, ) from bittensor.utils.balance import ( Balance, @@ -717,6 +719,47 @@ def get_children( except SubstrateRequestException as e: return False, [], format_error_message(e) + def get_children_pending( + self, + hotkey: str, + netuid: int, + block: Optional[int] = None, + ) -> tuple[ + list[tuple[float, str]], + int, + ]: + """ + This method retrieves the pending children of a given hotkey and netuid. + It queries the SubtensorModule's PendingChildKeys storage function. + + Arguments: + hotkey (str): The hotkey value. + netuid (int): The netuid value. + block (Optional[int]): The block number for which the children are to be retrieved. + + Returns: + list[tuple[float, str]]: A list of children with their proportions. + int: The cool-down block number. + """ + + children, cooldown = self.substrate.query( + module="SubtensorModule", + storage_function="PendingChildKeys", + params=[netuid, hotkey], + block_hash=self.determine_block_hash(block), + ).value + + return ( + [ + ( + u64_normalized_float(proportion), + decode_account_id(child[0]), + ) + for proportion, child in children + ], + cooldown, + ) + def get_commitment(self, netuid: int, uid: int, block: Optional[int] = None) -> str: """ Retrieves the on-chain commitment for a specific neuron in the Bittensor network. @@ -1572,6 +1615,75 @@ 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", diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index 534a2eedab..e0da1d9c99 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -1,4 +1,5 @@ import ast +import decimal import hashlib from collections import namedtuple from typing import Any, Literal, Union, Optional, TYPE_CHECKING @@ -165,6 +166,17 @@ def u64_normalized_float(x: int) -> float: return float(x) / float(U64_MAX) +def float_to_u64(value: float) -> int: + """Converts a float to a u64 int""" + + value = decimal.Decimal(str(value)) + + if not (0 <= value <= 1): + raise ValueError("Input value must be between 0 and 1") + + return int(value * U64_MAX) + + def get_hash(content, encoding="utf-8"): sha3 = hashlib.sha3_256() diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 7718ceede0..59ebeda7ba 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -70,24 +70,32 @@ DelegateTakeTooHigh, DelegateTakeTooLow, DelegateTxRateLimitExceeded, + DuplicateChild, HotKeyAccountNotExists, IdentityError, InternalServerError, + InvalidChild, InvalidRequestNameError, MetadataError, NominationError, NonAssociatedColdKey, NotDelegateError, + NotEnoughStakeToSetChildkeys, NotRegisteredError, NotVerifiedException, PostProcessException, PriorityException, + ProportionOverflow, RegistrationError, + RegistrationNotPermittedOnRootSubnet, RunException, StakeError, + SubNetworkDoesNotExist, SynapseDendriteNoneException, SynapseParsingError, + TooManyChildren, TransferError, + TxRateLimitExceeded, UnknownSynapseError, UnstakeError, ) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index 126690d46c..86ff768688 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -1,7 +1,8 @@ import pytest +import bittensor from tests.e2e_tests.utils.chain_interactions import ( - set_children, + sudo_set_admin_utils, wait_epoch, ) @@ -56,16 +57,44 @@ def test_hotkeys(subtensor, alice_wallet): @pytest.mark.asyncio -async def test_children(subtensor, alice_wallet, bob_wallet): +async def test_children(local_chain, subtensor, alice_wallet, bob_wallet): """ Tests: - Get default children (empty list) - Update children list + - Checking pending children - Checking cooldown period - Trigger rate limit - Clear children list """ + with pytest.raises(bittensor.RegistrationNotPermittedOnRootSubnet): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=0, + children=[], + raise_error=True, + ) + + with pytest.raises(bittensor.NonAssociatedColdKey): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=1, + children=[], + raise_error=True, + ) + + with pytest.raises(bittensor.SubNetworkDoesNotExist): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=2, + children=[], + raise_error=True, + ) + subtensor.burned_register( alice_wallet, netuid=1, @@ -84,16 +113,82 @@ async def test_children(subtensor, alice_wallet, bob_wallet): assert success is True assert children == [] - success, error = set_children( - subtensor, + with pytest.raises(bittensor.InvalidChild): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=1, + children=[ + ( + 1.0, + alice_wallet.hotkey.ss58_address, + ), + ], + raise_error=True, + ) + + with pytest.raises(bittensor.TooManyChildren): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=1, + children=[ + ( + 0.1, + bob_wallet.hotkey.ss58_address, + ) + for _ in range(10) + ], + raise_error=True, + ) + + with pytest.raises(bittensor.ProportionOverflow): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=1, + children=[ + ( + 1.0, + bob_wallet.hotkey.ss58_address, + ), + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ], + raise_error=True, + ) + + with pytest.raises(bittensor.DuplicateChild): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=1, + children=[ + ( + 0.5, + bob_wallet.hotkey.ss58_address, + ), + ( + 0.5, + bob_wallet.hotkey.ss58_address, + ), + ], + raise_error=True, + ) + + subtensor.set_children( alice_wallet, + alice_wallet.hotkey.ss58_address, netuid=1, children=[ ( - 2**64 - 1, + 1.0, bob_wallet.hotkey.ss58_address, ), ], + raise_error=True, ) assert error == "" @@ -101,6 +196,7 @@ async def test_children(subtensor, alice_wallet, bob_wallet): set_children_block = subtensor.get_current_block() + # children not set yet (have to wait cool-down period) success, children, error = subtensor.get_children( alice_wallet.hotkey.ss58_address, block=set_children_block, @@ -111,7 +207,20 @@ async def test_children(subtensor, alice_wallet, bob_wallet): assert children == [] assert error == "" - subtensor.wait_for_block(set_children_block + SET_CHILDREN_COOLDOWN_PERIOD) + # children are in pending state + pending, cooldown = subtensor.get_children_pending( + alice_wallet.hotkey.ss58_address, + netuid=1, + ) + + assert pending == [ + ( + 1.0, + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + ), + ] + + subtensor.wait_for_block(cooldown) await wait_epoch(subtensor, netuid=1) @@ -129,29 +238,42 @@ async def test_children(subtensor, alice_wallet, bob_wallet): ) ] - success, error = set_children( - subtensor, - alice_wallet, + # pending queue is empty + pending, cooldown = subtensor.get_children_pending( + alice_wallet.hotkey.ss58_address, netuid=1, - children=[], ) - assert "`TxRateLimitExceeded(Module)`" in error - assert success is False + assert pending == [] + + with pytest.raises(bittensor.TxRateLimitExceeded): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=1, + children=[], + raise_error=True, + ) subtensor.wait_for_block(set_children_block + SET_CHILDREN_RATE_LIMIT) - success, error = set_children( - subtensor, + subtensor.set_children( alice_wallet, + alice_wallet.hotkey.ss58_address, netuid=1, children=[], + raise_error=True, ) + set_children_block = subtensor.get_current_block() - assert error == "" - assert success is True + pending, cooldown = subtensor.get_children_pending( + alice_wallet.hotkey.ss58_address, + netuid=1, + ) - subtensor.wait_for_block(subtensor.block + SET_CHILDREN_COOLDOWN_PERIOD) + assert pending == [] + + subtensor.wait_for_block(cooldown) await wait_epoch(subtensor, netuid=1) @@ -163,3 +285,28 @@ async def test_children(subtensor, alice_wallet, bob_wallet): assert error == "" assert success is True assert children == [] + + subtensor.wait_for_block(set_children_block + SET_CHILDREN_RATE_LIMIT) + + sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_stake_threshold", + call_params={ + "min_stake": 1_000_000_000_000, + }, + ) + + with pytest.raises(bittensor.NotEnoughStakeToSetChildkeys): + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=1, + children=[ + ( + 1.0, + bob_wallet.hotkey.ss58_address, + ), + ], + raise_error=True, + ) diff --git a/tests/e2e_tests/utils/chain_interactions.py b/tests/e2e_tests/utils/chain_interactions.py index 2f59dfd1f7..287b2a4ae4 100644 --- a/tests/e2e_tests/utils/chain_interactions.py +++ b/tests/e2e_tests/utils/chain_interactions.py @@ -215,23 +215,6 @@ async def root_set_subtensor_hyperparameter_values( return response.is_success, "" -def set_children(subtensor, wallet, netuid, children): - return subtensor.sign_and_send_extrinsic( - subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="set_children", - call_params={ - "children": children, - "hotkey": wallet.hotkey.ss58_address, - "netuid": netuid, - }, - ), - wallet, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - def set_identity( subtensor, wallet, diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 3ddbf14f99..89c3290f72 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -12,6 +12,7 @@ from bittensor.core.chain_data.neuron_info import NeuronInfo from bittensor.core.chain_data.stake_info import StakeInfo from bittensor.core.chain_data import proposal_vote_data +from bittensor.utils import U64_MAX from bittensor.utils.balance import Balance from tests.helpers.helpers import assert_submit_signed_extrinsic @@ -1953,6 +1954,40 @@ async def test_get_children_substrate_request_exception(subtensor, mocker): assert result == (False, [], "Formatted error message") +@pytest.mark.asyncio +async def test_get_children_pending(mock_substrate, subtensor): + mock_substrate.query.return_value.value = [ + [ + ( + U64_MAX, + (tuple(bytearray(32)),), + ), + ], + 123, + ] + + children, cooldown = await subtensor.get_children_pending( + "hotkey_ss58", + netuid=1, + ) + + assert children == [ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ] + assert cooldown == 123 + + mock_substrate.query.assert_called_once_with( + module="SubtensorModule", + storage_function="PendingChildKeys", + params=[1, "hotkey_ss58"], + block_hash=None, + reuse_block_hash=False, + ) + + @pytest.mark.asyncio async def test_get_subnet_hyperparameters_success(subtensor, mocker): """Tests get_subnet_hyperparameters with successful hyperparameter retrieval.""" @@ -2491,6 +2526,44 @@ async def test_register_success(subtensor, fake_wallet, mocker): assert result == mocked_register_extrinsic.return_value +@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)(), + ) + + await subtensor.set_children( + fake_wallet, + fake_wallet.hotkey.ss58_address, + netuid=1, + children=[ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ], + ) + + 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, + wait_for_finalization=True, + ) + + @pytest.mark.asyncio async def test_set_delegate_take_equal(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18) diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index b0cd2afbf3..ec67747f97 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -12,6 +12,7 @@ from bittensor.core.chain_data.neuron_info_lite import NeuronInfoLite from bittensor.core.chain_data.prometheus_info import PrometheusInfo from bittensor.core.chain_data.stake_info import StakeInfo +from bittensor.utils import U16_MAX, U64_MAX from bittensor.utils.balance import Balance from tests.helpers.helpers import assert_submit_signed_extrinsic @@ -23,7 +24,7 @@ def mock_delegate_info(): "total_stake": {}, "nominators": [], "owner_ss58": tuple(bytearray(32)), - "take": 2**16 - 1, + "take": U16_MAX, "validator_permits": [], "registrations": [], "return_per_1000": 2, @@ -364,7 +365,7 @@ def test_get_block_hash_none(mock_substrate, subtensor): def test_get_children(mock_substrate, subtensor, fake_wallet): mock_substrate.query.return_value.value = [ ( - 2**64 - 1, + U64_MAX, (tuple(bytearray(32)),), ), ] @@ -391,6 +392,38 @@ def test_get_children(mock_substrate, subtensor, fake_wallet): ) +def test_get_children_pending(mock_substrate, subtensor): + mock_substrate.query.return_value.value = [ + [ + ( + U64_MAX, + (tuple(bytearray(32)),), + ), + ], + 123, + ] + + children, cooldown = subtensor.get_children_pending( + "hotkey_ss58", + netuid=1, + ) + + assert children == [ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ] + assert cooldown == 123 + + mock_substrate.query.assert_called_once_with( + module="SubtensorModule", + storage_function="PendingChildKeys", + params=[1, "hotkey_ss58"], + block_hash=None, + ) + + def test_get_current_weight_commit_info(mock_substrate, subtensor, fake_wallet, mocker): mock_substrate.query_map.return_value.records = [ ( @@ -878,6 +911,39 @@ def test_neurons_lite(mock_substrate, subtensor, mock_neuron_info): ) +def test_set_children(mock_substrate, subtensor, fake_wallet, mocker): + subtensor.set_children( + fake_wallet, + fake_wallet.hotkey.ss58_address, + netuid=1, + children=[ + ( + 1.0, + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + ), + ], + ) + + 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, + wait_for_finalization=True, + ) + + def test_set_delegate_take_equal(mock_substrate, subtensor, fake_wallet, mocker): mocker.patch.object(subtensor, "get_delegate_take", return_value=0.18)