Skip to content

Commit c473f85

Browse files
committed
fallback to old method of decoding
1 parent 23d08a3 commit c473f85

File tree

2 files changed

+309
-3
lines changed

2 files changed

+309
-3
lines changed

async_substrate_interface/sync_substrate.py

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66

77
from bittensor_wallet.keypair import Keypair
88
from bittensor_wallet.utils import SS58_FORMAT
9-
from bt_decode import MetadataV15, PortableRegistry, decode as decode_by_type_string
9+
from bt_decode import (
10+
MetadataV15,
11+
PortableRegistry,
12+
decode as decode_by_type_string,
13+
AxonInfo as OldAxonInfo,
14+
PrometheusInfo as OldPrometheusInfo,
15+
)
1016
from scalecodec import (
1117
GenericCall,
1218
GenericExtrinsic,
@@ -31,6 +37,7 @@
3137
)
3238
from async_substrate_interface.utils import hex_to_bytes, json
3339
from async_substrate_interface.utils.storage import StorageKey
40+
from async_substrate_interface.type_registry import _TYPE_REGISTRY
3441

3542

3643
ResultHandler = Callable[[dict, Any], tuple[dict, bool]]
@@ -505,6 +512,8 @@ def __init__(
505512
ss58_format=self.ss58_format, implements_scale_info=True
506513
)
507514
self._metadata_cache = {}
515+
self._metadata_v15_cache = {}
516+
self._old_metadata_v15 = None
508517
self.metadata_version_hex = "0x0f000000" # v15
509518
self.reload_type_registry()
510519
if not _mock:
@@ -593,6 +602,20 @@ def load_registry(self):
593602
)
594603
self.registry = PortableRegistry.from_metadata_v15(self.metadata_v15)
595604

