Skip to content

Commit 13d518b

Browse files
authored
Merge pull request #32 from opentensor/release/1.0.0rc11
Release/1.0.0rc11
2 parents 3fc79d1 + 9ea82fb commit 13d518b

File tree

7 files changed

+517
-75
lines changed

7 files changed

+517
-75
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 1.0.0rc11 /2025-02-06
4+
* Reuses the websocket for sync Substrate by @thewhaleking in https://github.com/opentensor/async-substrate-interface/pull/29
5+
* Feat/metadata v15 cache by @camfairchild in https://github.com/opentensor/async-substrate-interface/pull/30
6+
* Backmerge main to staging rc10 by @ibraheem-opentensor in https://github.com/opentensor/async-substrate-interface/pull/31
7+
38
## 1.0.0rc10 /2025-02-04
49
* Fixes decoding account ids for sync substrate
510

async_substrate_interface/async_substrate.py

Lines changed: 120 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@
4949
Preprocessed,
5050
)
5151
from async_substrate_interface.utils import hex_to_bytes, json
52+
from async_substrate_interface.utils.decoding import (
53+
_determine_if_old_runtime_call,
54+
_bt_decode_to_dict_or_list,
55+
)
5256
from async_substrate_interface.utils.storage import StorageKey
57+
from async_substrate_interface.type_registry import _TYPE_REGISTRY
5358

5459
if TYPE_CHECKING:
5560
from websockets.asyncio.client import ClientConnection
@@ -685,7 +690,7 @@ def __init__(
685690
self.ws = Websocket(
686691
url,
687692
options={
688-
"max_size": 2**32,
693+
"max_size": self.ws_max_size,
689694
"write_limit": 2**16,
690695
},
691696
)
@@ -706,6 +711,8 @@ def __init__(
706711
ss58_format=self.ss58_format, implements_scale_info=True
707712
)
708713
self._metadata_cache = {}
714+
self._metadata_v15_cache = {}
715+
self._old_metadata_v15 = None
709716
self._nonces = {}
710717
self.metadata_version_hex = "0x0f000000" # v15
711718
self.reload_type_registry()
@@ -800,6 +807,20 @@ async def load_registry(self):
800807
)
801808
self.registry = PortableRegistry.from_metadata_v15(self.metadata_v15)
802809

