Skip to content

Commit d80c1e6

Browse files
committed
New direction
1 parent b544fb9 commit d80c1e6

File tree

1 file changed

+175
-140
lines changed

1 file changed

+175
-140
lines changed

async_substrate_interface/substrate_addons.py

Lines changed: 175 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,90 @@
22
import logging
33
from functools import partial
44
from itertools import cycle
5-
from typing import Optional, Type, Union
5+
from typing import Optional
66

77
from async_substrate_interface.async_substrate import AsyncSubstrateInterface
88
from async_substrate_interface.errors import MaxRetriesExceeded
99
from async_substrate_interface.sync_substrate import SubstrateInterface
1010

11-
SubstrateClass = Type[Union[SubstrateInterface, AsyncSubstrateInterface]]
1211

12+
RETRY_METHODS = [
13+
"_get_block_handler",
14+
"apply_type_registry_presets",
15+
"close",
16+
"compose_call",
17+
"connect",
18+
"create_scale_object",
19+
"create_signed_extrinsic",
20+
"create_storage_key",
21+
"decode_scale",
22+
"encode_scale",
23+
"extension_call",
24+
"filter_events",
25+
"filter_extrinsics",
26+
"generate_signature_payload",
27+
"get_account_next_index",
28+
"get_account_nonce",
29+
"get_block",
30+
"get_block_hash",
31+
"get_block_header",
32+
"get_block_metadata",
33+
"get_block_number",
34+
"get_block_runtime_info",
35+
"get_block_runtime_version_for",
36+
"get_block_timestamp",
37+
"get_chain_finalised_head",
38+
"get_chain_head",
39+
"get_constant",
40+
"get_events",
41+
"get_extrinsics",
42+
"get_metadata_call_function",
43+
"get_metadata_constant",
44+
"get_metadata_error",
45+
"get_metadata_errors",
46+
"get_metadata_module",
47+
"get_metadata_modules",
48+
"get_metadata_runtime_call_function",
49+
"get_metadata_runtime_call_functions",
50+
"get_metadata_storage_function",
51+
"get_metadata_storage_functions",
52+
"get_parent_block_hash",
53+
"get_payment_info",
54+
"get_storage_item",
55+
"get_type_definition",
56+
"get_type_registry",
57+
"init_runtime",
58+
"initialize",
59+
"is_valid_ss58_address",
60+
"load_runtime",
61+
"make_payload",
62+
"query",
63+
"query_map",
64+
"query_multi",
65+
"query_multiple",
66+
"reload_type_registry",
67+
"retrieve_extrinsic_by_hash",
68+
"retrieve_extrinsic_by_identifier",
69+
"rpc_request",
70+
"runtime_call",
71+
"search_block_number",
72+
"serialize_constant",
73+
"serialize_module_call",
74+
"serialize_module_error",
75+
"serialize_module_event",
76+
"serialize_storage_item",
77+
"ss58_decode",
78+
"ss58_encode",
79+
"submit_extrinsic",
80+
"subscribe_block_headers",
81+
"supports_rpc_method",
82+
]
1383

