Skip to content

Commit 935b3c6

Browse files
authored
Merge branch 'staging' into feat/roman/add-drand-commitements
2 parents a1c5e81 + 07a62f3 commit 935b3c6

File tree

4 files changed

+167
-0
lines changed

4 files changed

+167
-0
lines changed

bittensor/core/async_subtensor.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,36 @@ async def get_neuron_for_pubkey_and_subnet(
16391639
reuse_block=reuse_block,
16401640
)
16411641

1642+
async def get_owned_hotkeys(
1643+
self,
1644+
coldkey_ss58: str,
1645+
block: Optional[int] = None,
1646+
block_hash: Optional[str] = None,
1647+
reuse_block: bool = False,
1648+
) -> list[str]:
1649+
"""
1650+
Retrieves all hotkeys owned by a specific coldkey address.
1651+
1652+
Args:
1653+
coldkey_ss58 (str): The SS58 address of the coldkey to query.
1654+
block (int): The blockchain block number for the query.
1655+
block_hash (str): The hash of the blockchain block number for the query.
1656+
reuse_block (bool): Whether to reuse the last-used blockchain block hash.
1657+
1658+
Returns:
1659+
list[str]: A list of hotkey SS58 addresses owned by the coldkey.
1660+
"""
1661+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
1662+
owned_hotkeys = await self.substrate.query(
1663+
module="SubtensorModule",
1664+
storage_function="OwnedHotkeys",
1665+
params=[coldkey_ss58],
1666+
block_hash=block_hash,
1667+
reuse_block_hash=reuse_block,
1668+
)
1669+
1670+
return [decode_account_id(hotkey[0]) for hotkey in owned_hotkeys or []]
1671+
16421672
async def get_stake(
16431673
self,
16441674
coldkey_ss58: str,

bittensor/core/subtensor.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,33 @@ def get_neuron_for_pubkey_and_subnet(
12521252

12531253
return NeuronInfo.from_dict(result)
12541254

1255+
def get_owned_hotkeys(
1256+
self,
1257+
coldkey_ss58: str,
1258+
block: Optional[int] = None,
1259+
reuse_block: bool = False,
1260+
) -> list[str]:
1261+
"""
1262+
Retrieves all hotkeys owned by a specific coldkey address.
1263+
1264+
Args:
1265+
coldkey_ss58 (str): The SS58 address of the coldkey to query.
1266+
block (int): The blockchain block number for the query.
1267+
reuse_block (bool): Whether to reuse the last-used blockchain block hash.
1268+
1269+
Returns:
1270+
list[str]: A list of hotkey SS58 addresses owned by the coldkey.
1271+
"""
1272+
block_hash = self.determine_block_hash(block)
1273+
owned_hotkeys = self.substrate.query(
1274+
module="SubtensorModule",
1275+
storage_function="OwnedHotkeys",
1276+
params=[coldkey_ss58],
1277+
block_hash=block_hash,
1278+
reuse_block_hash=reuse_block,
1279+
)
1280+
return [decode_account_id(hotkey[0]) for hotkey in owned_hotkeys or []]
1281+
12551282
def get_stake(
12561283
self,
12571284
coldkey_ss58: str,

tests/unit_tests/test_async_subtensor.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2956,3 +2956,59 @@ async def test_get_timestamp(mocker, subtensor):
29562956
)
29572957
actual_result = await subtensor.get_timestamp(block=fake_block)
29582958
assert expected_result == actual_result
2959+
2960+
2961+
@pytest.mark.asyncio
2962+
async def test_get_owned_hotkeys_happy_path(subtensor, mocker):
2963+
"""Tests that the output of get_owned_hotkeys."""
2964+
# Prep
2965+
fake_coldkey = "fake_hotkey"
2966+
fake_hotkey = "fake_hotkey"
2967+
fake_hotkeys = [
2968+
[
2969+
fake_hotkey,
2970+
]
2971+
]
2972+
mocked_subtensor = mocker.AsyncMock(return_value=fake_hotkeys)
2973+
mocker.patch.object(subtensor.substrate, "query", new=mocked_subtensor)
2974+
2975+
mocked_decode_account_id = mocker.Mock()
2976+
mocker.patch.object(
2977+
async_subtensor, "decode_account_id", new=mocked_decode_account_id
2978+
)
2979+
2980+
# Call
2981+
result = await subtensor.get_owned_hotkeys(fake_coldkey)
2982+
2983+
# Asserts
2984+
mocked_subtensor.assert_awaited_once_with(
2985+
module="SubtensorModule",
2986+
storage_function="OwnedHotkeys",
2987+
params=[fake_coldkey],
2988+
block_hash=None,
2989+
reuse_block_hash=False,
2990+
)
2991+
assert result == [mocked_decode_account_id.return_value]
2992+
mocked_decode_account_id.assert_called_once_with(fake_hotkey)
2993+
2994+
2995+
@pytest.mark.asyncio
2996+
async def test_get_owned_hotkeys_return_empty(subtensor, mocker):
2997+
"""Tests that the output of get_owned_hotkeys is empty."""
2998+
# Prep
2999+
fake_coldkey = "fake_hotkey"
3000+
mocked_subtensor = mocker.AsyncMock(return_value=[])
3001+
mocker.patch.object(subtensor.substrate, "query", new=mocked_subtensor)
3002+
3003+
# Call
3004+
result = await subtensor.get_owned_hotkeys(fake_coldkey)
3005+
3006+
# Asserts
3007+
mocked_subtensor.assert_awaited_once_with(
3008+
module="SubtensorModule",
3009+
storage_function="OwnedHotkeys",
3010+
params=[fake_coldkey],
3011+
block_hash=None,
3012+
reuse_block_hash=False,
3013+
)
3014+
assert result == []

tests/unit_tests/test_subtensor.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3332,3 +3332,57 @@ def test_stake_fee_methods(mocker, subtensor):
33323332
],
33333333
block=None,
33343334
)
3335+
3336+
3337+
def test_get_owned_hotkeys_happy_path(subtensor, mocker):
3338+
"""Tests that the output of get_owned_hotkeys."""
3339+
# Prep
3340+
fake_coldkey = "fake_hotkey"
3341+
fake_hotkey = "fake_hotkey"
3342+
fake_hotkeys = [
3343+
[
3344+
fake_hotkey,
3345+
]
3346+
]
3347+
mocked_subtensor = mocker.Mock(return_value=fake_hotkeys)
3348+
mocker.patch.object(subtensor.substrate, "query", new=mocked_subtensor)
3349+
3350+
mocked_decode_account_id = mocker.Mock()
3351+
mocker.patch.object(
3352+
subtensor_module, "decode_account_id", new=mocked_decode_account_id
3353+
)
3354+
3355+
# Call
3356+
result = subtensor.get_owned_hotkeys(fake_coldkey)
3357+
3358+
# Asserts
3359+
mocked_subtensor.assert_called_once_with(
3360+
module="SubtensorModule",
3361+
storage_function="OwnedHotkeys",
3362+
params=[fake_coldkey],
3363+
block_hash=None,
3364+
reuse_block_hash=False,
3365+
)
3366+
assert result == [mocked_decode_account_id.return_value]
3367+
mocked_decode_account_id.assert_called_once_with(fake_hotkey)
3368+
3369+
3370+
def test_get_owned_hotkeys_return_empty(subtensor, mocker):
3371+
"""Tests that the output of get_owned_hotkeys is empty."""
3372+
# Prep
3373+
fake_coldkey = "fake_hotkey"
3374+
mocked_subtensor = mocker.Mock(return_value=[])
3375+
mocker.patch.object(subtensor.substrate, "query", new=mocked_subtensor)
3376+
3377+
# Call
3378+
result = subtensor.get_owned_hotkeys(fake_coldkey)
3379+
3380+
# Asserts
3381+
mocked_subtensor.assert_called_once_with(
3382+
module="SubtensorModule",
3383+
storage_function="OwnedHotkeys",
3384+
params=[fake_coldkey],
3385+
block_hash=None,
3386+
reuse_block_hash=False,
3387+
)
3388+
assert result == []

0 commit comments

Comments
 (0)