Skip to content

Commit 50d247e

Browse files
authored
Merge pull request #2887 from opentensor/feat/roman/selective_metagraph_back
SelectiveMetagraph back
2 parents 5c828cc + d7e4d68 commit 50d247e

File tree

7 files changed

+525
-421
lines changed

7 files changed

+525
-421
lines changed

bittensor/core/async_subtensor.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
NeuronInfoLite,
2323
NeuronInfo,
2424
ProposalVoteData,
25+
SelectiveMetagraphIndex,
2526
StakeInfo,
2627
SubnetHyperparameters,
2728
SubnetIdentity,
@@ -1639,37 +1640,70 @@ async def get_minimum_required_stake(self):
16391640
async def get_metagraph_info(
16401641
self,
16411642
netuid: int,
1643+
field_indices: Optional[Union[list[SelectiveMetagraphIndex], list[int]]] = None,
16421644
block: Optional[int] = None,
16431645
block_hash: Optional[str] = None,
16441646
reuse_block: bool = False,
16451647
) -> Optional[MetagraphInfo]:
16461648
"""
1647-
Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid)
1649+
Retrieves full or partial metagraph information for the specified subnet (netuid).
16481650
16491651
Arguments:
1650-
netuid: The NetUID of the subnet.
1652+
netuid: The NetUID of the subnet to query.
1653+
field_indices: An optional list of SelectiveMetagraphIndex or int values specifying which fields to retrieve.
1654+
If not provided, all available fields will be returned.
16511655
block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or
16521656
reuse_block
16531657
block_hash: The hash of blockchain block number for the query. Do not specify if using
16541658
block or reuse_block
16551659
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block.
16561660
16571661
Returns:
1658-
MetagraphInfo dataclass
1662+
Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet
1663+
with the given netuid does not exist.
1664+
1665+
Example:
1666+
meta_info = await subtensor.get_metagraph_info(netuid=2)
1667+
1668+
partial_meta_info = await subtensor.get_metagraph_info(
1669+
netuid=2,
1670+
field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys]
1671+
)
16591672
"""
16601673
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
16611674
if not block_hash and reuse_block:
16621675
block_hash = self.substrate.last_block_hash
16631676

1664-
query = await self.substrate.runtime_call(
1665-
"SubnetInfoRuntimeApi",
1666-
"get_metagraph",
1667-
params=[netuid],
1668-
block_hash=block_hash,
1669-
)
1677+
if field_indices:
1678+
if isinstance(field_indices, list) and all(
1679+
isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices
1680+
):
1681+
indexes = [
1682+
f.value if isinstance(f, SelectiveMetagraphIndex) else f
1683+
for f in field_indices
1684+
]
1685+
else:
1686+
raise ValueError(
1687+
"`field_indices` must be a list of SelectiveMetagraphIndex enums or ints."
1688+
)
1689+
1690+
query = await self.substrate.runtime_call(
1691+
"SubnetInfoRuntimeApi",
1692+
"get_selective_metagraph",
1693+
params=[netuid, indexes if 0 in indexes else [0] + indexes],
1694+
block_hash=block_hash,
1695+
)
1696+
else:
1697+
query = await self.substrate.runtime_call(
1698+
"SubnetInfoRuntimeApi",
1699+
"get_metagraph",
1700+
params=[netuid],
1701+
)
1702+
16701703
if query.value is None:
16711704
logging.error(f"Subnet {netuid} does not exist.")
16721705
return None
1706+
16731707
return MetagraphInfo.from_dict(query.value)
16741708

16751709
async def get_all_metagraphs_info(

bittensor/core/chain_data/metagraph_info.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from bittensor.utils.balance import Balance, fixed_to_float
1414

1515

16-
# to balance with unit (just shortcut)
16+
# to balance with unit (shortcut)
1717
def _tbwu(val: Optional[int], netuid: Optional[int] = 0) -> Optional[Balance]:
1818
"""Returns a Balance object from a value and unit."""
1919
if val is None:
@@ -26,7 +26,9 @@ def _chr_str(codes: tuple[int]) -> str:
2626
return "".join(map(chr, codes))
2727

2828

29-
def process_nested(data: Union[tuple, dict], chr_transform):
29+
def process_nested(
30+
data: Union[tuple, dict], chr_transform
31+
) -> Optional[Union[list, dict]]:
3032
"""Processes nested data structures by applying a transformation function to their elements."""
3133
if isinstance(data, (list, tuple)):
3234
if len(data) > 0:
@@ -39,6 +41,7 @@ def process_nested(data: Union[tuple, dict], chr_transform):
3941
return {}
4042
elif isinstance(data, dict):
4143
return {k: chr_transform(v) for k, v in data.items()}
44+
return None
4245

4346

4447
@dataclass
@@ -143,6 +146,9 @@ class MetagraphInfo(InfoBase):
143146
tuple[str, Balance]
144147
] # List of dividend payout in alpha via subnet.
145148