810+
async def _load_registry_at_block(self, block_hash: str) -> MetadataV15:
811+
# Should be called for any block that fails decoding.
812+
# Possibly the metadata was different.
813+
metadata_rpc_result = await self.rpc_request(
814+
"state_call",
815+
["Metadata_metadata_at_version", self.metadata_version_hex],
816+
block_hash=block_hash,
817+
)
818+
metadata_option_hex_str = metadata_rpc_result["result"]
819+
metadata_option_bytes = bytes.fromhex(metadata_option_hex_str[2:])
820+
old_metadata = MetadataV15.decode_from_metadata_option(metadata_option_bytes)
821+
822+
return old_metadata
823+
803824
async def _wait_for_registry(self, _attempt: int = 1, _retries: int = 3) -> None:
804825
async def _waiter():
805826
while self.registry is None:
@@ -930,7 +951,10 @@ async def get_runtime(block_hash, block_id) -> Runtime:
930951
if (
931952
(block_hash and block_hash == self.last_block_hash)
932953
or (block_id and block_id == self.block_id)
933-
) and self._metadata is not None:
954+
) and all(
955+
x is not None
956+
for x in [self._metadata, self._old_metadata_v15, self.metadata_v15]
957+
):
934958
return Runtime(
935959
self.chain,
936960
self.runtime_config,
@@ -976,9 +1000,9 @@ async def get_runtime(block_hash, block_id) -> Runtime:
9761000
f"No runtime information for block '{block_hash}'"
9771001
)
9781002
# Check if runtime state already set to current block
979-
if (
980-
runtime_info.get("specVersion") == self.runtime_version
981-
and self._metadata is not None
1003+
if runtime_info.get("specVersion") == self.runtime_version and all(
1004+
x is not None
1005+
for x in [self._metadata, self._old_metadata_v15, self.metadata_v15]
9821006
):
9831007
return Runtime(
9841008
self.chain,
@@ -1002,6 +1026,8 @@ async def get_runtime(block_hash, block_id) -> Runtime:
10021026
self.runtime_version
10031027
]
10041028
else:
1029+
# TODO when I get time, I'd like to add this and the metadata v15 as tasks with callbacks
1030+
# TODO to update the caches, but I don't have time now.
10051031
metadata = self._metadata = await self.get_block_metadata(
10061032
block_hash=runtime_block_hash, decode=True
10071033
)
@@ -1015,6 +1041,30 @@ async def get_runtime(block_hash, block_id) -> Runtime:
10151041
self._metadata_cache[self.runtime_version] = self._metadata
10161042
else:
10171043
metadata = self._metadata
1044+
1045+
if self.runtime_version in self._metadata_v15_cache:
1046+
# Get metadata v15 from cache
1047+
logging.debug(
1048+
"Retrieved metadata v15 for {} from memory".format(
1049+
self.runtime_version
1050+
)
1051+
)
1052+
metadata_v15 = self._old_metadata_v15 = self._metadata_v15_cache[
1053+
self.runtime_version
1054+
]
1055+
else:
1056+
metadata_v15 = (
1057+
self._old_metadata_v15
1058+
) = await self._load_registry_at_block(block_hash=runtime_block_hash)
1059+
logging.debug(
1060+
"Retrieved metadata v15 for {} from Substrate node".format(
1061+
self.runtime_version
1062+
)
1063+
)
1064+
1065+
# Update metadata v15 cache
1066+
self._metadata_v15_cache[self.runtime_version] = metadata_v15
1067+
10181068
# Update type registry
10191069
self.reload_type_registry(use_remote_preset=False, auto_discover=True)
10201070

@@ -2487,6 +2537,56 @@ async def get_chain_finalised_head(self):
24872537

24882538
return response.get("result")
24892539

2540+
async def _do_runtime_call_old(
2541+
self,
2542+
api: str,
2543+
method: str,
2544+
params: Optional[Union[list, dict]] = None,
2545+
block_hash: Optional[str] = None,
2546+
) -> ScaleType:
2547+
logging.debug(
2548+
f"Decoding old runtime call: {api}.{method} with params: {params} at block hash: {block_hash}"
2549+
)
2550+
runtime_call_def = _TYPE_REGISTRY["runtime_api"][api]["methods"][method]
2551+
2552+
# Encode params
2553+
param_data = b""
2554+
2555+
if "encoder" in runtime_call_def:
2556+
param_data = runtime_call_def["encoder"](params)
2557+
else:
2558+
for idx, param in enumerate(runtime_call_def["params"]):
2559+
param_type_string = f"{param['type']}"
2560+
if isinstance(params, list):
2561+
param_data += await self.encode_scale(
2562+
param_type_string, params[idx]
2563+
)
2564+
else:
2565+
if param["name"] not in params:
2566+
raise ValueError(
2567+
f"Runtime Call param '{param['name']}' is missing"
2568+
)
2569+
2570+
param_data += await self.encode_scale(
2571+
param_type_string, params[param["name"]]
2572+
)
2573+
2574+
# RPC request
2575+
result_data = await self.rpc_request(
2576+
"state_call", [f"{api}_{method}", param_data.hex(), block_hash]
2577+
)
2578+
result_vec_u8_bytes = hex_to_bytes(result_data["result"])
2579+
result_bytes = await self.decode_scale("Vec<u8>", result_vec_u8_bytes)
2580+
2581+
# Decode result
2582+
# Get correct type
2583+
result_decoded = runtime_call_def["decoder"](bytes(result_bytes))
2584+
as_dict = _bt_decode_to_dict_or_list(result_decoded)
2585+
logging.debug("Decoded old runtime call result: ", as_dict)
2586+
result_obj = ScaleObj(as_dict)
2587+
2588+
return result_obj
2589+
24902590
async def runtime_call(
24912591
self,
24922592
api: str,
@@ -2513,14 +2613,27 @@ async def runtime_call(
25132613
params = {}
25142614

25152615
try:
2516-
metadata_v15 = self.metadata_v15.value()
2517-
apis = {entry["name"]: entry for entry in metadata_v15["apis"]}
2616+
if block_hash:
2617+
# Use old metadata v15 from init_runtime call
2618+
metadata_v15 = self._old_metadata_v15
2619+
else:
2620+
metadata_v15 = self.metadata_v15
2621+
2622+
self.registry = PortableRegistry.from_metadata_v15(metadata_v15)
2623+
metadata_v15_value = metadata_v15.value()
2624+
2625+
apis = {entry["name"]: entry for entry in metadata_v15_value["apis"]}
25182626
api_entry = apis[api]
25192627
methods = {entry["name"]: entry for entry in api_entry["methods"]}
25202628
runtime_call_def = methods[method]
25212629
except KeyError:
25222630
raise ValueError(f"Runtime API Call '{api}.{method}' not found in registry")
25232631

2632+
if _determine_if_old_runtime_call(runtime_call_def, metadata_v15_value):
2633+
result = await self._do_runtime_call_old(api, method, params, block_hash)
2634+
2635+
return result
2636+
25242637
if isinstance(params, list) and len(params) != len(runtime_call_def["inputs"]):
25252638
raise ValueError(
25262639
f"Number of parameter provided ({len(params)}) does not "

0 commit comments

Comments
 (0)