Skip to content

Commit 9041739

Browse files
author
Roman
committed
add get_owned_hotkeys to subtensor and async one + tests
1 parent 3ff743a commit 9041739

File tree

4 files changed

+163
-0
lines changed

4 files changed

+163
-0
lines changed

bittensor/core/async_subtensor.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,36 @@ async def get_neuron_for_pubkey_and_subnet(
15721572
reuse_block=reuse_block,
15731573
)
15741574

1575+
async def get_owned_hotkeys(
1576+
self,
1577+
coldkey_ss58: str,
1578+
block: Optional[int] = None,
1579+
block_hash: Optional[str] = None,
1580+
reuse_block: bool = False,
1581+
) -> list[str]:
1582+
"""
1583+
Retrieves all hotkeys owned by a specific coldkey address.
1584+
1585+
Args:
1586+
coldkey_ss58 (str): The SS58 address of the coldkey to query.
1587+
block (int): The blockchain block number for the query.
1588+
block_hash (str): The hash of the blockchain block number for the query.
1589+
reuse_block (bool): Whether to reuse the last-used blockchain block hash.
1590+
1591+
Returns:
1592+
list[str]: A list of hotkey SS58 addresses owned by the coldkey.
1593+
"""
1594+
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
1595+
owned_hotkeys = await self.substrate.query(
1596+
module="SubtensorModule",
1597+
storage_function="OwnedHotkeys",
1598+
params=[coldkey_ss58],
1599+
block_hash=block_hash,
1600+
reuse_block_hash=reuse_block,
1601+
)
1602+
1603+
return [decode_account_id(hotkey[0]) for hotkey in owned_hotkeys or []]
1604+
15751605
async def get_stake(
15761606
self,
15771607
coldkey_ss58: str,

bittensor/core/subtensor.py

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

12011201
return NeuronInfo.from_dict(result)
12021202

1203+
def get_owned_hotkeys(
1204+
self,
1205+
coldkey_ss58: str,
1206+
block: Optional[int] = None,
1207+
reuse_block: bool = False,
1208+
) -> list[str]:
1209+
"""
1210+
Retrieves all hotkeys owned by a specific coldkey address.
1211+
1212+
Args:
1213+
coldkey_ss58 (str): The SS58 address of the coldkey to query.
1214+
block (int): The blockchain block number for the query.
1215+
reuse_block (bool): Whether to reuse the last-used blockchain block hash.
1216+
1217+
Returns:
1218+
list[str]: A list of hotkey SS58 addresses owned by the coldkey.
1219+
"""
1220+
block_hash = self.determine_block_hash(block)
1221+
owned_hotkeys = self.substrate.query(
1222+
module="SubtensorModule",
1223+
storage_function="OwnedHotkeys",
1224+
params=[coldkey_ss58],
1225+
block_hash=block_hash,
1226+
reuse_block_hash=reuse_block,
1227+
)
1228+
return [decode_account_id(hotkey[0]) for hotkey in owned_hotkeys or []]
1229+
12031230
def get_stake(
12041231
self,
12051232
coldkey_ss58: str,

tests/unit_tests/test_async_subtensor.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2956,3 +2956,57 @@ 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+
subtensor.substrate.query = mocked_subtensor
2974+
2975+
mocked_decode_account_id = mocker.Mock()
2976+
async_subtensor.decode_account_id = mocked_decode_account_id
2977+
2978+
# Call
2979+
result = await subtensor.get_owned_hotkeys(fake_coldkey)
2980+
2981+
# Asserts
2982+
mocked_subtensor.assert_awaited_once_with(
2983+
module="SubtensorModule",
2984+
storage_function="OwnedHotkeys",
2985+
params=[fake_coldkey],
2986+
block_hash=None,
2987+
reuse_block_hash=False,
2988+
)
2989+
assert result == [mocked_decode_account_id.return_value]
2990+
mocked_decode_account_id.assert_called_once_with(fake_hotkey)
2991+
2992+
2993+
@pytest.mark.asyncio
2994+
async def test_get_owned_hotkeys_return_empty(subtensor, mocker):
2995+
"""Tests that the output of get_owned_hotkeys is empty."""
2996+
# Prep
2997+
fake_coldkey = "fake_hotkey"
2998+
mocked_subtensor = mocker.AsyncMock(return_value=[])
2999+
subtensor.substrate.query = mocked_subtensor
3000+
3001+
# Call
3002+
result = await subtensor.get_owned_hotkeys(fake_coldkey)
3003+
3004+
# Asserts
3005+
mocked_subtensor.assert_awaited_once_with(
3006+
module="SubtensorModule",
3007+
storage_function="OwnedHotkeys",
3008+
params=[fake_coldkey],
3009+
block_hash=None,
3010+
reuse_block_hash=False,
3011+
)
3012+
assert result == []

tests/unit_tests/test_subtensor.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3332,3 +3332,55 @@ 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+
subtensor.substrate.query = mocked_subtensor
3349+
3350+
mocked_decode_account_id = mocker.Mock()
3351+
subtensor_module.decode_account_id = mocked_decode_account_id
3352+
3353+
# Call
3354+
result = subtensor.get_owned_hotkeys(fake_coldkey)
3355+
3356+
# Asserts
3357+
mocked_subtensor.assert_called_once_with(
3358+
module="SubtensorModule",
3359+
storage_function="OwnedHotkeys",
3360+
params=[fake_coldkey],
3361+
block_hash=None,
3362+
reuse_block_hash=False,
3363+
)
3364+
assert result == [mocked_decode_account_id.return_value]
3365+
mocked_decode_account_id.assert_called_once_with(fake_hotkey)
3366+
3367+
3368+
def test_get_owned_hotkeys_return_empty(subtensor, mocker):
3369+
"""Tests that the output of get_owned_hotkeys is empty."""
3370+
# Prep
3371+
fake_coldkey = "fake_hotkey"
3372+
mocked_subtensor = mocker.Mock(return_value=[])
3373+
subtensor.substrate.query = mocked_subtensor
3374+
3375+
# Call
3376+
result = subtensor.get_owned_hotkeys(fake_coldkey)
3377+
3378+
# Asserts
3379+
mocked_subtensor.assert_called_once_with(
3380+
module="SubtensorModule",
3381+
storage_function="OwnedHotkeys",
3382+
params=[fake_coldkey],
3383+
block_hash=None,
3384+
reuse_block_hash=False,
3385+
)
3386+
assert result == []

0 commit comments

Comments
 (0)