diff --git a/ethtx/providers/node/connection_base.py b/ethtx/providers/node/connection_base.py index b593b7f9..0f166569 100644 --- a/ethtx/providers/node/connection_base.py +++ b/ethtx/providers/node/connection_base.py @@ -17,9 +17,10 @@ class NodeConnection: chain: str url: str poa: bool + timeout: int def __repr__(self) -> str: - return f"" + return f"" def __iter__(self): return iter(self.__dict__.items()) diff --git a/ethtx/providers/node/pool.py b/ethtx/providers/node/pool.py index b2bf156b..1632c557 100644 --- a/ethtx/providers/node/pool.py +++ b/ethtx/providers/node/pool.py @@ -42,7 +42,8 @@ def _set_connections(self, nodes) -> None: for chain, node_params in nodes.items(): nodes: List[str] = list(node_params.values())[0].split(",") poa: bool = list(node_params.values())[1] + timeout: int = list(node_params.values())[2] for url in nodes: self.add_connection( - NodeConnection(chain=chain, url=url.strip(), poa=poa) + NodeConnection(chain=chain, url=url.strip(), poa=poa, timeout=timeout) ) diff --git a/ethtx/providers/semantic_providers/repository.py b/ethtx/providers/semantic_providers/repository.py index c385d587..6af9fed9 100644 --- a/ethtx/providers/semantic_providers/repository.py +++ b/ethtx/providers/semantic_providers/repository.py @@ -13,6 +13,8 @@ from functools import lru_cache from typing import Optional, List, Dict, Tuple +from web3.exceptions import BadFunctionCallOutput + from ethtx.decoders.decoders.semantics import decode_events_and_functions from ethtx.models.semantics_model import ( AddressSemantics, @@ -159,10 +161,13 @@ def decode_parameter(_parameter): name = raw_address_semantics.get("name", address) if name == address and not raw_address_semantics["is_contract"]: - name = self._ens_provider.name( - provider=self._web3provider._get_node_connection(chain_id), - address=address, - ) + try: + name = self._ens_provider.name( + provider=self._web3provider._get_node_connection(chain_id), + address=address, + ) + except BadFunctionCallOutput as e: + pass # expected for sidechains like polygon address_semantics = AddressSemantics( chain_id=chain_id, @@ -263,10 +268,14 @@ def get_semantics(self, chain_id: str, address: str) -> Optional[AddressSemantic else: # externally owned address contract_semantics = ContractSemantics(code_hash=ZERO_HASH, name="EOA") - name = self._ens_provider.name( - provider=self._web3provider._get_node_connection(chain_id), - address=address, - ) + name = '' + try: + name = self._ens_provider.name( + provider=self._web3provider._get_node_connection(chain_id), + address=address, + ) + except BadFunctionCallOutput as e: + pass # expected for sidechains like polygon address_semantics = AddressSemantics( chain_id=chain_id, address=address, diff --git a/ethtx/providers/web3_provider.py b/ethtx/providers/web3_provider.py index 1979b642..e0743ce2 100644 --- a/ethtx/providers/web3_provider.py +++ b/ethtx/providers/web3_provider.py @@ -14,7 +14,9 @@ import os from functools import lru_cache from typing import List, Dict, Optional +from time import sleep +from requests.exceptions import HTTPError from web3 import Web3 from web3.datastructures import AttributeDict from web3.middleware import geth_poa_middleware @@ -30,7 +32,7 @@ def connect_chain( - http_hook: str = None, ipc_hook: str = None, ws_hook: str = None, poa: bool = False + http_hook: str = None, ipc_hook: str = None, ws_hook: str = None, poa: bool = False, timeout: int = 600 ) -> Web3 or None: if http_hook: provider = Web3.HTTPProvider @@ -45,7 +47,7 @@ def connect_chain( provider = Web3.IPCProvider hook = "\\\\.\\pipe\\geth.ipc" - w3 = Web3(provider(hook, request_kwargs={"timeout": 600})) + w3 = Web3(provider(hook, request_kwargs={"timeout": timeout})) # middleware injection for POA chains if poa: @@ -125,7 +127,7 @@ def _get_node_connection(self, chain_id: Optional[str] = None) -> Web3: for connection in NodeConnectionPool(nodes=self.nodes).get_connection( chain=chain_id ): - w3 = connect_chain(http_hook=connection.url, poa=connection.poa) + w3 = connect_chain(http_hook=connection.url, poa=connection.poa, timeout=connection.timeout) if w3.isConnected(): log.info( @@ -172,7 +174,7 @@ def get_block(self, block_number: int, chain_id: Optional[str] = None) -> W3Bloc # get the raw transaction data from the node @lru_cache(maxsize=512) def get_transaction( - self, tx_hash: str, chain_id: Optional[str] = None + self, tx_hash: str, chain_id: Optional[str] = None, timeout: Optional[int] = None ) -> W3Transaction: chain = self._get_node_connection(chain_id) raw_tx: TxData = chain.eth.get_transaction(HexStr(tx_hash)) @@ -248,9 +250,24 @@ def get_calls(self, tx_hash: str, chain_id: Optional[str] = None) -> W3CallTree: # tracer is a temporary fixed implementation of geth tracer chain = self._get_node_connection(chain_id) tracer = self._get_custom_calls_tracer() - response = chain.manager.request_blocking( - "debug_traceTransaction", [tx_hash, {"tracer": tracer, "timeout": "60s"}] - ) + retry_count = 10 + while True: + try: + response = chain.manager.request_blocking( + "debug_traceTransaction", [tx_hash, {"tracer": tracer, "timeout": "60s"}] + ) + break + except HTTPError as e: + if retry_count < 0: + raise e + retry_count = retry_count - 1 + log.debug( + "Retrying remote request - %s of 10.", + 10 - retry_count, + ) + sleep(1.25) # recommended retry sleep time for alchemy. + except Exception as e: + raise e return self._create_call_from_debug_trace_tx( tx_hash, chain_id or self.default_chain, response