Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 34 additions & 42 deletions async_substrate_interface/async_substrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import scalecodec
import websockets.exceptions
from bt_decode import MetadataV15, PortableRegistry, decode as decode_by_type_string
from scalecodec import GenericVariant
from scalecodec.base import ScaleBytes, ScaleType, RuntimeConfigurationObject
from scalecodec.type_registry import load_type_registry_preset
Expand Down Expand Up @@ -73,11 +72,13 @@
)
from async_substrate_interface.utils.decoding import (
_determine_if_old_runtime_call,
_bt_decode_to_dict_or_list,
legacy_scale_decode,
convert_account_ids,
decode_query_map_async,
)
from async_substrate_interface.types import (
_decode_option_opaque_metadata,
_decode_v15_metadata,
)
from async_substrate_interface.utils.storage import StorageKey
from async_substrate_interface.type_registry import _TYPE_REGISTRY

Expand Down Expand Up @@ -1263,7 +1264,6 @@ def __init__(
_mock: bool = False,
_log_raw_websockets: bool = False,
ws_shutdown_timer: Optional[float] = 5.0,
decode_ss58: bool = False,
_ssl_context: Optional[_SessionResumingSSLContext] = None,
dns_ttl: int = 300,
):
Expand All @@ -1285,7 +1285,6 @@ def __init__(
_mock: whether to use mock version of the subtensor interface
_log_raw_websockets: whether to log raw websocket requests during RPC requests
ws_shutdown_timer: how long after the last connection your websocket should close
decode_ss58: Whether to decode AccountIds to SS58 or leave them in raw bytes tuples.
_ssl_context: optional session-resuming SSL context; used internally by
DiskCachedAsyncSubstrateInterface to enable TLS session reuse.
dns_ttl: seconds to cache DNS results for the websocket URL (default 300). Set to 0
Expand All @@ -1297,7 +1296,6 @@ def __init__(
type_registry_preset,
use_remote_preset,
ss58_format,
decode_ss58,
)
self.max_retries = max_retries
self.retry_timeout = retry_timeout
Expand Down Expand Up @@ -1463,9 +1461,7 @@ async def _get_current_block_hash(
return self.last_block_hash
return block_hash

async def _load_registry_at_block(
self, block_hash: Optional[str]
) -> tuple[Optional[MetadataV15], Optional[PortableRegistry]]:
async def _load_registry_at_block(self, block_hash: Optional[str]):
# Should be called for any block that fails decoding.
# Possibly the metadata was different.
try:
Expand All @@ -1479,14 +1475,15 @@ async def _load_registry_at_block(
"Client error: Execution failed: Other: Exported method Metadata_metadata_at_version is not found"
in e.args
):
return None, None
return None
else:
raise e
metadata_option_hex_str = metadata_rpc_result["result"]
metadata_option_bytes = bytes.fromhex(metadata_option_hex_str[2:])
metadata = MetadataV15.decode_from_metadata_option(metadata_option_bytes)
registry = PortableRegistry.from_metadata_v15(metadata)
return metadata, registry
inner_bytes = _decode_option_opaque_metadata(metadata_option_bytes)
if inner_bytes is None:
return None
return _decode_v15_metadata(inner_bytes)

async def encode_scale(
self,
Expand Down Expand Up @@ -1550,19 +1547,16 @@ async def decode_scale(
else:
if runtime is None:
runtime = await self.init_runtime(block_hash=block_hash)
if runtime.metadata_v15 is not None and force_legacy is False:
obj = await asyncio.to_thread(
decode_by_type_string, type_string, runtime.registry, scale_bytes
)
if self.decode_ss58:
try:
type_str_int = int(type_string.split("::")[1])
decoded_type_str = runtime.type_id_to_name[type_str_int]
obj = convert_account_ids(
obj, decoded_type_str, runtime.ss58_format
)
except (ValueError, KeyError):
pass
if runtime.implements_scaleinfo and force_legacy is False:
try:
obj = await asyncio.to_thread(
runtime.runtime_config.batch_decode,
[type_string],
[scale_bytes],
)
obj = obj[0]
except NotImplementedError:
obj = legacy_scale_decode(type_string, scale_bytes, runtime)
else:
obj = legacy_scale_decode(type_string, scale_bytes, runtime)
if return_scale_obj:
Expand Down Expand Up @@ -1659,7 +1653,7 @@ async def _get_runtime_for_version(
self.get_parent_block_hash(block_hash),
self.get_block_number(block_hash),
)
runtime_info, metadata, (metadata_v15, registry) = await asyncio.gather(
runtime_info, metadata, metadata_v15 = await asyncio.gather(
self.get_block_runtime_info(runtime_block_hash),
self.get_block_metadata(
block_hash=runtime_block_hash,
Expand Down Expand Up @@ -1689,7 +1683,6 @@ async def _get_runtime_for_version(
runtime_config=runtime_config,
metadata_v15=metadata_v15,
runtime_info=runtime_info,
registry=registry,
ss58_format=self.ss58_format,
)
self.runtime_cache.add_item(
Expand Down Expand Up @@ -2482,13 +2475,13 @@ def convert_event_data(data):
block_hash=block_hash,
force_legacy_decode=True,
)
# bt-decode Metadata V15 is not ideal for events. Force legacy decoding for this
# Force legacy decoding for events
if storage_obj:
for item in list(storage_obj):
events.append(item)
return events

async def get_metadata(self, block_hash=None) -> MetadataV15:
async def get_metadata(self, block_hash=None):
"""
Returns `MetadataVersioned` object for given block_hash or chaintip if block_hash is omitted

Expand Down Expand Up @@ -2646,10 +2639,6 @@ async def _preprocess(
# SCALE type string of value
param_types = storage_item.get_params_type_string()
value_scale_type = storage_item.get_value_type_string()
# V14 and V15 metadata may have different portable type registry numbering.
# Use V15 type ID when available to ensure correct decoding with the V15 registry.
if v15_type_id := runtime.get_v15_storage_type_id(module, storage_function):
value_scale_type = f"scale_info::{v15_type_id}"

if len(params) != len(param_types):
raise ValueError(
Expand Down Expand Up @@ -3464,7 +3453,7 @@ async def _do_runtime_call_old(
if "encoder" in runtime_call_def:
if runtime is None:
runtime = await self.init_runtime(block_hash=block_hash)
param_data = runtime_call_def["encoder"](params, runtime.registry)
param_data = runtime_call_def["encoder"](params, runtime)
else:
for idx, param in enumerate(runtime_call_def["params"]):
param_type_string = f"{param['type']}"
Expand Down Expand Up @@ -3497,10 +3486,13 @@ async def _do_runtime_call_old(

# Decode result
# Get correct type
result_decoded = runtime_call_def["decoder"](bytes(result_bytes))
as_dict = _bt_decode_to_dict_or_list(result_decoded)
logger.debug("Decoded old runtime call result: ", as_dict)
result_obj = ScaleObj(as_dict)
if isinstance(result_bytes, str):
raw_bytes = hex_to_bytes(result_bytes)
else:
raw_bytes = bytes(result_bytes)
result_decoded = runtime_call_def["decoder"](raw_bytes, runtime)
logger.debug("Decoded old runtime call result: ", result_decoded)
result_obj = ScaleObj(result_decoded)

return result_obj

Expand Down Expand Up @@ -3542,7 +3534,9 @@ async def runtime_call(
)

else:
metadata_v15_value = runtime.metadata_v15.value()
metadata_v15_value = (
runtime.metadata_v15.get_metadata().value_object[1].value
)

apis = {entry["name"]: entry for entry in metadata_v15_value["apis"]}
api_entry = apis[api]
Expand Down Expand Up @@ -4050,7 +4044,6 @@ async def query_map(
value_type,
key_hashers,
ignore_decoding_errors,
self.decode_ss58,
)
else:
# storage item and value scale type are not included here because this is batch-decoded in rust
Expand Down Expand Up @@ -4097,7 +4090,6 @@ async def query_map(
value_type,
key_hashers,
ignore_decoding_errors,
self.decode_ss58,
)
return AsyncQueryMapResult(
records=result,
Expand Down
Loading
Loading