Skip to content

Commit 17a6d69

Browse files
use bt_decode in runtime call
1 parent 075f52a commit 17a6d69

18 files changed

+861
-1833
lines changed

bittensor/core/async_subtensor.py

+51-77
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,10 @@
1111
from bittensor_wallet.utils import SS58_FORMAT
1212
from numpy.typing import NDArray
1313
from scalecodec import GenericCall
14-
from scalecodec.base import RuntimeConfiguration
15-
from scalecodec.type_registry import load_type_registry_preset
1614
from substrateinterface.exceptions import SubstrateRequestException
1715

1816
from bittensor.core.chain_data import (
1917
DelegateInfo,
20-
custom_rpc_type_registry,
2118
StakeInfo,
2219
NeuronInfoLite,
2320
NeuronInfo,
@@ -50,7 +47,6 @@
5047
from bittensor.core.settings import version_as_int
5148
from bittensor.utils import (
5249
torch,
53-
ss58_to_vec_u8,
5450
format_error_message,
5551
decode_hex_identity_dict,
5652
validate_chain_endpoint,
@@ -498,17 +494,15 @@ async def get_delegates(
498494
List of DelegateInfo objects, or an empty list if there are no delegates.
499495
"""
500496
block_hash = await self._determine_block_hash(block, block_hash, reuse_block)
501-
hex_bytes_result = await self.query_runtime_api(
497+
result = await self.query_runtime_api(
502498
runtime_api="DelegateInfoRuntimeApi",
503499
method="get_delegates",
504500
params=[],
505501
block_hash=block_hash,
506502
reuse_block=reuse_block,
507503
)
508-
if hex_bytes_result is not None:
509-
return DelegateInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
510-
else:
511-
return []
504+
505+
return DelegateInfo.list_from_any(result) if result is not None else []
512506

513507
async def get_stake_info_for_coldkey(
514508
self,
@@ -534,20 +528,19 @@ async def get_stake_info_for_coldkey(
534528
Stake information is vital for account holders to assess their investment and participation in the network's
535529
delegation and consensus processes.
536530
"""
537-
encoded_coldkey = ss58_to_vec_u8(coldkey_ss58)
538531
block_hash = await self._determine_block_hash(block, block_hash, reuse_block)
539-
hex_bytes_result = await self.query_runtime_api(
532+
result = await self.query_runtime_api(
540533
runtime_api="StakeInfoRuntimeApi",
541534
method="get_stake_info_for_coldkey",
542-
params=[encoded_coldkey],
535+
params=[coldkey_ss58],
543536
block_hash=block_hash,
544537
reuse_block=reuse_block,
545538
)
546539

547-
if hex_bytes_result is None:
540+
if result is None:
548541
return []
549542

550-
return StakeInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
543+
return StakeInfo.list_from_any(result)
551544

552545
async def get_stake_for_coldkey_and_hotkey(
553546
self,
@@ -585,11 +578,11 @@ async def query_runtime_api(
585578
self,
586579
runtime_api: str,
587580
method: str,
588-
params: Optional[Union[list[list[int]], dict[str, int], list[int]]],
581+
params: Optional[Union[list[Any], dict[str, Any]]],
589582
block: Optional[int] = None,
590583
block_hash: Optional[str] = None,
591584
reuse_block: bool = False,
592-
) -> Optional[str]:
585+
) -> Optional[Any]:
593586
"""
594587
Queries the runtime API of the Bittensor blockchain, providing a way to interact with the underlying runtime and
595588
retrieve data encoded in Scale Bytes format. This function is essential for advanced users who need to
@@ -605,46 +598,17 @@ async def query_runtime_api(
605598
reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block
606599
607600
Returns:
608-
The Scale Bytes encoded result from the runtime API call, or `None` if the call fails.
601+
The decoded result from the runtime API call, or `None` if the call fails.
609602
610603
This function enables access to the deeper layers of the Bittensor blockchain, allowing for detailed and
611604
specific interactions with the network's runtime environment.
612605
"""
613606
block_hash = await self._determine_block_hash(block, block_hash, reuse_block)
614-
615-
call_definition = TYPE_REGISTRY["runtime_api"][runtime_api]["methods"][method]
616-
617-
data = (
618-
"0x"
619-
if params is None
620-
else await self.encode_params(
621-
call_definition=call_definition, params=params
622-
)
623-
)
624-
api_method = f"{runtime_api}_{method}"
625-
626-
json_result = await self.substrate.rpc_request(
627-
method="state_call",
628-
params=[api_method, data, block_hash] if block_hash else [api_method, data],
629-
reuse_block_hash=reuse_block,
607+
result = await self.substrate.runtime_call(
608+
runtime_api, method, params, block_hash
630609
)
631610

632-
if json_result is None:
633-
return None
634-
635-
return_type = call_definition["type"]
636-
637-
as_scale_bytes = scalecodec.ScaleBytes(json_result["result"]) # type: ignore
638-
639-
rpc_runtime_config = RuntimeConfiguration()
640-
rpc_runtime_config.update_type_registry(load_type_registry_preset("legacy"))
641-
rpc_runtime_config.update_type_registry(custom_rpc_type_registry)
642-
643-
obj = rpc_runtime_config.create_scale_object(return_type, as_scale_bytes)
644-
if obj.data.to_hex() == "0x0400": # RPC returned None result
645-
return None
646-
647-
return obj.decode()
611+
return result
648612

649613
async def get_balance(
650614
self,
@@ -1002,18 +966,18 @@ async def neurons(
1002966
decentralized structure and the dynamics of its consensus and governance processes.
1003967
"""
1004968
block_hash = await self._determine_block_hash(block, block_hash, reuse_block)
1005-
hex_bytes_result = await self.query_runtime_api(
969+
result = await self.query_runtime_api(
1006970
runtime_api="NeuronInfoRuntimeApi",
1007971
method="get_neurons",
1008972
params=[netuid],
1009973
block_hash=block_hash,
1010974
reuse_block=reuse_block,
1011975
)
1012976

1013-
if hex_bytes_result is None:
977+
if result is None:
1014978
return []
1015979

1016-
return NeuronInfo.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
980+
return NeuronInfo.list_from_any(result)
1017981

1018982
async def neurons_lite(
1019983
self,
@@ -1041,18 +1005,18 @@ async def neurons_lite(
10411005
of the network's decentralized structure and neuron dynamics.
10421006
"""
10431007
block_hash = await self._determine_block_hash(block, block_hash, reuse_block)
1044-
hex_bytes_result = await self.query_runtime_api(
1008+
result = await self.query_runtime_api(
10451009
runtime_api="NeuronInfoRuntimeApi",
10461010
method="get_neurons_lite",
10471011
params=[netuid],
10481012
block_hash=block_hash,
10491013
reuse_block=reuse_block,
10501014
)
10511015

1052-
if hex_bytes_result is None:
1016+
if result is None:
10531017
return []
10541018

1055-
return NeuronInfoLite.list_from_vec_u8(hex_to_bytes(hex_bytes_result))
1019+
return NeuronInfoLite.list_from_any(result)
10561020

10571021
async def get_neuron_for_pubkey_and_subnet(
10581022
self,
@@ -1092,16 +1056,20 @@ async def get_neuron_for_pubkey_and_subnet(
10921056
if uid is None:
10931057
return NeuronInfo.get_null_neuron()
10941058

1095-
params = [netuid, uid]
1096-
json_body = await self.substrate.rpc_request(
1097-
method="neuronInfo_getNeuron",
1098-
params=params,
1059+
result = await self.query_runtime_api(
1060+
runtime_api="NeuronInfoRuntimeApi",
1061+
method="get_neuron",
1062+
params=[
1063+
netuid,
1064+
uid,
1065+
], # TODO check to see if this can accept more than one at a time
1066+
block_hash=block_hash,
10991067
)
11001068

1101-
if not (result := json_body.get("result", None)):
1069+
if not result:
11021070
return NeuronInfo.get_null_neuron()
11031071

1104-
return NeuronInfo.from_vec_u8(bytes(result))
1072+
return NeuronInfo.from_any(result)
11051073

11061074
async def neuron_for_uid(
11071075
self,
@@ -1137,16 +1105,20 @@ async def neuron_for_uid(
11371105
if reuse_block:
11381106
block_hash = self.substrate.last_block_hash
11391107

1140-
params = [netuid, uid, block_hash] if block_hash else [netuid, uid]
1141-
json_body = await self.substrate.rpc_request(
1142-
method="neuronInfo_getNeuron",
1143-
params=params, # custom rpc method
1108+
result = await self.query_runtime_api(
1109+
runtime_api="NeuronInfoRuntimeApi",
1110+
method="get_neuron",
1111+
params=[
1112+
netuid,
1113+
uid,
1114+
],
1115+
block_hash=block_hash,
11441116
)
1145-
if not (result := json_body.get("result", None)):
1117+
1118+
if not result:
11461119
return NeuronInfo.get_null_neuron()
11471120

1148-
bytes_result = bytes(result)
1149-
return NeuronInfo.from_vec_u8(bytes_result)
1121+
return NeuronInfo.from_any(result)
11501122

11511123
async def get_delegated(
11521124
self,
@@ -1177,16 +1149,18 @@ async def get_delegated(
11771149
if (bh := await self._determine_block_hash(block, block_hash, reuse_block))
11781150
else (self.substrate.last_block_hash if reuse_block else None)
11791151
)
1180-
encoded_coldkey = ss58_to_vec_u8(coldkey_ss58)
1181-
json_body = await self.substrate.rpc_request(
1182-
method="delegateInfo_getDelegated",
1183-
params=([block_hash, encoded_coldkey] if block_hash else [encoded_coldkey]),
1152+
1153+
result = await self.query_runtime_api(
1154+
runtime_api="DelegateInfoRuntimeApi",
1155+
method="get_delegated",
1156+
params=[coldkey_ss58],
1157+
block_hash=block_hash,
11841158
)
11851159

1186-
if not (result := json_body.get("result")):
1160+
if not result:
11871161
return []
11881162

1189-
return DelegateInfo.delegated_list_from_vec_u8(bytes(result))
1163+
return DelegateInfo.delegated_list_from_any(result)
11901164

11911165
async def query_identity(
11921166
self,
@@ -1496,18 +1470,18 @@ async def get_subnet_hyperparameters(
14961470
they interact with the network's consensus and incentive mechanisms.
14971471
"""
14981472
block_hash = await self._determine_block_hash(block, block_hash, reuse_block)
1499-
hex_bytes_result = await self.query_runtime_api(
1473+
result = await self.query_runtime_api(
15001474
runtime_api="SubnetInfoRuntimeApi",
15011475
method="get_subnet_hyperparams",
15021476
params=[netuid],
15031477
block_hash=block_hash,
15041478
reuse_block=reuse_block,
15051479
)
15061480

1507-
if hex_bytes_result is None:
1481+
if result is None:
15081482
return []
15091483

1510-
return SubnetHyperparameters.from_vec_u8(hex_to_bytes(hex_bytes_result))
1484+
return SubnetHyperparameters.from_any(result)
15111485

15121486
async def get_vote_data(
15131487
self,

bittensor/core/chain_data/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
from .stake_info import StakeInfo
1919
from .subnet_hyperparameters import SubnetHyperparameters
2020
from .subnet_info import SubnetInfo
21-
from .utils import custom_rpc_type_registry, decode_account_id, process_stake_data
21+
from .utils import decode_account_id, process_stake_data
2222

2323
ProposalCallData = GenericCall

bittensor/core/chain_data/delegate_info.py

+25-32
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import bt_decode
2-
31
from dataclasses import dataclass
4-
from typing import Optional
2+
from typing import Any, Optional
3+
4+
import bt_decode
5+
import munch
56

7+
from bittensor.core.chain_data.info_base import InfoBase
68
from bittensor.core.chain_data.utils import decode_account_id
79
from bittensor.utils import u16_normalized_float
810
from bittensor.utils.balance import Balance
911

1012

1113
@dataclass
12-
class DelegateInfo:
14+
class DelegateInfo(InfoBase):
1315
"""
1416
Dataclass for delegate information. For a lighter version of this class, see ``DelegateInfoLite``.
1517
@@ -40,8 +42,7 @@ class DelegateInfo:
4042
total_daily_return: Balance # Total daily return of the delegate
4143

4244
@classmethod
43-
def from_vec_u8(cls, vec_u8: bytes) -> Optional["DelegateInfo"]:
44-
decoded = bt_decode.DelegateInfo.decode(vec_u8)
45+
def _fix_decoded(cls, decoded: "DelegateInfo") -> Optional["DelegateInfo"]:
4546
hotkey = decode_account_id(decoded.delegate_ss58)
4647
owner = decode_account_id(decoded.owner_ss58)
4748
nominators = [
@@ -63,36 +64,14 @@ def from_vec_u8(cls, vec_u8: bytes) -> Optional["DelegateInfo"]:
6364
@classmethod
6465
def list_from_vec_u8(cls, vec_u8: bytes) -> list["DelegateInfo"]:
6566
decoded = bt_decode.DelegateInfo.decode_vec(vec_u8)
66-
results = []
67-
for d in decoded:
68-
hotkey = decode_account_id(d.delegate_ss58)
69-
owner = decode_account_id(d.owner_ss58)
70-
nominators = [
71-
(decode_account_id(x), Balance.from_rao(y)) for x, y in d.nominators
72-
]
73-
total_stake = sum((x[1] for x in nominators)) if nominators else Balance(0)
74-
results.append(
75-
DelegateInfo(
76-
hotkey_ss58=hotkey,
77-
total_stake=total_stake,
78-
nominators=nominators,
79-
owner_ss58=owner,
80-
take=u16_normalized_float(d.take),
81-
validator_permits=d.validator_permits,
82-
registrations=d.registrations,
83-
return_per_1000=Balance.from_rao(d.return_per_1000),
84-
total_daily_return=Balance.from_rao(d.total_daily_return),
85-
)
86-
)
87-
return results
67+
return [cls._fix_decoded(d) for d in decoded]
8868

8969
@classmethod
90-
def delegated_list_from_vec_u8(
91-
cls, vec_u8: bytes
70+
def fix_delegated_list(
71+
cls, delegated_list: list[tuple["DelegateInfo", Balance]]
9272
) -> list[tuple["DelegateInfo", Balance]]:
93-
decoded = bt_decode.DelegateInfo.decode_delegated(vec_u8)
9473
results = []
95-
for d, b in decoded:
74+
for d, b in delegated_list:
9675
nominators = [
9776
(decode_account_id(x), Balance.from_rao(y)) for x, y in d.nominators
9877
]
@@ -110,3 +89,17 @@ def delegated_list_from_vec_u8(
11089
)
11190
results.append((delegate, Balance.from_rao(b)))
11291
return results
92+
93+
@classmethod
94+
def delegated_list_from_vec_u8(
95+
cls, vec_u8: bytes
96+
) -> list[tuple["DelegateInfo", Balance]]:
97+
decoded = bt_decode.DelegateInfo.decode_delegated(vec_u8)
98+
return cls.fix_delegated_list(decoded)
99+
100+
@classmethod
101+
def delegated_list_from_any(
102+
cls, any_list: list[Any]
103+
) -> list[tuple["DelegateInfo", Balance]]:
104+
any_list = [munch.munchify(any_) for any_ in any_list]
105+
return cls.fix_delegated_list(any_list)

0 commit comments

Comments
 (0)