Skip to content

Tron collector for Exporter #63

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,56 @@ def client_version(self):
def latency(self):
"""Returns connection latency."""
return self.interface.latest_query_latency

class TronCollector():
"""A collector to fetch information from Tron 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': 'TronCollector',
'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
3 changes: 2 additions & 1 deletion src/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ def endpoints(self):
def _load_configuration(self):
allowed_providers = self._load_validation_file()
supported_collectors = ('evm', 'cardano', 'conflux', 'solana',
'bitcoin', 'doge', 'filecoin', 'starknet', 'aptos')
'bitcoin', 'doge', 'filecoin', 'starknet', 'aptos',
'tron')

configuration_schema = Schema({
'blockchain':
Expand Down
2 changes: 2 additions & 0 deletions src/registries.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ def get_collector_registry(self) -> list:
collector = collectors.StarknetCollector
case "aptos", "aptos":
collector = collectors.AptosCollector
case "tron", "tron":
collector = collectors.TronCollector
case "evm", other: # pylint: disable=unused-variable
collector = collectors.EvmCollector
if collector is None:
Expand Down
77 changes: 77 additions & 0 deletions src/test_collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,3 +734,80 @@ def test_latency(self):
"""Tests that the latency is obtained from the interface based on latest_query_latency"""
self.mocked_connection.return_value.latest_query_latency = 0.123
self.assertEqual(0.123, self.aptos_collector.latency())

class TestTronCollector(TestCase):
"""Tests the Tron collector class"""

def setUp(self):
self.url = "https://test.com"
self.labels = ["dummy", "labels"]
self.chain_id = 123
self.open_timeout = 8
self.ping_timeout = 9
self.client_params = {
"open_timeout": self.open_timeout, "ping_timeout": self.ping_timeout}
with mock.patch('collectors.HttpsInterface') as mocked_connection:
self.tron_collector = collectors.TronCollector(
self.url, self.labels, self.chain_id, **self.client_params)
self.mocked_connection = mocked_connection

def test_logger_metadata(self):
"""Validate logger metadata. Makes sure url is stripped by helpers.strip_url function."""
expected_metadata = {
'component': 'TronCollector', 'url': 'test.com'}
self.assertEqual(expected_metadata,
self.tron_collector._logger_metadata)

def test_https_interface_created(self):
"""Tests that the Tron collector calls the https interface with the correct args"""
self.mocked_connection.assert_called_once_with(
self.url, self.open_timeout, self.ping_timeout)

def test_interface_attribute_exists(self):
"""Tests that the interface attribute exists."""
self.assertTrue(hasattr(self.tron_collector, 'interface'))

def test_alive_call(self):
"""Tests the alive function uses the correct call"""
self.tron_collector.alive()
self.mocked_connection.return_value.cached_json_rpc_post.assert_called_once_with(
self.tron_collector.client_version_payload)

def test_alive_false(self):
"""Tests the alive function returns false when post returns None"""
self.mocked_connection.return_value.cached_json_rpc_post.return_value = None
result = self.tron_collector.alive()
self.assertFalse(result)

def test_block_height(self):
"""Tests the block_height function uses the correct call to get block height"""
self.mocked_connection.return_value.cached_json_rpc_post.return_value = "0x1a2b3c"
result = self.tron_collector.block_height()
self.mocked_connection.return_value.cached_json_rpc_post.assert_called_once_with(
self.tron_collector.block_height_payload)
self.assertEqual(result, 1715004)

def test_block_height_raises_value_error(self):
"""Tests that the block height raises ValueError if result is invalid"""
self.mocked_connection.return_value.cached_json_rpc_post.return_value = "invalid"
with self.assertRaises(ValueError):
self.tron_collector.block_height()

def test_client_version(self):
"""Tests the client_version function uses the correct call to get client version"""
self.mocked_connection.return_value.cached_json_rpc_post.return_value = "Tron/v1.0.0"
result = self.tron_collector.client_version()
self.mocked_connection.return_value.cached_json_rpc_post.assert_called_once_with(
self.tron_collector.client_version_payload)
self.assertEqual(result, {"client_version": "Tron/v1.0.0"})

def test_client_version_returns_none(self):
"""Tests that the client_version returns None if cached_json_rpc_post returns None"""
self.mocked_connection.return_value.cached_json_rpc_post.return_value = None
result = self.tron_collector.client_version()
self.assertIsNone(result)

def test_latency(self):
"""Tests that the latency is obtained from the interface based on latest_query_latency"""
self.mocked_connection.return_value.latest_query_latency = 0.123
self.assertEqual(0.123, self.tron_collector.latency())
11 changes: 11 additions & 0 deletions src/test_registries.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,17 @@ def test_get_collector_registry_for_aptos(self):
with mock.patch('collectors.AptosCollector', new=mock.Mock()) as collector:
helper_test_collector_registry(self, collector)

@mock.patch.dict(os.environ, {
"CONFIG_FILE_PATH": "tests/fixtures/configuration_tron.yaml",
"VALIDATION_FILE_PATH": "tests/fixtures/validation.yaml"
})
def test_get_collector_registry_for_tron(self):
"""Tests that the Tron collector is called with the correct args"""
self.collector_registry = CollectorRegistry()
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_evm.yaml",
"VALIDATION_FILE_PATH": "tests/fixtures/validation.yaml"
Expand Down
12 changes: 12 additions & 0 deletions src/tests/fixtures/configuration_tron.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
blockchain: "Tron"
chain_id: 1234
network_name: "Testnet"
network_type: "Testnet"
collector: "tron"
endpoints:
- url: https://test1.com
provider: TestProvider1
- url: https://test2.com
provider: TestProvider2
- url: https://test3.com
provider: TestProvider3