Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
a1503e5
improve `MetagraphInfo`
May 20, 2025
3a2c44a
update async `get_metagraph_info`
May 20, 2025
64c68af
update sync `get_metagraph_info`
May 20, 2025
0e32e31
add call into e2e tests
May 20, 2025
7af36f5
Merge branch 'staging' into feat/roman/selective_metagraph_back
May 20, 2025
3225b14
bring back unit tests for SelectiveMetagraph logic
May 23, 2025
e252ab7
Merge branch 'staging' into feat/roman/selective_metagraph_back
May 27, 2025
2947620
improve get_metagraph_info logic in subtensors
May 27, 2025
d328528
convert tuple to list to be consistent in case if `validators` is not…
May 27, 2025
4c202a5
fix e2e test
May 27, 2025
f44b826
fix bug in `get_next_epoch_start_block`
May 27, 2025
4640d49
improve test SelectiveMetagraph (validators) in `tests/e2e_tests/test…
May 27, 2025
ed5f744
extend annotation in `get_metagraph_info`
May 27, 2025
6eca45f
fix unit tests related with updated `get_metagraph_info` logic
May 27, 2025
e557c59
Merge branch 'staging' into feat/roman/selective_metagraph_back
Jun 4, 2025
e5b0b60
Merge branch 'staging' into feat/roman/selective_metagraph_back
basfroman Jun 4, 2025
675e7f3
Update bittensor/core/async_subtensor.py
basfroman Jun 4, 2025
4663395
Update bittensor/core/subtensor.py
basfroman Jun 4, 2025
b5f979b
Update bittensor/core/async_subtensor.py
basfroman Jun 4, 2025
c5533ef
Update bittensor/core/subtensor.py
basfroman Jun 4, 2025
fcf1223
ruff
Jun 4, 2025
f3ec701
fix tests assertions
Jun 4, 2025
a07eceb
Merge branch 'staging' into feat/roman/selective_metagraph_back
basfroman Jun 18, 2025
5df2797
Merge branch 'staging' into feat/roman/selective_metagraph_back
Jun 23, 2025
3e8b103
Merge branch 'staging' into feat/roman/selective_metagraph_back
Jun 25, 2025
d7e4d68
Merge branch 'staging' into feat/roman/selective_metagraph_back
basfroman Jul 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 45 additions & 9 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
NeuronInfoLite,
NeuronInfo,
ProposalVoteData,
SelectiveMetagraphIndex,
StakeInfo,
SubnetHyperparameters,
SubnetIdentity,
Expand Down Expand Up @@ -1585,37 +1586,72 @@ async def get_minimum_required_stake(self):
async def get_metagraph_info(
self,
netuid: int,
field_indices: Optional[
Union[list["SelectiveMetagraphIndex"], list[int]]
] = None,
block: Optional[int] = None,
block_hash: Optional[str] = None,
reuse_block: bool = False,
) -> Optional[MetagraphInfo]:
"""
Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid)
Retrieves full or partial metagraph information for the specified subnet (netuid).

Arguments:
netuid: The NetUID of the subnet.
netuid: The NetUID of the subnet to query.
field_indices: An optional list of SelectiveMetagraphIndex or int values specifying which fields to retrieve.
If not provided, all available fields will be returned.
block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or
reuse_block
block_hash: The hash of blockchain block number for the query. Do not specify if using
block or reuse_block
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block.

Returns:
MetagraphInfo dataclass
Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet
with the given netuid does not exist.

Example:
meta_info = await subtensor.get_metagraph_info(netuid=2)

partial_meta_info = await subtensor.get_metagraph_info(
netuid=2,
field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys]
)
"""
block_hash = await self.determine_block_hash(block, block_hash, reuse_block)
if not block_hash and reuse_block:
block_hash = self.substrate.last_block_hash

query = await self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_metagraph",
params=[netuid],
block_hash=block_hash,
)
if field_indices:
if isinstance(field_indices, list) and all(
isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices
):
indexes = [
f.value if isinstance(f, SelectiveMetagraphIndex) else f
for f in field_indices
]
else:
raise ValueError(
"`field_indices` must be a list of SelectiveMetagraphIndex items."
)

query = await self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_selective_metagraph",
params=[netuid, indexes if 0 in indexes else [0] + indexes],
block_hash=block_hash,
)
else:
query = await self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_metagraph",
params=[netuid],
)

if query.value is None:
logging.error(f"Subnet {netuid} does not exist.")
return None

return MetagraphInfo.from_dict(query.value)

async def get_all_metagraphs_info(
Expand Down
14 changes: 12 additions & 2 deletions bittensor/core/chain_data/metagraph_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from bittensor.utils.balance import Balance, fixed_to_float


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


def process_nested(data: Union[tuple, dict], chr_transform):
def process_nested(
data: Union[tuple, dict], chr_transform
) -> Optional[Union[list, dict]]:
"""Processes nested data structures by applying a transformation function to their elements."""
if isinstance(data, (list, tuple)):
if len(data) > 0:
Expand All @@ -39,6 +41,7 @@ def process_nested(data: Union[tuple, dict], chr_transform):
return {}
elif isinstance(data, dict):
return {k: chr_transform(v) for k, v in data.items()}
return None


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

# List of validators
validators: list[str]

@classmethod
def _from_dict(cls, decoded: dict) -> "MetagraphInfo":
"""Returns a MetagraphInfo object from decoded chain data."""
Expand Down Expand Up @@ -366,6 +372,9 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo":
if decoded.get("alpha_dividends_per_hotkey") is not None
else None
),
validators=[v for v in decoded["validators"]]
if decoded.get("validators") is not None
else None,
)


Expand Down Expand Up @@ -498,6 +507,7 @@ class SelectiveMetagraphIndex(Enum):
TotalStake = 69
TaoDividendsPerHotkey = 70
AlphaDividendsPerHotkey = 71
Validators = 72

@staticmethod
def all_indices() -> list[int]:
Expand Down
64 changes: 52 additions & 12 deletions bittensor/core/subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
MetagraphInfo,
NeuronInfo,
NeuronInfoLite,
SelectiveMetagraphIndex,
StakeInfo,
SubnetInfo,
SubnetIdentity,
Expand Down Expand Up @@ -1223,29 +1224,68 @@ def get_minimum_required_stake(self) -> Balance:
return Balance.from_rao(getattr(result, "value", 0))

def get_metagraph_info(
self, netuid: int, block: Optional[int] = None
self,
netuid: int,
field_indices: Optional[
Union[list["SelectiveMetagraphIndex"], list[int]]
] = None,
block: Optional[int] = None,
) -> Optional[MetagraphInfo]:
"""
Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid)
Retrieves full or partial metagraph information for the specified subnet (netuid).

Arguments:
netuid: The NetUID of the subnet.
block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or
reuse_block
netuid: The NetUID of the subnet to query.
field_indices: An optional list of SelectiveMetagraphIndex or int values specifying which fields to retrieve.
If not provided, all available fields will be returned.
block: The block number at which to query the data. If not specified, the current block or one determined
via reuse_block or block_hash will be used.

Returns:
MetagraphInfo dataclass
Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet
with the given netuid does not exist.

Example:
meta_info = subtensor.get_metagraph_info(netuid=2)

partial_meta_info = subtensor.get_metagraph_info(
netuid=2,
field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys]
)
"""
block_hash = self.determine_block_hash(block)
query = self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_metagraph",
params=[netuid],
block_hash=block_hash,
)

if field_indices:
if isinstance(field_indices, list) and all(
isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices
):
indexes = [
f.value if isinstance(f, SelectiveMetagraphIndex) else f
for f in field_indices
]
else:
raise ValueError(
"`field_indices` must be a list of SelectiveMetagraphIndex items."
)

query = self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_selective_metagraph",
params=[netuid, indexes if 0 in indexes else [0] + indexes],
block_hash=block_hash,
)
else:
query = self.substrate.runtime_call(
"SubnetInfoRuntimeApi",
"get_metagraph",
params=[netuid],
block_hash=block_hash,
)

if query.value is None:
logging.error(f"Subnet {netuid} does not exist.")
return None

return MetagraphInfo.from_dict(query.value)

def get_all_metagraphs_info(
Expand Down
21 changes: 19 additions & 2 deletions tests/e2e_tests/test_incentive.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,25 @@ async def test_incentive(local_chain, subtensor, templates, alice_wallet, bob_wa
continue
raise

# wait one tempo (fast block
subtensor.wait_for_block(subtensor.block + subtensor.tempo(alice_subnet_netuid))
# wait one tempo (fast block)
next_epoch_start_block = subtensor.subnets.get_next_epoch_start_block(
alice_subnet_netuid
)
subtensor.wait_for_block(next_epoch_start_block + tempo + 1)

validators = subtensor.metagraphs.get_metagraph_info(
alice_subnet_netuid, field_indices=[72]
).validators

alice_uid = subtensor.subnets.get_uid_for_hotkey_on_subnet(
hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid
)
assert validators[alice_uid] == 1

bob_uid = subtensor.subnets.get_uid_for_hotkey_on_subnet(
hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid
)
assert validators[bob_uid] == 0

while True:
try:
Expand Down
Loading