From 6283e44ccfb26caee0ded79e17fd2bfe863bb725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Pawe=C5=82=20G=C5=82azik?= Date: Wed, 18 Dec 2024 12:52:54 +0100 Subject: [PATCH] Add EVM HTTP generic collector --- .gitignore | 2 + src/collectors.py | 54 +++++++++++++++++++ src/configuration.py | 2 +- src/registries.py | 2 + src/test_registries.py | 9 ++++ src/tests/fixtures/configuration_evmhttp.yaml | 15 ++++++ 6 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/tests/fixtures/configuration_evmhttp.yaml diff --git a/.gitignore b/.gitignore index 703d661..1994d09 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ *.out local* +.idea + # Python venv __pycache__ diff --git a/src/collectors.py b/src/collectors.py index dd838eb..c44f9ce 100644 --- a/src/collectors.py +++ b/src/collectors.py @@ -446,3 +446,57 @@ def client_version(self): def latency(self): """Returns connection latency.""" return self.interface.latest_query_latency + + +class EvmHttpCollector(): + """A collector to fetch information from EVM HTTPS endpoints.""" + + def __init__(self, url, labels, chain_id, **client_parameters): + + self.labels = labels + self.chain_id = chain_id + self.interface = HttpsInterface(url, client_parameters.get('open_timeout'), + client_parameters.get('ping_timeout')) + + self._logger_metadata = { + 'component': 'EvmHttpCollector', + 'url': strip_url(url) + } + self.client_version_payload = { + 'jsonrpc': '2.0', + 'method': "web3_clientVersion", + 'id': 1 + } + self.block_height_payload = { + 'jsonrpc': '2.0', + 'method': "eth_blockNumber", + 'id': 1 + } + + def alive(self): + """Returns true if endpoint is alive, false if not.""" + # Run cached query because we can also fetch client version from this + # later on. This will save us an RPC call per run. + return self.interface.cached_json_rpc_post( + self.client_version_payload) is not None + + def block_height(self): + """Cached query and returns blockheight after converting hex string value to an int""" + result = self.interface.cached_json_rpc_post(self.block_height_payload) + + if result and isinstance(result, str) and result.startswith('0x'): + return int(result, 16) + raise ValueError(f"Invalid block height result: {result}") + + def client_version(self): + """Runs a cached query to return client version.""" + version = self.interface.cached_json_rpc_post( + self.client_version_payload) + if version is None: + return None + client_version = {"client_version": version} + return client_version + + def latency(self): + """Returns connection latency.""" + return self.interface.latest_query_latency diff --git a/src/configuration.py b/src/configuration.py index 284fd27..4eac0bc 100644 --- a/src/configuration.py +++ b/src/configuration.py @@ -46,7 +46,7 @@ def endpoints(self): def _load_configuration(self): allowed_providers = self._load_validation_file() - supported_collectors = ('evm', 'cardano', 'conflux', 'solana', + supported_collectors = ('evm', 'evmhttp', 'cardano', 'conflux', 'solana', 'bitcoin', 'doge', 'filecoin', 'starknet', 'aptos', 'tron') diff --git a/src/registries.py b/src/registries.py index 1e4b7c8..bf225ee 100644 --- a/src/registries.py +++ b/src/registries.py @@ -90,6 +90,8 @@ def get_collector_registry(self) -> list: collector = collectors.AptosCollector case "tron", "tron": collector = collectors.TronCollector + case "evmhttp", other: # pylint: disable=unused-variable + collector = collectors.EvmHttpCollector case "evm", other: # pylint: disable=unused-variable collector = collectors.EvmCollector if collector is None: diff --git a/src/test_registries.py b/src/test_registries.py index d72c03b..ddc0aaf 100644 --- a/src/test_registries.py +++ b/src/test_registries.py @@ -157,6 +157,15 @@ def test_get_collector_registry_for_tron(self): with mock.patch('collectors.TronCollector', new=mock.Mock()) as collector: helper_test_collector_registry(self, collector) + @mock.patch.dict(os.environ, { + "CONFIG_FILE_PATH": "tests/fixtures/configuration_evmhttp.yaml", + "VALIDATION_FILE_PATH": "tests/fixtures/validation.yaml" + }) + def test_get_collector_registry_for_evmhttp(self): + """Tests that the EVM HTTP collector is called with the correct args""" + self.collector_registry = CollectorRegistry() + with mock.patch('collectors.EvmHttpCollector', new=mock.Mock()) as collector: + helper_test_collector_registry(self, collector) @mock.patch.dict(os.environ, { "CONFIG_FILE_PATH": "tests/fixtures/configuration_evm.yaml", diff --git a/src/tests/fixtures/configuration_evmhttp.yaml b/src/tests/fixtures/configuration_evmhttp.yaml new file mode 100644 index 0000000..c1d7e60 --- /dev/null +++ b/src/tests/fixtures/configuration_evmhttp.yaml @@ -0,0 +1,15 @@ +blockchain: "Evm" +chain_id: 1234 +network_name: "TestNetwork" +network_type: "Testnet" +integration_maturity: "development" +canonical_name: "test-network-testnet" +chain_selector: 121212 +collector: "evmhttp" +endpoints: + - url: https://test1.com + provider: TestProvider1 + - url: https://test2.com + provider: TestProvider2 + - url: https://test3.com + provider: TestProvider3