From 8000b16670f8b45c5cb7e1b27a88746bda3201e3 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 08:20:48 -0700 Subject: [PATCH 01/25] initial --- bittensor/core/subtensor_api.py | 130 ++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 bittensor/core/subtensor_api.py diff --git a/bittensor/core/subtensor_api.py b/bittensor/core/subtensor_api.py new file mode 100644 index 0000000000..a28c1ae82b --- /dev/null +++ b/bittensor/core/subtensor_api.py @@ -0,0 +1,130 @@ +from typing import Optional, Union, TYPE_CHECKING +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + +if TYPE_CHECKING: + from bittensor.core.config import Config + + +class _Extrinsics: + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.add_stake = subtensor.add_stake + self.add_stake_multiple = subtensor.add_stake_multiple + self.burned_register = subtensor.burned_register + self.commit_weights = subtensor.commit_weights + self.move_stake = subtensor.move_stake + self.register = subtensor.register + self.register_subnet = subtensor.register_subnet + self.reveal_weights = subtensor.reveal_weights + self.root_register = subtensor.root_register + self.root_set_weights = subtensor.root_set_weights + self.set_subnet_identity = subtensor.set_subnet_identity + self.set_weights = subtensor.set_weights + self.serve_axon = subtensor.serve_axon + self.start_call = subtensor.start_call + self.swap_stake = subtensor.swap_stake + self.transfer = subtensor.transfer + self.transfer_stake = subtensor.transfer_stake + self.unstake = subtensor.unstake + self.unstake_multiple = subtensor.unstake_multiple + + +class _Queries: + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.query_constant = subtensor.query_constant + self.query_map = subtensor.query_map + self.query_map_subtensor = subtensor.query_map_subtensor + self.query_module = subtensor.query_module + self.query_runtime_api = subtensor.query_runtime_api + self.query_subtensor = subtensor.query_subtensor + + +class _Subnets: + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.all_subnets = subtensor.all_subnets + self.get_all_subnets_info = subtensor.get_all_subnets_info + self.get_neuron_for_pubkey_and_subnet = subtensor.get_neuron_for_pubkey_and_subnet + self.get_subnet_burn_cost = subtensor.get_subnet_burn_cost + self.get_subnet_hyperparameters = subtensor.get_subnet_hyperparameters + self.get_subnet_owner_hotkey = subtensor.get_subnet_owner_hotkey + self.get_subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs + self.get_subnet_validator_permits = subtensor.get_subnet_validator_permits + self.get_subnets = subtensor.get_subnets + self.get_total_subnets = subtensor.get_total_subnets + self.get_uid_for_hotkey_on_subnet = subtensor.get_uid_for_hotkey_on_subnet + self.is_hotkey_registered_on_subnet = subtensor.is_hotkey_registered_on_subnet + self.register_subnet = subtensor.register_subnet + self.set_subnet_identity = subtensor.set_subnet_identity + self.subnet = subtensor.subnet + self.subnet_exists = subtensor.subnet_exists + self.subnetwork_n = subtensor.subnetwork_n + + +class SubtensorApi: + def __init__( + self, + network: Optional[str] = None, + config: Optional["Config"] = None, + _mock: bool = False, + log_verbose: bool = False, + async_subtensor: bool = False, + ): + if async_subtensor: + self._subtensor = _AsyncSubtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) + else: + self._subtensor = _Subtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) + + self.network = network + self._mock = _mock + self.log_verbose = log_verbose + self.is_async = async_subtensor + + self.config = None + self.start_call = None + self.chain_endpoint = None + self.substrate = None + self.close = None + + self.add_important_fields() + + def add_important_fields(self): + """Adds important fields from the subtensor instance to this API instance.""" + self.substrate = self._subtensor.substrate + self.chain_endpoint = self._subtensor.chain_endpoint + self.config = self._subtensor.config + self.start_call = self._subtensor.start_call + self.close = self._subtensor.close + + def __str__(self): + return f"" + + def __repr__(self): + return self.__str__() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + async def __aenter__(self): + return await self._subtensor.__aenter__() + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.substrate.close() + + @property + def block(self): + return self._subtensor.block + + @property + def extrinsics(self): + return _Extrinsics(self._subtensor) + + @property + def queries(self): + return _Queries(self._subtensor) + + @property + def subnets(self): + return _Subnets(self._subtensor) From 8621ede42f05557889860399ee7bb3320e629c75 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:01:51 -0700 Subject: [PATCH 02/25] add sub-package --- bittensor/core/subtensor_api/__init__.py | 155 +++++++++++++++++++++++ bittensor/utils/easy_imports.py | 1 + 2 files changed, 156 insertions(+) create mode 100644 bittensor/core/subtensor_api/__init__.py diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py new file mode 100644 index 0000000000..c96080d04f --- /dev/null +++ b/bittensor/core/subtensor_api/__init__.py @@ -0,0 +1,155 @@ +from typing import Optional, Union, TYPE_CHECKING + +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor +from bittensor.core.subtensor import Subtensor as _Subtensor +from .chain import Chain as _Chain +from .commitments import Commitments as _Commitments +from .delegates import Delegates as _Delegates +from .extrinsics import Extrinsics as _Extrinsics +from .metagraph import Metagraphs as _Metagraphs +from .neurons import Neurons as _Neurons +from .queries import Queries as _Queries +from .stakes import Stakes as _Stakes +from .subnet import Subnet as _Subnet +from .wallet import Wallet as _Wallet + +if TYPE_CHECKING: + from bittensor.core.config import Config + + +class SubtensorApi: + """Subtensor API class. + + Arguments: + network: The network to connect to. Defaults to `None` -> `finney`. + config: Bittensor configuration object. Defaults to `None`. + log_verbose: If true, sets the subtensor to log verbosely. Defaults to `False`. + async_subtensor: If true, uses the async subtensor to create the connection. Defaults to `False`. + + Example: + # sync version + import bittensor as bt + + subtensor = bt.SubtensorApi() + print(subtensor.block) + print(subtensor.delegates.get_delegate_identities()) + subtensor.chain.tx_rate_limit() + + # async version + import bittensor as bt + + subtensor = bt.SubtensorApi(async_subtensor=True) + async with subtensor: + print(await subtensor.block) + print(await subtensor.delegates.get_delegate_identities()) + print(await subtensor.chain.tx_rate_limit()) + """ + + def __init__( + self, + network: Optional[str] = None, + config: Optional["Config"] = None, + log_verbose: bool = False, + async_subtensor: bool = False, + _mock: bool = False, + ): + self.network = network + self._mock = _mock + self.log_verbose = log_verbose + self.is_async = async_subtensor + self._config = config + self._subtensor = self._get_subtensor() + + # define empty fields + self.substrate = self._subtensor.substrate + self.add_args = self._subtensor.add_args + self.chain_endpoint = self._subtensor.chain_endpoint + self.close = self._subtensor.close + self.config = self._subtensor.config + self.setup_config = self._subtensor.setup_config + self.help = self._subtensor.help + + self.determine_block_hash = self._subtensor.determine_block_hash + self.encode_params = self._subtensor.encode_params + self.sign_and_send_extrinsic = self._subtensor.sign_and_send_extrinsic + self.start_call = self._subtensor.start_call + self.wait_for_block = self._subtensor.wait_for_block + + def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: + """Returns the subtensor instance based on the provided config and subtensor type flag.""" + if self.is_async: + return _AsyncSubtensor( + network=self.network, + config=self._config, + _mock=self._mock, + log_verbose=self.log_verbose, + ) + else: + return _Subtensor( + network=self.network, + config=self._config, + _mock=self._mock, + log_verbose=self.log_verbose, + ) + + def __str__(self): + return f"" + + def __repr__(self): + return self.__str__() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + async def __aenter__(self): + return await self._subtensor.__aenter__() + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.substrate.close() + + @property + def block(self): + return self._subtensor.block + + @property + def chain(self): + return _Chain(self._subtensor) + + @property + def commitments(self): + return _Commitments(self._subtensor) + + @property + def delegates(self): + return _Delegates(self._subtensor) + + @property + def extrinsics(self): + return _Extrinsics(self._subtensor) + + @property + def metagraphs(self): + return _Metagraphs(self._subtensor) + + @property + def neurons(self): + return _Neurons(self._subtensor) + + @property + def queries(self): + return _Queries(self._subtensor) + + @property + def stakes(self): + return _Stakes(self._subtensor) + + @property + def subnet(self): + return _Subnet(self._subtensor) + + @property + def wallet(self): + return _Wallet(self._subtensor) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index ea0e72bce9..8fa826d4d9 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -104,6 +104,7 @@ from bittensor.core.settings import BLOCKTIME from bittensor.core.stream import StreamingSynapse # noqa: F401 from bittensor.core.subtensor import Subtensor +from bittensor.core.subtensor_api import SubtensorApi # noqa: F401 from bittensor.core.synapse import TerminalInfo, Synapse # noqa: F401 from bittensor.core.tensor import Tensor # noqa: F401 from bittensor.core.threadpool import ( # noqa: F401 From 36c447fa84ff8b9aa173eed123670a2e01325169 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:02:47 -0700 Subject: [PATCH 03/25] add sub-classes --- bittensor/core/subtensor_api.py | 130 -------------------- bittensor/core/subtensor_api/chain.py | 19 +++ bittensor/core/subtensor_api/commitments.py | 18 +++ bittensor/core/subtensor_api/delegates.py | 16 +++ bittensor/core/subtensor_api/extrinsics.py | 29 +++++ bittensor/core/subtensor_api/metagraph.py | 12 ++ bittensor/core/subtensor_api/neurons.py | 15 +++ bittensor/core/subtensor_api/queries.py | 15 +++ bittensor/core/subtensor_api/stakes.py | 22 ++++ bittensor/core/subtensor_api/subnet.py | 46 +++++++ bittensor/core/subtensor_api/wallet.py | 36 ++++++ 11 files changed, 228 insertions(+), 130 deletions(-) delete mode 100644 bittensor/core/subtensor_api.py create mode 100644 bittensor/core/subtensor_api/chain.py create mode 100644 bittensor/core/subtensor_api/commitments.py create mode 100644 bittensor/core/subtensor_api/delegates.py create mode 100644 bittensor/core/subtensor_api/extrinsics.py create mode 100644 bittensor/core/subtensor_api/metagraph.py create mode 100644 bittensor/core/subtensor_api/neurons.py create mode 100644 bittensor/core/subtensor_api/queries.py create mode 100644 bittensor/core/subtensor_api/stakes.py create mode 100644 bittensor/core/subtensor_api/subnet.py create mode 100644 bittensor/core/subtensor_api/wallet.py diff --git a/bittensor/core/subtensor_api.py b/bittensor/core/subtensor_api.py deleted file mode 100644 index a28c1ae82b..0000000000 --- a/bittensor/core/subtensor_api.py +++ /dev/null @@ -1,130 +0,0 @@ -from typing import Optional, Union, TYPE_CHECKING -from bittensor.core.subtensor import Subtensor as _Subtensor -from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor - -if TYPE_CHECKING: - from bittensor.core.config import Config - - -class _Extrinsics: - def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): - self.add_stake = subtensor.add_stake - self.add_stake_multiple = subtensor.add_stake_multiple - self.burned_register = subtensor.burned_register - self.commit_weights = subtensor.commit_weights - self.move_stake = subtensor.move_stake - self.register = subtensor.register - self.register_subnet = subtensor.register_subnet - self.reveal_weights = subtensor.reveal_weights - self.root_register = subtensor.root_register - self.root_set_weights = subtensor.root_set_weights - self.set_subnet_identity = subtensor.set_subnet_identity - self.set_weights = subtensor.set_weights - self.serve_axon = subtensor.serve_axon - self.start_call = subtensor.start_call - self.swap_stake = subtensor.swap_stake - self.transfer = subtensor.transfer - self.transfer_stake = subtensor.transfer_stake - self.unstake = subtensor.unstake - self.unstake_multiple = subtensor.unstake_multiple - - -class _Queries: - def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): - self.query_constant = subtensor.query_constant - self.query_map = subtensor.query_map - self.query_map_subtensor = subtensor.query_map_subtensor - self.query_module = subtensor.query_module - self.query_runtime_api = subtensor.query_runtime_api - self.query_subtensor = subtensor.query_subtensor - - -class _Subnets: - def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): - self.all_subnets = subtensor.all_subnets - self.get_all_subnets_info = subtensor.get_all_subnets_info - self.get_neuron_for_pubkey_and_subnet = subtensor.get_neuron_for_pubkey_and_subnet - self.get_subnet_burn_cost = subtensor.get_subnet_burn_cost - self.get_subnet_hyperparameters = subtensor.get_subnet_hyperparameters - self.get_subnet_owner_hotkey = subtensor.get_subnet_owner_hotkey - self.get_subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs - self.get_subnet_validator_permits = subtensor.get_subnet_validator_permits - self.get_subnets = subtensor.get_subnets - self.get_total_subnets = subtensor.get_total_subnets - self.get_uid_for_hotkey_on_subnet = subtensor.get_uid_for_hotkey_on_subnet - self.is_hotkey_registered_on_subnet = subtensor.is_hotkey_registered_on_subnet - self.register_subnet = subtensor.register_subnet - self.set_subnet_identity = subtensor.set_subnet_identity - self.subnet = subtensor.subnet - self.subnet_exists = subtensor.subnet_exists - self.subnetwork_n = subtensor.subnetwork_n - - -class SubtensorApi: - def __init__( - self, - network: Optional[str] = None, - config: Optional["Config"] = None, - _mock: bool = False, - log_verbose: bool = False, - async_subtensor: bool = False, - ): - if async_subtensor: - self._subtensor = _AsyncSubtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) - else: - self._subtensor = _Subtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) - - self.network = network - self._mock = _mock - self.log_verbose = log_verbose - self.is_async = async_subtensor - - self.config = None - self.start_call = None - self.chain_endpoint = None - self.substrate = None - self.close = None - - self.add_important_fields() - - def add_important_fields(self): - """Adds important fields from the subtensor instance to this API instance.""" - self.substrate = self._subtensor.substrate - self.chain_endpoint = self._subtensor.chain_endpoint - self.config = self._subtensor.config - self.start_call = self._subtensor.start_call - self.close = self._subtensor.close - - def __str__(self): - return f"" - - def __repr__(self): - return self.__str__() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - - async def __aenter__(self): - return await self._subtensor.__aenter__() - - async def __aexit__(self, exc_type, exc_val, exc_tb): - await self.substrate.close() - - @property - def block(self): - return self._subtensor.block - - @property - def extrinsics(self): - return _Extrinsics(self._subtensor) - - @property - def queries(self): - return _Queries(self._subtensor) - - @property - def subnets(self): - return _Subnets(self._subtensor) diff --git a/bittensor/core/subtensor_api/chain.py b/bittensor/core/subtensor_api/chain.py new file mode 100644 index 0000000000..fe03aada99 --- /dev/null +++ b/bittensor/core/subtensor_api/chain.py @@ -0,0 +1,19 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Chain: + """Class for managing chain state operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.get_block_hash = subtensor.get_block_hash + self.get_current_block = subtensor.get_current_block + self.get_delegate_identities = subtensor.get_delegate_identities + self.get_existential_deposit = subtensor.get_existential_deposit + self.get_minimum_required_stake = subtensor.get_minimum_required_stake + self.get_vote_data = subtensor.get_vote_data + self.get_timestamp = subtensor.get_timestamp + self.last_drand_round = subtensor.last_drand_round + self.state_call = subtensor.state_call + self.tx_rate_limit = subtensor.tx_rate_limit diff --git a/bittensor/core/subtensor_api/commitments.py b/bittensor/core/subtensor_api/commitments.py new file mode 100644 index 0000000000..26c2163a4b --- /dev/null +++ b/bittensor/core/subtensor_api/commitments.py @@ -0,0 +1,18 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Commitments: + """Class for managing any commitment operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.commit_reveal_enabled = subtensor.commit_reveal_enabled + self.get_all_commitments = subtensor.get_all_commitments + self.get_all_revealed_commitments = subtensor.get_all_revealed_commitments + self.get_commitment = subtensor.get_commitment + self.get_current_weight_commit_info = subtensor.get_current_weight_commit_info + self.get_revealed_commitment = subtensor.get_revealed_commitment + self.get_revealed_commitment_by_hotkey = subtensor.get_revealed_commitment_by_hotkey + self.set_commitment = subtensor.set_commitment + self.set_reveal_commitment = subtensor.set_reveal_commitment diff --git a/bittensor/core/subtensor_api/delegates.py b/bittensor/core/subtensor_api/delegates.py new file mode 100644 index 0000000000..1cdbfca08c --- /dev/null +++ b/bittensor/core/subtensor_api/delegates.py @@ -0,0 +1,16 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Delegates: + """Class for managing delegate operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.is_hotkey_delegate = subtensor.is_hotkey_delegate + self.get_delegate_by_hotkey = subtensor.get_delegate_by_hotkey + self.set_delegate_take = subtensor.set_delegate_take + self.get_delegate_identities = subtensor.get_delegate_identities + self.get_delegate_take = subtensor.get_delegate_take + self.get_delegated = subtensor.get_delegated + self.get_delegates = subtensor.get_delegates diff --git a/bittensor/core/subtensor_api/extrinsics.py b/bittensor/core/subtensor_api/extrinsics.py new file mode 100644 index 0000000000..0ff4439201 --- /dev/null +++ b/bittensor/core/subtensor_api/extrinsics.py @@ -0,0 +1,29 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Extrinsics: + """Class for managing extrinsic operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.add_stake = subtensor.add_stake + self.add_stake_multiple = subtensor.add_stake_multiple + self.burned_register = subtensor.burned_register + self.commit_weights = subtensor.commit_weights + self.move_stake = subtensor.move_stake + self.register = subtensor.register + self.register_subnet = subtensor.register_subnet + self.reveal_weights = subtensor.reveal_weights + self.root_register = subtensor.root_register + self.root_set_weights = subtensor.root_set_weights + self.set_children = subtensor.set_children + self.set_subnet_identity = subtensor.set_subnet_identity + self.set_weights = subtensor.set_weights + self.serve_axon = subtensor.serve_axon + self.start_call = subtensor.start_call + self.swap_stake = subtensor.swap_stake + self.transfer = subtensor.transfer + self.transfer_stake = subtensor.transfer_stake + self.unstake = subtensor.unstake + self.unstake_multiple = subtensor.unstake_multiple diff --git a/bittensor/core/subtensor_api/metagraph.py b/bittensor/core/subtensor_api/metagraph.py new file mode 100644 index 0000000000..af143a1620 --- /dev/null +++ b/bittensor/core/subtensor_api/metagraph.py @@ -0,0 +1,12 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Metagraphs: + """Class for managing metagraph operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.get_metagraph_info = subtensor.get_metagraph_info + self.get_all_metagraphs_info = subtensor.get_all_metagraphs_info + self.metagraph = subtensor.metagraph diff --git a/bittensor/core/subtensor_api/neurons.py b/bittensor/core/subtensor_api/neurons.py new file mode 100644 index 0000000000..c1fd40066f --- /dev/null +++ b/bittensor/core/subtensor_api/neurons.py @@ -0,0 +1,15 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Neurons: + """Class for managing neuron operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.get_all_neuron_certificates = subtensor.get_all_neuron_certificates + self.get_neuron_certificate = subtensor.get_neuron_certificate + self.neuron_for_uid = subtensor.neuron_for_uid + self.neurons = subtensor.neurons + self.neurons_lite = subtensor.neurons_lite + self.query_identity = subtensor.query_identity diff --git a/bittensor/core/subtensor_api/queries.py b/bittensor/core/subtensor_api/queries.py new file mode 100644 index 0000000000..7209ffb7ae --- /dev/null +++ b/bittensor/core/subtensor_api/queries.py @@ -0,0 +1,15 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Queries: + """Class for managing subtensor query operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.query_constant = subtensor.query_constant + self.query_map = subtensor.query_map + self.query_map_subtensor = subtensor.query_map_subtensor + self.query_module = subtensor.query_module + self.query_runtime_api = subtensor.query_runtime_api + self.query_subtensor = subtensor.query_subtensor diff --git a/bittensor/core/subtensor_api/stakes.py b/bittensor/core/subtensor_api/stakes.py new file mode 100644 index 0000000000..442f98cad6 --- /dev/null +++ b/bittensor/core/subtensor_api/stakes.py @@ -0,0 +1,22 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Stakes: + """Class for managing stake operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.add_stake = subtensor.add_stake + self.add_stake_multiple = subtensor.add_stake_multiple + self.get_hotkey_stake = subtensor.get_hotkey_stake + self.get_minimum_required_stake = subtensor.get_minimum_required_stake + self.get_stake = subtensor.get_stake + self.get_stake_add_fee = subtensor.get_stake_add_fee + self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey + self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey + self.get_stake_movement_fee = subtensor.get_stake_movement_fee + self.get_unstake_fee = subtensor.get_unstake_fee + self.unstake = subtensor.unstake + self.unstake_multiple = subtensor.unstake_multiple diff --git a/bittensor/core/subtensor_api/subnet.py b/bittensor/core/subtensor_api/subnet.py new file mode 100644 index 0000000000..1fb3d0f911 --- /dev/null +++ b/bittensor/core/subtensor_api/subnet.py @@ -0,0 +1,46 @@ +from typing import Union + +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor +from bittensor.core.subtensor import Subtensor as _Subtensor + + +class Subnet: + """Class for managing subnet operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.all_subnets = subtensor.all_subnets + self.blocks_since_last_step = subtensor.blocks_since_last_step + self.blocks_since_last_update = subtensor.blocks_since_last_update + self.bonds = subtensor.bonds + self.difficulty = subtensor.difficulty + self.get_all_subnets_info = subtensor.get_all_subnets_info + self.get_children = subtensor.get_children + self.get_children_pending = subtensor.get_children_pending + self.get_current_weight_commit_info = subtensor.get_current_weight_commit_info + self.get_hyperparameter = subtensor.get_hyperparameter + self.get_neuron_for_pubkey_and_subnet = ( + subtensor.get_neuron_for_pubkey_and_subnet + ) + self.get_next_epoch_start_block = subtensor.get_next_epoch_start_block + self.get_subnet_burn_cost = subtensor.get_subnet_burn_cost + self.get_subnet_hyperparameters = subtensor.get_subnet_hyperparameters + self.get_subnet_owner_hotkey = subtensor.get_subnet_owner_hotkey + self.get_subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs + self.get_subnet_validator_permits = subtensor.get_subnet_validator_permits + self.get_subnets = subtensor.get_subnets + self.get_total_subnets = subtensor.get_total_subnets + self.get_uid_for_hotkey_on_subnet = subtensor.get_uid_for_hotkey_on_subnet + self.immunity_period = subtensor.immunity_period + self.is_hotkey_registered_on_subnet = subtensor.is_hotkey_registered_on_subnet + self.max_weight_limit = subtensor.max_weight_limit + self.min_allowed_weights = subtensor.min_allowed_weights + self.recycle = subtensor.recycle + self.register_subnet = subtensor.register_subnet + self.set_subnet_identity = subtensor.set_subnet_identity + self.subnet = subtensor.subnet + self.subnet_exists = subtensor.subnet_exists + self.subnetwork_n = subtensor.subnetwork_n + self.tempo = subtensor.tempo + self.weights_rate_limit = subtensor.weights_rate_limit + self.weights = subtensor.weights + diff --git a/bittensor/core/subtensor_api/wallet.py b/bittensor/core/subtensor_api/wallet.py new file mode 100644 index 0000000000..348d786608 --- /dev/null +++ b/bittensor/core/subtensor_api/wallet.py @@ -0,0 +1,36 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Wallet: + """Class for managing coldkey, hotkey, wallet operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.does_hotkey_exist = subtensor.does_hotkey_exist + self.filter_netuids_by_registered_hotkeys = subtensor.filter_netuids_by_registered_hotkeys + self.is_hotkey_registered_any = subtensor.is_hotkey_registered_any + self.is_hotkey_registered = subtensor.is_hotkey_registered + self.is_hotkey_delegate = subtensor.is_hotkey_delegate + self.get_balance = subtensor.get_balance + self.get_balances = subtensor.get_balances + self.get_children = subtensor.get_children + self.get_children_pending = subtensor.get_children_pending + self.get_delegate_by_hotkey = subtensor.get_delegate_by_hotkey + self.get_delegate_take = subtensor.get_delegate_take + self.get_delegated = subtensor.get_delegated + self.get_hotkey_owner = subtensor.get_hotkey_owner + self.get_hotkey_stake = subtensor.get_hotkey_stake + self.get_minimum_required_stake = subtensor.get_minimum_required_stake + self.get_netuids_for_hotkey = subtensor.get_netuids_for_hotkey + self.get_owned_hotkeys = subtensor.get_owned_hotkeys + self.get_stake = subtensor.get_stake + self.get_stake_add_fee = subtensor.get_stake_add_fee + self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey + self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_for_hotkey = subtensor.get_stake_for_hotkey + self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey + self.get_stake_movement_fee = subtensor.get_stake_movement_fee + self.get_transfer_fee = subtensor.get_transfer_fee + self.get_unstake_fee = subtensor.get_unstake_fee + From 0068f2ab511dc3d935a8f878e44dca91252eb2d2 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:28:34 -0700 Subject: [PATCH 04/25] add `utils.add_classic_fields` --- bittensor/core/subtensor_api/utils.py | 156 ++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 bittensor/core/subtensor_api/utils.py diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py new file mode 100644 index 0000000000..d7643c6c0d --- /dev/null +++ b/bittensor/core/subtensor_api/utils.py @@ -0,0 +1,156 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from bittensor.core.subtensor_api import SubtensorApi + + +def add_classic_fields(subtensor: "SubtensorApi"): + """If SubtensorApi get `subtensor_fields=True` arguments, then all classic Subtensor fields added to root level.""" + subtensor.add_stake = subtensor._subtensor.add_stake + subtensor.add_stake_multiple = subtensor._subtensor.add_stake_multiple + subtensor.all_subnets = subtensor._subtensor.all_subnets + subtensor.blocks_since_last_step = subtensor._subtensor.blocks_since_last_step + subtensor.blocks_since_last_update = subtensor._subtensor.blocks_since_last_update + subtensor.bonds = subtensor._subtensor.bonds + subtensor.burned_register = subtensor._subtensor.burned_register + subtensor.chain_endpoint = subtensor._subtensor.chain_endpoint + subtensor.commit = subtensor._subtensor.commit + subtensor.commit_reveal_enabled = subtensor._subtensor.commit_reveal_enabled + subtensor.commit_weights = subtensor._subtensor.commit_weights + subtensor.determine_block_hash = subtensor._subtensor.determine_block_hash + subtensor.difficulty = subtensor._subtensor.difficulty + subtensor.does_hotkey_exist = subtensor._subtensor.does_hotkey_exist + subtensor.encode_params = subtensor._subtensor.encode_params + subtensor.filter_netuids_by_registered_hotkeys = ( + subtensor._subtensor.filter_netuids_by_registered_hotkeys + ) + subtensor.get_all_commitments = subtensor._subtensor.get_all_commitments + subtensor.get_all_metagraphs_info = subtensor._subtensor.get_all_metagraphs_info + subtensor.get_all_neuron_certificates = ( + subtensor._subtensor.get_all_neuron_certificates + ) + subtensor.get_all_revealed_commitments = ( + subtensor._subtensor.get_all_revealed_commitments + ) + subtensor.get_all_subnets_info = subtensor._subtensor.get_all_subnets_info + subtensor.get_balance = subtensor._subtensor.get_balance + subtensor.get_balances = subtensor._subtensor.get_balances + subtensor.get_block_hash = subtensor._subtensor.get_block_hash + subtensor.get_children = subtensor._subtensor.get_children + subtensor.get_children_pending = subtensor._subtensor.get_children_pending + subtensor.get_commitment = subtensor._subtensor.get_commitment + subtensor.get_current_block = subtensor._subtensor.get_current_block + subtensor.get_current_weight_commit_info = ( + subtensor._subtensor.get_current_weight_commit_info + ) + subtensor.get_delegate_by_hotkey = subtensor._subtensor.get_delegate_by_hotkey + subtensor.get_delegate_identities = subtensor._subtensor.get_delegate_identities + subtensor.get_delegate_take = subtensor._subtensor.get_delegate_take + subtensor.get_delegated = subtensor._subtensor.get_delegated + subtensor.get_delegates = subtensor._subtensor.get_delegates + subtensor.get_existential_deposit = subtensor._subtensor.get_existential_deposit + subtensor.get_hotkey_owner = subtensor._subtensor.get_hotkey_owner + subtensor.get_hotkey_stake = subtensor._subtensor.get_hotkey_stake + subtensor.get_hyperparameter = subtensor._subtensor.get_hyperparameter + subtensor.get_metagraph_info = subtensor._subtensor.get_metagraph_info + subtensor.get_minimum_required_stake = ( + subtensor._subtensor.get_minimum_required_stake + ) + subtensor.get_netuids_for_hotkey = subtensor._subtensor.get_netuids_for_hotkey + subtensor.get_neuron_certificate = subtensor._subtensor.get_neuron_certificate + subtensor.get_neuron_for_pubkey_and_subnet = ( + subtensor._subtensor.get_neuron_for_pubkey_and_subnet + ) + subtensor.get_next_epoch_start_block = ( + subtensor._subtensor.get_next_epoch_start_block + ) + subtensor.get_owned_hotkeys = subtensor._subtensor.get_owned_hotkeys + subtensor.get_revealed_commitment = subtensor._subtensor.get_revealed_commitment + subtensor.get_revealed_commitment_by_hotkey = ( + subtensor._subtensor.get_revealed_commitment_by_hotkey + ) + subtensor.get_stake = subtensor._subtensor.get_stake + subtensor.get_stake_add_fee = subtensor._subtensor.get_stake_add_fee + subtensor.get_stake_for_coldkey = subtensor._subtensor.get_stake_for_coldkey + subtensor.get_stake_for_coldkey_and_hotkey = ( + subtensor._subtensor.get_stake_for_coldkey_and_hotkey + ) + subtensor.get_stake_for_hotkey = subtensor._subtensor.get_stake_for_hotkey + subtensor.get_stake_info_for_coldkey = ( + subtensor._subtensor.get_stake_info_for_coldkey + ) + subtensor.get_stake_movement_fee = subtensor._subtensor.get_stake_movement_fee + subtensor.get_subnet_burn_cost = subtensor._subtensor.get_subnet_burn_cost + subtensor.get_subnet_hyperparameters = ( + subtensor._subtensor.get_subnet_hyperparameters + ) + subtensor.get_subnet_owner_hotkey = subtensor._subtensor.get_subnet_owner_hotkey + subtensor.get_subnet_reveal_period_epochs = ( + subtensor._subtensor.get_subnet_reveal_period_epochs + ) + subtensor.get_subnet_validator_permits = ( + subtensor._subtensor.get_subnet_validator_permits + ) + subtensor.get_subnets = subtensor._subtensor.get_subnets + subtensor.get_timestamp = subtensor._subtensor.get_timestamp + subtensor.get_total_subnets = subtensor._subtensor.get_total_subnets + subtensor.get_transfer_fee = subtensor._subtensor.get_transfer_fee + subtensor.get_uid_for_hotkey_on_subnet = ( + subtensor._subtensor.get_uid_for_hotkey_on_subnet + ) + subtensor.get_unstake_fee = subtensor._subtensor.get_unstake_fee + subtensor.get_vote_data = subtensor._subtensor.get_vote_data + subtensor.immunity_period = subtensor._subtensor.immunity_period + subtensor.is_hotkey_delegate = subtensor._subtensor.is_hotkey_delegate + subtensor.is_hotkey_registered = subtensor._subtensor.is_hotkey_registered + subtensor.is_hotkey_registered_any = subtensor._subtensor.is_hotkey_registered_any + subtensor.is_hotkey_registered_on_subnet = ( + subtensor._subtensor.is_hotkey_registered_on_subnet + ) + subtensor.last_drand_round = subtensor._subtensor.last_drand_round + subtensor.log_verbose = subtensor._subtensor.log_verbose + subtensor.max_weight_limit = subtensor._subtensor.max_weight_limit + subtensor.metagraph = subtensor._subtensor.metagraph + subtensor.min_allowed_weights = subtensor._subtensor.min_allowed_weights + subtensor.move_stake = subtensor._subtensor.move_stake + subtensor.network = subtensor._subtensor.network + subtensor.neuron_for_uid = subtensor._subtensor.neuron_for_uid + subtensor.neurons_lite = subtensor._subtensor.neurons_lite + subtensor.query_constant = subtensor._subtensor.query_constant + subtensor.query_identity = subtensor._subtensor.query_identity + subtensor.query_map = subtensor._subtensor.query_map + subtensor.query_map_subtensor = subtensor._subtensor.query_map_subtensor + subtensor.query_module = subtensor._subtensor.query_module + subtensor.query_runtime_api = subtensor._subtensor.query_runtime_api + subtensor.query_subtensor = subtensor._subtensor.query_subtensor + subtensor.recycle = subtensor._subtensor.recycle + subtensor.register = subtensor._subtensor.register + subtensor.register_subnet = subtensor._subtensor.register_subnet + subtensor.reveal_weights = subtensor._subtensor.reveal_weights + subtensor.root_register = subtensor._subtensor.root_register + subtensor.root_set_weights = subtensor._subtensor.root_set_weights + subtensor.serve_axon = subtensor._subtensor.serve_axon + subtensor.set_children = subtensor._subtensor.set_children + subtensor.set_commitment = subtensor._subtensor.set_commitment + subtensor.set_delegate_take = subtensor._subtensor.set_delegate_take + subtensor.set_reveal_commitment = subtensor._subtensor.set_reveal_commitment + subtensor.set_subnet_identity = subtensor._subtensor.set_subnet_identity + subtensor.set_weights = subtensor._subtensor.set_weights + subtensor.setup_config = subtensor._subtensor.setup_config + subtensor.sign_and_send_extrinsic = subtensor._subtensor.sign_and_send_extrinsic + subtensor.start_call = subtensor._subtensor.start_call + subtensor.state_call = subtensor._subtensor.state_call + subtensor.subnet = subtensor._subtensor.subnet + subtensor.subnet_exists = subtensor._subtensor.subnet_exists + subtensor.subnetwork_n = subtensor._subtensor.subnetwork_n + subtensor.substrate = subtensor._subtensor.substrate + subtensor.swap_stake = subtensor._subtensor.swap_stake + subtensor.tempo = subtensor._subtensor.tempo + subtensor.transfer = subtensor._subtensor.transfer + subtensor.transfer_stake = subtensor._subtensor.transfer_stake + subtensor.tx_rate_limit = subtensor._subtensor.tx_rate_limit + subtensor.unstake = subtensor._subtensor.unstake + subtensor.unstake_multiple = subtensor._subtensor.unstake_multiple + subtensor.wait_for_block = subtensor._subtensor.wait_for_block + subtensor.weights = subtensor._subtensor.weights + subtensor.weights_rate_limit = subtensor._subtensor.weights_rate_limit From 66ccb7da56b4a4ecda4db577ff883d350981d42a Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:28:52 -0700 Subject: [PATCH 05/25] ruff --- bittensor/core/subtensor_api/commitments.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor/core/subtensor_api/commitments.py b/bittensor/core/subtensor_api/commitments.py index 26c2163a4b..2e594ba6db 100644 --- a/bittensor/core/subtensor_api/commitments.py +++ b/bittensor/core/subtensor_api/commitments.py @@ -13,6 +13,8 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_commitment = subtensor.get_commitment self.get_current_weight_commit_info = subtensor.get_current_weight_commit_info self.get_revealed_commitment = subtensor.get_revealed_commitment - self.get_revealed_commitment_by_hotkey = subtensor.get_revealed_commitment_by_hotkey + self.get_revealed_commitment_by_hotkey = ( + subtensor.get_revealed_commitment_by_hotkey + ) self.set_commitment = subtensor.set_commitment self.set_reveal_commitment = subtensor.set_reveal_commitment From f46a2491531990828d6df8cc864c3497a5126689 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:29:15 -0700 Subject: [PATCH 06/25] to be consistent --- .../subtensor_api/{metagraph.py => metagraphs.py} | 0 bittensor/core/subtensor_api/stakes.py | 4 +++- .../core/subtensor_api/{subnet.py => subnets.py} | 3 +-- .../core/subtensor_api/{wallet.py => wallets.py} | 11 +++++++---- 4 files changed, 11 insertions(+), 7 deletions(-) rename bittensor/core/subtensor_api/{metagraph.py => metagraphs.py} (100%) rename bittensor/core/subtensor_api/{subnet.py => subnets.py} (99%) rename bittensor/core/subtensor_api/{wallet.py => wallets.py} (87%) diff --git a/bittensor/core/subtensor_api/metagraph.py b/bittensor/core/subtensor_api/metagraphs.py similarity index 100% rename from bittensor/core/subtensor_api/metagraph.py rename to bittensor/core/subtensor_api/metagraphs.py diff --git a/bittensor/core/subtensor_api/stakes.py b/bittensor/core/subtensor_api/stakes.py index 442f98cad6..78e497d901 100644 --- a/bittensor/core/subtensor_api/stakes.py +++ b/bittensor/core/subtensor_api/stakes.py @@ -14,7 +14,9 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_stake = subtensor.get_stake self.get_stake_add_fee = subtensor.get_stake_add_fee self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey - self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_for_coldkey_and_hotkey = ( + subtensor.get_stake_for_coldkey_and_hotkey + ) self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey self.get_stake_movement_fee = subtensor.get_stake_movement_fee self.get_unstake_fee = subtensor.get_unstake_fee diff --git a/bittensor/core/subtensor_api/subnet.py b/bittensor/core/subtensor_api/subnets.py similarity index 99% rename from bittensor/core/subtensor_api/subnet.py rename to bittensor/core/subtensor_api/subnets.py index 1fb3d0f911..c3333daf30 100644 --- a/bittensor/core/subtensor_api/subnet.py +++ b/bittensor/core/subtensor_api/subnets.py @@ -4,7 +4,7 @@ from bittensor.core.subtensor import Subtensor as _Subtensor -class Subnet: +class Subnets: """Class for managing subnet operations.""" def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): @@ -43,4 +43,3 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.tempo = subtensor.tempo self.weights_rate_limit = subtensor.weights_rate_limit self.weights = subtensor.weights - diff --git a/bittensor/core/subtensor_api/wallet.py b/bittensor/core/subtensor_api/wallets.py similarity index 87% rename from bittensor/core/subtensor_api/wallet.py rename to bittensor/core/subtensor_api/wallets.py index 348d786608..7314a4fe39 100644 --- a/bittensor/core/subtensor_api/wallet.py +++ b/bittensor/core/subtensor_api/wallets.py @@ -3,12 +3,14 @@ from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor -class Wallet: +class Wallets: """Class for managing coldkey, hotkey, wallet operations.""" def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.does_hotkey_exist = subtensor.does_hotkey_exist - self.filter_netuids_by_registered_hotkeys = subtensor.filter_netuids_by_registered_hotkeys + self.filter_netuids_by_registered_hotkeys = ( + subtensor.filter_netuids_by_registered_hotkeys + ) self.is_hotkey_registered_any = subtensor.is_hotkey_registered_any self.is_hotkey_registered = subtensor.is_hotkey_registered self.is_hotkey_delegate = subtensor.is_hotkey_delegate @@ -27,10 +29,11 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_stake = subtensor.get_stake self.get_stake_add_fee = subtensor.get_stake_add_fee self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey - self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_for_coldkey_and_hotkey = ( + subtensor.get_stake_for_coldkey_and_hotkey + ) self.get_stake_for_hotkey = subtensor.get_stake_for_hotkey self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey self.get_stake_movement_fee = subtensor.get_stake_movement_fee self.get_transfer_fee = subtensor.get_transfer_fee self.get_unstake_fee = subtensor.get_unstake_fee - From 745a12686b1b7e3207130d30f5a02a7f9953e3c2 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:29:22 -0700 Subject: [PATCH 07/25] to be consistent + setter --- bittensor/core/subtensor_api/__init__.py | 38 +++++++++++++++++------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index c96080d04f..4b5c90acbe 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -6,12 +6,13 @@ from .commitments import Commitments as _Commitments from .delegates import Delegates as _Delegates from .extrinsics import Extrinsics as _Extrinsics -from .metagraph import Metagraphs as _Metagraphs +from .metagraphs import Metagraphs as _Metagraphs from .neurons import Neurons as _Neurons from .queries import Queries as _Queries from .stakes import Stakes as _Stakes -from .subnet import Subnet as _Subnet -from .wallet import Wallet as _Wallet +from .subnets import Subnets as _Subnets +from .utils import add_classic_fields as _add_classic_fields +from .wallets import Wallets as _Wallets if TYPE_CHECKING: from bittensor.core.config import Config @@ -23,8 +24,9 @@ class SubtensorApi: Arguments: network: The network to connect to. Defaults to `None` -> `finney`. config: Bittensor configuration object. Defaults to `None`. - log_verbose: If true, sets the subtensor to log verbosely. Defaults to `False`. - async_subtensor: If true, uses the async subtensor to create the connection. Defaults to `False`. + log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. + async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. + subtensor_fields: If `True`, all methods from the Subtensor class will be added to the root level of this class. Example: # sync version @@ -43,6 +45,12 @@ class SubtensorApi: print(await subtensor.block) print(await subtensor.delegates.get_delegate_identities()) print(await subtensor.chain.tx_rate_limit()) + + # using `subtensor_fields` + import bittensor as bt + + subtensor = bt.SubtensorApi(subtensor_fields=True) + print(subtensor.bonds(0)) """ def __init__( @@ -51,6 +59,7 @@ def __init__( config: Optional["Config"] = None, log_verbose: bool = False, async_subtensor: bool = False, + subtensor_fields: bool = False, _mock: bool = False, ): self.network = network @@ -60,6 +69,9 @@ def __init__( self._config = config self._subtensor = self._get_subtensor() + # fix naming collision + self._neurons = _Neurons(self._subtensor) + # define empty fields self.substrate = self._subtensor.substrate self.add_args = self._subtensor.add_args @@ -74,6 +86,8 @@ def __init__( self.sign_and_send_extrinsic = self._subtensor.sign_and_send_extrinsic self.start_call = self._subtensor.start_call self.wait_for_block = self._subtensor.wait_for_block + if subtensor_fields: + _add_classic_fields(self) def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: """Returns the subtensor instance based on the provided config and subtensor type flag.""" @@ -136,7 +150,11 @@ def metagraphs(self): @property def neurons(self): - return _Neurons(self._subtensor) + return self._neurons + + @neurons.setter + def neurons(self, value): + self._neurons = value @property def queries(self): @@ -147,9 +165,9 @@ def stakes(self): return _Stakes(self._subtensor) @property - def subnet(self): - return _Subnet(self._subtensor) + def subnets(self): + return _Subnets(self._subtensor) @property - def wallet(self): - return _Wallet(self._subtensor) + def wallets(self): + return _Wallets(self._subtensor) From 6595299da8b8f4efe0287cb7909b97b51c906b29 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:58:57 -0700 Subject: [PATCH 08/25] initialize for async instance --- bittensor/core/subtensor_api/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 4b5c90acbe..3e528c06f0 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -67,6 +67,8 @@ def __init__( self.log_verbose = log_verbose self.is_async = async_subtensor self._config = config + # assigned only for async instance + self.initialize = None self._subtensor = self._get_subtensor() # fix naming collision @@ -92,6 +94,7 @@ def __init__( def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: """Returns the subtensor instance based on the provided config and subtensor type flag.""" if self.is_async: + self.initialize = self._subtensor.initialize return _AsyncSubtensor( network=self.network, config=self._config, From 62ebe3acb0877c9668a2c4b1791c59890eaccefa Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 14:10:58 -0700 Subject: [PATCH 09/25] add compatability tests --- tests/unit_tests/test_subtensor_api.py | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/unit_tests/test_subtensor_api.py diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py new file mode 100644 index 0000000000..f332a640bf --- /dev/null +++ b/tests/unit_tests/test_subtensor_api.py @@ -0,0 +1,77 @@ +from bittensor.core.subtensor import Subtensor +from bittensor.core.subtensor_api import SubtensorApi + + +def test_properties_methods_comparable(): + """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" + # Preps + subtensor = Subtensor(_mock=True) + subtensor_api = SubtensorApi(_mock=True) + + subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] + + excluded_subtensor_methods = ["commit"] + + subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] + chain_methods = [m for m in dir(subtensor_api.chain) if not m.startswith("_")] + commitments_methods = [ + m for m in dir(subtensor_api.commitments) if not m.startswith("_") + ] + delegates_methods = [ + m for m in dir(subtensor_api.delegates) if not m.startswith("_") + ] + extrinsics_methods = [ + m for m in dir(subtensor_api.extrinsics) if not m.startswith("_") + ] + metagraphs_methods = [ + m for m in dir(subtensor_api.metagraphs) if not m.startswith("_") + ] + neurons_methods = [m for m in dir(subtensor_api.neurons) if not m.startswith("_")] + queries_methods = [m for m in dir(subtensor_api.queries) if not m.startswith("_")] + stakes_methods = [m for m in dir(subtensor_api.stakes) if not m.startswith("_")] + subnets_methods = [m for m in dir(subtensor_api.subnets) if not m.startswith("_")] + wallets_methods = [m for m in dir(subtensor_api.wallets) if not m.startswith("_")] + + all_subtensor_api_methods = ( + subtensor_api_methods + + chain_methods + + commitments_methods + + delegates_methods + + extrinsics_methods + + metagraphs_methods + + neurons_methods + + queries_methods + + stakes_methods + + subnets_methods + + wallets_methods + ) + + # Assertions + for method in subtensor_methods: + # skipp excluded methods + if method in excluded_subtensor_methods: + continue + assert method in all_subtensor_api_methods, ( + f"`Subtensor.{method}`is not present in class `SubtensorApi`." + ) + + +def test__methods_comparable_with_passed_subtensor_fields(): + """Verifies that methods in SubtensorApi contains all Subtensors methods if `subtensor_fields=True` is passed.""" + # Preps + subtensor = Subtensor(_mock=True) + subtensor_api = SubtensorApi(_mock=True, subtensor_fields=True) + + subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] + subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] + + excluded_subtensor_methods = ["commit"] + + # Assertions + for method in subtensor_methods: + # skipp excluded methods + if method in excluded_subtensor_methods: + continue + assert method in subtensor_api_methods, ( + f"`Subtensor.{method}`is not present in class `SubtensorApi`." + ) From 23a862f6cf6ab8c7314ca8f2ddac2e12a5a04833 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 14:20:47 -0700 Subject: [PATCH 10/25] one more compatability test --- tests/unit_tests/test_subtensor_api.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index f332a640bf..3d5ab26f47 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -1,11 +1,12 @@ from bittensor.core.subtensor import Subtensor from bittensor.core.subtensor_api import SubtensorApi +import pytest -def test_properties_methods_comparable(): +def test_properties_methods_comparable(other_class: "Subtensor" = None): """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" # Preps - subtensor = Subtensor(_mock=True) + subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) subtensor_api = SubtensorApi(_mock=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] @@ -56,10 +57,12 @@ def test_properties_methods_comparable(): ) -def test__methods_comparable_with_passed_subtensor_fields(): +def test__methods_comparable_with_passed_subtensor_fields( + other_class: "Subtensor" = None, +): """Verifies that methods in SubtensorApi contains all Subtensors methods if `subtensor_fields=True` is passed.""" # Preps - subtensor = Subtensor(_mock=True) + subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) subtensor_api = SubtensorApi(_mock=True, subtensor_fields=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] @@ -75,3 +78,17 @@ def test__methods_comparable_with_passed_subtensor_fields(): assert method in subtensor_api_methods, ( f"`Subtensor.{method}`is not present in class `SubtensorApi`." ) + + +def test_failed_if_subtensor_has_new_method(): + """Verifies that SubtensorApi fails if Subtensor has a new method.""" + # Preps + + class SubtensorWithNewMethod(Subtensor): + def return_my_id(self): + return id(self) + + # Call and assert + + with pytest.raises(AssertionError): + test_properties_methods_comparable(other_class=SubtensorWithNewMethod) From 8f9623e63b219cd3914b7c4b9400d6fd6bb64ce9 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 14:24:28 -0700 Subject: [PATCH 11/25] fix async instance `initialize` --- bittensor/core/subtensor_api/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 3e528c06f0..e7893dd5ec 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -94,13 +94,14 @@ def __init__( def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: """Returns the subtensor instance based on the provided config and subtensor type flag.""" if self.is_async: - self.initialize = self._subtensor.initialize - return _AsyncSubtensor( + _subtensor = _AsyncSubtensor( network=self.network, config=self._config, _mock=self._mock, log_verbose=self.log_verbose, ) + self.initialize = _subtensor.initialize + return _subtensor else: return _Subtensor( network=self.network, From ff0ddf5beb53d28cd94d026f484ee646431d524e Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 16:47:54 -0700 Subject: [PATCH 12/25] rename argument --- bittensor/core/subtensor_api/__init__.py | 12 +++++++----- tests/unit_tests/test_subtensor_api.py | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index e7893dd5ec..de0170eee6 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -26,7 +26,7 @@ class SubtensorApi: config: Bittensor configuration object. Defaults to `None`. log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. - subtensor_fields: If `True`, all methods from the Subtensor class will be added to the root level of this class. + backward_compatibility: If `True`, all methods from the Subtensor class will be added to the root level of this class. Example: # sync version @@ -46,10 +46,10 @@ class SubtensorApi: print(await subtensor.delegates.get_delegate_identities()) print(await subtensor.chain.tx_rate_limit()) - # using `subtensor_fields` + # using `backward_compatibility` import bittensor as bt - subtensor = bt.SubtensorApi(subtensor_fields=True) + subtensor = bt.SubtensorApi(backward_compatibility=True) print(subtensor.bonds(0)) """ @@ -59,7 +59,7 @@ def __init__( config: Optional["Config"] = None, log_verbose: bool = False, async_subtensor: bool = False, - subtensor_fields: bool = False, + backward_compatibility: bool = False, _mock: bool = False, ): self.network = network @@ -88,7 +88,9 @@ def __init__( self.sign_and_send_extrinsic = self._subtensor.sign_and_send_extrinsic self.start_call = self._subtensor.start_call self.wait_for_block = self._subtensor.wait_for_block - if subtensor_fields: + + # adds all Subtensor methods into main level os SubtensorApi class + if backward_compatibility: _add_classic_fields(self) def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index 3d5ab26f47..c97378b427 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -57,13 +57,13 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): ) -def test__methods_comparable_with_passed_subtensor_fields( +def test__methods_comparable_with_passed_backward_compatibility( other_class: "Subtensor" = None, ): - """Verifies that methods in SubtensorApi contains all Subtensors methods if `subtensor_fields=True` is passed.""" + """Verifies that methods in SubtensorApi contains all Subtensors methods if `backward_compatibility=True` is passed.""" # Preps subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True, subtensor_fields=True) + subtensor_api = SubtensorApi(_mock=True, backward_compatibility=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] From 68c3472129f0bb488715bc54816ec3303d3bbd70 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 15:45:39 -0500 Subject: [PATCH 13/25] rename argument legacy_methods --- bittensor/core/subtensor_api/__init__.py | 12 ++++++------ bittensor/core/subtensor_api/utils.py | 2 +- tests/unit_tests/test_subtensor_api.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index de0170eee6..914f9e7d85 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -11,7 +11,7 @@ from .queries import Queries as _Queries from .stakes import Stakes as _Stakes from .subnets import Subnets as _Subnets -from .utils import add_classic_fields as _add_classic_fields +from .utils import add_legacy_methods as _add_classic_fields from .wallets import Wallets as _Wallets if TYPE_CHECKING: @@ -26,7 +26,7 @@ class SubtensorApi: config: Bittensor configuration object. Defaults to `None`. log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. - backward_compatibility: If `True`, all methods from the Subtensor class will be added to the root level of this class. + legacy_methods: If `True`, all methods from the Subtensor class will be added to the root level of this class. Example: # sync version @@ -46,10 +46,10 @@ class SubtensorApi: print(await subtensor.delegates.get_delegate_identities()) print(await subtensor.chain.tx_rate_limit()) - # using `backward_compatibility` + # using `legacy_methods` import bittensor as bt - subtensor = bt.SubtensorApi(backward_compatibility=True) + subtensor = bt.SubtensorApi(legacy_methods=True) print(subtensor.bonds(0)) """ @@ -59,7 +59,7 @@ def __init__( config: Optional["Config"] = None, log_verbose: bool = False, async_subtensor: bool = False, - backward_compatibility: bool = False, + legacy_methods: bool = False, _mock: bool = False, ): self.network = network @@ -90,7 +90,7 @@ def __init__( self.wait_for_block = self._subtensor.wait_for_block # adds all Subtensor methods into main level os SubtensorApi class - if backward_compatibility: + if legacy_methods: _add_classic_fields(self) def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py index d7643c6c0d..5d8783f6e6 100644 --- a/bittensor/core/subtensor_api/utils.py +++ b/bittensor/core/subtensor_api/utils.py @@ -4,7 +4,7 @@ from bittensor.core.subtensor_api import SubtensorApi -def add_classic_fields(subtensor: "SubtensorApi"): +def add_legacy_methods(subtensor: "SubtensorApi"): """If SubtensorApi get `subtensor_fields=True` arguments, then all classic Subtensor fields added to root level.""" subtensor.add_stake = subtensor._subtensor.add_stake subtensor.add_stake_multiple = subtensor._subtensor.add_stake_multiple diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index c97378b427..f92ce48b40 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -57,13 +57,13 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): ) -def test__methods_comparable_with_passed_backward_compatibility( +def test__methods_comparable_with_passed_legacy_methods( other_class: "Subtensor" = None, ): - """Verifies that methods in SubtensorApi contains all Subtensors methods if `backward_compatibility=True` is passed.""" + """Verifies that methods in SubtensorApi contains all Subtensors methods if `legacy_methods=True` is passed.""" # Preps subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True, backward_compatibility=True) + subtensor_api = SubtensorApi(_mock=True, legacy_methods=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] From e85e2694dfe6795c15bb844e7cd3b0ddb0bcec99 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:32:06 -0500 Subject: [PATCH 14/25] improve subtensors --- bittensor/core/async_subtensor.py | 44 +++++++++++++++++++++++-- bittensor/core/subtensor.py | 54 ++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c9b05ceef2..955467b8da 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -9,6 +9,7 @@ import numpy as np import scalecodec from async_substrate_interface import AsyncSubstrateInterface +from async_substrate_interface.substrate_addons import RetryAsyncSubstrate from bittensor_drand import get_encrypted_commitment from bittensor_wallet.utils import SS58_FORMAT from numpy.typing import NDArray @@ -114,8 +115,10 @@ def __init__( self, network: Optional[str] = None, config: Optional["Config"] = None, - _mock: bool = False, log_verbose: bool = False, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, ): """ Initializes an instance of the AsyncSubtensor class. @@ -123,8 +126,10 @@ def __init__( Arguments: network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. - _mock: Whether this is a mock instance. Mainly just for use in testing. log_verbose (bool): Enables or disables verbose logging. + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. Raises: Any exceptions raised during the setup, configuration, or connection process. @@ -151,6 +156,9 @@ def __init__( chain_name="Bittensor", _mock=_mock, ) + self.substrate = self._get_substrate( + fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock + ) if self.log_verbose: logging.info( f"Connected to {self.network} network and {self.chain_endpoint}." @@ -283,6 +291,38 @@ async def get_hyperparameter( return getattr(result, "value", result) + def _get_substrate( + self, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, + ) -> Union[AsyncSubstrateInterface, RetryAsyncSubstrate]: + """Creates the Substrate instance based on provided arguments. + + Arguments: + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. + + Returns: + the instance of the SubstrateInterface or RetrySyncSubstrate class. + """ + if fallback_chains or retry_forever: + return RetryAsyncSubstrate( + url=self.chain_endpoint, + fallback_chains=fallback_chains, + retry_forever=retry_forever, + _mock=_mock, + ) + return AsyncSubstrateInterface( + url=self.chain_endpoint, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, + use_remote_preset=True, + chain_name="Bittensor", + _mock=_mock, + ) + # Subtensor queries =========================================================================================== async def query_constant( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 800790ea40..9b71686ef8 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -6,6 +6,7 @@ import numpy as np import scalecodec from async_substrate_interface.errors import SubstrateRequestException +from async_substrate_interface.substrate_addons import RetrySyncSubstrate from async_substrate_interface.sync_substrate import SubstrateInterface from async_substrate_interface.types import ScaleObj from bittensor_drand import get_encrypted_commitment @@ -116,8 +117,10 @@ def __init__( self, network: Optional[str] = None, config: Optional["Config"] = None, - _mock: bool = False, log_verbose: bool = False, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, ): """ Initializes an instance of the Subtensor class. @@ -125,8 +128,10 @@ def __init__( Arguments: network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. - _mock: Whether this is a mock instance. Mainly just for use in testing. log_verbose (bool): Enables or disables verbose logging. + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. Raises: Any exceptions raised during the setup, configuration, or connection process. @@ -143,13 +148,8 @@ def __init__( f"Connecting to network: [blue]{self.network}[/blue], " f"chain_endpoint: [blue]{self.chain_endpoint}[/blue]> ..." ) - self.substrate = SubstrateInterface( - url=self.chain_endpoint, - ss58_format=SS58_FORMAT, - type_registry=TYPE_REGISTRY, - use_remote_preset=True, - chain_name="Bittensor", - _mock=_mock, + self.substrate = self._get_substrate( + fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock ) if self.log_verbose: logging.info( @@ -163,11 +163,41 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.close() def close(self): - """ - Closes the websocket connection - """ + """Closes the websocket connection.""" self.substrate.close() + def _get_substrate( + self, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, + ) -> Union[SubstrateInterface, RetrySyncSubstrate]: + """Creates the Substrate instance based on provided arguments. + + Arguments: + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. + + Returns: + the instance of the SubstrateInterface or RetrySyncSubstrate class. + """ + if fallback_chains or retry_forever: + return RetrySyncSubstrate( + url=self.chain_endpoint, + fallback_chains=fallback_chains, + retry_forever=retry_forever, + _mock=_mock, + ) + return SubstrateInterface( + url=self.chain_endpoint, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, + use_remote_preset=True, + chain_name="Bittensor", + _mock=_mock, + ) + # Subtensor queries =========================================================================================== def query_constant( From b3c71596449569c5a7993cea28afd479558d65a9 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:33:33 -0500 Subject: [PATCH 15/25] Update SubtensorApi --- bittensor/core/subtensor_api/__init__.py | 49 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 914f9e7d85..f760a87a7c 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -24,9 +24,11 @@ class SubtensorApi: Arguments: network: The network to connect to. Defaults to `None` -> `finney`. config: Bittensor configuration object. Defaults to `None`. - log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. - async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. legacy_methods: If `True`, all methods from the Subtensor class will be added to the root level of this class. + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + log_verbose (bool): Enables or disables verbose logging. + mock: Whether this is a mock instance. Mainly just for use in testing. Example: # sync version @@ -57,16 +59,21 @@ def __init__( self, network: Optional[str] = None, config: Optional["Config"] = None, - log_verbose: bool = False, async_subtensor: bool = False, legacy_methods: bool = False, - _mock: bool = False, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + log_verbose: bool = False, + mock: bool = False, ): self.network = network - self._mock = _mock + self._fallback_chains = fallback_chains + self._retry_forever = retry_forever + self._mock = mock self.log_verbose = log_verbose self.is_async = async_subtensor self._config = config + # assigned only for async instance self.initialize = None self._subtensor = self._get_subtensor() @@ -99,8 +106,10 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: _subtensor = _AsyncSubtensor( network=self.network, config=self._config, - _mock=self._mock, log_verbose=self.log_verbose, + fallback_chains=self._fallback_chains, + retry_forever=self._retry_forever, + _mock=self._mock, ) self.initialize = _subtensor.initialize return _subtensor @@ -108,12 +117,18 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: return _Subtensor( network=self.network, config=self._config, - _mock=self._mock, log_verbose=self.log_verbose, + fallback_chains=self._fallback_chains, + retry_forever=self._retry_forever, + _mock=self._mock, ) def __str__(self): - return f"" + return ( + f"" + ) def __repr__(self): return self.__str__() @@ -130,50 +145,68 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_val, exc_tb): await self.substrate.close() + def _determine_chain_endpoint(self) -> str: + """Determines the connection and mock flag.""" + if self._mock: + return "Mock" + return self.substrate.url + @property def block(self): + """Returns current chain block number.""" return self._subtensor.block @property def chain(self): + """Property to access chain methods.""" return _Chain(self._subtensor) @property def commitments(self): + """Property to access commitments methods.""" return _Commitments(self._subtensor) @property def delegates(self): + """Property to access delegates methods.""" return _Delegates(self._subtensor) @property def extrinsics(self): + """Property to access extrinsics methods.""" return _Extrinsics(self._subtensor) @property def metagraphs(self): + """Property to access metagraphs methods.""" return _Metagraphs(self._subtensor) @property def neurons(self): + """Property to access neurons methods.""" return self._neurons @neurons.setter def neurons(self, value): + """Setter for neurons property.""" self._neurons = value @property def queries(self): + """Property to access queries methods.""" return _Queries(self._subtensor) @property def stakes(self): + """Property to access stakes methods.""" return _Stakes(self._subtensor) @property def subnets(self): + """Property to access subnets methods.""" return _Subnets(self._subtensor) @property def wallets(self): + """Property to access wallets methods.""" return _Wallets(self._subtensor) From 329ce85291fc7432ac0cb506716a641bedb3dd17 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:33:38 -0500 Subject: [PATCH 16/25] fix tests --- tests/unit_tests/test_subtensor_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index f92ce48b40..4078cb5a96 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -7,7 +7,7 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" # Preps subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True) + subtensor_api = SubtensorApi(mock=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] @@ -62,8 +62,8 @@ def test__methods_comparable_with_passed_legacy_methods( ): """Verifies that methods in SubtensorApi contains all Subtensors methods if `legacy_methods=True` is passed.""" # Preps - subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True, legacy_methods=True) + subtensor = other_class(mock=True) if other_class else Subtensor(_mock=True) + subtensor_api = SubtensorApi(mock=True, legacy_methods=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] From b470d788ad257b3df75b8b34ec62b02ea214c368 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:42:08 -0500 Subject: [PATCH 17/25] update docstrings, fix bug --- bittensor/core/async_subtensor.py | 12 ++---------- bittensor/core/subtensor.py | 4 ++-- bittensor/core/subtensor_api/__init__.py | 17 +++++++++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 955467b8da..2cda6446d1 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -127,7 +127,7 @@ def __init__( network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. log_verbose (bool): Enables or disables verbose logging. - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. @@ -148,14 +148,6 @@ def __init__( f"Connecting to network: [blue]{self.network}[/blue], " f"chain_endpoint: [blue]{self.chain_endpoint}[/blue]..." ) - self.substrate = AsyncSubstrateInterface( - url=self.chain_endpoint, - ss58_format=SS58_FORMAT, - type_registry=TYPE_REGISTRY, - use_remote_preset=True, - chain_name="Bittensor", - _mock=_mock, - ) self.substrate = self._get_substrate( fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock ) @@ -300,7 +292,7 @@ def _get_substrate( """Creates the Substrate instance based on provided arguments. Arguments: - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 9b71686ef8..e315205e0e 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -129,7 +129,7 @@ def __init__( network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. log_verbose (bool): Enables or disables verbose logging. - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. @@ -175,7 +175,7 @@ def _get_substrate( """Creates the Substrate instance based on provided arguments. Arguments: - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index f760a87a7c..84ad91a124 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -53,6 +53,11 @@ class SubtensorApi: subtensor = bt.SubtensorApi(legacy_methods=True) print(subtensor.bonds(0)) + + # using `fallback_chains` or `retry_forever` + import bittensor as bt + + """ def __init__( @@ -123,6 +128,12 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: _mock=self._mock, ) + def _determine_chain_endpoint(self) -> str: + """Determines the connection and mock flag.""" + if self._mock: + return "Mock" + return self.substrate.url + def __str__(self): return ( f" str: - """Determines the connection and mock flag.""" - if self._mock: - return "Mock" - return self.substrate.url - @property def block(self): """Returns current chain block number.""" From 04d4c425a9ec034ae2e4135bddc85e358281ee25 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 16:10:08 -0500 Subject: [PATCH 18/25] move add_args --- bittensor/core/subtensor_api/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 84ad91a124..2b24ad2f6b 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -88,7 +88,6 @@ def __init__( # define empty fields self.substrate = self._subtensor.substrate - self.add_args = self._subtensor.add_args self.chain_endpoint = self._subtensor.chain_endpoint self.close = self._subtensor.close self.config = self._subtensor.config @@ -156,6 +155,10 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_val, exc_tb): await self.substrate.close() + @classmethod + def add_args(cls, parser): + _Subtensor.add_args(parser) + @property def block(self): """Returns current chain block number.""" From 2093abf995e551c2da911e01a96481fd7fe4f2b1 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 17:17:18 -0500 Subject: [PATCH 19/25] fix docstrings --- bittensor/core/subtensor_api/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 2b24ad2f6b..b88609fbac 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -166,7 +166,7 @@ def block(self): @property def chain(self): - """Property to access chain methods.""" + """Property of interaction with chain methods.""" return _Chain(self._subtensor) @property @@ -201,7 +201,7 @@ def neurons(self, value): @property def queries(self): - """Property to access queries methods.""" + """Property to access subtensor queries methods.""" return _Queries(self._subtensor) @property @@ -211,10 +211,10 @@ def stakes(self): @property def subnets(self): - """Property to access subnets methods.""" + """Property of interaction with subnets methods.""" return _Subnets(self._subtensor) @property def wallets(self): - """Property to access wallets methods.""" + """Property of interaction methods with cold/hotkeys, and balances, etc.""" return _Wallets(self._subtensor) From 5b05f4de26934ccd556dbc89cba6904ae43eca67 Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Wed, 7 May 2025 13:31:32 -0700 Subject: [PATCH 20/25] Update bittensor/core/subtensor.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/subtensor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index e315205e0e..e642ad40aa 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -185,6 +185,11 @@ def _get_substrate( if fallback_chains or retry_forever: return RetrySyncSubstrate( url=self.chain_endpoint, + url=self.chain_endpoint, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, + use_remote_preset=True, + chain_name="Bittensor", fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock, From d0243e4e598f956839371d506bdb5b1abff43c7f Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Wed, 7 May 2025 13:31:39 -0700 Subject: [PATCH 21/25] Update bittensor/core/subtensor_api/__init__.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/subtensor_api/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index b88609fbac..98038d28de 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -22,7 +22,7 @@ class SubtensorApi: """Subtensor API class. Arguments: - network: The network to connect to. Defaults to `None` -> `finney`. + network: The network to connect to. Defaults to `None` -> "finney". config: Bittensor configuration object. Defaults to `None`. legacy_methods: If `True`, all methods from the Subtensor class will be added to the root level of this class. fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. From 6a255d76b312989d687fcee5e36f85b8d3464bdc Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Wed, 7 May 2025 13:37:56 -0700 Subject: [PATCH 22/25] Update bittensor/core/async_subtensor.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/async_subtensor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2cda6446d1..7b8a87b37f 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -303,7 +303,11 @@ def _get_substrate( return RetryAsyncSubstrate( url=self.chain_endpoint, fallback_chains=fallback_chains, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, retry_forever=retry_forever, + use_remote_preset=True, + chain_name="Bittensor", _mock=_mock, ) return AsyncSubstrateInterface( From 0e1ccbf1ddf82e0e0677538c0c779a99c831b2bf Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 15:45:26 -0500 Subject: [PATCH 23/25] fix --- bittensor/core/subtensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index e642ad40aa..47d5741113 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -184,7 +184,6 @@ def _get_substrate( """ if fallback_chains or retry_forever: return RetrySyncSubstrate( - url=self.chain_endpoint, url=self.chain_endpoint, ss58_format=SS58_FORMAT, type_registry=TYPE_REGISTRY, From afb5f3dc307fd330baec9dc918c84b1ab2b14436 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 15:45:49 -0500 Subject: [PATCH 24/25] bumping version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7ef6313fcc..fe5d089278 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ "uvicorn", "bittensor-drand>=0.5.0", "bittensor-wallet>=3.0.8", - "async-substrate-interface>=1.1.0" + "async-substrate-interface>=1.2.0" ] [project.optional-dependencies] From ff9c533a7006ab7d28dd491faea466488b6c70a6 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 15:48:33 -0500 Subject: [PATCH 25/25] checker --- bittensor/core/subtensor_api/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 98038d28de..3c1bacda8a 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -144,15 +144,31 @@ def __repr__(self): return self.__str__() def __enter__(self): + if self.is_async: + raise NotImplementedError( + "Async version of SubtensorApi cannot be used with sync context manager." + ) return self def __exit__(self, exc_type, exc_val, exc_tb): + if self.is_async: + raise NotImplementedError( + "Async version of SubtensorApi cannot be used with sync context manager." + ) self.close() async def __aenter__(self): + if not self.is_async: + raise NotImplementedError( + "Sync version of SubtensorApi cannot be used with async context manager." + ) return await self._subtensor.__aenter__() async def __aexit__(self, exc_type, exc_val, exc_tb): + if not self.is_async: + raise NotImplementedError( + "Sync version of SubtensorApi cannot be used with async context manager." + ) await self.substrate.close() @classmethod