1313from websockets .exceptions import ConnectionClosed
1414
1515from async_substrate_interface .async_substrate import AsyncSubstrateInterface , Websocket
16- from async_substrate_interface .errors import MaxRetriesExceeded
16+ from async_substrate_interface .errors import MaxRetriesExceeded , StateDiscardedError
1717from async_substrate_interface .sync_substrate import SubstrateInterface
1818
1919logger = logging .getLogger ("async_substrate_interface" )
@@ -117,13 +117,17 @@ def __init__(
117117 max_retries : int = 5 ,
118118 retry_timeout : float = 60.0 ,
119119 _mock : bool = False ,
120+ archive_nodes : Optional [list [str ]] = None ,
120121 ):
121122 fallback_chains = fallback_chains or []
122123 self .fallback_chains = (
123124 iter (fallback_chains )
124125 if not retry_forever
125126 else cycle (fallback_chains + [url ])
126127 )
128+ self .archive_nodes = (
129+ iter (archive_nodes ) if not retry_forever else cycle (archive_nodes )
130+ )
127131 self .use_remote_preset = use_remote_preset
128132 self .chain_name = chain_name
129133 self ._mock = _mock
@@ -174,20 +178,32 @@ def _retry(self, method, *args, **kwargs):
174178 EOFError ,
175179 ConnectionClosed ,
176180 TimeoutError ,
181+ socket .gaierror ,
182+ StateDiscardedError ,
177183 ) as e :
184+ use_archive = isinstance (e , StateDiscardedError )
178185 try :
179- self ._reinstantiate_substrate (e )
186+ self ._reinstantiate_substrate (e , use_archive = use_archive )
180187 return method_ (* args , ** kwargs )
181188 except StopIteration :
182189 logger .error (
183190 f"Max retries exceeded with { self .url } . No more fallback chains."
184191 )
185192 raise MaxRetriesExceeded
186193
187- def _reinstantiate_substrate (self , e : Optional [Exception ] = None ) -> None :
188- next_network = next (self .fallback_chains )
194+ def _reinstantiate_substrate (
195+ self , e : Optional [Exception ] = None , use_archive : bool = False
196+ ) -> None :
197+ if use_archive :
198+ bh = getattr (e , "block_hash" , "Unknown Block Hash" )
199+ logger .info (
200+ f"Attempt made to { bh } failed for state discarded. Attempting to switch to archive node."
201+ )
202+ next_network = next (self .archive_nodes )
203+ else :
204+ next_network = next (self .fallback_chains )
189205 self .ws .close ()
190- if e . __class__ == MaxRetriesExceeded :
206+ if isinstance ( e , MaxRetriesExceeded ) :
191207 logger .error (
192208 f"Max retries exceeded with { self .url } . Retrying with { next_network } ."
193209 )
@@ -243,13 +259,17 @@ def __init__(
243259 max_retries : int = 5 ,
244260 retry_timeout : float = 60.0 ,
245261 _mock : bool = False ,
262+ archive_nodes : Optional [list [str ]] = None ,
246263 ):
247264 fallback_chains = fallback_chains or []
248265 self .fallback_chains = (
249266 iter (fallback_chains )
250267 if not retry_forever
251268 else cycle (fallback_chains + [url ])
252269 )
270+ self .archive_nodes = (
271+ iter (archive_nodes ) if not retry_forever else cycle (archive_nodes )
272+ )
253273 self .use_remote_preset = use_remote_preset
254274 self .chain_name = chain_name
255275 self ._mock = _mock
@@ -272,9 +292,18 @@ def __init__(
272292 for method in RETRY_METHODS :
273293 setattr (self , method , partial (self ._retry , method ))
274294
275- async def _reinstantiate_substrate (self , e : Optional [Exception ] = None ) -> None :
276- next_network = next (self .fallback_chains )
277- if e .__class__ == MaxRetriesExceeded :
295+ async def _reinstantiate_substrate (
296+ self , e : Optional [Exception ] = None , use_archive : bool = False
297+ ) -> None :
298+ if use_archive :
299+ bh = getattr (e , "block_hash" , "Unknown Block Hash" )
300+ logger .info (
301+ f"Attempt made to { bh } failed for state discarded. Attempting to switch to archive node."
302+ )
303+ next_network = next (self .archive_nodes )
304+ else :
305+ next_network = next (self .fallback_chains )
306+ if isinstance (e , MaxRetriesExceeded ):
278307 logger .error (
279308 f"Max retries exceeded with { self .url } . Retrying with { next_network } ."
280309 )
@@ -314,9 +343,11 @@ async def _retry(self, method, *args, **kwargs):
314343 ConnectionClosed ,
315344 EOFError ,
316345 socket .gaierror ,
346+ StateDiscardedError ,
317347 ) as e :
348+ use_archive = isinstance (e , StateDiscardedError )
318349 try :
319- await self ._reinstantiate_substrate (e )
350+ await self ._reinstantiate_substrate (e , use_archive = use_archive )
320351 return await method_ (* args , ** kwargs )
321352 except StopAsyncIteration :
322353 logger .error (
0 commit comments