149+
# List of validators
150+
validators: list[str]
151+
146152
@classmethod
147153
def _from_dict(cls, decoded: dict) -> "MetagraphInfo":
148154
"""Returns a MetagraphInfo object from decoded chain data."""
@@ -366,6 +372,9 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo":
366372
if decoded.get("alpha_dividends_per_hotkey") is not None
367373
else None
368374
),
375+
validators=[v for v in decoded["validators"]]
376+
if decoded.get("validators") is not None
377+
else None,
369378
)
370379

371380

@@ -498,6 +507,7 @@ class SelectiveMetagraphIndex(Enum):
498507
TotalStake = 69
499508
TaoDividendsPerHotkey = 70
500509
AlphaDividendsPerHotkey = 71
510+
Validators = 72
501511

502512
@staticmethod
503513
def all_indices() -> list[int]:

bittensor/core/subtensor.py

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
MetagraphInfo,
2222
NeuronInfo,
2323
NeuronInfoLite,
24+
SelectiveMetagraphIndex,
2425
StakeInfo,
2526
SubnetInfo,
2627
SubnetIdentity,
@@ -1268,29 +1269,66 @@ def get_minimum_required_stake(self) -> Balance:
12681269
return Balance.from_rao(getattr(result, "value", 0))
12691270

12701271
def get_metagraph_info(
1271-
self, netuid: int, block: Optional[int] = None
1272+
self,
1273+
netuid: int,
1274+
field_indices: Optional[Union[list[SelectiveMetagraphIndex], list[int]]] = None,
1275+
block: Optional[int] = None,
12721276
) -> Optional[MetagraphInfo]:
12731277
"""
1274-
Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid)
1278+
Retrieves full or partial metagraph information for the specified subnet (netuid).
12751279
12761280
Arguments:
1277-
netuid: The NetUID of the subnet.
1278-
block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or
1279-
reuse_block
1281+
netuid: The NetUID of the subnet to query.
1282+
field_indices: An optional list of SelectiveMetagraphIndex or int values specifying which fields to retrieve.
1283+
If not provided, all available fields will be returned.
1284+
block: The block number at which to query the data. If not specified, the current block or one determined
1285+
via reuse_block or block_hash will be used.
12801286
12811287
Returns:
1282-
MetagraphInfo dataclass
1288+
Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet
1289+
with the given netuid does not exist.
1290+
1291+
Example:
1292+
meta_info = subtensor.get_metagraph_info(netuid=2)
1293+
1294+
partial_meta_info = subtensor.get_metagraph_info(
1295+
netuid=2,
1296+
field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys]
1297+
)
12831298
"""
12841299
block_hash = self.determine_block_hash(block)
1285-
query = self.substrate.runtime_call(
1286-
"SubnetInfoRuntimeApi",
1287-
"get_metagraph",
1288-
params=[netuid],
1289-
block_hash=block_hash,
1290-
)
1300+
1301+
if field_indices:
1302+
if isinstance(field_indices, list) and all(
1303+
isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices
1304+
):
1305+
indexes = [
1306+
f.value if isinstance(f, SelectiveMetagraphIndex) else f
1307+
for f in field_indices
1308+
]
1309+
else:
1310+
raise ValueError(
1311+
"`field_indices` must be a list of SelectiveMetagraphIndex enums or ints."
1312+
)
1313+
1314+
query = self.substrate.runtime_call(
1315+
"SubnetInfoRuntimeApi",
1316+
"get_selective_metagraph",
1317+
params=[netuid, indexes if 0 in indexes else [0] + indexes],
1318+
block_hash=block_hash,
1319+
)
1320+
else:
1321+
query = self.substrate.runtime_call(
1322+
"SubnetInfoRuntimeApi",
1323+
"get_metagraph",
1324+
params=[netuid],
1325+
block_hash=block_hash,
1326+
)
1327+
12911328
if query.value is None:
12921329
logging.error(f"Subnet {netuid} does not exist.")
12931330
return None
1331+
12941332
return MetagraphInfo.from_dict(query.value)
12951333

12961334
def get_all_metagraphs_info(

tests/e2e_tests/test_incentive.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,25 @@ async def test_incentive(local_chain, subtensor, templates, alice_wallet, bob_wa
103103
continue
104104
raise
105105

106-
# wait one tempo (fast block
107-
subtensor.wait_for_block(subtensor.block + subtensor.tempo(alice_subnet_netuid))
106+
# wait one tempo (fast block)
107+
next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(
108+
alice_subnet_netuid
109+
)
110+
subtensor.wait_for_block(next_epoch_start_block + tempo + 1)
111+
112+
validators = subtensor.metagraphs.get_metagraph_info(
113+
alice_subnet_netuid, field_indices=[72]
114+
).validators
115+
116+
alice_uid = subtensor.subnets.get_uid_for_hotkey_on_subnet(
117+
hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid
118+
)
119+
assert validators[alice_uid] == 1
120+
121+
bob_uid = subtensor.subnets.get_uid_for_hotkey_on_subnet(
122+
hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid
123+
)
124+
assert validators[bob_uid] == 0
108125

109126
while True:
110127
try:

0 commit comments

Comments
 (0)