diff --git a/bittensor/_subtensor/chain_data.py b/bittensor/_subtensor/chain_data.py index 84a30649b6..fd4a9bb8c4 100644 --- a/bittensor/_subtensor/chain_data.py +++ b/bittensor/_subtensor/chain_data.py @@ -16,10 +16,10 @@ # DEALINGS IN THE SOFTWARE. from dataclasses import dataclass -from typing import List, Tuple, Dict +from typing import List, Tuple, Dict, Optional import bittensor from bittensor import Balance -import scalecodec +import json import torch @@ -28,6 +28,17 @@ U16_MAX = 65535 U64_MAX = 18446744073709551615 +def json_from_vec_u8( vec_u8: List[int] ) -> Optional[Dict]: + r""" Returns a json dictionary from a bytes object. + """ + if len(vec_u8) == 0: + return None + + as_bytes = bytes(vec_u8) + as_json_str = as_bytes.decode('utf-8') + as_json = json.loads(as_json_str) + return as_json + # Dataclasses for chain data. @dataclass class NeuronInfo: @@ -57,6 +68,26 @@ class NeuronInfo: axon_info: 'AxonInfo' is_null: bool = False + @classmethod + def from_vec_u8(cls, vec_u8: List[int]) -> 'NeuronInfo': + r""" Returns a NeuronInfo object from a vec_u8. + """ + json = json_from_vec_u8(vec_u8) + if json is None: + return NeuronInfo._null_neuron() # return null neuron instead of None + + return NeuronInfo.from_json(json) + + @classmethod + def list_from_vec_u8(cls, vec_u8: List[int]) -> List['NeuronInfo']: + r""" Returns a list of NeuronInfo objects from a vec_u8. + """ + json = json_from_vec_u8(vec_u8) + if json is None: + return [] + + return [NeuronInfo.from_json(neuron) for neuron in json] + @classmethod def from_json(cls, json: Dict) -> 'NeuronInfo': r""" Returns a NeuronInfo object from a json dictionary. @@ -78,8 +109,8 @@ def from_json(cls, json: Dict) -> 'NeuronInfo': dividends = json['dividends'] / U16_MAX, last_update = json['last_update'], validator_permit = json['validator_permit'], - weights = json['weights'], - bonds = json['bonds'], + weights = [ (uid, w) for uid, w in enumerate(json['weights']) ], + bonds = [ (uid, b) for uid, b in enumerate(json['bonds']) ], prometheus_info = PrometheusInfo.from_json(json['prometheus_info']), axon_info = AxonInfo.from_json(json['axon_info']), ) @@ -194,6 +225,26 @@ class DelegateInfo: owner_ss58: str # Coldkey of owner take: float # Take of the delegate as a percentage + @classmethod + def from_vec_u8(cls, vec_u8: List[int]) -> Optional['DelegateInfo']: + r""" Returns a DelegateInfo object from a vec_u8. + """ + json = json_from_vec_u8(vec_u8) + if json is None: + return None + + return DelegateInfo.from_json(json) + + @classmethod + def list_from_vec_u8(cls, vec_u8: List[int]) -> List['DelegateInfo']: + r""" Returns a list of DelegateInfo objects from a vec_u8. + """ + json = json_from_vec_u8(vec_u8) + if json is None: + return [] + + return [DelegateInfo.from_json(delegate) for delegate in json] + @classmethod def from_json(cls, json: Dict) -> 'DelegateInfo': r""" Returns a DelegateInfo object from a json dictionary. @@ -243,6 +294,26 @@ class SubnetInfo: connection_requirements: Dict[str, int] # netuid -> connection requirements emission_value: float + @classmethod + def from_vec_u8(cls, vec_u8: List[int]) -> Optional['SubnetInfo']: + r""" Returns a SubnetInfo object from a vec_u8. + """ + json = json_from_vec_u8(vec_u8) + if json is None: + return None + + return SubnetInfo.from_json(json) + + @classmethod + def list_from_vec_u8(cls, vec_u8: List[int]) -> List['SubnetInfo']: + r""" Returns a list of SubnetInfo objects from a vec_u8. + """ + json = json_from_vec_u8(vec_u8) + if json is None: + return [] + + return [SubnetInfo.from_json(subnet) for subnet in json] + @classmethod def from_json(cls, json: Dict) -> 'SubnetInfo': r""" Returns a SubnetInfo object from a json dictionary. diff --git a/bittensor/_subtensor/subtensor_impl.py b/bittensor/_subtensor/subtensor_impl.py index 646f64b7e6..bf856b4990 100644 --- a/bittensor/_subtensor/subtensor_impl.py +++ b/bittensor/_subtensor/subtensor_impl.py @@ -630,10 +630,10 @@ def make_substrate_call_with_retry(): json_body = make_substrate_call_with_retry() result = json_body['result'] - if result == None: + if result in (None, []): return [] - return [ SubnetInfo.from_json(subnet_info) for subnet_info in result ] + return SubnetInfo.list_from_vec_u8( result ) def get_subnet_info( self, netuid: int, block: Optional[int] = None ) -> Optional[SubnetInfo]: @retry(delay=2, tries=3, backoff=2, max_delay=4) @@ -651,10 +651,10 @@ def make_substrate_call_with_retry(): json_body = make_substrate_call_with_retry() result = json_body['result'] - if result == None: + if result in (None, []): return None - return SubnetInfo.from_json(result) + return SubnetInfo.from_vec_u8( result ) #################### #### Nomination #### @@ -688,10 +688,12 @@ def make_substrate_call_with_retry(encoded_hotkey: List[int]): hotkey_bytes: bytes = bittensor.utils.ss58_address_to_bytes( hotkey_ss58 ) encoded_hotkey: List[int] = [ int( byte ) for byte in hotkey_bytes ] json_body = make_substrate_call_with_retry(encoded_hotkey) - if json_body['result'] == None: + result = json_body['result'] + + if result in (None, []): return None - return DelegateInfo.from_json( json_body['result'] ) + return DelegateInfo.from_vec_u8( result ) def get_delegates( self, block: Optional[int] = None ) -> List[DelegateInfo]: @retry(delay=2, tries=3, backoff=2, max_delay=4) @@ -706,10 +708,12 @@ def make_substrate_call_with_retry(): params=params ) json_body = make_substrate_call_with_retry() - if json_body['result'] == None: + result = json_body['result'] + + if result in (None, []): return [] - return [DelegateInfo.from_json( delegate ) for delegate in json_body['result']] + return DelegateInfo.list_from_vec_u8( result ) ######################################## @@ -779,9 +783,12 @@ def make_substrate_call_with_retry(): params=params ) json_body = make_substrate_call_with_retry() - if json_body['result'] == None: + result = json_body['result'] + + if result in (None, []): return NeuronInfo._null_neuron() - return NeuronInfo.from_json( json_body['result'] ) + + return NeuronInfo.from_vec_u8( result ) def neurons(self, netuid: int, block: Optional[int] = None ) -> List[NeuronInfo]: r""" Returns a list of neuron from the chain. @@ -808,8 +815,11 @@ def make_substrate_call_with_retry(): json_body = make_substrate_call_with_retry() result = json_body['result'] + + if result in (None, []): + return [] - return [ NeuronInfo.from_json( neuron ) for neuron in result ] + return NeuronInfo.list_from_vec_u8( result ) def metagraph( self, netuid: int, block: Optional[int] = None ) -> 'bittensor.Metagraph': r""" Returns the metagraph for the subnet.