14-
class RetrySubstrate:
84+
85+
class RetrySyncSubstrate(SubstrateInterface):
1586
def __init__(
1687
self,
17-
substrate: SubstrateClass,
18-
main_url: str,
88+
url: str,
1989
use_remote_preset: bool = False,
2090
fallback_chains: Optional[list[str]] = None,
2191
retry_forever: bool = False,
@@ -28,187 +98,152 @@ def __init__(
2898
_mock: bool = False,
2999
):
30100
fallback_chains = fallback_chains or []
31-
self._substrate_class: SubstrateClass = substrate
32-
self.ss58_format: int = ss58_format
33-
self.type_registry: dict = type_registry
34-
self.use_remote_preset: bool = use_remote_preset
35-
self.chain_name: Optional[str] = chain_name
36-
self.max_retries: int = max_retries
37-
self.retry_timeout: float = retry_timeout
38-
self._mock = _mock
39-
self.type_registry_preset: Optional[str] = type_registry_preset
40101
self.fallback_chains = (
41102
iter(fallback_chains)
42103
if not retry_forever
43-
else cycle(fallback_chains + [main_url])
104+
else cycle(fallback_chains + [url])
44105
)
106+
self.use_remote_preset = use_remote_preset
107+
self.chain_name = chain_name
108+
self._mock = _mock
109+
self.retry_timeout = retry_timeout
110+
self.max_retries = max_retries
45111
initialized = False
46-
for chain_url in [main_url] + fallback_chains:
112+
for chain_url in [url] + fallback_chains:
47113
try:
48-
self._substrate = self._substrate_class(
114+
super().__init__(
49115
url=chain_url,
50116
ss58_format=ss58_format,
51117
type_registry=type_registry,
52118
use_remote_preset=use_remote_preset,
119+
type_registry_preset=type_registry_preset,
53120
chain_name=chain_name,
54121
_mock=_mock,
122+
retry_timeout=retry_timeout,
123+
max_retries=max_retries,
55124
)
56125
initialized = True
57126
break
58127
except ConnectionError:
59128
logging.warning(f"Unable to connect to {chain_url}")
60129
if not initialized:
61130
raise ConnectionError(
62-
f"Unable to connect at any chains specified: {[main_url] + fallback_chains}"
131+
f"Unable to connect at any chains specified: {[url] + fallback_chains}"
63132
)
64-
65-
# retries
66-
67-
# TODO: properties that need retry logic
68-
# properties
69-
# version
70-
# token_decimals
71-
# token_symbol
72-
# name
73-
74-
retry = (
75-
self._async_retry
76-
if self._substrate_class == AsyncSubstrateInterface
77-
else self._retry
78-
)
79-
80-
self._get_block_handler = partial(retry, "_get_block_handler")
81-
self.apply_type_registry_presets = partial(retry, "apply_type_registry_presets")
82-
self.close = partial(retry, "close")
83-
self.compose_call = partial(retry, "compose_call")
84-
self.connect = partial(retry, "connect")
85-
self.create_scale_object = partial(retry, "create_scale_object")
86-
self.create_signed_extrinsic = partial(retry, "create_signed_extrinsic")
87-
self.create_storage_key = partial(retry, "create_storage_key")
88-
self.decode_scale = partial(retry, "decode_scale")
89-
self.encode_scale = partial(retry, "encode_scale")
90-
self.extension_call = partial(retry, "extension_call")
91-
self.filter_events = partial(retry, "filter_events")
92-
self.filter_extrinsics = partial(retry, "filter_extrinsics")
93-
self.generate_signature_payload = partial(retry, "generate_signature_payload")
94-
self.get_account_next_index = partial(retry, "get_account_next_index")
95-
self.get_account_nonce = partial(retry, "get_account_nonce")
96-
self.get_block = partial(retry, "get_block")
97-
self.get_block_hash = partial(retry, "get_block_hash")
98-
self.get_block_header = partial(retry, "get_block_header")
99-
self.get_block_metadata = partial(retry, "get_block_metadata")
100-
self.get_block_number = partial(retry, "get_block_number")
101-
self.get_block_runtime_info = partial(retry, "get_block_runtime_info")
102-
self.get_block_runtime_version_for = partial(
103-
retry, "get_block_runtime_version_for"
104-
)
105-
self.get_block_timestamp = partial(retry, "get_block_timestamp")
106-
self.get_chain_finalised_head = partial(retry, "get_chain_finalised_head")
107-
self.get_chain_head = partial(retry, "get_chain_head")
108-
self.get_constant = partial(retry, "get_constant")
109-
self.get_events = partial(retry, "get_events")
110-
self.get_extrinsics = partial(retry, "get_extrinsics")
111-
self.get_metadata_call_function = partial(retry, "get_metadata_call_function")
112-
self.get_metadata_constant = partial(retry, "get_metadata_constant")
113-
self.get_metadata_error = partial(retry, "get_metadata_error")
114-
self.get_metadata_errors = partial(retry, "get_metadata_errors")
115-
self.get_metadata_module = partial(retry, "get_metadata_module")
116-
self.get_metadata_modules = partial(retry, "get_metadata_modules")
117-
self.get_metadata_runtime_call_function = partial(
118-
retry, "get_metadata_runtime_call_function"
119-
)
120-
self.get_metadata_runtime_call_functions = partial(
121-
retry, "get_metadata_runtime_call_functions"
122-
)
123-
self.get_metadata_storage_function = partial(
124-
retry, "get_metadata_storage_function"
125-
)
126-
self.get_metadata_storage_functions = partial(
127-
retry, "get_metadata_storage_functions"
128-
)
129-
self.get_parent_block_hash = partial(retry, "get_parent_block_hash")
130-
self.get_payment_info = partial(retry, "get_payment_info")
131-
self.get_storage_item = partial(retry, "get_storage_item")
132-
self.get_type_definition = partial(retry, "get_type_definition")
133-
self.get_type_registry = partial(retry, "get_type_registry")
134-
self.init_runtime = partial(retry, "init_runtime")
135-
self.initialize = partial(retry, "initialize")
136-
self.is_valid_ss58_address = partial(retry, "is_valid_ss58_address")
137-
self.load_runtime = partial(retry, "load_runtime")
138-
self.make_payload = partial(retry, "make_payload")
139-
self.query = partial(retry, "query")
140-
self.query_map = partial(retry, "query_map")
141-
self.query_multi = partial(retry, "query_multi")
142-
self.query_multiple = partial(retry, "query_multiple")
143-
self.reload_type_registry = partial(retry, "reload_type_registry")
144-
self.retrieve_extrinsic_by_hash = partial(retry, "retrieve_extrinsic_by_hash")
145-
self.retrieve_extrinsic_by_identifier = partial(
146-
retry, "retrieve_extrinsic_by_identifier"
147-
)
148-
self.rpc_request = partial(retry, "rpc_request")
149-
self.runtime_call = partial(retry, "runtime_call")
150-
self.search_block_number = partial(retry, "search_block_number")
151-
self.serialize_constant = partial(retry, "serialize_constant")
152-
self.serialize_module_call = partial(retry, "serialize_module_call")
153-
self.serialize_module_error = partial(retry, "serialize_module_error")
154-
self.serialize_module_event = partial(retry, "serialize_module_event")
155-
self.serialize_storage_item = partial(retry, "serialize_storage_item")
156-
self.ss58_decode = partial(retry, "ss58_decode")
157-
self.ss58_encode = partial(retry, "ss58_encode")
158-
self.submit_extrinsic = partial(retry, "submit_extrinsic")
159-
self.subscribe_block_headers = partial(retry, "subscribe_block_headers")
160-
self.supports_rpc_method = partial(retry, "supports_rpc_method")
161-
self.ws = self._substrate.ws
133+
for method in RETRY_METHODS:
134+
setattr(self, method, partial(self._retry, method))
162135

163136
def _retry(self, method, *args, **kwargs):
164137
try:
165-
method_ = getattr(self._substrate, method)
138+
method_ = getattr(self, method)
166139
return method_(*args, **kwargs)
167140
except (MaxRetriesExceeded, ConnectionError, ConnectionRefusedError) as e:
168141
try:
169142
self._reinstantiate_substrate(e)
170-
method_ = getattr(self._substrate, method)
143+
method_ = getattr(self, method)
171144
return self._retry(method_(*args, **kwargs))
172145
except StopIteration:
173146
logging.error(
174-
f"Max retries exceeded with {self._substrate.url}. No more fallback chains."
147+
f"Max retries exceeded with {self.url}. No more fallback chains."
175148
)
176149
raise MaxRetriesExceeded
177150

178-
async def _async_retry(self, method, *args, **kwargs):
151+
def _reinstantiate_substrate(self, e: Optional[Exception] = None) -> None:
152+
next_network = next(self.fallback_chains)
153+
if e.__class__ == MaxRetriesExceeded:
154+
logging.error(
155+
f"Max retries exceeded with {self.url}. Retrying with {next_network}."
156+
)
157+
else:
158+
print(f"Connection error. Trying again with {next_network}")
159+
super().__init__(
160+
url=next_network,
161+
ss58_format=self.ss58_format,
162+
type_registry=self.type_registry,
163+
use_remote_preset=self.use_remote_preset,
164+
chain_name=self.chain_name,
165+
_mock=self._mock,
166+
retry_timeout=self.retry_timeout,
167+
max_retries=self.max_retries,
168+
)
169+
170+
171+
class RetryAsyncSubstrate(AsyncSubstrateInterface):
172+
def __init__(
173+
self,
174+
url: str,
175+
use_remote_preset: bool = False,
176+
fallback_chains: Optional[list[str]] = None,
177+
retry_forever: bool = False,
178+
ss58_format: Optional[int] = None,
179+
type_registry: Optional[dict] = None,
180+
type_registry_preset: Optional[str] = None,
181+
chain_name: str = "",
182+
max_retries: int = 5,
183+
retry_timeout: float = 60.0,
184+
_mock: bool = False,
185+
):
186+
fallback_chains = fallback_chains or []
187+
self.fallback_chains = (
188+
iter(fallback_chains)
189+
if not retry_forever
190+
else cycle(fallback_chains + [url])
191+
)
192+
self.use_remote_preset = use_remote_preset
193+
self.chain_name = chain_name
194+
self._mock = _mock
195+
self.retry_timeout = retry_timeout
196+
self.max_retries = max_retries
197+
super().__init__(
198+
url=url,
199+
ss58_format=ss58_format,
200+
type_registry=type_registry,
201+
use_remote_preset=use_remote_preset,
202+
type_registry_preset=type_registry_preset,
203+
chain_name=chain_name,
204+
_mock=_mock,
205+
retry_timeout=retry_timeout,
206+
max_retries=max_retries,
207+
)
208+
for method in RETRY_METHODS:
209+
setattr(self, method, partial(self._retry, method))
210+
211+
def _reinstantiate_substrate(self, e: Optional[Exception] = None) -> None:
212+
next_network = next(self.fallback_chains)
213+
if e.__class__ == MaxRetriesExceeded:
214+
logging.error(
215+
f"Max retries exceeded with {self.url}. Retrying with {next_network}."
216+
)
217+
else:
218+
print(f"Connection error. Trying again with {next_network}")
219+
super().__init__(
220+
url=next_network,
221+
ss58_format=self.ss58_format,
222+
type_registry=self.type_registry,
223+
use_remote_preset=self.use_remote_preset,
224+
chain_name=self.chain_name,
225+
_mock=self._mock,
226+
retry_timeout=self.retry_timeout,
227+
max_retries=self.max_retries,
228+
)
229+
230+
async def _retry(self, method, *args, **kwargs):
179231
try:
180-
method_ = getattr(self._substrate, method)
232+
method_ = getattr(self, method)
181233
if asyncio.iscoroutinefunction(method_):
182234
return await method_(*args, **kwargs)
183235
else:
184236
return method_(*args, **kwargs)
185237
except (MaxRetriesExceeded, ConnectionError, ConnectionRefusedError) as e:
186238
try:
187239
self._reinstantiate_substrate(e)
188-
method_ = getattr(self._substrate, method)
240+
method_ = getattr(self, method)
189241
if asyncio.iscoroutinefunction(method_):
190242
return await method_(*args, **kwargs)
191243
else:
192244
return method_(*args, **kwargs)
193245
except StopIteration:
194246
logging.error(
195-
f"Max retries exceeded with {self._substrate.url}. No more fallback chains."
247+
f"Max retries exceeded with {self.url}. No more fallback chains."
196248
)
197249
raise MaxRetriesExceeded
198-
199-
def _reinstantiate_substrate(self, e: Optional[Exception] = None) -> None:
200-
next_network = next(self.fallback_chains)
201-
if e.__class__ == MaxRetriesExceeded:
202-
logging.error(
203-
f"Max retries exceeded with {self._substrate.url}. Retrying with {next_network}."
204-
)
205-
else:
206-
print(f"Connection error. Trying again with {next_network}")
207-
self._substrate = self._substrate_class(
208-
url=next_network,
209-
ss58_format=self.ss58_format,
210-
type_registry=self.type_registry,
211-
use_remote_preset=self.use_remote_preset,
212-
chain_name=self.chain_name,
213-
_mock=self._mock,
214-
)

0 commit comments

Comments
 (0)