From d96324267afd19c6a31f9c05985f13fcffea6461 Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Thu, 12 Jun 2025 20:33:33 +0200 Subject: [PATCH 01/10] fix: improved type hints --- bittensor/core/async_subtensor.py | 57 +++++++++++++++----------- bittensor/core/chain_data/info_base.py | 10 ++--- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 77ec7afd47..4016e94c5b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3030,19 +3030,22 @@ async def set_reveal_commitment( # increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed # and stored. data_ = {"encrypted": encrypted, "reveal_round": reveal_round} - return await publish_metadata( - subtensor=self, - wallet=wallet, - netuid=netuid, - data_type="TimelockEncrypted", - data=data_, - period=period, - ), reveal_round + return ( + await publish_metadata( + subtensor=self, + wallet=wallet, + netuid=netuid, + data_type="TimelockEncrypted", + data=data_, + period=period, + ), + reveal_round, + ) async def subnet( self, netuid: int, - block: int = None, + block: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, ) -> Optional[DynamicInfo]: @@ -3052,23 +3055,26 @@ async def subnet( Args: netuid (int): The unique identifier of the subnet. block (Optional[int]): The block number to get the subnets at. - block_hash (str): The hash of the blockchain block number for the query. + block_hash (Optional[str]): The hash of the blockchain block number for the query. reuse_block (bool): Whether to reuse the last-used blockchain block hash. Returns: Optional[DynamicInfo]: A DynamicInfo object, containing detailed information about a subnet. """ 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_dynamic_info", params=[netuid], block_hash=block_hash, ) - subnet = DynamicInfo.from_dict(query.decode()) - return subnet + + if isinstance(decoded := query.decode(), dict): + return DynamicInfo.from_dict(decoded) async def subnet_exists( self, @@ -3216,7 +3222,9 @@ async def handler(block_data: dict): return None current_block = await self.substrate.get_block() + assert current_block is not None, "Failed to retrieve current block" current_block_hash = current_block.get("header", {}).get("hash") + if block is not None: target_block = block else: @@ -3314,16 +3322,16 @@ async def get_timestamp( Returns: datetime object for the timestamp of the block """ - unix = ( - await self.query_module( - "Timestamp", - "Now", - block=block, - block_hash=block_hash, - reuse_block=reuse_block, - ) - ).value - return datetime.fromtimestamp(unix / 1000, tz=timezone.utc) + unix = await self.query_module( + "Timestamp", + "Now", + block=block, + block_hash=block_hash, + reuse_block=reuse_block, + ) + assert unix is not None, "Failed to retrieve timestamp" + assert isinstance(unix.value, int), "Timestamp value is not an integer" + return datetime.fromtimestamp(unix.value / 1000, tz=timezone.utc) async def get_subnet_owner_hotkey( self, netuid: int, block: Optional[int] = None @@ -3341,9 +3349,12 @@ async def get_subnet_owner_hotkey( Returns: The hotkey of the subnet owner if available; None otherwise. """ - return await self.query_subtensor( + hotkey = await self.query_subtensor( name="SubnetOwnerHotkey", params=[netuid], block=block ) + assert hotkey is not None, "Failed to retrieve subnet owner hotkey" + assert isinstance(hotkey.value, str), "Subnet owner hotkey is not a string" + return hotkey.value async def get_subnet_validator_permits( self, netuid: int, block: Optional[int] = None diff --git a/bittensor/core/chain_data/info_base.py b/bittensor/core/chain_data/info_base.py index 1b17f068b3..722f9b32ee 100644 --- a/bittensor/core/chain_data/info_base.py +++ b/bittensor/core/chain_data/info_base.py @@ -1,17 +1,15 @@ from dataclasses import dataclass -from typing import Any, TypeVar +from typing import Any, Self from bittensor.core.errors import SubstrateRequestException -T = TypeVar("T", bound="InfoBase") - @dataclass class InfoBase: """Base dataclass for info objects.""" @classmethod - def from_dict(cls, decoded: dict) -> T: + def from_dict(cls, decoded: dict) -> Self: try: return cls._from_dict(decoded) except KeyError as e: @@ -20,9 +18,9 @@ def from_dict(cls, decoded: dict) -> T: ) @classmethod - def list_from_dicts(cls, any_list: list[Any]) -> list[T]: + def list_from_dicts(cls, any_list: list[Any]) -> list[Self]: return [cls.from_dict(any_) for any_ in any_list] @classmethod - def _from_dict(cls, decoded: dict) -> T: + def _from_dict(cls, decoded: dict) -> Self: return cls(**decoded) From be2452bd551b2a079066993c11b04c5b67321f23 Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Thu, 12 Jun 2025 20:42:47 +0200 Subject: [PATCH 02/10] rev: python3.9 does not yet have `typing.Self` --- bittensor/core/chain_data/info_base.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/bittensor/core/chain_data/info_base.py b/bittensor/core/chain_data/info_base.py index 722f9b32ee..6d747cd5b0 100644 --- a/bittensor/core/chain_data/info_base.py +++ b/bittensor/core/chain_data/info_base.py @@ -1,15 +1,18 @@ from dataclasses import dataclass -from typing import Any, Self +from typing import Any, TypeVar from bittensor.core.errors import SubstrateRequestException +# NOTE: once pyhton 3.10+ is required, we can use `typing.Self` instead of this +T = TypeVar("T", bound="InfoBase") + @dataclass class InfoBase: """Base dataclass for info objects.""" @classmethod - def from_dict(cls, decoded: dict) -> Self: + def from_dict(cls, decoded: dict) -> T: try: return cls._from_dict(decoded) except KeyError as e: @@ -18,9 +21,9 @@ def from_dict(cls, decoded: dict) -> Self: ) @classmethod - def list_from_dicts(cls, any_list: list[Any]) -> list[Self]: + def list_from_dicts(cls, any_list: list[Any]) -> list[T]: return [cls.from_dict(any_) for any_ in any_list] @classmethod - def _from_dict(cls, decoded: dict) -> Self: + def _from_dict(cls, decoded: dict) -> T: return cls(**decoded) From cb830169a9fe0360b65ae3445e52635b02971fe4 Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Thu, 12 Jun 2025 20:50:04 +0200 Subject: [PATCH 03/10] fix: should not get the value as a str is returned --- bittensor/core/async_subtensor.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 4016e94c5b..4eb306fe20 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3352,9 +3352,10 @@ async def get_subnet_owner_hotkey( hotkey = await self.query_subtensor( name="SubnetOwnerHotkey", params=[netuid], block=block ) - assert hotkey is not None, "Failed to retrieve subnet owner hotkey" - assert isinstance(hotkey.value, str), "Subnet owner hotkey is not a string" - return hotkey.value + assert hotkey is None or isinstance( + hotkey, str + ), f"Expected str or None, got {type(hotkey).__name__}" + return hotkey async def get_subnet_validator_permits( self, netuid: int, block: Optional[int] = None From 933e96938252c50b191ff83e7fe138c36ccd58bb Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Thu, 12 Jun 2025 21:02:32 +0200 Subject: [PATCH 04/10] refactor: ruff reformat --- bittensor/core/async_subtensor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 4eb306fe20..3084b85605 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3352,9 +3352,9 @@ async def get_subnet_owner_hotkey( hotkey = await self.query_subtensor( name="SubnetOwnerHotkey", params=[netuid], block=block ) - assert hotkey is None or isinstance( - hotkey, str - ), f"Expected str or None, got {type(hotkey).__name__}" + assert hotkey is None or isinstance(hotkey, str), ( + f"Expected str or None, got {type(hotkey).__name__}" + ) return hotkey async def get_subnet_validator_permits( From afa80d3c218d39763f4d2f464f1f4a1255331e1e Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Sun, 22 Jun 2025 21:32:58 +0200 Subject: [PATCH 05/10] rev: revert some changes according to PR review --- bittensor/core/async_subtensor.py | 25 ++++++++----------------- bittensor/core/chain_data/info_base.py | 3 ++- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8e6ed09d3e..bfdbb2275a 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3032,17 +3032,14 @@ async def set_reveal_commitment( # increase reveal_round in return + 1 because we want to fetch data from the chain after that round was revealed # and stored. data_ = {"encrypted": encrypted, "reveal_round": reveal_round} - return ( - await publish_metadata( - subtensor=self, - wallet=wallet, - netuid=netuid, - data_type="TimelockEncrypted", - data=data_, - period=period, - ), - reveal_round, - ) + return await publish_metadata( + subtensor=self, + wallet=wallet, + netuid=netuid, + data_type="TimelockEncrypted", + data=data_, + period=period, + ), reveal_round, async def subnet( self, @@ -3224,7 +3221,6 @@ async def handler(block_data: dict): return None current_block = await self.substrate.get_block() - assert current_block is not None, "Failed to retrieve current block" current_block_hash = current_block.get("header", {}).get("hash") if block is not None: @@ -3331,8 +3327,6 @@ async def get_timestamp( block_hash=block_hash, reuse_block=reuse_block, ) - assert unix is not None, "Failed to retrieve timestamp" - assert isinstance(unix.value, int), "Timestamp value is not an integer" return datetime.fromtimestamp(unix.value / 1000, tz=timezone.utc) async def get_subnet_owner_hotkey( @@ -3354,9 +3348,6 @@ async def get_subnet_owner_hotkey( hotkey = await self.query_subtensor( name="SubnetOwnerHotkey", params=[netuid], block=block ) - assert hotkey is None or isinstance(hotkey, str), ( - f"Expected str or None, got {type(hotkey).__name__}" - ) return hotkey async def get_subnet_validator_permits( diff --git a/bittensor/core/chain_data/info_base.py b/bittensor/core/chain_data/info_base.py index 6d747cd5b0..e2718619af 100644 --- a/bittensor/core/chain_data/info_base.py +++ b/bittensor/core/chain_data/info_base.py @@ -3,7 +3,8 @@ from bittensor.core.errors import SubstrateRequestException -# NOTE: once pyhton 3.10+ is required, we can use `typing.Self` instead of this +# NOTE: once Python 3.10+ is required, we can use `typing.Self` instead of this for better ide integration and type hinting. +# This current generic does not play so nice with the inherited type hinting. T = TypeVar("T", bound="InfoBase") From 525d9c597b98379c8661dbcc487d75d4e361bd8f Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Sun, 22 Jun 2025 21:36:21 +0200 Subject: [PATCH 06/10] fix: should only return tuple of 2 --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index bfdbb2275a..8b0d8b1c9b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3039,7 +3039,7 @@ async def set_reveal_commitment( data_type="TimelockEncrypted", data=data_, period=period, - ), reveal_round, + ), reveal_round async def subnet( self, From 5bac5b2f1a8adeaec2c29d72277b6584206763f1 Mon Sep 17 00:00:00 2001 From: Arthur Date: Mon, 23 Jun 2025 12:44:47 +0200 Subject: [PATCH 07/10] rev: inline as there are no assertions anymore in bittensor/core/async_subtensor.py Co-authored-by: Roman <167799377+basfroman@users.noreply.github.com> --- bittensor/core/async_subtensor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 5a0ea33840..e6f448e919 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3362,10 +3362,9 @@ async def get_subnet_owner_hotkey( Returns: The hotkey of the subnet owner if available; None otherwise. """ - hotkey = await self.query_subtensor( + return await self.query_subtensor( name="SubnetOwnerHotkey", params=[netuid], block=block ) - return hotkey async def get_subnet_validator_permits( self, netuid: int, block: Optional[int] = None From a6cb1f76ce479b720740f87f3b4befae74706b21 Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Mon, 23 Jun 2025 12:46:16 +0200 Subject: [PATCH 08/10] fix: should directly access the value on the unix prop --- bittensor/core/async_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8b0d8b1c9b..a936d656ca 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3326,8 +3326,8 @@ async def get_timestamp( block=block, block_hash=block_hash, reuse_block=reuse_block, - ) - return datetime.fromtimestamp(unix.value / 1000, tz=timezone.utc) + ).value + return datetime.fromtimestamp(unix / 1000, tz=timezone.utc) async def get_subnet_owner_hotkey( self, netuid: int, block: Optional[int] = None From 495b3da2f1ce7f6f34f08dfce44c16cf436712e4 Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Tue, 24 Jun 2025 12:50:53 +0200 Subject: [PATCH 09/10] fix: lets not wrap the coroutine in brackets and just define it like this --- bittensor/core/async_subtensor.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index d573bd72b1..37b5267dbf 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3337,13 +3337,14 @@ async def get_timestamp( Returns: datetime object for the timestamp of the block """ - unix = await self.query_module( - "Timestamp", - "Now", + res = await self.query_module( + "timestamp", + "now", block=block, block_hash=block_hash, reuse_block=reuse_block, - ).value + ) + unix = res.value return datetime.fromtimestamp(unix / 1000, tz=timezone.utc) async def get_subnet_owner_hotkey( From 557b650d840208a773b7497cd6446c98497c28e9 Mon Sep 17 00:00:00 2001 From: Arthurdw Date: Tue, 24 Jun 2025 17:11:19 +0200 Subject: [PATCH 10/10] fix: idk how these got changed? --- bittensor/core/async_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 37b5267dbf..dffc8ebfa6 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3338,8 +3338,8 @@ async def get_timestamp( datetime object for the timestamp of the block """ res = await self.query_module( - "timestamp", - "now", + "Timestamp", + "Now", block=block, block_hash=block_hash, reuse_block=reuse_block,