Skip to content

Commit a272dd6

Browse files
Tron collector for Exporter (#63)
* Initial Changes to Enable Tron collector for Exporter * Update text * Update comments * Initial tests * Lint changes
1 parent 62b68f9 commit a272dd6

File tree

6 files changed

+157
-1
lines changed

6 files changed

+157
-1
lines changed

src/collectors.py

+53
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,56 @@ def client_version(self):
393393
def latency(self):
394394
"""Returns connection latency."""
395395
return self.interface.latest_query_latency
396+
397+
class TronCollector():
398+
"""A collector to fetch information from Tron endpoints."""
399+
400+
def __init__(self, url, labels, chain_id, **client_parameters):
401+
402+
self.labels = labels
403+
self.chain_id = chain_id
404+
self.interface = HttpsInterface(url, client_parameters.get('open_timeout'),
405+
client_parameters.get('ping_timeout'))
406+
407+
self._logger_metadata = {
408+
'component': 'TronCollector',
409+
'url': strip_url(url)
410+
}
411+
self.client_version_payload = {
412+
'jsonrpc': '2.0',
413+
'method': "web3_clientVersion",
414+
'id': 1
415+
}
416+
self.block_height_payload = {
417+
'jsonrpc': '2.0',
418+
'method': "eth_blockNumber",
419+
'id': 1
420+
}
421+
422+
def alive(self):
423+
"""Returns true if endpoint is alive, false if not."""
424+
# Run cached query because we can also fetch client version from this
425+
# later on. This will save us an RPC call per run.
426+
return self.interface.cached_json_rpc_post(
427+
self.client_version_payload) is not None
428+
429+
def block_height(self):
430+
"""Cached query and returns blockheight after converting hex string value to an int"""
431+
result = self.interface.cached_json_rpc_post(self.block_height_payload)
432+
433+
if result and isinstance(result, str) and result.startswith('0x'):
434+
return int(result, 16)
435+
raise ValueError(f"Invalid block height result: {result}")
436+
437+
def client_version(self):
438+
"""Runs a cached query to return client version."""
439+
version = self.interface.cached_json_rpc_post(
440+
self.client_version_payload)
441+
if version is None:
442+
return None
443+
client_version = {"client_version": version}
444+
return client_version
445+
446+
def latency(self):
447+
"""Returns connection latency."""
448+
return self.interface.latest_query_latency

src/configuration.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ def endpoints(self):
4747
def _load_configuration(self):
4848
allowed_providers = self._load_validation_file()
4949
supported_collectors = ('evm', 'cardano', 'conflux', 'solana',
50-
'bitcoin', 'doge', 'filecoin', 'starknet', 'aptos')
50+
'bitcoin', 'doge', 'filecoin', 'starknet', 'aptos',
51+
'tron')
5152

