From 70d13fb0791b09dcd41fedf7456359e67bb09eb5 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 29 Nov 2024 16:28:54 +0200 Subject: [PATCH 01/10] [Experimental] Improve `submit_extrinsic` util --- bittensor/core/extrinsics/commit_weights.py | 4 +- bittensor/core/extrinsics/registration.py | 4 +- bittensor/core/extrinsics/root.py | 4 +- bittensor/core/extrinsics/serving.py | 4 +- bittensor/core/extrinsics/set_weights.py | 2 +- bittensor/core/extrinsics/transfer.py | 2 +- bittensor/core/extrinsics/utils.py | 25 +++++++----- bittensor/core/subtensor.py | 12 ++++-- tests/unit_tests/extrinsics/test_utils.py | 44 +++++++++++---------- 9 files changed, 58 insertions(+), 43 deletions(-) diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index ef93a15d3e..dacfd27ea0 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -71,7 +71,7 @@ def do_commit_weights( keypair=wallet.hotkey, ) response = submit_extrinsic( - substrate=self.substrate, + subtensor=self, extrinsic=extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, @@ -184,7 +184,7 @@ def do_reveal_weights( keypair=wallet.hotkey, ) response = submit_extrinsic( - substrate=self.substrate, + subtensor=self, extrinsic=extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index e9836938d8..f4a0848d68 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -66,7 +66,7 @@ def _do_pow_register( ) extrinsic = self.substrate.create_signed_extrinsic(call=call, keypair=wallet.hotkey) response = submit_extrinsic( - substrate=self.substrate, + self, extrinsic=extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, @@ -297,7 +297,7 @@ def _do_burned_register( call=call, keypair=wallet.coldkey ) response = submit_extrinsic( - self.substrate, + self, extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index e4fec236fc..629109f9a6 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -33,7 +33,7 @@ def _do_root_register( call=call, keypair=wallet.coldkey ) response = submit_extrinsic( - self.substrate, + self, extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, @@ -159,7 +159,7 @@ def _do_set_root_weights( era={"period": period}, ) response = submit_extrinsic( - self.substrate, + self, extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index 1c26b87e76..87dc96cc37 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -75,7 +75,7 @@ def do_serve_axon( ) extrinsic = self.substrate.create_signed_extrinsic(call=call, keypair=wallet.hotkey) response = submit_extrinsic( - substrate=self.substrate, + self, extrinsic=extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, @@ -289,7 +289,7 @@ def publish_metadata( extrinsic = substrate.create_signed_extrinsic(call=call, keypair=wallet.hotkey) response = submit_extrinsic( - substrate, + self, extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index 880480e998..79c7e409a4 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -83,7 +83,7 @@ def do_set_weights( era={"period": period}, ) response = submit_extrinsic( - substrate=self.substrate, + self, extrinsic=extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index c3b919f381..1544476756 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -70,7 +70,7 @@ def do_transfer( call=call, keypair=wallet.coldkey ) response = submit_extrinsic( - substrate=self.substrate, + self, extrinsic=extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 967d8c5a6f..135af094ce 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -11,7 +11,8 @@ from bittensor.utils import format_error_message if TYPE_CHECKING: - from substrateinterface import SubstrateInterface, ExtrinsicReceipt + from bittensor.core.subtensor import Subtensor + from substrateinterface import ExtrinsicReceipt from scalecodec.types import GenericExtrinsic try: @@ -26,7 +27,7 @@ def submit_extrinsic( - substrate: "SubstrateInterface", + subtensor: "Subtensor", extrinsic: "GenericExtrinsic", wait_for_inclusion: bool, wait_for_finalization: bool, @@ -39,7 +40,7 @@ def submit_extrinsic( it logs the error and re-raises the exception. Args: - substrate (substrateinterface.SubstrateInterface): The substrate interface instance used to interact with the blockchain. + subtensor: The Subtensor instance used to interact with the blockchain. extrinsic (scalecodec.types.GenericExtrinsic): The extrinsic to be submitted to the blockchain. wait_for_inclusion (bool): Whether to wait for the extrinsic to be included in a block. wait_for_finalization (bool): Whether to wait for the extrinsic to be finalized on the blockchain. @@ -51,20 +52,22 @@ def submit_extrinsic( SubstrateRequestException: If the submission of the extrinsic fails, the error is logged and re-raised. """ extrinsic_hash = extrinsic.extrinsic_hash - starting_block = substrate.get_block() + starting_block = subtensor.substrate.get_block() timeout = EXTRINSIC_SUBMISSION_TIMEOUT event = threading.Event() def submit(): try: - response_ = substrate.submit_extrinsic( + response_ = subtensor.substrate.submit_extrinsic( extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, ) except SubstrateRequestException as e: - logging.error(format_error_message(e.args[0], substrate=substrate)) + logging.error( + format_error_message(e.args[0], substrate=subtensor.substrate) + ) # Re-raise the exception for retrying of the extrinsic call. If we remove the retry logic, # the raise will need to be removed. raise @@ -76,16 +79,18 @@ def submit(): response = None future = executor.submit(submit) if not event.wait(timeout): - logging.error("Timed out waiting for extrinsic submission.") - after_timeout_block = substrate.get_block() + logging.error("Timed out waiting for extrinsic submission. Reconnecting.") + # force reconnection of the websocket + subtensor._get_substrate(force=True) + after_timeout_block = subtensor.substrate.get_block() for block_num in range( starting_block["header"]["number"], after_timeout_block["header"]["number"] + 1, ): - block_hash = substrate.get_block_hash(block_num) + block_hash = subtensor.substrate.get_block_hash(block_num) try: - response = substrate.retrieve_extrinsic_by_hash( + response = subtensor.substrate.retrieve_extrinsic_by_hash( block_hash, f"0x{extrinsic_hash.hex()}" ) except ExtrinsicNotFound: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index fa46242277..c5daa87221 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -212,11 +212,17 @@ def close(self): if self.substrate: self.substrate.close() - def _get_substrate(self): - """Establishes a connection to the Substrate node using configured parameters.""" + def _get_substrate(self, force: bool = False): + """ + Establishes a connection to the Substrate node using configured parameters. + + Args: + force: forces a reconnection if this flag is set + + """ try: # Set up params. - if self.websocket is None or self.websocket.close_code is not None: + if force or self.websocket is None or self.websocket.close_code is not None: self.websocket = ws_client.connect( self.chain_endpoint, open_timeout=self._connection_timeout, diff --git a/tests/unit_tests/extrinsics/test_utils.py b/tests/unit_tests/extrinsics/test_utils.py index 38a92e3dc2..3f21a0c0a1 100644 --- a/tests/unit_tests/extrinsics/test_utils.py +++ b/tests/unit_tests/extrinsics/test_utils.py @@ -9,6 +9,7 @@ ) from bittensor.core.extrinsics import utils +from bittensor.core.subtensor import Subtensor @pytest.fixture @@ -16,30 +17,36 @@ def set_extrinsics_timeout_env(monkeypatch): monkeypatch.setenv("EXTRINSIC_SUBMISSION_TIMEOUT", "1") -def test_submit_extrinsic_timeout(): +@pytest.fixture +def mock_subtensor(): + mock_subtensor = MagicMock(autospec=Subtensor) + mock_substrate = MagicMock(autospec=SubstrateInterface) + mock_subtensor.substrate = mock_substrate + yield mock_subtensor + + +def test_submit_extrinsic_timeout(mock_subtensor): timeout = 1 def wait(extrinsic, wait_for_inclusion, wait_for_finalization): time.sleep(timeout + 0.01) return True - mock_substrate = MagicMock(autospec=SubstrateInterface) - mock_substrate.submit_extrinsic = wait + mock_subtensor.substrate.submit_extrinsic = wait mock_extrinsic = MagicMock(autospec=GenericExtrinsic) with patch.object(utils, "EXTRINSIC_SUBMISSION_TIMEOUT", timeout): with pytest.raises(SubstrateRequestException): - utils.submit_extrinsic(mock_substrate, mock_extrinsic, True, True) + utils.submit_extrinsic(mock_subtensor, mock_extrinsic, True, True) -def test_submit_extrinsic_success(): - mock_substrate = MagicMock(autospec=SubstrateInterface) - mock_substrate.submit_extrinsic.return_value = True +def test_submit_extrinsic_success(mock_subtensor): + mock_subtensor.substrate.submit_extrinsic.return_value = True mock_extrinsic = MagicMock(autospec=GenericExtrinsic) - result = utils.submit_extrinsic(mock_substrate, mock_extrinsic, True, True) + result = utils.submit_extrinsic(mock_subtensor, mock_extrinsic, True, True) assert result is True -def test_submit_extrinsic_timeout_env(set_extrinsics_timeout_env): +def test_submit_extrinsic_timeout_env(set_extrinsics_timeout_env, mock_subtensor): importlib.reload(utils) timeout = utils.EXTRINSIC_SUBMISSION_TIMEOUT assert timeout < 5 # should be less than 5 seconds as taken from test env var @@ -48,23 +55,21 @@ def wait(extrinsic, wait_for_inclusion, wait_for_finalization): time.sleep(timeout + 1) return True - mock_substrate = MagicMock(autospec=SubstrateInterface) - mock_substrate.submit_extrinsic = wait + mock_subtensor.substrate.submit_extrinsic = wait mock_extrinsic = MagicMock(autospec=GenericExtrinsic) with pytest.raises(SubstrateRequestException): - utils.submit_extrinsic(mock_substrate, mock_extrinsic, True, True) + utils.submit_extrinsic(mock_subtensor, mock_extrinsic, True, True) -def test_submit_extrinsic_success_env(set_extrinsics_timeout_env): +def test_submit_extrinsic_success_env(set_extrinsics_timeout_env, mock_subtensor): importlib.reload(utils) - mock_substrate = MagicMock(autospec=SubstrateInterface) - mock_substrate.submit_extrinsic.return_value = True + mock_subtensor.substrate.submit_extrinsic.return_value = True mock_extrinsic = MagicMock(autospec=GenericExtrinsic) - result = utils.submit_extrinsic(mock_substrate, mock_extrinsic, True, True) + result = utils.submit_extrinsic(mock_subtensor, mock_extrinsic, True, True) assert result is True -def test_submit_extrinsic_timeout_env_float(monkeypatch): +def test_submit_extrinsic_timeout_env_float(monkeypatch, mock_subtensor): monkeypatch.setenv("EXTRINSIC_SUBMISSION_TIMEOUT", "1.45") # use float importlib.reload(utils) @@ -76,11 +81,10 @@ def wait(extrinsic, wait_for_inclusion, wait_for_finalization): time.sleep(timeout + 0.3) # sleep longer by float return True - mock_substrate = MagicMock(autospec=SubstrateInterface) - mock_substrate.submit_extrinsic = wait + mock_subtensor.substrate.submit_extrinsic = wait mock_extrinsic = MagicMock(autospec=GenericExtrinsic) with pytest.raises(SubstrateRequestException): - utils.submit_extrinsic(mock_substrate, mock_extrinsic, True, True) + utils.submit_extrinsic(mock_subtensor, mock_extrinsic, True, True) def test_import_timeout_env_parse(monkeypatch): From 8cc5d7706f70007aa833e5534e69fd1f152965a0 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Sat, 30 Nov 2024 10:49:52 +0200 Subject: [PATCH 02/10] Improve `submit_extrinsic` with testing. Now correctly doesn't freeze, recovers connection, and has improved e2e test. --- bittensor/core/extrinsics/utils.py | 3 ++- bittensor/core/subtensor.py | 6 +++++- tests/e2e_tests/test_commit_weights.py | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 135af094ce..22827979fb 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -93,9 +93,10 @@ def submit(): response = subtensor.substrate.retrieve_extrinsic_by_hash( block_hash, f"0x{extrinsic_hash.hex()}" ) - except ExtrinsicNotFound: + except (ExtrinsicNotFound, SubstrateRequestException): continue if response: + logging.debug(f"Recovered extrinsic: {extrinsic}") break if response is None: logging.error( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index c5daa87221..bb110559c2 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -222,6 +222,10 @@ def _get_substrate(self, force: bool = False): """ try: # Set up params. + if force and self.websocket: + logging.debug("Closing websocket connection") + self.websocket.close() + if force or self.websocket is None or self.websocket.close_code is not None: self.websocket = ws_client.connect( self.chain_endpoint, @@ -235,7 +239,7 @@ def _get_substrate(self, force: bool = False): websocket=self.websocket, ) if self.log_verbose: - logging.debug( + logging.info( f"Connected to {self.network} network and {self.chain_endpoint}." ) diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 7596004603..3e29dc56ec 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -6,6 +6,7 @@ from bittensor.core.subtensor import Subtensor from bittensor.utils.balance import Balance from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit +from bittensor.core.extrinsics import utils from tests.e2e_tests.utils.chain_interactions import ( add_stake, register_subnet, @@ -32,6 +33,7 @@ async def test_commit_and_reveal_weights(local_chain): AssertionError: If any of the checks or verifications fail """ netuid = 1 + utils.EXTRINSIC_SUBMISSION_TIMEOUT = 12 # handle fast blocks print("Testing test_commit_and_reveal_weights") # Register root as Alice keypair, alice_wallet = setup_wallet("//Alice") From 6cf8bc50a4ad673c6cfeb70dd1868ddaf75ce99b Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Sun, 1 Dec 2024 18:36:43 +0200 Subject: [PATCH 03/10] Further improve `submit_extrinsic` by breaking it up more and adding more testing, especially to the extrinsic recovery part. --- bittensor/core/extrinsics/utils.py | 124 +++++++++++++--------- tests/unit_tests/extrinsics/test_utils.py | 61 ++++++++++- 2 files changed, 127 insertions(+), 58 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 22827979fb..990a99c992 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -3,7 +3,7 @@ from concurrent.futures import ThreadPoolExecutor import os import threading -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Optional from substrateinterface.exceptions import SubstrateRequestException, ExtrinsicNotFound @@ -26,12 +26,49 @@ raise ValueError("EXTRINSIC_SUBMISSION_TIMEOUT cannot be negative.") +def extrinsic_recovery( + extrinsic_hash_hex: str, subtensor: "Subtensor", starting_block: dict[str, Any] +) -> Optional["ExtrinsicReceipt"]: + """ + Attempts to recover an extrinsic from the chain that was previously submitted + + Args: + extrinsic_hash_hex: the hex representation (including '0x' prefix) of the extrinsic hash + subtensor: the Subtensor object to interact with the chain + starting_block: the initial block dict at the time the extrinsic was submitted + + Returns: + ExtrinsicReceipt of the extrinsic if recovered, None otherwise. + """ + + after_timeout_block = subtensor.substrate.get_block() + response = None + for block_num in range( + starting_block["header"]["number"], + after_timeout_block["header"]["number"] + 1, + ): + block_hash = subtensor.substrate.get_block_hash(block_num) + try: + response = subtensor.substrate.retrieve_extrinsic_by_hash( + block_hash, extrinsic_hash_hex + ) + except (ExtrinsicNotFound, SubstrateRequestException): + continue + if response: + break + return response + + def submit_extrinsic( subtensor: "Subtensor", extrinsic: "GenericExtrinsic", wait_for_inclusion: bool, wait_for_finalization: bool, ) -> "ExtrinsicReceipt": + event = threading.Event() + extrinsic_hash = extrinsic.extrinsic_hash + starting_block = subtensor.substrate.get_block() + timeout = EXTRINSIC_SUBMISSION_TIMEOUT """ Submits an extrinsic to the substrate blockchain and handles potential exceptions. @@ -51,61 +88,42 @@ def submit_extrinsic( Raises: SubstrateRequestException: If the submission of the extrinsic fails, the error is logged and re-raised. """ - extrinsic_hash = extrinsic.extrinsic_hash - starting_block = subtensor.substrate.get_block() - - timeout = EXTRINSIC_SUBMISSION_TIMEOUT - event = threading.Event() - def submit(): - try: - response_ = subtensor.substrate.submit_extrinsic( - extrinsic, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - except SubstrateRequestException as e: - logging.error( - format_error_message(e.args[0], substrate=subtensor.substrate) - ) - # Re-raise the exception for retrying of the extrinsic call. If we remove the retry logic, - # the raise will need to be removed. - raise - finally: - event.set() - return response_ - - with ThreadPoolExecutor(max_workers=1) as executor: - response = None - future = executor.submit(submit) - if not event.wait(timeout): - logging.error("Timed out waiting for extrinsic submission. Reconnecting.") - # force reconnection of the websocket - subtensor._get_substrate(force=True) - after_timeout_block = subtensor.substrate.get_block() - - for block_num in range( - starting_block["header"]["number"], - after_timeout_block["header"]["number"] + 1, - ): - block_hash = subtensor.substrate.get_block_hash(block_num) - try: - response = subtensor.substrate.retrieve_extrinsic_by_hash( - block_hash, f"0x{extrinsic_hash.hex()}" - ) - except (ExtrinsicNotFound, SubstrateRequestException): - continue - if response: - logging.debug(f"Recovered extrinsic: {extrinsic}") - break - if response is None: + def try_submission(): + def submit(): + try: + response__ = subtensor.substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + except SubstrateRequestException as e: logging.error( - f"Extrinsic '0x{extrinsic_hash.hex()}' not submitted. " - f"Initially attempted to submit at block {starting_block['header']['number']}." + format_error_message(e.args[0], substrate=subtensor.substrate) ) - raise SubstrateRequestException + raise + finally: + event.set() + return response__ + + with ThreadPoolExecutor(max_workers=1) as executor: + future = executor.submit(submit) + if not event.wait(timeout): + logging.error( + "Timed out waiting for extrinsic submission. Reconnecting." + ) + response_ = None + else: + response_ = future.result() + return response_ - else: - response = future.result() + response = try_submission() + if response is None: + subtensor._get_substrate(force=True) + response = extrinsic_recovery( + f"0x{extrinsic_hash.hex()}", subtensor, starting_block + ) + if response is None: + raise SubstrateRequestException return response diff --git a/tests/unit_tests/extrinsics/test_utils.py b/tests/unit_tests/extrinsics/test_utils.py index 3f21a0c0a1..666a9d4fce 100644 --- a/tests/unit_tests/extrinsics/test_utils.py +++ b/tests/unit_tests/extrinsics/test_utils.py @@ -2,11 +2,9 @@ from unittest.mock import MagicMock, patch import importlib import pytest -from substrateinterface.base import ( - SubstrateInterface, - GenericExtrinsic, - SubstrateRequestException, -) +from scalecodec.types import GenericExtrinsic +from substrateinterface.base import SubstrateInterface, ExtrinsicReceipt +from substrateinterface.exceptions import ExtrinsicNotFound, SubstrateRequestException from bittensor.core.extrinsics import utils from bittensor.core.subtensor import Subtensor @@ -25,6 +23,11 @@ def mock_subtensor(): yield mock_subtensor +@pytest.fixture +def starting_block(): + yield {"header": {"number": 1}} + + def test_submit_extrinsic_timeout(mock_subtensor): timeout = 1 @@ -115,3 +118,51 @@ def test_import_timeout_env_parse(monkeypatch): importlib.reload(utils) assert isinstance(utils.EXTRINSIC_SUBMISSION_TIMEOUT, float) # has a default value assert utils.EXTRINSIC_SUBMISSION_TIMEOUT > 0 # is positive + + +def test_extrinsic_recovery_found(mock_subtensor, starting_block): + """Test extrinsic_recovery when extrinsic is found within given block range""" + extrinsic_hash_hex = "0x123abc" + mock_subtensor.substrate.get_block.return_value = {"header": {"number": 10}} + expected_response = ExtrinsicReceipt(mock_subtensor) + + mock_subtensor.substrate.retrieve_extrinsic_by_hash.return_value = expected_response + response = utils.extrinsic_recovery( + extrinsic_hash_hex, mock_subtensor, starting_block + ) + + assert response == expected_response + mock_subtensor.substrate.get_block.assert_called_once() + mock_subtensor.substrate.retrieve_extrinsic_by_hash.assert_called() + + +def test_extrinsic_recovery_not_found(mock_subtensor, starting_block): + """Test extrinsic_recovery when extrinsic is not found within given block range""" + extrinsic_hash_hex = "0x123abc" + mock_subtensor.substrate.get_block.return_value = {"header": {"number": 10}} + + mock_subtensor.substrate.retrieve_extrinsic_by_hash.side_effect = ( + ExtrinsicNotFound() + ) + response = utils.extrinsic_recovery( + extrinsic_hash_hex, mock_subtensor, starting_block + ) + + assert response is None + mock_subtensor.substrate.get_block.assert_called_once() + + +def test_extrinsic_recovery_request_exception(mock_subtensor, starting_block): + """Test extrinsic_recovery when there is a SubstrateRequestException""" + extrinsic_hash_hex = "0x123abc" + mock_subtensor.substrate.get_block.return_value = {"header": {"number": 10}} + + mock_subtensor.substrate.retrieve_extrinsic_by_hash.side_effect = ( + SubstrateRequestException() + ) + response = utils.extrinsic_recovery( + extrinsic_hash_hex, mock_subtensor, starting_block + ) + + assert response is None + mock_subtensor.substrate.get_block.assert_called_once() From fd6b6d385eb21c1fe7fd9c197d4b65adea4ef57b Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 2 Dec 2024 12:18:15 +0200 Subject: [PATCH 04/10] Add in `process_events()` try:catch in extrinsics recovery. --- bittensor/core/extrinsics/utils.py | 1 + tests/e2e_tests/test_commit_weights.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 990a99c992..cec761448c 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -52,6 +52,7 @@ def extrinsic_recovery( response = subtensor.substrate.retrieve_extrinsic_by_hash( block_hash, extrinsic_hash_hex ) + response.process_events() except (ExtrinsicNotFound, SubstrateRequestException): continue if response: diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 3e29dc56ec..7573ba5b4b 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from bittensor.utils.btlogging import logging from bittensor.core.subtensor import Subtensor from bittensor.utils.balance import Balance from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit @@ -34,6 +35,7 @@ async def test_commit_and_reveal_weights(local_chain): """ netuid = 1 utils.EXTRINSIC_SUBMISSION_TIMEOUT = 12 # handle fast blocks + logging.set_trace() print("Testing test_commit_and_reveal_weights") # Register root as Alice keypair, alice_wallet = setup_wallet("//Alice") From 3703bb2cad18801fe7d13497443a84cd0213cdbb Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 2 Dec 2024 13:14:18 +0200 Subject: [PATCH 05/10] Fix tests --- tests/unit_tests/extrinsics/test_utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/extrinsics/test_utils.py b/tests/unit_tests/extrinsics/test_utils.py index 666a9d4fce..f0f0392ce1 100644 --- a/tests/unit_tests/extrinsics/test_utils.py +++ b/tests/unit_tests/extrinsics/test_utils.py @@ -25,7 +25,7 @@ def mock_subtensor(): @pytest.fixture def starting_block(): - yield {"header": {"number": 1}} + yield {"header": {"number": 1, "hash": "0x0100"}} def test_submit_extrinsic_timeout(mock_subtensor): @@ -124,7 +124,9 @@ def test_extrinsic_recovery_found(mock_subtensor, starting_block): """Test extrinsic_recovery when extrinsic is found within given block range""" extrinsic_hash_hex = "0x123abc" mock_subtensor.substrate.get_block.return_value = {"header": {"number": 10}} - expected_response = ExtrinsicReceipt(mock_subtensor) + expected_response = ExtrinsicReceipt( + mock_subtensor, extrinsic_hash_hex, block_hash=starting_block["header"]["hash"] + ) mock_subtensor.substrate.retrieve_extrinsic_by_hash.return_value = expected_response response = utils.extrinsic_recovery( From c766875000d06744bc2ed02f576a64ee0ed258ce Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 2 Dec 2024 18:23:20 +0200 Subject: [PATCH 06/10] Trigger no-op From 723d65e30a4ac879f1dfe23be668a604e8b9e544 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 2 Dec 2024 20:23:04 +0200 Subject: [PATCH 07/10] [WIP] Temp --- bittensor/core/extrinsics/utils.py | 132 +++++++++++-------------- tests/e2e_tests/test_commit_weights.py | 109 ++++++++++---------- 2 files changed, 113 insertions(+), 128 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index cec761448c..495a908c4c 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,9 +1,9 @@ """Module with helper functions for extrinsics.""" - +import traceback from concurrent.futures import ThreadPoolExecutor import os import threading -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING from substrateinterface.exceptions import SubstrateRequestException, ExtrinsicNotFound @@ -26,50 +26,12 @@ raise ValueError("EXTRINSIC_SUBMISSION_TIMEOUT cannot be negative.") -def extrinsic_recovery( - extrinsic_hash_hex: str, subtensor: "Subtensor", starting_block: dict[str, Any] -) -> Optional["ExtrinsicReceipt"]: - """ - Attempts to recover an extrinsic from the chain that was previously submitted - - Args: - extrinsic_hash_hex: the hex representation (including '0x' prefix) of the extrinsic hash - subtensor: the Subtensor object to interact with the chain - starting_block: the initial block dict at the time the extrinsic was submitted - - Returns: - ExtrinsicReceipt of the extrinsic if recovered, None otherwise. - """ - - after_timeout_block = subtensor.substrate.get_block() - response = None - for block_num in range( - starting_block["header"]["number"], - after_timeout_block["header"]["number"] + 1, - ): - block_hash = subtensor.substrate.get_block_hash(block_num) - try: - response = subtensor.substrate.retrieve_extrinsic_by_hash( - block_hash, extrinsic_hash_hex - ) - response.process_events() - except (ExtrinsicNotFound, SubstrateRequestException): - continue - if response: - break - return response - - def submit_extrinsic( subtensor: "Subtensor", extrinsic: "GenericExtrinsic", wait_for_inclusion: bool, wait_for_finalization: bool, ) -> "ExtrinsicReceipt": - event = threading.Event() - extrinsic_hash = extrinsic.extrinsic_hash - starting_block = subtensor.substrate.get_block() - timeout = EXTRINSIC_SUBMISSION_TIMEOUT """ Submits an extrinsic to the substrate blockchain and handles potential exceptions. @@ -89,42 +51,66 @@ def submit_extrinsic( Raises: SubstrateRequestException: If the submission of the extrinsic fails, the error is logged and re-raised. """ + extrinsic_hash = extrinsic.extrinsic_hash + starting_block = subtensor.substrate.get_block() - def try_submission(): - def submit(): - try: - response__ = subtensor.substrate.submit_extrinsic( - extrinsic, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - except SubstrateRequestException as e: - logging.error( - format_error_message(e.args[0], substrate=subtensor.substrate) - ) - raise - finally: - event.set() - return response__ - - with ThreadPoolExecutor(max_workers=1) as executor: - future = executor.submit(submit) - if not event.wait(timeout): + timeout = EXTRINSIC_SUBMISSION_TIMEOUT + event = threading.Event() + + def submit(): + try: + response_ = subtensor.substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + except SubstrateRequestException as e: + logging.error( + format_error_message(e.args[0], substrate=subtensor.substrate) + ) + # Re-raise the exception for retrying of the extrinsic call. If we remove the retry logic, + # the raise will need to be removed. + raise + finally: + event.set() + return response_ + + with ThreadPoolExecutor(max_workers=1) as executor: + response = None + future = executor.submit(submit) + if not event.wait(timeout): + logging.error("Timed out waiting for extrinsic submission. Reconnecting.") + # force reconnection of the websocket + subtensor._get_substrate(force=True) + after_timeout_block = subtensor.substrate.get_block() + + for block_num in range( + starting_block["header"]["number"], + after_timeout_block["header"]["number"] + 1, + ): + block_hash = subtensor.substrate.get_block_hash(block_num) + try: + response = subtensor.substrate.retrieve_extrinsic_by_hash( + block_hash, f"0x{extrinsic_hash.hex()}" + ) + except (SubstrateRequestException): + continue + if response: + logging.debug(f"Recovered extrinsic: {extrinsic}") + try: + response.process_events() + except (SubstrateRequestException, ExtrinsicNotFound) as e: + logging.error(traceback.format_tb(e)) + # logging.error(format_error_message(e, substrate=subtensor.substrate)) + break + if response is None: logging.error( - "Timed out waiting for extrinsic submission. Reconnecting." + f"Extrinsic '0x{extrinsic_hash.hex()}' not submitted. " + f"Initially attempted to submit at block {starting_block['header']['number']}." ) - response_ = None - else: - response_ = future.result() - return response_ + raise SubstrateRequestException - response = try_submission() - if response is None: - subtensor._get_substrate(force=True) - response = extrinsic_recovery( - f"0x{extrinsic_hash.hex()}", subtensor, starting_block - ) - if response is None: - raise SubstrateRequestException + else: + response = future.result() return response diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 7573ba5b4b..026bfc3ab1 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -1,4 +1,3 @@ -import time import numpy as np import pytest @@ -13,7 +12,6 @@ register_subnet, sudo_set_hyperparameter_bool, sudo_set_hyperparameter_values, - wait_interval, ) from tests.e2e_tests.utils.e2e_test_utils import setup_wallet @@ -36,6 +34,7 @@ async def test_commit_and_reveal_weights(local_chain): netuid = 1 utils.EXTRINSIC_SUBMISSION_TIMEOUT = 12 # handle fast blocks logging.set_trace() + logging.info("Testing test_commit") print("Testing test_commit_and_reveal_weights") # Register root as Alice keypair, alice_wallet = setup_wallet("//Alice") @@ -121,56 +120,56 @@ async def test_commit_and_reveal_weights(local_chain): wait_for_finalization=True, ) - assert success is True - - weight_commits = subtensor.query_module( - module="SubtensorModule", - name="WeightCommits", - params=[netuid, alice_wallet.hotkey.ss58_address], - ) - # Assert that the committed weights are set correctly - assert weight_commits.value is not None, "Weight commit not found in storage" - commit_hash, commit_block, reveal_block, expire_block = weight_commits.value[0] - assert commit_block > 0, f"Invalid block number: {commit_block}" - - # Query the WeightCommitRevealInterval storage map - reveal_periods = subtensor.query_module( - module="SubtensorModule", name="RevealPeriodEpochs", params=[netuid] - ) - periods = reveal_periods.value - assert periods > 0, "Invalid RevealPeriodEpochs" - - # Wait until the reveal block range - await wait_interval( - subtensor.get_subnet_hyperparameters(netuid=netuid).tempo, subtensor - ) - - # Reveal weights - success, message = subtensor.reveal_weights( - alice_wallet, - netuid, - uids=weight_uids, - weights=weight_vals, - salt=salt, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert success is True - - time.sleep(10) - - # Query the Weights storage map - revealed_weights = subtensor.query_module( - module="SubtensorModule", - name="Weights", - params=[netuid, 0], # netuid and uid - ) - - # Assert that the revealed weights are set correctly - assert revealed_weights.value is not None, "Weight reveal not found in storage" - - assert ( - weight_vals[0] == revealed_weights.value[0][1] - ), f"Incorrect revealed weights. Expected: {weights[0]}, Actual: {revealed_weights.value[0][1]}" - print("✅ Passed test_commit_and_reveal_weights") + # assert success is True + # + # weight_commits = subtensor.query_module( + # module="SubtensorModule", + # name="WeightCommits", + # params=[netuid, alice_wallet.hotkey.ss58_address], + # ) + # # Assert that the committed weights are set correctly + # assert weight_commits.value is not None, "Weight commit not found in storage" + # commit_hash, commit_block, reveal_block, expire_block = weight_commits.value[0] + # assert commit_block > 0, f"Invalid block number: {commit_block}" + # + # # Query the WeightCommitRevealInterval storage map + # reveal_periods = subtensor.query_module( + # module="SubtensorModule", name="RevealPeriodEpochs", params=[netuid] + # ) + # periods = reveal_periods.value + # assert periods > 0, "Invalid RevealPeriodEpochs" + # + # # Wait until the reveal block range + # await wait_interval( + # subtensor.get_subnet_hyperparameters(netuid=netuid).tempo, subtensor + # ) + # + # # Reveal weights + # success, message = subtensor.reveal_weights( + # alice_wallet, + # netuid, + # uids=weight_uids, + # weights=weight_vals, + # salt=salt, + # wait_for_inclusion=True, + # wait_for_finalization=True, + # ) + # + # assert success is True + # + # time.sleep(10) + # + # # Query the Weights storage map + # revealed_weights = subtensor.query_module( + # module="SubtensorModule", + # name="Weights", + # params=[netuid, 0], # netuid and uid + # ) + # + # # Assert that the revealed weights are set correctly + # assert revealed_weights.value is not None, "Weight reveal not found in storage" + # + # assert ( + # weight_vals[0] == revealed_weights.value[0][1] + # ), f"Incorrect revealed weights. Expected: {weights[0]}, Actual: {revealed_weights.value[0][1]}" + # print("✅ Passed test_commit_and_reveal_weights") From a1e1a1e0b93fa3b88fd9243d5e6b44d5c27fd4f2 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 2 Dec 2024 21:39:22 +0200 Subject: [PATCH 08/10] Unable to retrieve extrinsic. Better to raise error and retry. --- bittensor/core/extrinsics/utils.py | 29 +----- tests/e2e_tests/test_commit_weights.py | 107 +++++++++++----------- tests/unit_tests/extrinsics/test_utils.py | 50 ---------- 3 files changed, 55 insertions(+), 131 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 495a908c4c..5db70c1e08 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,4 +1,5 @@ """Module with helper functions for extrinsics.""" + import traceback from concurrent.futures import ThreadPoolExecutor import os @@ -82,33 +83,7 @@ def submit(): logging.error("Timed out waiting for extrinsic submission. Reconnecting.") # force reconnection of the websocket subtensor._get_substrate(force=True) - after_timeout_block = subtensor.substrate.get_block() - - for block_num in range( - starting_block["header"]["number"], - after_timeout_block["header"]["number"] + 1, - ): - block_hash = subtensor.substrate.get_block_hash(block_num) - try: - response = subtensor.substrate.retrieve_extrinsic_by_hash( - block_hash, f"0x{extrinsic_hash.hex()}" - ) - except (SubstrateRequestException): - continue - if response: - logging.debug(f"Recovered extrinsic: {extrinsic}") - try: - response.process_events() - except (SubstrateRequestException, ExtrinsicNotFound) as e: - logging.error(traceback.format_tb(e)) - # logging.error(format_error_message(e, substrate=subtensor.substrate)) - break - if response is None: - logging.error( - f"Extrinsic '0x{extrinsic_hash.hex()}' not submitted. " - f"Initially attempted to submit at block {starting_block['header']['number']}." - ) - raise SubstrateRequestException + raise SubstrateRequestException else: response = future.result() diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 026bfc3ab1..5a82c55d03 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -1,4 +1,3 @@ - import numpy as np import pytest @@ -120,56 +119,56 @@ async def test_commit_and_reveal_weights(local_chain): wait_for_finalization=True, ) - # assert success is True - # - # weight_commits = subtensor.query_module( - # module="SubtensorModule", - # name="WeightCommits", - # params=[netuid, alice_wallet.hotkey.ss58_address], - # ) - # # Assert that the committed weights are set correctly - # assert weight_commits.value is not None, "Weight commit not found in storage" - # commit_hash, commit_block, reveal_block, expire_block = weight_commits.value[0] - # assert commit_block > 0, f"Invalid block number: {commit_block}" - # - # # Query the WeightCommitRevealInterval storage map - # reveal_periods = subtensor.query_module( - # module="SubtensorModule", name="RevealPeriodEpochs", params=[netuid] - # ) - # periods = reveal_periods.value - # assert periods > 0, "Invalid RevealPeriodEpochs" - # - # # Wait until the reveal block range - # await wait_interval( - # subtensor.get_subnet_hyperparameters(netuid=netuid).tempo, subtensor - # ) - # - # # Reveal weights - # success, message = subtensor.reveal_weights( - # alice_wallet, - # netuid, - # uids=weight_uids, - # weights=weight_vals, - # salt=salt, - # wait_for_inclusion=True, - # wait_for_finalization=True, - # ) - # - # assert success is True - # - # time.sleep(10) - # - # # Query the Weights storage map - # revealed_weights = subtensor.query_module( - # module="SubtensorModule", - # name="Weights", - # params=[netuid, 0], # netuid and uid - # ) - # - # # Assert that the revealed weights are set correctly - # assert revealed_weights.value is not None, "Weight reveal not found in storage" - # - # assert ( - # weight_vals[0] == revealed_weights.value[0][1] - # ), f"Incorrect revealed weights. Expected: {weights[0]}, Actual: {revealed_weights.value[0][1]}" - # print("✅ Passed test_commit_and_reveal_weights") + assert success is True + + weight_commits = subtensor.query_module( + module="SubtensorModule", + name="WeightCommits", + params=[netuid, alice_wallet.hotkey.ss58_address], + ) + # Assert that the committed weights are set correctly + assert weight_commits.value is not None, "Weight commit not found in storage" + commit_hash, commit_block, reveal_block, expire_block = weight_commits.value[0] + assert commit_block > 0, f"Invalid block number: {commit_block}" + + # Query the WeightCommitRevealInterval storage map + reveal_periods = subtensor.query_module( + module="SubtensorModule", name="RevealPeriodEpochs", params=[netuid] + ) + periods = reveal_periods.value + assert periods > 0, "Invalid RevealPeriodEpochs" + + # Wait until the reveal block range + await wait_interval( + subtensor.get_subnet_hyperparameters(netuid=netuid).tempo, subtensor + ) + + # Reveal weights + success, message = subtensor.reveal_weights( + alice_wallet, + netuid, + uids=weight_uids, + weights=weight_vals, + salt=salt, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert success is True + + time.sleep(10) + + # Query the Weights storage map + revealed_weights = subtensor.query_module( + module="SubtensorModule", + name="Weights", + params=[netuid, 0], # netuid and uid + ) + + # Assert that the revealed weights are set correctly + assert revealed_weights.value is not None, "Weight reveal not found in storage" + + assert ( + weight_vals[0] == revealed_weights.value[0][1] + ), f"Incorrect revealed weights. Expected: {weights[0]}, Actual: {revealed_weights.value[0][1]}" + print("✅ Passed test_commit_and_reveal_weights") diff --git a/tests/unit_tests/extrinsics/test_utils.py b/tests/unit_tests/extrinsics/test_utils.py index f0f0392ce1..ca8d92e29e 100644 --- a/tests/unit_tests/extrinsics/test_utils.py +++ b/tests/unit_tests/extrinsics/test_utils.py @@ -118,53 +118,3 @@ def test_import_timeout_env_parse(monkeypatch): importlib.reload(utils) assert isinstance(utils.EXTRINSIC_SUBMISSION_TIMEOUT, float) # has a default value assert utils.EXTRINSIC_SUBMISSION_TIMEOUT > 0 # is positive - - -def test_extrinsic_recovery_found(mock_subtensor, starting_block): - """Test extrinsic_recovery when extrinsic is found within given block range""" - extrinsic_hash_hex = "0x123abc" - mock_subtensor.substrate.get_block.return_value = {"header": {"number": 10}} - expected_response = ExtrinsicReceipt( - mock_subtensor, extrinsic_hash_hex, block_hash=starting_block["header"]["hash"] - ) - - mock_subtensor.substrate.retrieve_extrinsic_by_hash.return_value = expected_response - response = utils.extrinsic_recovery( - extrinsic_hash_hex, mock_subtensor, starting_block - ) - - assert response == expected_response - mock_subtensor.substrate.get_block.assert_called_once() - mock_subtensor.substrate.retrieve_extrinsic_by_hash.assert_called() - - -def test_extrinsic_recovery_not_found(mock_subtensor, starting_block): - """Test extrinsic_recovery when extrinsic is not found within given block range""" - extrinsic_hash_hex = "0x123abc" - mock_subtensor.substrate.get_block.return_value = {"header": {"number": 10}} - - mock_subtensor.substrate.retrieve_extrinsic_by_hash.side_effect = ( - ExtrinsicNotFound() - ) - response = utils.extrinsic_recovery( - extrinsic_hash_hex, mock_subtensor, starting_block - ) - - assert response is None - mock_subtensor.substrate.get_block.assert_called_once() - - -def test_extrinsic_recovery_request_exception(mock_subtensor, starting_block): - """Test extrinsic_recovery when there is a SubstrateRequestException""" - extrinsic_hash_hex = "0x123abc" - mock_subtensor.substrate.get_block.return_value = {"header": {"number": 10}} - - mock_subtensor.substrate.retrieve_extrinsic_by_hash.side_effect = ( - SubstrateRequestException() - ) - response = utils.extrinsic_recovery( - extrinsic_hash_hex, mock_subtensor, starting_block - ) - - assert response is None - mock_subtensor.substrate.get_block.assert_called_once() From 56a31b6845e64c68ad84d1732a288006f4229310 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 2 Dec 2024 22:23:29 +0200 Subject: [PATCH 09/10] Tests --- bittensor/core/extrinsics/utils.py | 3 +-- tests/e2e_tests/test_commit_weights.py | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 5db70c1e08..7a73b75669 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,12 +1,11 @@ """Module with helper functions for extrinsics.""" -import traceback from concurrent.futures import ThreadPoolExecutor import os import threading from typing import TYPE_CHECKING -from substrateinterface.exceptions import SubstrateRequestException, ExtrinsicNotFound +from substrateinterface.exceptions import SubstrateRequestException from bittensor.utils.btlogging import logging from bittensor.utils import format_error_message diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 5a82c55d03..62b92e3178 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -1,7 +1,6 @@ import numpy as np import pytest -from bittensor.utils.btlogging import logging from bittensor.core.subtensor import Subtensor from bittensor.utils.balance import Balance from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit @@ -11,6 +10,7 @@ register_subnet, sudo_set_hyperparameter_bool, sudo_set_hyperparameter_values, + wait_interval, ) from tests.e2e_tests.utils.e2e_test_utils import setup_wallet @@ -32,8 +32,6 @@ async def test_commit_and_reveal_weights(local_chain): """ netuid = 1 utils.EXTRINSIC_SUBMISSION_TIMEOUT = 12 # handle fast blocks - logging.set_trace() - logging.info("Testing test_commit") print("Testing test_commit_and_reveal_weights") # Register root as Alice keypair, alice_wallet = setup_wallet("//Alice") From 7708dd66ebf3029aa34981d2ea8778565124e6e7 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Mon, 2 Dec 2024 22:39:59 +0200 Subject: [PATCH 10/10] Test --- tests/e2e_tests/test_commit_weights.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 62b92e3178..3e29dc56ec 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -1,3 +1,5 @@ +import time + import numpy as np import pytest