diff --git a/bittensor/core/extrinsics/prometheus.py b/bittensor/core/extrinsics/prometheus.py deleted file mode 100644 index a6ab1cfb16..0000000000 --- a/bittensor/core/extrinsics/prometheus.py +++ /dev/null @@ -1,187 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. -# -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -import json -from typing import Optional, TYPE_CHECKING - -from retry import retry - -from bittensor.core.extrinsics.utils import submit_extrinsic -from bittensor.core.settings import version_as_int, bt_console -from bittensor.utils import networking as net, format_error_message -from bittensor.utils.btlogging import logging -from bittensor.utils.networking import ensure_connected - -# For annotation purposes -if TYPE_CHECKING: - from bittensor_wallet import Wallet - from bittensor.core.subtensor import Subtensor - from bittensor.core.types import PrometheusServeCallParams - - -# Chain call for `prometheus_extrinsic` -@ensure_connected -def do_serve_prometheus( - self: "Subtensor", - wallet: "Wallet", - call_params: "PrometheusServeCallParams", - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, -) -> tuple[bool, Optional[dict]]: - """ - Sends a serve prometheus extrinsic to the chain. - - Args: - self (bittensor.core.subtensor.Subtensor): Bittensor subtensor object - wallet (bittensor_wallet.Wallet): Wallet object. - call_params (bittensor.core.types.PrometheusServeCallParams): Prometheus serve call parameters. - wait_for_inclusion (bool): If ``true``, waits for inclusion. - wait_for_finalization (bool): If ``true``, waits for finalization. - - Returns: - success (bool): ``True`` if serve prometheus was successful. - error (Optional[str]): Error message if serve prometheus failed, ``None`` otherwise. - """ - - @retry(delay=1, tries=3, backoff=2, max_delay=4) - def make_substrate_call_with_retry(): - call = self.substrate.compose_call( - call_module="SubtensorModule", - call_function="serve_prometheus", - call_params=call_params, - ) - extrinsic = self.substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = submit_extrinsic( - substrate=self.substrate, - extrinsic=extrinsic, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - if wait_for_inclusion or wait_for_finalization: - response.process_events() - if response.is_success: - return True, None - else: - return False, response.error_message - else: - return True, None - - return make_substrate_call_with_retry() - - -def prometheus_extrinsic( - subtensor: "Subtensor", - wallet: "Wallet", - port: int, - netuid: int, - ip: int = None, - wait_for_inclusion: bool = False, - wait_for_finalization=True, -) -> bool: - """Subscribes a Bittensor endpoint to the Subtensor chain. - - Args: - subtensor (bittensor.core.subtensor.Subtensor): Bittensor subtensor object. - wallet (bittensor_wallet.Wallet): Bittensor wallet object. - ip (str): Endpoint host port i.e., ``192.122.31.4``. - port (int): Endpoint port number i.e., `9221`. - netuid (int): Network `uid` to serve on. - wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning ``true``, or returns ``false`` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``true``, or returns ``false`` if the extrinsic fails to be finalized within the timeout. - - Returns: - success (bool): Flag is ``true`` if extrinsic was finalized or uncluded in the block. If we did not wait for finalization / inclusion, the response is ``true``. - """ - - # Get external ip - if ip is None: - try: - external_ip = net.get_external_ip() - bt_console.print( - f":white_heavy_check_mark: [green]Found external ip: {external_ip}[/green]" - ) - logging.success(prefix="External IP", suffix="{external_ip}") - except Exception as e: - raise RuntimeError( - f"Unable to attain your external ip. Check your internet connection. error: {e}" - ) from e - else: - external_ip = ip - - call_params: "PrometheusServeCallParams" = { - "version": version_as_int, - "ip": net.ip_to_int(external_ip), - "port": port, - "ip_type": net.ip_version(external_ip), - } - - with bt_console.status(":satellite: Checking Prometheus..."): - neuron = subtensor.get_neuron_for_pubkey_and_subnet( - wallet.hotkey.ss58_address, netuid=netuid - ) - neuron_up_to_date = not neuron.is_null and call_params == { - "version": neuron.prometheus_info.version, - "ip": net.ip_to_int(neuron.prometheus_info.ip), - "port": neuron.prometheus_info.port, - "ip_type": neuron.prometheus_info.ip_type, - } - - if neuron_up_to_date: - bt_console.print( - f":white_heavy_check_mark: [green]Prometheus already Served[/green]\n" - f"[green not bold]- Status: [/green not bold] |" - f"[green not bold] ip: [/green not bold][white not bold]{neuron.prometheus_info.ip}[/white not bold] |" - f"[green not bold] ip_type: [/green not bold][white not bold]{neuron.prometheus_info.ip_type}[/white not bold] |" - f"[green not bold] port: [/green not bold][white not bold]{neuron.prometheus_info.port}[/white not bold] | " - f"[green not bold] version: [/green not bold][white not bold]{neuron.prometheus_info.version}[/white not bold] |" - ) - - bt_console.print( - f":white_heavy_check_mark: [white]Prometheus already served.[/white]" - ) - return True - - # Add netuid, not in prometheus_info - call_params["netuid"] = netuid - - with bt_console.status( - f":satellite: Serving prometheus on: [white]{subtensor.network}:{netuid}[/white] ..." - ): - success, error_message = do_serve_prometheus( - self=subtensor, - wallet=wallet, - call_params=call_params, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - ) - - if wait_for_inclusion or wait_for_finalization: - if success is True: - json_ = json.dumps(call_params, indent=4, sort_keys=True) - bt_console.print( - f":white_heavy_check_mark: [green]Served prometheus[/green]\n [bold white]{json_}[/bold white]" - ) - return True - else: - bt_console.print( - f":cross_mark: [red]Failed[/red]: {format_error_message(error_message)}" - ) - return False - else: - return True diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ac6c46bc46..3ca0dc146d 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -52,10 +52,6 @@ commit_weights_extrinsic, reveal_weights_extrinsic, ) -from bittensor.core.extrinsics.prometheus import ( - do_serve_prometheus, - prometheus_extrinsic, -) from bittensor.core.extrinsics.registration import ( burned_register_extrinsic, register_extrinsic, @@ -1269,37 +1265,6 @@ def make_substrate_call_with_retry(): return NeuronInfo.from_vec_u8(result) - # Community uses this method - def serve_prometheus( - self, - wallet: "Wallet", - port: int, - netuid: int, - wait_for_inclusion: bool = False, - wait_for_finalization: bool = True, - ) -> bool: - """ - Serves Prometheus metrics by submitting an extrinsic to a blockchain network via the specified wallet. The function allows configuring whether to wait for the transaction's inclusion in a block and its finalization. - - Args: - wallet (bittensor_wallet.Wallet): Bittensor wallet instance used for submitting the extrinsic. - port (int): The port number on which Prometheus metrics are served. - netuid (int): The unique identifier of the subnetwork. - wait_for_inclusion (bool): If True, waits for the transaction to be included in a block. Defaults to ``False``. - wait_for_finalization (bool): If True, waits for the transaction to be finalized. Defaults to ``True``. - - Returns: - bool: Returns True if the Prometheus extrinsic is successfully processed, otherwise False. - """ - return prometheus_extrinsic( - self, - wallet=wallet, - port=port, - netuid=netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - # Community uses this method def get_subnet_hyperparameters( self, netuid: int, block: Optional[int] = None @@ -2047,7 +2012,5 @@ def make_substrate_call_with_retry(encoded_hotkey_: list[int]): return DelegateInfo.from_vec_u8(result) - # Subnet 27 uses this method - _do_serve_prometheus = do_serve_prometheus # Subnet 27 uses this method name _do_serve_axon = do_serve_axon diff --git a/tests/unit_tests/extrinsics/test_prometheus.py b/tests/unit_tests/extrinsics/test_prometheus.py deleted file mode 100644 index dbcfed1e47..0000000000 --- a/tests/unit_tests/extrinsics/test_prometheus.py +++ /dev/null @@ -1,167 +0,0 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the “Software”), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. -# -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - -from unittest.mock import MagicMock, patch - -import pytest -from bittensor_wallet import Wallet - -from bittensor.core.extrinsics.prometheus import ( - prometheus_extrinsic, -) -from bittensor.core.subtensor import Subtensor -from bittensor.core.settings import version_as_int - - -# Mocking the bittensor and networking modules -@pytest.fixture -def mock_bittensor(): - with patch("bittensor.core.subtensor.Subtensor") as mock: - yield mock - - -@pytest.fixture -def mock_wallet(): - with patch("bittensor_wallet.Wallet") as mock: - yield mock - - -@pytest.fixture -def mock_net(): - with patch("bittensor.utils.networking") as mock: - yield mock - - -@pytest.mark.parametrize( - "ip, port, netuid, wait_for_inclusion, wait_for_finalization, expected_result, test_id", - [ - (None, 9221, 0, False, True, True, "happy-path-default-ip"), - ("192.168.0.1", 9221, 0, False, True, True, "happy-path-custom-ip"), - (None, 9221, 0, True, False, True, "happy-path-wait-for-inclusion"), - (None, 9221, 0, False, False, True, "happy-path-no-waiting"), - ], -) -def test_prometheus_extrinsic_happy_path( - mock_bittensor, - mock_wallet, - mock_net, - ip, - port, - netuid, - wait_for_inclusion, - wait_for_finalization, - expected_result, - test_id, -): - # Arrange - subtensor = MagicMock(spec=Subtensor) - subtensor.network = "test_network" - subtensor.substrate = MagicMock() - wallet = MagicMock(spec=Wallet) - mock_net.get_external_ip.return_value = "192.168.0.1" - mock_net.ip_to_int.return_value = 3232235521 # IP in integer form - mock_net.ip_version.return_value = 4 - neuron = MagicMock() - neuron.is_null = False - neuron.prometheus_info.version = version_as_int - neuron.prometheus_info.ip = 3232235521 - neuron.prometheus_info.port = port - neuron.prometheus_info.ip_type = 4 - subtensor.get_neuron_for_pubkey_and_subnet.return_value = neuron - subtensor._do_serve_prometheus.return_value = (True, None) - - # Act - result = prometheus_extrinsic( - subtensor=subtensor, - wallet=wallet, - ip=ip, - port=port, - netuid=netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - - # Assert - assert result == expected_result, f"Test ID: {test_id}" - - -# Edge cases -@pytest.mark.parametrize( - "ip, port, netuid, test_id", - [ - ("0.0.0.0", 0, 0, "edge-case-min-values"), - ("255.255.255.255", 65535, 2147483647, "edge-case-max-values"), - ], -) -def test_prometheus_extrinsic_edge_cases( - mock_bittensor, mock_wallet, mock_net, ip, port, netuid, test_id -): - # Arrange - subtensor = MagicMock(spec=Subtensor) - subtensor.network = "test_network" - subtensor.substrate = MagicMock() - wallet = MagicMock(spec=Wallet) - mock_net.get_external_ip.return_value = ip - mock_net.ip_to_int.return_value = 3232235521 # IP in integer form - mock_net.ip_version.return_value = 4 - neuron = MagicMock() - neuron.is_null = True - subtensor.get_neuron_for_pubkey_and_subnet.return_value = neuron - subtensor._do_serve_prometheus.return_value = (True, None) - - # Act - result = prometheus_extrinsic( - subtensor=subtensor, - wallet=wallet, - ip=ip, - port=port, - netuid=netuid, - wait_for_inclusion=False, - wait_for_finalization=True, - ) - - # Assert - assert result is True, f"Test ID: {test_id}" - - -# Error cases -def test_prometheus_extrinsic_error_cases(mock_bittensor, mock_wallet, mocker): - # Arrange - subtensor = MagicMock(spec=Subtensor) - subtensor.network = "test_network" - subtensor.substrate = MagicMock() - subtensor.substrate.websocket.sock.getsockopt.return_value = 0 - wallet = MagicMock(spec=Wallet) - neuron = MagicMock() - neuron.is_null = True - subtensor.get_neuron_for_pubkey_and_subnet.return_value = neuron - subtensor._do_serve_prometheus.return_value = (True,) - - with mocker.patch( - "bittensor.utils.networking.get_external_ip", side_effect=RuntimeError - ): - # Act & Assert - with pytest.raises(RuntimeError): - prometheus_extrinsic( - subtensor=subtensor, - wallet=wallet, - ip=None, - port=9221, - netuid=1, - wait_for_inclusion=False, - wait_for_finalization=True, - ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 6d8fb1ff5f..a818f22c55 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1406,160 +1406,6 @@ def test_neuron_for_uid_success(subtensor, mocker): assert result == mocked_neuron_from_vec_u8.return_value -def test_do_serve_prometheus_is_success(subtensor, mocker): - """Successful do_serve_prometheus call.""" - # Prep - fake_wallet = mocker.MagicMock() - fake_call_params = mocker.MagicMock() - fake_wait_for_inclusion = True - fake_wait_for_finalization = True - - subtensor.substrate.submit_extrinsic.return_value.is_success = True - - # Call - result = subtensor._do_serve_prometheus( - wallet=fake_wallet, - call_params=fake_call_params, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function="serve_prometheus", - call_params=fake_call_params, - ) - - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - subtensor.substrate.create_signed_extrinsic.return_value, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() - assert result == (True, None) - - -def test_do_serve_prometheus_is_not_success(subtensor, mocker): - """Unsuccessful do_serve_axon call.""" - # Prep - fake_wallet = mocker.MagicMock() - fake_call_params = mocker.MagicMock() - fake_wait_for_inclusion = True - fake_wait_for_finalization = True - - subtensor.substrate.submit_extrinsic.return_value.is_success = None - - # Call - result = subtensor._do_serve_prometheus( - wallet=fake_wallet, - call_params=fake_call_params, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function="serve_prometheus", - call_params=fake_call_params, - ) - - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - subtensor.substrate.create_signed_extrinsic.return_value, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() - assert result == ( - False, - subtensor.substrate.submit_extrinsic.return_value.error_message, - ) - - -def test_do_serve_prometheus_no_waits(subtensor, mocker): - """Unsuccessful do_serve_axon call.""" - # Prep - fake_wallet = mocker.MagicMock() - fake_call_params = mocker.MagicMock() - fake_wait_for_inclusion = False - fake_wait_for_finalization = False - - # Call - result = subtensor._do_serve_prometheus( - wallet=fake_wallet, - call_params=fake_call_params, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function="serve_prometheus", - call_params=fake_call_params, - ) - - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - subtensor.substrate.create_signed_extrinsic.return_value, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - assert result == (True, None) - - -def test_serve_prometheus(subtensor, mocker): - """Test serve_prometheus function successful call.""" - # Preps - fake_wallet = mocker.MagicMock() - fake_port = 1234 - fake_netuid = 1 - wait_for_inclusion = True - wait_for_finalization = False - - mocked_prometheus_extrinsic = mocker.patch.object( - subtensor_module, "prometheus_extrinsic" - ) - - # Call - result = subtensor.serve_prometheus( - fake_wallet, - fake_port, - fake_netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - - # Asserts - mocked_prometheus_extrinsic.assert_called_once_with( - subtensor, - wallet=fake_wallet, - port=fake_port, - netuid=fake_netuid, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - - assert result == mocked_prometheus_extrinsic.return_value - - def test_do_serve_axon_is_success(subtensor, mocker): """Successful do_serve_axon call.""" # Prep