5253
configuration_schema = Schema({
5354
'blockchain':

src/registries.py

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ def get_collector_registry(self) -> list:
8383
collector = collectors.StarknetCollector
8484
case "aptos", "aptos":
8585
collector = collectors.AptosCollector
86+
case "tron", "tron":
87+
collector = collectors.TronCollector
8688
case "evm", other: # pylint: disable=unused-variable
8789
collector = collectors.EvmCollector
8890
if collector is None:

src/test_collectors.py

+77
Original file line numberDiff line numberDiff line change
@@ -734,3 +734,80 @@ def test_latency(self):
734734
"""Tests that the latency is obtained from the interface based on latest_query_latency"""
735735
self.mocked_connection.return_value.latest_query_latency = 0.123
736736
self.assertEqual(0.123, self.aptos_collector.latency())
737+
738+
class TestTronCollector(TestCase):
739+
"""Tests the Tron collector class"""
740+
741+
def setUp(self):
742+
self.url = "https://test.com"
743+
self.labels = ["dummy", "labels"]
744+
self.chain_id = 123
745+
self.open_timeout = 8
746+
self.ping_timeout = 9
747+
self.client_params = {
748+
"open_timeout": self.open_timeout, "ping_timeout": self.ping_timeout}
749+
with mock.patch('collectors.HttpsInterface') as mocked_connection:
750+
self.tron_collector = collectors.TronCollector(
751+
self.url, self.labels, self.chain_id, **self.client_params)
752+
self.mocked_connection = mocked_connection
753+
754+
def test_logger_metadata(self):
755+
"""Validate logger metadata. Makes sure url is stripped by helpers.strip_url function."""
756+
expected_metadata = {
757+
'component': 'TronCollector', 'url': 'test.com'}
758+
self.assertEqual(expected_metadata,
759+
self.tron_collector._logger_metadata)
760+
761+
def test_https_interface_created(self):
762+
"""Tests that the Tron collector calls the https interface with the correct args"""
763+
self.mocked_connection.assert_called_once_with(
764+
self.url, self.open_timeout, self.ping_timeout)
765+
766+
def test_interface_attribute_exists(self):
767+
"""Tests that the interface attribute exists."""
768+
self.assertTrue(hasattr(self.tron_collector, 'interface'))
769+
770+
def test_alive_call(self):
771+
"""Tests the alive function uses the correct call"""
772+
self.tron_collector.alive()
773+
self.mocked_connection.return_value.cached_json_rpc_post.assert_called_once_with(
774+
self.tron_collector.client_version_payload)
775+
776+
def test_alive_false(self):
777+
"""Tests the alive function returns false when post returns None"""
778+
self.mocked_connection.return_value.cached_json_rpc_post.return_value = None
779+
result = self.tron_collector.alive()
780+
self.assertFalse(result)
781+
782+
def test_block_height(self):
783+
"""Tests the block_height function uses the correct call to get block height"""
784+
self.mocked_connection.return_value.cached_json_rpc_post.return_value = "0x1a2b3c"
785+
result = self.tron_collector.block_height()
786+
self.mocked_connection.return_value.cached_json_rpc_post.assert_called_once_with(
787+
self.tron_collector.block_height_payload)
788+
self.assertEqual(result, 1715004)
789+
790+
def test_block_height_raises_value_error(self):
791+
"""Tests that the block height raises ValueError if result is invalid"""
792+
self.mocked_connection.return_value.cached_json_rpc_post.return_value = "invalid"
793+
with self.assertRaises(ValueError):
794+
self.tron_collector.block_height()
795+
796+
def test_client_version(self):
797+
"""Tests the client_version function uses the correct call to get client version"""
798+
self.mocked_connection.return_value.cached_json_rpc_post.return_value = "Tron/v1.0.0"
799+
result = self.tron_collector.client_version()
800+
self.mocked_connection.return_value.cached_json_rpc_post.assert_called_once_with(
801+
self.tron_collector.client_version_payload)
802+
self.assertEqual(result, {"client_version": "Tron/v1.0.0"})
803+
804+
def test_client_version_returns_none(self):
805+
"""Tests that the client_version returns None if cached_json_rpc_post returns None"""
806+
self.mocked_connection.return_value.cached_json_rpc_post.return_value = None
807+
result = self.tron_collector.client_version()
808+
self.assertIsNone(result)
809+
810+
def test_latency(self):
811+
"""Tests that the latency is obtained from the interface based on latest_query_latency"""
812+
self.mocked_connection.return_value.latest_query_latency = 0.123
813+
self.assertEqual(0.123, self.tron_collector.latency())

src/test_registries.py

+11
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,17 @@ def test_get_collector_registry_for_aptos(self):
139139
with mock.patch('collectors.AptosCollector', new=mock.Mock()) as collector:
140140
helper_test_collector_registry(self, collector)
141141

142+
@mock.patch.dict(os.environ, {
143+
"CONFIG_FILE_PATH": "tests/fixtures/configuration_tron.yaml",
144+
"VALIDATION_FILE_PATH": "tests/fixtures/validation.yaml"
145+
})
146+
def test_get_collector_registry_for_tron(self):
147+
"""Tests that the Tron collector is called with the correct args"""
148+
self.collector_registry = CollectorRegistry()
149+
with mock.patch('collectors.TronCollector', new=mock.Mock()) as collector:
150+
helper_test_collector_registry(self, collector)
151+
152+
142153
@mock.patch.dict(os.environ, {
143154
"CONFIG_FILE_PATH": "tests/fixtures/configuration_evm.yaml",
144155
"VALIDATION_FILE_PATH": "tests/fixtures/validation.yaml"
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
blockchain: "Tron"
2+
chain_id: 1234
3+
network_name: "Testnet"
4+
network_type: "Testnet"
5+
collector: "tron"
6+
endpoints:
7+
- url: https://test1.com
8+
provider: TestProvider1
9+
- url: https://test2.com
10+
provider: TestProvider2
11+
- url: https://test3.com
12+
provider: TestProvider3

0 commit comments

Comments
 (0)