diff --git a/CHANGELOG.md b/CHANGELOG.md index 76fd44d797..e2c0eaf006 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 9.0.0rc5 /2025-02-07 +* Fix InfoBase + dataclasses @roman-opentensor in https://github.com/opentensor/bittensor/pull/2649 + +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v9.0.0rc4...v9.0.0rc5 + ## 9.0.0rc4 /2025-02-07 * Fix for extra fields from chain data by @roman-opentensor in https://github.com/opentensor/bittensor/pull/2647 * Adds get_all_commitments and fixes commitment tests and query_map @thewhaleking in https://github.com/opentensor/bittensor/pull/2644 diff --git a/VERSION b/VERSION index 994a35715d..dd477240eb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.0.0rc4 \ No newline at end of file +9.0.0rc5 \ No newline at end of file diff --git a/bittensor/core/chain_data/axon_info.py b/bittensor/core/chain_data/axon_info.py index 3bf1fb21e1..7b9e666bc2 100644 --- a/bittensor/core/chain_data/axon_info.py +++ b/bittensor/core/chain_data/axon_info.py @@ -83,6 +83,7 @@ def to_string(self) -> str: @classmethod def _from_dict(cls, data): + """Returns a AxonInfo object from decoded chain data.""" return AxonInfo( version=data["version"], ip=str(netaddr.IPAddress(data["ip"])), diff --git a/bittensor/core/chain_data/chain_identity.py b/bittensor/core/chain_data/chain_identity.py index 14655c221f..7514bde5f0 100644 --- a/bittensor/core/chain_data/chain_identity.py +++ b/bittensor/core/chain_data/chain_identity.py @@ -16,6 +16,7 @@ class ChainIdentity(InfoBase): @classmethod def _from_dict(cls, decoded: dict) -> "ChainIdentity": + """Returns a ChainIdentity object from decoded chain data.""" return cls( name=decoded["name"], url=decoded["url"], diff --git a/bittensor/core/chain_data/delegate_info.py b/bittensor/core/chain_data/delegate_info.py index a1f6e71402..21311e29e0 100644 --- a/bittensor/core/chain_data/delegate_info.py +++ b/bittensor/core/chain_data/delegate_info.py @@ -40,6 +40,7 @@ class DelegateInfo(InfoBase): @classmethod def _from_dict(cls, decoded: dict) -> Optional["DelegateInfo"]: + """Returns a DelegateInfo object from decoded chain data.""" nominators = [ (decode_account_id(x), Balance.from_rao(y)) for x, y in decoded["nominators"] diff --git a/bittensor/core/chain_data/delegate_info_lite.py b/bittensor/core/chain_data/delegate_info_lite.py index da087a4195..1138696314 100644 --- a/bittensor/core/chain_data/delegate_info_lite.py +++ b/bittensor/core/chain_data/delegate_info_lite.py @@ -1,6 +1,9 @@ from dataclasses import dataclass from bittensor.core.chain_data.info_base import InfoBase +from bittensor.core.chain_data.utils import decode_account_id +from bittensor.utils import u16_normalized_float +from bittensor.utils.balance import Balance @dataclass @@ -27,5 +30,18 @@ class DelegateInfoLite(InfoBase): validator_permits: list[ int ] # List of subnets that the delegate is allowed to validate on - return_per_1000: int # Return per 1000 tao for the delegate over a day - total_daily_return: int # Total daily return of the delegate + return_per_1000: Balance # Return per 1000 tao for the delegate over a day + total_daily_return: Balance # Total daily return of the delegate + + @classmethod + def _from_dict(cls, decoded: dict) -> "DelegateInfoLite": + return DelegateInfoLite( + delegate_ss58=decode_account_id(decoded["delegate_ss58"]), + take=u16_normalized_float(decoded["take"]), + nominators=decoded["nominators"], + owner_ss58=decode_account_id(decoded["owner_ss58"]), + registrations=decoded["registrations"], + validator_permits=decoded["validator_permits"], + return_per_1000=Balance.from_rao(decoded["return_per_1000"]), + total_daily_return=Balance.from_rao(decoded["total_daily_return"]), + ) diff --git a/bittensor/core/chain_data/dynamic_info.py b/bittensor/core/chain_data/dynamic_info.py index 09de89cde0..cf55ace329 100644 --- a/bittensor/core/chain_data/dynamic_info.py +++ b/bittensor/core/chain_data/dynamic_info.py @@ -41,7 +41,7 @@ class DynamicInfo(InfoBase): @classmethod def _from_dict(cls, decoded: dict) -> "DynamicInfo": - """Returns a DynamicInfo object from a decoded DynamicInfo dictionary.""" + """Returns a DynamicInfo object from decoded chain data.""" netuid = int(decoded["netuid"]) symbol = bytes([int(b) for b in decoded["token_symbol"]]).decode() diff --git a/bittensor/core/chain_data/info_base.py b/bittensor/core/chain_data/info_base.py index 2912bde68e..1b17f068b3 100644 --- a/bittensor/core/chain_data/info_base.py +++ b/bittensor/core/chain_data/info_base.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass, fields +from dataclasses import dataclass from typing import Any, TypeVar from bittensor.core.errors import SubstrateRequestException @@ -13,13 +13,7 @@ class InfoBase: @classmethod def from_dict(cls, decoded: dict) -> T: try: - class_fields = {f.name for f in fields(cls)} - extra_keys = decoded.keys() - class_fields - instance = cls._from_dict( - {k: v for k, v in decoded.items() if k in class_fields} - ) - [setattr(instance, k, decoded[k]) for k in extra_keys] - return instance + return cls._from_dict(decoded) except KeyError as e: raise SubstrateRequestException( f"The {cls} structure is missing {e} from the chain.", diff --git a/bittensor/core/chain_data/ip_info.py b/bittensor/core/chain_data/ip_info.py index 5e93cc1450..02cde8f905 100644 --- a/bittensor/core/chain_data/ip_info.py +++ b/bittensor/core/chain_data/ip_info.py @@ -31,7 +31,7 @@ def encode(self) -> dict[str, Any]: @classmethod def _from_dict(cls, decoded: dict) -> "IPInfo": - """Returns a SubnetInfo object from a decoded IPInfo dictionary.""" + """Returns a IPInfo object from decoded chain data.""" return IPInfo( ip_type=decoded["ip_type_and_protocol"] >> 4, ip=net.int_to_ip(decoded["ip"]), diff --git a/bittensor/core/chain_data/metagraph_info.py b/bittensor/core/chain_data/metagraph_info.py index 67aabd7c57..f79f0d4cc4 100644 --- a/bittensor/core/chain_data/metagraph_info.py +++ b/bittensor/core/chain_data/metagraph_info.py @@ -140,7 +140,7 @@ class MetagraphInfo(InfoBase): @classmethod def _from_dict(cls, decoded: dict) -> "MetagraphInfo": - """Returns a Metagraph object from a decoded MetagraphInfo dictionary.""" + """Returns a MetagraphInfo object from decoded chain data.""" # Subnet index _netuid = decoded["netuid"] @@ -152,80 +152,101 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": processed = process_nested(raw_data, _chr_str) decoded.update({key: processed}) - # Keys for owner. - decoded["owner_hotkey"] = decode_account_id(decoded["owner_hotkey"]) - decoded["owner_coldkey"] = decode_account_id(decoded["owner_coldkey"]) - - # Subnet emission terms - decoded["subnet_emission"] = _tbwu(decoded["subnet_emission"]) - decoded["alpha_in"] = _tbwu(decoded["alpha_in"], _netuid) - decoded["alpha_out"] = _tbwu(decoded["alpha_out"], _netuid) - decoded["tao_in"] = _tbwu(decoded["tao_in"]) - decoded["alpha_out_emission"] = _tbwu(decoded["alpha_out_emission"], _netuid) - decoded["alpha_in_emission"] = _tbwu(decoded["alpha_in_emission"], _netuid) - decoded["tao_in_emission"] = _tbwu(decoded["tao_in_emission"]) - decoded["pending_alpha_emission"] = _tbwu( - decoded["pending_alpha_emission"], _netuid - ) - decoded["pending_root_emission"] = _tbwu(decoded["pending_root_emission"]) - decoded["subnet_volume"] = _tbwu(decoded["subnet_volume"], _netuid) - decoded["moving_price"] = Balance.from_tao( - fixed_to_float(decoded.get("moving_price"), 32) + return cls( + # Subnet index + netuid=_netuid, + # Name and symbol + name=decoded["name"], + symbol=decoded["symbol"], + identity=decoded["identity"], + network_registered_at=decoded["network_registered_at"], + # Keys for owner. + owner_hotkey=decoded["owner_hotkey"], + owner_coldkey=decoded["owner_coldkey"], + # Tempo terms. + block=decoded["block"], + tempo=decoded["tempo"], + last_step=decoded["last_step"], + blocks_since_last_step=decoded["blocks_since_last_step"], + # Subnet emission terms + subnet_emission=_tbwu(decoded["subnet_emission"]), + alpha_in=_tbwu(decoded["alpha_in"], _netuid), + alpha_out=_tbwu(decoded["alpha_out"], _netuid), + tao_in=_tbwu(decoded["tao_in"]), + alpha_out_emission=_tbwu(decoded["alpha_out_emission"], _netuid), + alpha_in_emission=_tbwu(decoded["alpha_in_emission"], _netuid), + tao_in_emission=_tbwu(decoded["tao_in_emission"]), + pending_alpha_emission=_tbwu(decoded["pending_alpha_emission"], _netuid), + pending_root_emission=_tbwu(decoded["pending_root_emission"]), + subnet_volume=_tbwu(decoded["subnet_volume"], _netuid), + moving_price=Balance.from_tao( + fixed_to_float(decoded.get("moving_price"), 32) + ), + # Hparams for epoch + rho=decoded["rho"], + kappa=decoded["kappa"], + # Validator params + min_allowed_weights=u16tf(decoded["min_allowed_weights"]), + max_weights_limit=u16tf(decoded["max_weights_limit"]), + weights_version=decoded["weights_version"], + weights_rate_limit=decoded["weights_rate_limit"], + activity_cutoff=decoded["activity_cutoff"], + max_validators=decoded["max_validators"], + # Registration + num_uids=decoded["num_uids"], + max_uids=decoded["max_uids"], + burn=_tbwu(decoded["burn"]), + difficulty=u64tf(decoded["difficulty"]), + registration_allowed=decoded["registration_allowed"], + pow_registration_allowed=decoded["pow_registration_allowed"], + immunity_period=decoded["immunity_period"], + min_difficulty=u64tf(decoded["min_difficulty"]), + max_difficulty=u64tf(decoded["max_difficulty"]), + min_burn=_tbwu(decoded["min_burn"]), + max_burn=_tbwu(decoded["max_burn"]), + adjustment_alpha=u64tf(decoded["adjustment_alpha"]), + adjustment_interval=decoded["adjustment_interval"], + target_regs_per_interval=decoded["target_regs_per_interval"], + max_regs_per_block=decoded["max_regs_per_block"], + serving_rate_limit=decoded["serving_rate_limit"], + # CR + commit_reveal_weights_enabled=decoded["commit_reveal_weights_enabled"], + commit_reveal_period=decoded["commit_reveal_period"], + # Bonds + liquid_alpha_enabled=decoded["liquid_alpha_enabled"], + alpha_high=u16tf(decoded["alpha_high"]), + alpha_low=u16tf(decoded["alpha_low"]), + bonds_moving_avg=u64tf(decoded["bonds_moving_avg"]), + # Metagraph info. + hotkeys=[decode_account_id(ck) for ck in decoded.get("hotkeys", [])], + coldkeys=[decode_account_id(hk) for hk in decoded.get("coldkeys", [])], + identities=decoded["identities"], + axons=decoded.get("axons", []), + active=decoded["active"], + validator_permit=decoded["validator_permit"], + pruning_score=[u16tf(ps) for ps in decoded.get("pruning_score", [])], + last_update=decoded["last_update"], + emission=[_tbwu(em, _netuid) for em in decoded.get("emission", [])], + dividends=[u16tf(dv) for dv in decoded.get("dividends", [])], + incentives=[u16tf(ic) for ic in decoded.get("incentives", [])], + consensus=[u16tf(cs) for cs in decoded.get("consensus", [])], + trust=[u16tf(tr) for tr in decoded.get("trust", [])], + rank=[u16tf(rk) for rk in decoded.get("rank", [])], + block_at_registration=decoded["block_at_registration"], + alpha_stake=[_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]], + tao_stake=[_tbwu(ts) for ts in decoded["tao_stake"]], + total_stake=[_tbwu(ts, _netuid) for ts in decoded["total_stake"]], + # Dividend break down + tao_dividends_per_hotkey=[ + (decode_account_id(alpha[0]), _tbwu(alpha[1])) + for alpha in decoded["tao_dividends_per_hotkey"] + ], + alpha_dividends_per_hotkey=[ + (decode_account_id(adphk[0]), _tbwu(adphk[1], _netuid)) + for adphk in decoded["alpha_dividends_per_hotkey"] + ], ) - # Hparams for epoch - decoded["kappa"] = u16tf(decoded["kappa"]) - - # Validator params - decoded["min_allowed_weights"] = u16tf(decoded["min_allowed_weights"]) - decoded["max_weights_limit"] = u16tf(decoded["max_weights_limit"]) - - # Registration - decoded["burn"] = _tbwu(decoded["burn"]) - decoded["difficulty"] = u64tf(decoded["difficulty"]) - decoded["min_difficulty"] = u64tf(decoded["min_difficulty"]) - decoded["max_difficulty"] = u64tf(decoded["max_difficulty"]) - decoded["min_burn"] = _tbwu(decoded["min_burn"]) - decoded["max_burn"] = _tbwu(decoded["max_burn"]) - decoded["adjustment_alpha"] = u64tf(decoded["adjustment_alpha"]) - - # Bonds - decoded["alpha_high"] = u16tf(decoded["alpha_high"]) - decoded["alpha_low"] = u16tf(decoded["alpha_low"]) - decoded["bonds_moving_avg"] = u64tf(decoded["bonds_moving_avg"]) - - # Metagraph info. - decoded["hotkeys"] = [ - decode_account_id(ck) for ck in decoded.get("hotkeys", []) - ] - decoded["coldkeys"] = [ - decode_account_id(hk) for hk in decoded.get("coldkeys", []) - ] - decoded["axons"] = decoded.get("axons", []) - decoded["pruning_score"] = [ - u16tf(ps) for ps in decoded.get("pruning_score", []) - ] - decoded["emission"] = [_tbwu(em, _netuid) for em in decoded.get("emission", [])] - decoded["dividends"] = [u16tf(dv) for dv in decoded.get("dividends", [])] - decoded["incentives"] = [u16tf(ic) for ic in decoded.get("incentives", [])] - decoded["consensus"] = [u16tf(cs) for cs in decoded.get("consensus", [])] - decoded["trust"] = [u16tf(tr) for tr in decoded.get("trust", [])] - decoded["rank"] = [u16tf(rk) for rk in decoded.get("trust", [])] - decoded["alpha_stake"] = [_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]] - decoded["tao_stake"] = [_tbwu(ts) for ts in decoded["tao_stake"]] - decoded["total_stake"] = [_tbwu(ts, _netuid) for ts in decoded["total_stake"]] - - # Dividend break down - decoded["tao_dividends_per_hotkey"] = [ - (decode_account_id(alpha[0]), _tbwu(alpha[1])) - for alpha in decoded["tao_dividends_per_hotkey"] - ] - decoded["alpha_dividends_per_hotkey"] = [ - (decode_account_id(adphk[0]), _tbwu(adphk[1], _netuid)) - for adphk in decoded["alpha_dividends_per_hotkey"] - ] - return MetagraphInfo(**decoded) - @dataclass class MetagraphInfoEmissions: diff --git a/bittensor/core/chain_data/neuron_info.py b/bittensor/core/chain_data/neuron_info.py index e8bb958ba5..0cf917a5fb 100644 --- a/bittensor/core/chain_data/neuron_info.py +++ b/bittensor/core/chain_data/neuron_info.py @@ -126,7 +126,7 @@ def get_null_neuron() -> "NeuronInfo": @classmethod def _from_dict(cls, decoded: Any) -> "NeuronInfo": - """Instantiates NeuronInfo from a byte vector.""" + """Returns a NeuronInfo object from decoded chain data.""" stake_dict = process_stake_data(decoded["stake"]) total_stake = sum(stake_dict.values()) if stake_dict else Balance(0) coldkey = decode_account_id(decoded["coldkey"]) diff --git a/bittensor/core/chain_data/neuron_info_lite.py b/bittensor/core/chain_data/neuron_info_lite.py index e3241839e2..5dd60cef82 100644 --- a/bittensor/core/chain_data/neuron_info_lite.py +++ b/bittensor/core/chain_data/neuron_info_lite.py @@ -95,6 +95,7 @@ def get_null_neuron() -> "NeuronInfoLite": @classmethod def _from_dict(cls, decoded: Any) -> "NeuronInfoLite": + """Returns a NeuronInfoLite object from decoded chain data.""" coldkey = decode_account_id(decoded["coldkey"]) hotkey = decode_account_id(decoded["hotkey"]) stake_dict = process_stake_data(decoded["stake"]) diff --git a/bittensor/core/chain_data/prometheus_info.py b/bittensor/core/chain_data/prometheus_info.py index 6e975cd515..6aac398d72 100644 --- a/bittensor/core/chain_data/prometheus_info.py +++ b/bittensor/core/chain_data/prometheus_info.py @@ -26,6 +26,7 @@ class PrometheusInfo(InfoBase): @classmethod def _from_dict(cls, data): + """Returns a PrometheusInfo object from decoded chain data.""" return cls( block=data["block"], ip_type=data["ip_type"], diff --git a/bittensor/core/chain_data/scheduled_coldkey_swap_info.py b/bittensor/core/chain_data/scheduled_coldkey_swap_info.py index 0ad01b2ff2..d5717a25fc 100644 --- a/bittensor/core/chain_data/scheduled_coldkey_swap_info.py +++ b/bittensor/core/chain_data/scheduled_coldkey_swap_info.py @@ -25,6 +25,7 @@ class ScheduledColdkeySwapInfo(InfoBase): @classmethod def _from_dict(cls, decoded: dict) -> "ScheduledColdkeySwapInfo": + """Returns a ScheduledColdkeySwapInfo object from decoded chain data.""" return cls( arbitration_block=decoded["arbitration_block"], new_coldkey=ss58_encode(decoded["new_coldkey"], SS58_FORMAT), diff --git a/bittensor/core/chain_data/stake_info.py b/bittensor/core/chain_data/stake_info.py index 8b1dbd2ab0..ade4515743 100644 --- a/bittensor/core/chain_data/stake_info.py +++ b/bittensor/core/chain_data/stake_info.py @@ -27,7 +27,7 @@ class StakeInfo(InfoBase): @classmethod def from_dict(cls, decoded: dict) -> "StakeInfo": - """Returns a StakeInfo object.""" + """Returns a StakeInfo object from decoded chain data.""" netuid = decoded["netuid"] return cls( hotkey_ss58=decode_account_id(decoded["hotkey"]), diff --git a/bittensor/core/chain_data/subnet_hyperparameters.py b/bittensor/core/chain_data/subnet_hyperparameters.py index 479ab78f14..4f5e28f514 100644 --- a/bittensor/core/chain_data/subnet_hyperparameters.py +++ b/bittensor/core/chain_data/subnet_hyperparameters.py @@ -68,19 +68,7 @@ class SubnetHyperparameters(InfoBase): @classmethod def _from_dict(cls, decoded: dict) -> "SubnetHyperparameters": - """ - Create a `SubnetHyperparameters` instance from a vector of bytes. - - This method decodes the given vector of bytes using the `bt_decode` module and creates a new instance of - `SubnetHyperparameters` with the decoded values. - - Args: - vec_u8 (bytes): A vector of bytes to decode into `SubnetHyperparameters`. - - Returns: - Optional[SubnetHyperparameters]: An instance of `SubnetHyperparameters` if decoding is successful, None - otherwise. - """ + """Returns a SubnetHyperparameters object from decoded chain data.""" return SubnetHyperparameters( activity_cutoff=decoded["activity_cutoff"], adjustment_alpha=decoded["adjustment_alpha"], diff --git a/bittensor/core/chain_data/subnet_identity.py b/bittensor/core/chain_data/subnet_identity.py index 487122bfb4..9c19b2f2e2 100644 --- a/bittensor/core/chain_data/subnet_identity.py +++ b/bittensor/core/chain_data/subnet_identity.py @@ -15,6 +15,7 @@ class SubnetIdentity: @classmethod def _from_dict(cls, decoded: dict) -> "SubnetIdentity": + """Returns a SubnetIdentity object from decoded chain data.""" return cls( subnet_name=decoded["subnet_name"], github_repo=decoded["github_repo"], diff --git a/bittensor/core/chain_data/subnet_info.py b/bittensor/core/chain_data/subnet_info.py index c8919d0c8b..978dab29f7 100644 --- a/bittensor/core/chain_data/subnet_info.py +++ b/bittensor/core/chain_data/subnet_info.py @@ -32,6 +32,7 @@ class SubnetInfo(InfoBase): @classmethod def _from_dict(cls, decoded: Any) -> "SubnetInfo": + """Returns a SubnetInfo object from decoded chain data.""" return SubnetInfo( blocks_since_epoch=decoded["blocks_since_last_step"], burn=Balance.from_rao(decoded["burn"]), diff --git a/bittensor/core/chain_data/subnet_state.py b/bittensor/core/chain_data/subnet_state.py index f8c09f1ca5..caf01bfc2c 100644 --- a/bittensor/core/chain_data/subnet_state.py +++ b/bittensor/core/chain_data/subnet_state.py @@ -34,6 +34,7 @@ class SubnetState(InfoBase): @classmethod def _from_dict(cls, decoded: dict) -> "SubnetState": + """Returns a SubnetState object from decoded chain data.""" netuid = decoded["netuid"] return SubnetState( netuid=netuid, diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index df8e9f4146..6190befd44 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -1,4 +1,4 @@ -__version__ = "9.0.0rc4" +__version__ = "9.0.0rc5" import os import re