605+
def _load_registry_at_block(self, block_hash: str) -> MetadataV15:
606+
# Should be called for any block that fails decoding.
607+
# Possibly the metadata was different.
608+
metadata_rpc_result = self.rpc_request(
609+
"state_call",
610+
["Metadata_metadata_at_version", self.metadata_version_hex],
611+
block_hash=block_hash,
612+
)
613+
metadata_option_hex_str = metadata_rpc_result["result"]
614+
metadata_option_bytes = bytes.fromhex(metadata_option_hex_str[2:])
615+
old_metadata = MetadataV15.decode_from_metadata_option(metadata_option_bytes)
616+
617+
return old_metadata
618+
596619
def decode_scale(
597620
self,
598621
type_string: str,
@@ -634,6 +657,7 @@ def _first_initialize_runtime(self):
634657
metadata = self.get_block_metadata()
635658
self._metadata = metadata
636659
self._metadata_cache[self.runtime_version] = self._metadata
660+
self._metadata_v15_cache[self.runtime_version] = self.metadata_v15
637661
self.runtime_version = runtime_info.get("specVersion")
638662
self.runtime_config.set_active_spec_version_id(self.runtime_version)
639663
self.transaction_version = runtime_info.get("transactionVersion")
@@ -755,6 +779,30 @@ def get_runtime(block_hash, block_id) -> Runtime:
755779
self._metadata_cache[self.runtime_version] = self._metadata
756780
else:
757781
metadata = self._metadata
782+
783+
if self.runtime_version in self._metadata_v15_cache:
784+
# Get metadata v15 from cache
785+
logging.debug(
786+
"Retrieved metadata v15 for {} from memory".format(
787+
self.runtime_version
788+
)
789+
)
790+
metadata_v15 = self._old_metadata_v15 = self._metadata_v15_cache[
791+
self.runtime_version
792+
]
793+
else:
794+
metadata_v15 = self._old_metadata_v15 = self._load_registry_at_block(
795+
block_hash=runtime_block_hash
796+
)
797+
logging.debug(
798+
"Retrieved metadata v15 for {} from Substrate node".format(
799+
self.runtime_version
800+
)
801+
)
802+
803+
# Update metadata v15 cache
804+
self._metadata_v15_cache[self.runtime_version] = metadata_v15
805+
758806
# Update type registry
759807
self.reload_type_registry(use_remote_preset=False, auto_discover=True)
760808

@@ -2202,6 +2250,61 @@ def get_chain_finalised_head(self):
22022250

22032251
return response.get("result")
22042252

2253+
def _do_runtime_call_old(
2254+
self,
2255+
api: str,
2256+
method: str,
2257+
params: Optional[Union[list, dict]] = None,
2258+
block_hash: Optional[str] = None,
2259+
) -> ScaleType:
2260+
runtime_call_def = _TYPE_REGISTRY["runtime_api"][api]["methods"][method]
2261+
2262+
# Encode params
2263+
param_data = b""
2264+
2265+
if "encoder" in runtime_call_def:
2266+
param_data = runtime_call_def["encoder"](params)
2267+
else:
2268+
for idx, param in enumerate(runtime_call_def["params"]):
2269+
param_type_string = f"{param['type']}"
2270+
if isinstance(params, list):
2271+
param_data += self.encode_scale(param_type_string, params[idx])
2272+
else:
2273+
if param["name"] not in params:
2274+
raise ValueError(
2275+
f"Runtime Call param '{param['name']}' is missing"
2276+
)
2277+
2278+
param_data += self.encode_scale(
2279+
param_type_string, params[param["name"]]
2280+
)
2281+
2282+
# RPC request
2283+
result_data = self.rpc_request(
2284+
"state_call", [f"{api}_{method}", param_data.hex(), block_hash]
2285+
)
2286+
result_vec_u8_bytes = hex_to_bytes(result_data["result"])
2287+
result_bytes = self.decode_scale("Vec<u8>", result_vec_u8_bytes)
2288+
2289+
def _as_dict(obj):
2290+
as_dict = {}
2291+
for key in dir(obj):
2292+
if not key.startswith("_"):
2293+
val = getattr(obj, key)
2294+
if isinstance(val, (OldAxonInfo, OldPrometheusInfo)):
2295+
as_dict[key] = _as_dict(val)
2296+
else:
2297+
as_dict[key] = val
2298+
return as_dict
2299+
2300+
# Decode result
2301+
# Get correct type
2302+
result_decoded = runtime_call_def["decoder"](bytes(result_bytes))
2303+
as_dict = _as_dict(result_decoded)
2304+
result_obj = ScaleObj(as_dict)
2305+
2306+
return result_obj
2307+
22052308
def runtime_call(
22062309
self,
22072310
api: str,
@@ -2228,14 +2331,54 @@ def runtime_call(
22282331
params = {}
22292332

22302333
try:
2231-
metadata_v15 = self.metadata_v15.value()
2232-
apis = {entry["name"]: entry for entry in metadata_v15["apis"]}
2334+
if block_hash:
2335+
# Use old metadata v15 from init_runtime call
2336+
metadata_v15 = self._old_metadata_v15
2337+
else:
2338+
metadata_v15 = self.metadata_v15
2339+
2340+
self.registry = PortableRegistry.from_metadata_v15(metadata_v15)
2341+
metadata_v15_value = metadata_v15.value()
2342+
2343+
apis = {entry["name"]: entry for entry in metadata_v15_value["apis"]}
22332344
api_entry = apis[api]
22342345
methods = {entry["name"]: entry for entry in api_entry["methods"]}
22352346
runtime_call_def = methods[method]
22362347
except KeyError:
22372348
raise ValueError(f"Runtime API Call '{api}.{method}' not found in registry")
22382349

2350+
# Check if the output type is a Vec<u8>
2351+
# If so, call the API using the old method
2352+
output_type_def = [
2353+
x
2354+
for x in metadata_v15_value["types"]["types"]
2355+
if x["id"] == runtime_call_def["output"]
2356+
]
2357+
if output_type_def:
2358+
output_type_def = output_type_def[0]
2359+
2360+
if "sequence" in output_type_def["type"]["def"]:
2361+
output_type_seq_def_id = output_type_def["type"]["def"]["sequence"][
2362+
"type"
2363+
]
2364+
output_type_seq_def = [
2365+
x
2366+
for x in metadata_v15_value["types"]["types"]
2367+
if x["id"] == output_type_seq_def_id
2368+
]
2369+
if output_type_seq_def:
2370+
output_type_seq_def = output_type_seq_def[0]
2371+
if (
2372+
"primitive" in output_type_seq_def["type"]["def"]
2373+
and output_type_seq_def["type"]["def"]["primitive"] == "u8"
2374+
):
2375+
# This is Vec<u8>
2376+
result = self._do_runtime_call_old(
2377+
api, method, params, block_hash
2378+
)
2379+
2380+
return result
2381+
22392382
if isinstance(params, list) and len(params) != len(runtime_call_def["inputs"]):
22402383
raise ValueError(
22412384
f"Number of parameter provided ({len(params)}) does not "
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
from bt_decode import (
2+
NeuronInfo,
3+
NeuronInfoLite,
4+
DelegateInfo,
5+
StakeInfo,
6+
SubnetHyperparameters,
7+
SubnetInfo,
8+
SubnetInfoV2,
9+
encode,
10+
)
11+
from scalecodec import ss58_encode
12+
13+
_TYPE_REGISTRY: dict[str, dict] = {
14+
"types": {
15+
"Balance": "u64", # Need to override default u128
16+
},
17+
"runtime_api": {
18+
"DelegateInfoRuntimeApi": {
19+
"methods": {
20+
"get_delegated": {
21+
"params": [
22+
{
23+
"name": "coldkey",
24+
"type": "Vec<u8>",
25+
},
26+
],
27+
"encoder": lambda addr: encode(ss58_encode(addr), "Vec<u8>"),
28+
"type": "Vec<u8>",
29+
"decoder": DelegateInfo.decode_delegated,
30+
},
31+
"get_delegates": {
32+
"params": [],
33+
"type": "Vec<u8>",
34+
"decoder": DelegateInfo.decode_vec,
35+
},
36+
}
37+
},
38+
"NeuronInfoRuntimeApi": {
39+
"methods": {
40+
"get_neuron_lite": {
41+
"params": [
42+
{
43+
"name": "netuid",
44+
"type": "u16",
45+
},
46+
{
47+
"name": "uid",
48+
"type": "u16",
49+
},
50+
],
51+
"type": "Vec<u8>",
52+
"decoder": NeuronInfoLite.decode,
53+
},
54+
"get_neurons_lite": {
55+
"params": [
56+
{
57+
"name": "netuid",
58+
"type": "u16",
59+
},
60+
],
61+
"type": "Vec<u8>",
62+
"decoder": NeuronInfoLite.decode_vec,
63+
},
64+
"get_neuron": {
65+
"params": [
66+
{
67+
"name": "netuid",
68+
"type": "u16",
69+
},
70+
{
71+
"name": "uid",
72+
"type": "u16",
73+
},
74+
],
75+
"type": "Vec<u8>",
76+
"decoder": NeuronInfo.decode,
77+
},
78+
"get_neurons": {
79+
"params": [
80+
{
81+
"name": "netuid",
82+
"type": "u16",
83+
},
84+
],
85+
"type": "Vec<u8>",
86+
"decoder": NeuronInfo.decode_vec,
87+
},
88+
}
89+
},
90+
"StakeInfoRuntimeApi": {
91+
"methods": {
92+
"get_stake_info_for_coldkey": {
93+
"params": [
94+
{
95+
"name": "coldkey_account_vec",
96+
"type": "Vec<u8>",
97+
},
98+
],
99+
"type": "Vec<u8>",
100+
"encoder": lambda addr: encode(ss58_encode(addr), "Vec<u8>"),
101+
"decoder": StakeInfo.decode_vec,
102+
},
103+
"get_stake_info_for_coldkeys": {
104+
"params": [
105+
{
106+
"name": "coldkey_account_vecs",
107+
"type": "Vec<Vec<u8>>",
108+
},
109+
],
110+
"type": "Vec<u8>",
111+
"encoder": lambda addrs: encode(
112+
[ss58_encode(addr) for addr in addrs], "Vec<Vec<u8>>"
113+
),
114+
"decoder": StakeInfo.decode_vec_tuple_vec,
115+
},
116+
},
117+
},
118+
"SubnetInfoRuntimeApi": {
119+
"methods": {
120+
"get_subnet_hyperparams": {
121+
"params": [
122+
{
123+
"name": "netuid",
124+
"type": "u16",
125+
},
126+
],
127+
"type": "Vec<u8>",
128+
"decoder": SubnetHyperparameters.decode_option,
129+
},
130+
"get_subnet_info": {
131+
"params": [
132+
{
133+
"name": "netuid",
134+
"type": "u16",
135+
},
136+
],
137+
"type": "Vec<u8>",
138+
"decoder": SubnetInfo.decode_option,
139+
},
140+
"get_subnet_info_v2": {
141+
"params": [
142+
{
143+
"name": "netuid",
144+
"type": "u16",
145+
},
146+
],
147+
"type": "Vec<u8>",
148+
"decoder": SubnetInfoV2.decode_option,
149+
},
150+
"get_subnets_info": {
151+
"params": [],
152+
"type": "Vec<u8>",
153+
"decoder": SubnetInfo.decode_vec_option,
154+
},
155+
"get_subnets_info_v2": {
156+
"params": [],
157+
"type": "Vec<u8>",
158+
"decoder": SubnetInfo.decode_vec_option,
159+
},
160+
}
161+
},
162+
},
163+
}

0 commit comments

Comments
 (0)