Compares system time with block time for each default RPC provider.

pull/3496/head
KPrasch 2024-05-09 15:56:55 +02:00
parent be515e26be
commit 6509b2b3d7
No known key found for this signature in database
1 changed files with 24 additions and 6 deletions

View File

@ -1,3 +1,4 @@
import time
from decimal import Decimal from decimal import Decimal
from typing import List, Union, Dict from typing import List, Union, Dict
@ -67,7 +68,11 @@ def get_tx_cost_data(transaction_dict: TxParams):
return max_cost, max_price_gwei, tx_type return max_cost, max_price_gwei, tx_type
def rpc_endpoint_health_check(endpoint: str) -> bool: def rpc_endpoint_health_check(endpoint: str, max_drift_seconds: int = 60) -> bool:
"""
Checks the health of an Ethereum RPC endpoint by comparing the timestamp of the latest block
with the system time. The maximum drift allowed is `max_drift_seconds`.
"""
query = { query = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": "eth_getBlockByNumber", "method": "eth_getBlockByNumber",
@ -84,27 +89,40 @@ def rpc_endpoint_health_check(endpoint: str) -> bool:
if response.status_code == 200: if response.status_code == 200:
data = response.json() data = response.json()
if "result" in data and data["result"] is not None: if "result" in data and data["result"] is not None:
return True block_data = data["result"]
timestamp = int(block_data.get("timestamp"), 16)
system_time = time.time()
drift = abs(system_time - timestamp)
if drift < max_drift_seconds:
return True
return False
except requests.exceptions.RequestException: except requests.exceptions.RequestException:
return False return False
def get_default_rpc_endpoints() -> Dict[int, List[str]]: def get_default_rpc_endpoints() -> Dict[int, List[str]]:
"""
Fetches the default RPC endpoints for various chains from the nucypher/chainlist repository.
"""
# TODO: Memoize? When to refresh? # TODO: Memoize? When to refresh?
response = requests.get(CHAINLIST_URL) response = requests.get(CHAINLIST_URL)
if response.status_code == 200: if response.status_code == 200:
return response.json() return {int(chain_id): endpoints for chain_id, endpoints in response.json().items()}
else: else:
# TODO: use an embedded fallback here? # TODO: use an embedded fallback here?
return {} return {}
def get_healthy_default_rpc_endpoints(chain_id: int) -> List[str]: def get_healthy_default_rpc_endpoints(chain_id: int) -> List[str]:
"""
Returns a list of healthy RPC endpoints for a given chain ID.
"""
healthy = [] healthy = []
endpoints = get_default_rpc_endpoints().get(chain_id) endpoints = get_default_rpc_endpoints()
if not endpoints: chain_endpoints = endpoints.get(chain_id)
if not chain_endpoints:
return healthy return healthy
for endpoint in endpoints: for endpoint in chain_endpoints:
if rpc_endpoint_health_check(endpoint=endpoint): if rpc_endpoint_health_check(endpoint=endpoint):
healthy.append(endpoint) healthy.append(endpoint)
return healthy return healthy