From 37bb233191b07514eeceb4d9fd319db2b2759862 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 25 Nov 2024 20:25:19 -0800 Subject: [PATCH 01/48] add 'Subtensor.commit_reveal_enabled' + tests --- bittensor/core/subtensor.py | 18 ++++++++++++++++++ tests/unit_tests/test_subtensor.py | 17 +++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index a26a02638c..eb0a79cde4 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1106,6 +1106,24 @@ def max_weight_limit( ) return None if call is None else u16_normalized_float(int(call)) + def commit_reveal_enabled( + self, netuid: int, block: Optional[int] = None + ) -> Optional[bool]: + """ + Check if commit-reveal mechanism is enabled for a given network at a specific block. + + Arguments: + netuid (int): The network identifier for which to check the commit-reveal mechanism. + block (Optional[int]): The block number at which to check the parameter (default is None, which implies the current block). + + Returns: + (Optional[bool]): Returns the integer value of the hyperparameter if available; otherwise, returns None. + """ + call = self._get_hyperparameter( + param_name="CommitRevealWeightsEnabled", block=block, netuid=netuid + ) + return call + def get_prometheus_info( self, netuid: int, hotkey_ss58: str, block: Optional[int] = None ) -> Optional["PrometheusInfo"]: diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 740df91884..9a0ef7c808 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -489,6 +489,23 @@ def test_hyperparameter_normalization( ########################### +def test_commit_reveal_enabled(subtensor, mocker): + """Test commit_reveal_enabled.""" + # Preps + netuid = 1 + block = 123 + mocked_get_hyperparameter = mocker.patch.object(subtensor, "_get_hyperparameter") + + # Call + result = subtensor.commit_reveal_enabled(netuid, block) + + # Assertions + mocked_get_hyperparameter.assert_called_once_with( + param_name="CommitRevealWeightsEnabled", block=block, netuid=netuid + ) + assert result == mocked_get_hyperparameter.return_value + + # get_prometheus_info tests def test_get_prometheus_info_success(mocker, subtensor): """Test get_prometheus_info returns correct data when information is found.""" From e85c85b91b4935b972cac791681cdbcfd30ea0e3 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 25 Nov 2024 20:37:48 -0800 Subject: [PATCH 02/48] add 'AsyncSubtensor.commit_reveal_enabled' + test --- bittensor/core/async_subtensor.py | 20 ++++++++++++++++++++ tests/unit_tests/test_async_subtensor.py | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 4c456ac83a..264e458ecc 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1359,6 +1359,26 @@ async def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int] call = await self.get_hyperparameter(param_name="LastUpdate", netuid=netuid) return None if call is None else await self.get_current_block() - int(call[uid]) + async def commit_reveal_enabled( + self, netuid: int, block_hash: Optional[str] = None + ) -> Optional[bool]: + """ + Check if commit-reveal mechanism is enabled for a given network at a specific block. + + Arguments: + netuid (int): The network identifier for which to check the commit-reveal mechanism. + block_hash (Optional[str]): The block hash of block at which to check the parameter (default is None, which implies the current block). + + Returns: + (Optional[bool]): Returns the integer value of the hyperparameter if available; otherwise, returns None. + """ + call = await self.get_hyperparameter( + param_name="CommitRevealWeightsEnabled", + block_hash=block_hash, + netuid=netuid, + ) + return call if call is not None else False + # Extrinsics ======================================================================================================= async def transfer( diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index c90309e808..ce30199efc 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2352,6 +2352,26 @@ async def test_blocks_since_last_update_no_last_update(subtensor, mocker): assert result is None +@pytest.mark.asyncio +async def test_commit_reveal_enabled(subtensor, mocker): + """Test commit_reveal_enabled.""" + # Preps + netuid = 1 + block_hash = "block_hash" + mocked_get_hyperparameter = mocker.patch.object( + subtensor, "get_hyperparameter", return_value=mocker.AsyncMock() + ) + + # Call + result = await subtensor.commit_reveal_enabled(netuid, block_hash) + + # Assertions + mocked_get_hyperparameter.assert_awaited_once_with( + param_name="CommitRevealWeightsEnabled", block_hash=block_hash, netuid=netuid + ) + assert result == mocked_get_hyperparameter.return_value + + @pytest.mark.asyncio async def test_transfer_success(subtensor, mocker): """Tests transfer when the transfer is successful.""" From 2eb86138dcd74319099abfd5c8f8fc3520cd0c49 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Nov 2024 18:07:52 -0800 Subject: [PATCH 03/48] add extrinsic --- bittensor/core/extrinsics/commit_reveal.py | 187 +++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 bittensor/core/extrinsics/commit_reveal.py diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py new file mode 100644 index 0000000000..dd5c3e0911 --- /dev/null +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -0,0 +1,187 @@ +import random +from typing import Optional, Union, TYPE_CHECKING + +import numpy as np +from numpy.typing import NDArray + +from bittensor.core.extrinsics.utils import submit_extrinsic +from bittensor.core.settings import version_as_int +from bittensor.utils import format_error_message +from bittensor.utils.btlogging import logging +from bittensor.utils.networking import ensure_connected +from bittensor.utils.registration import torch, use_torch +from bittensor.utils.weight_utils import ( + convert_weights_and_uids_for_emit, + generate_weight_hash, +) + +if TYPE_CHECKING: + from bittensor_wallet import Wallet + from bittensor.core.subtensor import Subtensor + + +# this will be replaced with rust-based ffi import from here https://github.com/opentensor/bittensor-commit-reveal +def get_encrypted_commit( + commit_hash: str, + subnet_reveal_period_epochs: int, +) -> tuple[bytes, int]: + """ + Decrypts to t-lock bytes. + + Arguments: + commit_hash: The hash of the commit (uids, weights) to be revealed. + subnet_reveal_period_epochs: Number of epochs after which the revive will be performed. + + Returns: + t-lock encrypted commit for commit_crv3_weights extrinsic. + reveal_period: drand period when Subtensor reveal the weights to the chain. + """ + return commit_hash.encode(), subnet_reveal_period_epochs + + +@ensure_connected +def _do_commit_reveal_v3( + self: "Subtensor", + wallet: "Wallet", + netuid: int, + commit: bytes, + reveal_round: int, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, +) -> tuple[bool, Optional[str]]: + """ + Executes the commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or finalization. + + Arguments: + wallet : Wallet An instance of the Wallet class containing the user's keypair. + netuid : int The network unique identifier. + commit : bytes The commit data in bytes format. + reveal_round : int The round number for the reveal phase. + wait_for_inclusion : bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. + wait_for_finalization : bool, optional Flag indicating whether to wait for the extrinsic to be finalized. + + Returns: + A tuple where the first element is a boolean indicating success or failure, and the second element is an optional string containing error message if any. + """ + logging.info( + f"Committing weights hash [blue]{commit}[/blue] for subnet #[blue]{netuid}[/blue] with reveal round [blue]{reveal_round}[/blue]..." + ) + + call = self.substrate.compose_call( + call_module="SubtensorModule", + call_function="commit_crv3_weights", + call_params={ + "netuid": netuid, + "commit": commit, + "reveal_round": reveal_round, + }, + ) + 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 not wait_for_finalization and not wait_for_inclusion: + return True, "Not waiting for finalization or inclusion." + + response.process_events() + if response.is_success: + return True, None + else: + return False, format_error_message( + response.error_message, substrate=self.substrate + ) + + +def commit_reveal_v3_extrinsic( + subtensor: "Subtensor", + wallet: "Wallet", + netuid: int, + uids: Union[NDArray[np.int64], "torch.LongTensor", list], + weights: Union[NDArray[np.float32], "torch.FloatTensor", list], + version_key: int = version_as_int, + wait_for_inclusion: bool = False, + wait_for_finalization: bool = False, +) -> tuple[bool, str]: + """ + Commits and reveals weights for given subtensor and wallet with provided uids and weights. + + Arguments: + subtensor (Subtensor): The Subtensor instance. + wallet (Wallet): The wallet to use for committing and revealing. + netuid (int): The id of the network. + uids (Union[NDArray[np.int64], torch.LongTensor, list]): The uids to commit. + weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights associated with the uids. + version_key (int, optional): The version key to use for committing and revealing. Default is version_as_int. + wait_for_inclusion (bool, optional): Whether to wait for the inclusion of the transaction. Default is False. + wait_for_finalization (bool, optional): Whether to wait for the finalization of the transaction. Default is False. + + Returns: + tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure, and the second element is a message associated with the result. + """ + try: + # Convert uids and weights + if use_torch(): + if isinstance(uids, list): + uids = torch.tensor(uids, dtype=torch.int64) + if isinstance(weights, list): + weights = torch.tensor(weights, dtype=torch.float32) + else: + if isinstance(uids, list): + uids = np.array(uids, dtype=np.int64) + if isinstance(weights, list): + weights = np.array(weights, dtype=np.float32) + + # Reformat and normalize. + uids, weights = convert_weights_and_uids_for_emit(uids, weights) + + # Generate the salt + salt = [random.randint(0, 350) for _ in range(8)] + + # Generate the hash of the weights + commit_hash = generate_weight_hash( + address=wallet.hotkey.ss58_address, + netuid=netuid, + uids=list(uids), + values=list(weights), + salt=salt, + version_key=version_key, + ) + + # Get subnet's reveal (in epochs) + subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs(netuid) + + # Encrypt `commit_hash` with t-lock and `get reveal_round` + commit_for_reveal, reveal_round = get_encrypted_commit( + commit_hash, subnet_reveal_period_epochs + ) + + success, message = _do_commit_reveal_v3( + self=subtensor, + wallet=wallet, + netuid=netuid, + commit=commit_for_reveal, + reveal_round=reveal_round, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + + if success is True: + logging.success( + f"[green]Finalized![/green] Weights commited with reveal round [blue]{reveal_round}[/blue]." + ) + return True, message + else: + logging.error(message) + return False, message + + except Exception as e: + logging.error(f":cross_mark: [red]Failed. Error:[/red] {e}") + return False, str(e) From e2bbf7b3a3b7a203be14516e3c2fd751d0115b30 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Nov 2024 18:08:03 -0800 Subject: [PATCH 04/48] add tests for extrinsic --- .../extrinsics/test_commit_reveal.py | 302 ++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 tests/unit_tests/extrinsics/test_commit_reveal.py diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py new file mode 100644 index 0000000000..df3a0a81ad --- /dev/null +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -0,0 +1,302 @@ +from bittensor.core import subtensor as subtensor_module +from bittensor.core.subtensor import Subtensor +from bittensor.core.extrinsics import commit_reveal +import pytest +import torch +import numpy as np + + +@pytest.fixture +def subtensor(mocker): + fake_substrate = mocker.MagicMock() + fake_substrate.websocket.sock.getsockopt.return_value = 0 + mocker.patch.object( + subtensor_module, "SubstrateInterface", return_value=fake_substrate + ) + return Subtensor() + + +def test_do_commit_reveal_v3_success(mocker, subtensor): + """Test successful commit-reveal with wait for finalization.""" + # Preps + fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) + fake_netuid = 1 + fake_commit = b"fake_commit" + fake_reveal_round = 1 + + mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") + mocked_create_signed_extrinsic = mocker.patch.object( + subtensor.substrate, "create_signed_extrinsic" + ) + mocked_submit_extrinsic = mocker.patch.object(commit_reveal, "submit_extrinsic") + + # Call + result = commit_reveal._do_commit_reveal_v3( + self=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + commit=fake_commit, + reveal_round=fake_reveal_round, + ) + + # Asserts + mocked_compose_call.assert_called_once_with( + call_module="SubtensorModule", + call_function="commit_crv3_weights", + call_params={ + "netuid": fake_netuid, + "commit": fake_commit, + "reveal_round": fake_reveal_round, + }, + ) + mocked_create_signed_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, keypair=fake_wallet.hotkey + ) + mocked_submit_extrinsic.assert_called_once_with( + substrate=subtensor.substrate, + extrinsic=mocked_create_signed_extrinsic.return_value, + wait_for_inclusion=False, + wait_for_finalization=False, + ) + assert result == (True, "Not waiting for finalization or inclusion.") + + +def test_do_commit_reveal_v3_failure_due_to_error(mocker, subtensor): + """Test commit-reveal fails due to an error in submission.""" + # Preps + fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) + fake_netuid = 1 + fake_commit = b"fake_commit" + fake_reveal_round = 1 + + mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") + mocked_create_signed_extrinsic = mocker.patch.object( + subtensor.substrate, "create_signed_extrinsic" + ) + mocked_submit_extrinsic = mocker.patch.object( + commit_reveal, + "submit_extrinsic", + return_value=mocker.Mock(is_success=False, error_message="Mocked error"), + ) + mocked_format_error_message = mocker.patch.object( + commit_reveal, "format_error_message", return_value="Formatted error" + ) + + # Call + result = commit_reveal._do_commit_reveal_v3( + self=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + commit=fake_commit, + reveal_round=fake_reveal_round, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # Asserts + mocked_compose_call.assert_called_once_with( + call_module="SubtensorModule", + call_function="commit_crv3_weights", + call_params={ + "netuid": fake_netuid, + "commit": fake_commit, + "reveal_round": fake_reveal_round, + }, + ) + mocked_create_signed_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, keypair=fake_wallet.hotkey + ) + mocked_submit_extrinsic.assert_called_once_with( + substrate=subtensor.substrate, + extrinsic=mocked_create_signed_extrinsic.return_value, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + mocked_format_error_message.assert_called_once_with( + "Mocked error", substrate=subtensor.substrate + ) + assert result == (False, "Formatted error") + + +def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): + """Test successful commit-reveal with torch tensors.""" + # Preps + fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) + fake_netuid = 1 + fake_uids = torch.tensor([1, 2, 3], dtype=torch.int64) + fake_weights = torch.tensor([0.1, 0.2, 0.7], dtype=torch.float32) + fake_salt = [42] * 8 + fake_commit_hash = b"mock_commit_hash" + fake_commit_for_reveal = b"mock_commit_for_reveal" + fake_reveal_round = 1 + + # Mocks + mocker.patch.object(commit_reveal, "use_torch", return_value=True) + mocker.patch.object( + commit_reveal, + "convert_weights_and_uids_for_emit", + return_value=(fake_uids, fake_weights), + ) + mocker.patch.object(commit_reveal.random, "randint", return_value=42) + mocker.patch.object( + commit_reveal, "generate_weight_hash", return_value=fake_commit_hash + ) + mocker.patch.object( + commit_reveal, + "get_encrypted_commit", + return_value=(fake_commit_for_reveal, fake_reveal_round), + ) + mock_do_commit_reveal_v3 = mocker.patch.object( + commit_reveal, "_do_commit_reveal_v3", return_value=(True, "Success") + ) + + # Call + success, message = commit_reveal.commit_reveal_v3_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # Asserts + assert success is True + assert message == "Success" + mock_do_commit_reveal_v3.assert_called_once_with( + self=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + commit=fake_commit_for_reveal, + reveal_round=fake_reveal_round, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + +def test_commit_reveal_v3_extrinsic_success_with_numpy(mocker, subtensor): + """Test successful commit-reveal with numpy arrays.""" + # Preps + fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) + fake_netuid = 1 + fake_uids = np.array([1, 2, 3], dtype=np.int64) + fake_weights = np.array([0.1, 0.2, 0.7], dtype=np.float32) + + mocker.patch.object(commit_reveal, "use_torch", return_value=False) + mock_convert = mocker.patch.object( + commit_reveal, + "convert_weights_and_uids_for_emit", + return_value=(fake_uids, fake_weights), + ) + mock_generate_hash = mocker.patch.object(commit_reveal, "generate_weight_hash") + mock_encode_drand = mocker.patch.object( + commit_reveal, "get_encrypted_commit", return_value=(b"commit", 0) + ) + mock_do_commit = mocker.patch.object( + commit_reveal, "_do_commit_reveal_v3", return_value=(True, "Committed!") + ) + + # Call + success, message = commit_reveal.commit_reveal_v3_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + wait_for_inclusion=False, + wait_for_finalization=False, + ) + + # Asserts + assert success is True + assert message == "Committed!" + mock_convert.assert_called_once_with(fake_uids, fake_weights) + mock_generate_hash.assert_called_once() + mock_encode_drand.assert_called_once() + mock_do_commit.assert_called_once() + + +def test_commit_reveal_v3_extrinsic_response_false(mocker, subtensor): + """Test unsuccessful commit-reveal with torch.""" + # Preps + fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) + fake_netuid = 1 + fake_uids = torch.tensor([1, 2, 3], dtype=torch.int64) + fake_weights = torch.tensor([0.1, 0.2, 0.7], dtype=torch.float32) + fake_salt = [42] * 8 + fake_commit_hash = b"mock_commit_hash" + fake_commit_for_reveal = b"mock_commit_for_reveal" + fake_reveal_round = 1 + + # Mocks + mocker.patch.object(commit_reveal, "use_torch", return_value=True) + mocker.patch.object( + commit_reveal, + "convert_weights_and_uids_for_emit", + return_value=(fake_uids, fake_weights), + ) + mocker.patch.object(commit_reveal.random, "randint", return_value=42) + mocker.patch.object( + commit_reveal, "generate_weight_hash", return_value=fake_commit_hash + ) + mocker.patch.object( + commit_reveal, + "get_encrypted_commit", + return_value=(fake_commit_for_reveal, fake_reveal_round), + ) + mock_do_commit_reveal_v3 = mocker.patch.object( + commit_reveal, "_do_commit_reveal_v3", return_value=(False, "Failed") + ) + + # Call + success, message = commit_reveal.commit_reveal_v3_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # Asserts + assert success is False + assert message == "Failed" + mock_do_commit_reveal_v3.assert_called_once_with( + self=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + commit=fake_commit_for_reveal, + reveal_round=fake_reveal_round, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + +def test_commit_reveal_v3_extrinsic_exception(mocker, subtensor): + """Test exception handling in commit-reveal.""" + # Preps + fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) + fake_netuid = 1 + fake_uids = [1, 2, 3] + fake_weights = [0.1, 0.2, 0.7] + + mocker.patch.object( + commit_reveal, + "convert_weights_and_uids_for_emit", + side_effect=Exception("Test Error"), + ) + + # Call + success, message = commit_reveal.commit_reveal_v3_extrinsic( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + ) + + # Asserts + assert success is False + assert "Test Error" in message From d382e887c7a8458cdf816bc2d4b6bfdb55c7644c Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Nov 2024 18:08:37 -0800 Subject: [PATCH 05/48] add commit reveal v3 logic to set_weights methods --- bittensor/core/async_subtensor.py | 71 ++++++++++++++----------- bittensor/core/subtensor.py | 87 +++++++++++++++++++------------ 2 files changed, 97 insertions(+), 61 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 264e458ecc..090456f70e 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1377,7 +1377,15 @@ async def commit_reveal_enabled( block_hash=block_hash, netuid=netuid, ) - return call if call is not None else False + return True if call is True else False + + async def get_subnet_reveal_period_epochs( + self, netuid: int, block_hash: Optional[str] = None + ) -> int: + """Retrieve the SubnetRevealPeriodEpochs hyperparameter.""" + return await self.get_hyperparameter( + param_name="RevealPeriodEpochs", block_hash=block_hash, netuid=netuid + ) # Extrinsics ======================================================================================================= @@ -1520,35 +1528,40 @@ async def set_weights( This function is crucial in shaping the network's collective intelligence, where each neuron's learning and contribution are influenced by the weights it sets towards others【81†source】. """ - uid = await self.get_uid_for_hotkey_on_subnet( - wallet.hotkey.ss58_address, netuid - ) - retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to set weights!" - while retries < max_retries and await self.blocks_since_last_update( - netuid, uid - ) > await self.weights_rate_limit(netuid): - try: - logging.info( - f"Setting weights for subnet #[blue]{netuid}[/blue]. Attempt [blue]{retries + 1} of {max_retries}[/blue]." - ) - success, message = await set_weights_extrinsic( - subtensor=self, - wallet=wallet, - netuid=netuid, - uids=uids, - weights=weights, - version_key=version_key, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - except Exception as e: - logging.error(f"Error setting weights: {e}") - finally: - retries += 1 + if self.commit_reveal_enabled(netuid=netuid) is True: + # go with `commit reveal v3` extrinsic + raise NotImplemented("Not implemented yet for AsyncSubtensor. Coming soon.") + else: + # go with classic `set weights extrinsic` + uid = await self.get_uid_for_hotkey_on_subnet( + wallet.hotkey.ss58_address, netuid + ) + retries = 0 + success = False + message = "No attempt made. Perhaps it is too soon to set weights!" + while retries < max_retries and await self.blocks_since_last_update( + netuid, uid + ) > await self.weights_rate_limit(netuid): + try: + logging.info( + f"Setting weights for subnet #[blue]{netuid}[/blue]. Attempt [blue]{retries + 1} of {max_retries}[/blue]." + ) + success, message = await set_weights_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + uids=uids, + weights=weights, + version_key=version_key, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + except Exception as e: + logging.error(f"Error setting weights: {e}") + finally: + retries += 1 - return success, message + return success, message async def root_set_weights( self, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index eb0a79cde4..fba2aa2eb2 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -31,6 +31,7 @@ SubnetInfo, ) from bittensor.core.config import Config +from bittensor.core.extrinsics.commit_reveal import commit_reveal_v3_extrinsic from bittensor.core.extrinsics.commit_weights import ( commit_weights_extrinsic, reveal_weights_extrinsic, @@ -50,13 +51,13 @@ get_metadata, ) from bittensor.core.extrinsics.set_weights import set_weights_extrinsic -from bittensor.core.extrinsics.transfer import ( - transfer_extrinsic, -) from bittensor.core.extrinsics.staking import ( add_stake_extrinsic, add_stake_multiple_extrinsic, ) +from bittensor.core.extrinsics.transfer import ( + transfer_extrinsic, +) from bittensor.core.extrinsics.unstaking import ( unstake_extrinsic, unstake_multiple_extrinsic, @@ -1122,7 +1123,15 @@ def commit_reveal_enabled( call = self._get_hyperparameter( param_name="CommitRevealWeightsEnabled", block=block, netuid=netuid ) - return call + return True if call is True else False + + def get_subnet_reveal_period_epochs( + self, netuid: int, block: Optional[int] = None + ) -> Optional[int]: + """Retrieve the SubnetRevealPeriodEpochs hyperparameter.""" + return self._get_hyperparameter( + param_name="RevealPeriodEpochs", block=block, netuid=netuid + ) def get_prometheus_info( self, netuid: int, hotkey_ss58: str, block: Optional[int] = None @@ -1713,34 +1722,48 @@ def set_weights( This function is crucial in shaping the network's collective intelligence, where each neuron's learning and contribution are influenced by the weights it sets towards others【81†source】. """ - uid = self.get_uid_for_hotkey_on_subnet(wallet.hotkey.ss58_address, netuid) - retries = 0 - success = False - message = "No attempt made. Perhaps it is too soon to set weights!" - while ( - self.blocks_since_last_update(netuid, uid) > self.weights_rate_limit(netuid) # type: ignore - and retries < max_retries - ): - try: - logging.info( - f"Setting weights for subnet #{netuid}. Attempt {retries + 1} of {max_retries}." - ) - success, message = set_weights_extrinsic( - subtensor=self, - wallet=wallet, - netuid=netuid, - uids=uids, - weights=weights, - version_key=version_key, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - except Exception as e: - logging.error(f"Error setting weights: {e}") - finally: - retries += 1 - - return success, message + if self.commit_reveal_enabled(netuid=netuid) is True: + # go with `commit reveal v3` extrinsic + return commit_reveal_v3_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + uids=uids, + weights=weights, + version_key=version_key, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + else: + # go with classic `set weights` logic + uid = self.get_uid_for_hotkey_on_subnet(wallet.hotkey.ss58_address, netuid) + retries = 0 + success = False + message = "No attempt made. Perhaps it is too soon to set weights!" + while ( + self.blocks_since_last_update(netuid, uid) # type: ignore + > self.weights_rate_limit(netuid) # type: ignore + and retries < max_retries + ): + try: + logging.info( + f"Setting weights for subnet #{netuid}. Attempt {retries + 1} of {max_retries}." + ) + success, message = set_weights_extrinsic( + subtensor=self, + wallet=wallet, + netuid=netuid, + uids=uids, + weights=weights, + version_key=version_key, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + except Exception as e: + logging.error(f"Error setting weights: {e}") + finally: + retries += 1 + return success, message @legacy_torch_api_compat def root_set_weights( From ecbc57c4aba66327c866a69edfd32c44ebad9c79 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Nov 2024 18:08:43 -0800 Subject: [PATCH 06/48] add tests --- tests/unit_tests/test_async_subtensor.py | 20 ++++++++ tests/unit_tests/test_subtensor.py | 59 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index ce30199efc..a6b8a5b40f 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2369,6 +2369,26 @@ async def test_commit_reveal_enabled(subtensor, mocker): mocked_get_hyperparameter.assert_awaited_once_with( param_name="CommitRevealWeightsEnabled", block_hash=block_hash, netuid=netuid ) + assert result is False + + +@pytest.mark.asyncio +async def test_get_subnet_reveal_period_epochs(subtensor, mocker): + """Test get_subnet_reveal_period_epochs.""" + # Preps + netuid = 1 + block_hash = "block_hash" + mocked_get_hyperparameter = mocker.patch.object( + subtensor, "get_hyperparameter", return_value=mocker.AsyncMock() + ) + + # Call + result = await subtensor.get_subnet_reveal_period_epochs(netuid, block_hash) + + # Assertions + mocked_get_hyperparameter.assert_awaited_once_with( + param_name="RevealPeriodEpochs", block_hash=block_hash, netuid=netuid + ) assert result == mocked_get_hyperparameter.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 9a0ef7c808..e56e52994e 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -503,6 +503,23 @@ def test_commit_reveal_enabled(subtensor, mocker): mocked_get_hyperparameter.assert_called_once_with( param_name="CommitRevealWeightsEnabled", block=block, netuid=netuid ) + assert result is False + + +def test_get_subnet_reveal_period_epochs(subtensor, mocker): + """Test get_subnet_reveal_period_epochs.""" + # Preps + netuid = 1 + block = 123 + mocked_get_hyperparameter = mocker.patch.object(subtensor, "_get_hyperparameter") + + # Call + result = subtensor.get_subnet_reveal_period_epochs(netuid, block) + + # Assertions + mocked_get_hyperparameter.assert_called_once_with( + param_name="RevealPeriodEpochs", block=block, netuid=netuid + ) assert result == mocked_get_hyperparameter.return_value @@ -2811,3 +2828,45 @@ def test_unstake_multiple_success(mocker, subtensor): wait_for_finalization=False, ) assert result == mock_unstake_multiple_extrinsic.return_value + + +def test_set_weights_with_commit_reveal_enabled(subtensor, mocker): + """Test set_weights with commit_reveal_enabled is True.""" + # Preps + fake_wallet = mocker.Mock() + fake_netuid = 1 + fake_uids = [1, 5] + fake_weights = [0.1, 0.9] + fake_wait_for_inclusion = True + fake_wait_for_finalization = False + + mocked_commit_reveal_enabled = mocker.patch.object( + subtensor, "commit_reveal_enabled", return_value=True + ) + mocked_commit_reveal_v3_extrinsic = mocker.patch.object( + subtensor_module, "commit_reveal_v3_extrinsic" + ) + + # Call + result = subtensor.set_weights( + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, + ) + + # Asserts + mocked_commit_reveal_enabled.assert_called_once_with(netuid=fake_netuid) + mocked_commit_reveal_v3_extrinsic.assert_called_once_with( + subtensor=subtensor, + wallet=fake_wallet, + netuid=fake_netuid, + uids=fake_uids, + weights=fake_weights, + version_key=subtensor_module.settings.version_as_int, + wait_for_inclusion=fake_wait_for_inclusion, + wait_for_finalization=fake_wait_for_finalization, + ) + assert result == mocked_commit_reveal_v3_extrinsic.return_value From bff84961ffd5b25814f89981f10f54b48cfdfc7e Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Nov 2024 22:19:57 -0800 Subject: [PATCH 07/48] update get_encrypted_commit and extrinsic tests --- bittensor/core/extrinsics/commit_reveal.py | 63 ++++++++----------- .../extrinsics/test_commit_reveal.py | 32 +++++----- 2 files changed, 43 insertions(+), 52 deletions(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index dd5c3e0911..222b5deb4f 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -10,10 +10,7 @@ from bittensor.utils.btlogging import logging from bittensor.utils.networking import ensure_connected from bittensor.utils.registration import torch, use_torch -from bittensor.utils.weight_utils import ( - convert_weights_and_uids_for_emit, - generate_weight_hash, -) +from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -22,21 +19,25 @@ # this will be replaced with rust-based ffi import from here https://github.com/opentensor/bittensor-commit-reveal def get_encrypted_commit( - commit_hash: str, + uids: list[int], + weights: list[int], subnet_reveal_period_epochs: int, + version_key: int = version_as_int, ) -> tuple[bytes, int]: """ Decrypts to t-lock bytes. Arguments: - commit_hash: The hash of the commit (uids, weights) to be revealed. + uids (Union[NDArray[np.int64], torch.LongTensor, list]): The uids to commit. + weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights associated with the uids. subnet_reveal_period_epochs: Number of epochs after which the revive will be performed. + version_key (int, optional): The version key to use for committing and revealing. Default is version_as_int. Returns: t-lock encrypted commit for commit_crv3_weights extrinsic. reveal_period: drand period when Subtensor reveal the weights to the chain. """ - return commit_hash.encode(), subnet_reveal_period_epochs + return b"encrypted commit", subnet_reveal_period_epochs @ensure_connected @@ -53,12 +54,12 @@ def _do_commit_reveal_v3( Executes the commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or finalization. Arguments: - wallet : Wallet An instance of the Wallet class containing the user's keypair. - netuid : int The network unique identifier. - commit : bytes The commit data in bytes format. - reveal_round : int The round number for the reveal phase. - wait_for_inclusion : bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. - wait_for_finalization : bool, optional Flag indicating whether to wait for the extrinsic to be finalized. + wallet: Wallet An instance of the Wallet class containing the user's keypair. + netuid: int The network unique identifier. + commit bytes The commit data in bytes format. + reveal_round: int The round number for the reveal phase. + wait_for_inclusion: bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. + wait_for_finalization: bool, optional Flag indicating whether to wait for the extrinsic to be finalized. Returns: A tuple where the first element is a boolean indicating success or failure, and the second element is an optional string containing error message if any. @@ -114,14 +115,14 @@ def commit_reveal_v3_extrinsic( Commits and reveals weights for given subtensor and wallet with provided uids and weights. Arguments: - subtensor (Subtensor): The Subtensor instance. - wallet (Wallet): The wallet to use for committing and revealing. - netuid (int): The id of the network. - uids (Union[NDArray[np.int64], torch.LongTensor, list]): The uids to commit. - weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights associated with the uids. - version_key (int, optional): The version key to use for committing and revealing. Default is version_as_int. - wait_for_inclusion (bool, optional): Whether to wait for the inclusion of the transaction. Default is False. - wait_for_finalization (bool, optional): Whether to wait for the finalization of the transaction. Default is False. + subtensor: The Subtensor instance. + wallet: The wallet to use for committing and revealing. + netuid: The id of the network. + uids: The uids to commit. + weights: The weights associated with the uids. + version_key: The version key to use for committing and revealing. Default is version_as_int. + wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. + wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure, and the second element is a message associated with the result. @@ -142,25 +143,15 @@ def commit_reveal_v3_extrinsic( # Reformat and normalize. uids, weights = convert_weights_and_uids_for_emit(uids, weights) - # Generate the salt - salt = [random.randint(0, 350) for _ in range(8)] - - # Generate the hash of the weights - commit_hash = generate_weight_hash( - address=wallet.hotkey.ss58_address, - netuid=netuid, - uids=list(uids), - values=list(weights), - salt=salt, - version_key=version_key, - ) - # Get subnet's reveal (in epochs) - subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs(netuid) + subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs(netuid=netuid) # Encrypt `commit_hash` with t-lock and `get reveal_round` commit_for_reveal, reveal_round = get_encrypted_commit( - commit_hash, subnet_reveal_period_epochs + uids=uids, + weights=weights, + subnet_reveal_period_epochs=subnet_reveal_period_epochs, + version_key=version_key, ) success, message = _do_commit_reveal_v3( diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index df3a0a81ad..c769496dc4 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -125,23 +125,23 @@ def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): fake_netuid = 1 fake_uids = torch.tensor([1, 2, 3], dtype=torch.int64) fake_weights = torch.tensor([0.1, 0.2, 0.7], dtype=torch.float32) - fake_salt = [42] * 8 - fake_commit_hash = b"mock_commit_hash" fake_commit_for_reveal = b"mock_commit_for_reveal" fake_reveal_round = 1 # Mocks mocker.patch.object(commit_reveal, "use_torch", return_value=True) - mocker.patch.object( + + mocked_uids = mocker.Mock() + mocked_weights = mocker.Mock() + mocked_convert_weights_and_uids_for_emit = mocker.patch.object( commit_reveal, "convert_weights_and_uids_for_emit", - return_value=(fake_uids, fake_weights), + return_value=(mocked_uids, mocked_weights), ) - mocker.patch.object(commit_reveal.random, "randint", return_value=42) - mocker.patch.object( - commit_reveal, "generate_weight_hash", return_value=fake_commit_hash + mocked_get_subnet_reveal_period_epochs = mocker.patch.object( + subtensor, "get_subnet_reveal_period_epochs" ) - mocker.patch.object( + mocked_get_encrypted_commit = mocker.patch.object( commit_reveal, "get_encrypted_commit", return_value=(fake_commit_for_reveal, fake_reveal_round), @@ -164,6 +164,14 @@ def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): # Asserts assert success is True assert message == "Success" + mocked_convert_weights_and_uids_for_emit.assert_called_once_with(fake_uids, fake_weights) + mocked_get_subnet_reveal_period_epochs.assert_called_once_with(netuid=fake_netuid) + mocked_get_encrypted_commit.assert_called_once_with( + uids=mocked_uids, + weights=mocked_weights, + subnet_reveal_period_epochs=mocked_get_subnet_reveal_period_epochs.return_value, + version_key=commit_reveal.version_as_int, + ) mock_do_commit_reveal_v3.assert_called_once_with( self=subtensor, wallet=fake_wallet, @@ -189,7 +197,6 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy(mocker, subtensor): "convert_weights_and_uids_for_emit", return_value=(fake_uids, fake_weights), ) - mock_generate_hash = mocker.patch.object(commit_reveal, "generate_weight_hash") mock_encode_drand = mocker.patch.object( commit_reveal, "get_encrypted_commit", return_value=(b"commit", 0) ) @@ -212,7 +219,6 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy(mocker, subtensor): assert success is True assert message == "Committed!" mock_convert.assert_called_once_with(fake_uids, fake_weights) - mock_generate_hash.assert_called_once() mock_encode_drand.assert_called_once() mock_do_commit.assert_called_once() @@ -224,8 +230,6 @@ def test_commit_reveal_v3_extrinsic_response_false(mocker, subtensor): fake_netuid = 1 fake_uids = torch.tensor([1, 2, 3], dtype=torch.int64) fake_weights = torch.tensor([0.1, 0.2, 0.7], dtype=torch.float32) - fake_salt = [42] * 8 - fake_commit_hash = b"mock_commit_hash" fake_commit_for_reveal = b"mock_commit_for_reveal" fake_reveal_round = 1 @@ -236,10 +240,6 @@ def test_commit_reveal_v3_extrinsic_response_false(mocker, subtensor): "convert_weights_and_uids_for_emit", return_value=(fake_uids, fake_weights), ) - mocker.patch.object(commit_reveal.random, "randint", return_value=42) - mocker.patch.object( - commit_reveal, "generate_weight_hash", return_value=fake_commit_hash - ) mocker.patch.object( commit_reveal, "get_encrypted_commit", From b61c408a4d881042bc245c697a2fc59da0968cff Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Nov 2024 22:28:10 -0800 Subject: [PATCH 08/48] ruff --- bittensor/core/extrinsics/commit_reveal.py | 4 +++- tests/unit_tests/extrinsics/test_commit_reveal.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 222b5deb4f..4f2fef8a37 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -144,7 +144,9 @@ def commit_reveal_v3_extrinsic( uids, weights = convert_weights_and_uids_for_emit(uids, weights) # Get subnet's reveal (in epochs) - subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs(netuid=netuid) + subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs( + netuid=netuid + ) # Encrypt `commit_hash` with t-lock and `get reveal_round` commit_for_reveal, reveal_round = get_encrypted_commit( diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index c769496dc4..a58600a326 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -164,7 +164,9 @@ def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): # Asserts assert success is True assert message == "Success" - mocked_convert_weights_and_uids_for_emit.assert_called_once_with(fake_uids, fake_weights) + mocked_convert_weights_and_uids_for_emit.assert_called_once_with( + fake_uids, fake_weights + ) mocked_get_subnet_reveal_period_epochs.assert_called_once_with(netuid=fake_netuid) mocked_get_encrypted_commit.assert_called_once_with( uids=mocked_uids, From 2db55f596e968415b0f8d53c4b50e0c43ece70b7 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 26 Nov 2024 22:36:03 -0800 Subject: [PATCH 09/48] unused import --- bittensor/core/extrinsics/commit_reveal.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 4f2fef8a37..775529ff6a 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -1,4 +1,3 @@ -import random from typing import Optional, Union, TYPE_CHECKING import numpy as np @@ -28,8 +27,8 @@ def get_encrypted_commit( Decrypts to t-lock bytes. Arguments: - uids (Union[NDArray[np.int64], torch.LongTensor, list]): The uids to commit. - weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights associated with the uids. + uids: The uids to commit. + weights: The weights associated with the uids. subnet_reveal_period_epochs: Number of epochs after which the revive will be performed. version_key (int, optional): The version key to use for committing and revealing. Default is version_as_int. From 00628ab0adad26378aa11be58ec8b42ee83251c2 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 27 Nov 2024 12:55:39 -0800 Subject: [PATCH 10/48] update commit_reveal.py --- bittensor/core/extrinsics/commit_reveal.py | 44 +++++++++++----------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 775529ff6a..f1690e4369 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -1,6 +1,7 @@ from typing import Optional, Union, TYPE_CHECKING import numpy as np +from bittensor_commit_reveal import get_encrypted_commit from numpy.typing import NDArray from bittensor.core.extrinsics.utils import submit_extrinsic @@ -16,27 +17,28 @@ from bittensor.core.subtensor import Subtensor -# this will be replaced with rust-based ffi import from here https://github.com/opentensor/bittensor-commit-reveal -def get_encrypted_commit( - uids: list[int], - weights: list[int], - subnet_reveal_period_epochs: int, - version_key: int = version_as_int, -) -> tuple[bytes, int]: - """ - Decrypts to t-lock bytes. - - Arguments: - uids: The uids to commit. - weights: The weights associated with the uids. - subnet_reveal_period_epochs: Number of epochs after which the revive will be performed. - version_key (int, optional): The version key to use for committing and revealing. Default is version_as_int. - - Returns: - t-lock encrypted commit for commit_crv3_weights extrinsic. - reveal_period: drand period when Subtensor reveal the weights to the chain. - """ - return b"encrypted commit", subnet_reveal_period_epochs +# TODO: delete this after all +# # this will be replaced with rust-based ffi import from here https://github.com/opentensor/bittensor-commit-reveal +# def get_encrypted_commit( +# uids: list[int], +# weights: list[int], +# subnet_reveal_period_epochs: int, +# version_key: int = version_as_int, +# ) -> tuple[bytes, int]: +# """ +# Decrypts to t-lock bytes. +# +# Arguments: +# uids: The uids to commit. +# weights: The weights associated with the uids. +# subnet_reveal_period_epochs: Number of epochs after which the revive will be performed. +# version_key (int, optional): The version key to use for committing and revealing. Default is version_as_int. +# +# Returns: +# t-lock encrypted commit for commit_crv3_weights extrinsic. +# reveal_period: drand period when Subtensor reveal the weights to the chain. +# """ +# return b"encrypted commit", subnet_reveal_period_epochs @ensure_connected From 49b13012326af4420f60a87c202515d747308186 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 29 Nov 2024 22:25:31 -0800 Subject: [PATCH 11/48] update extrinsic --- bittensor/core/extrinsics/set_weights.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index 880480e998..063dab30cf 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -150,7 +150,7 @@ def set_weights_extrinsic( logging.debug(f"Weights: {[float(v / 65535) for v in weight_vals]}") try: - success, error_message = do_set_weights( + success, message = do_set_weights( self=subtensor, wallet=wallet, netuid=netuid, @@ -168,8 +168,8 @@ def set_weights_extrinsic( logging.success(f"[green]Finalized![/green] Set weights: {str(success)}") return True, "Successfully set weights and Finalized." else: - logging.error(error_message) - return False, error_message + logging.error(message) + return False, message except Exception as e: logging.error(f":cross_mark: [red]Failed.[/red]: Error: {e}") From cafc7d0f58d66786b870ccd3e7e36c963e568e94 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 29 Nov 2024 23:02:15 -0800 Subject: [PATCH 12/48] remove old function --- bittensor/core/extrinsics/commit_reveal.py | 26 +--------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index f1690e4369..b789258c71 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -17,30 +17,6 @@ from bittensor.core.subtensor import Subtensor -# TODO: delete this after all -# # this will be replaced with rust-based ffi import from here https://github.com/opentensor/bittensor-commit-reveal -# def get_encrypted_commit( -# uids: list[int], -# weights: list[int], -# subnet_reveal_period_epochs: int, -# version_key: int = version_as_int, -# ) -> tuple[bytes, int]: -# """ -# Decrypts to t-lock bytes. -# -# Arguments: -# uids: The uids to commit. -# weights: The weights associated with the uids. -# subnet_reveal_period_epochs: Number of epochs after which the revive will be performed. -# version_key (int, optional): The version key to use for committing and revealing. Default is version_as_int. -# -# Returns: -# t-lock encrypted commit for commit_crv3_weights extrinsic. -# reveal_period: drand period when Subtensor reveal the weights to the chain. -# """ -# return b"encrypted commit", subnet_reveal_period_epochs - - @ensure_connected def _do_commit_reveal_v3( self: "Subtensor", @@ -171,7 +147,7 @@ def commit_reveal_v3_extrinsic( logging.success( f"[green]Finalized![/green] Weights commited with reveal round [blue]{reveal_round}[/blue]." ) - return True, message + return True, f"reveal_round:{reveal_round}" else: logging.error(message) return False, message From a5364745027211c31e620caea59cac5427fd3cb2 Mon Sep 17 00:00:00 2001 From: johnreedv Date: Mon, 2 Dec 2024 18:13:29 -0800 Subject: [PATCH 13/48] add params --- bittensor/core/extrinsics/commit_reveal.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index b789258c71..be82482b5f 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -125,12 +125,18 @@ def commit_reveal_v3_extrinsic( netuid=netuid ) + tempo = subtensor.get_subnet_hyperparameters(netuid).tempo + current_block = subtensor.get_current_block() + # Encrypt `commit_hash` with t-lock and `get reveal_round` commit_for_reveal, reveal_round = get_encrypted_commit( uids=uids, weights=weights, - subnet_reveal_period_epochs=subnet_reveal_period_epochs, version_key=version_key, + tempo=tempo, + current_block=current_block, + netuid=netuid, + subnet_reveal_period_epochs=subnet_reveal_period_epochs, ) success, message = _do_commit_reveal_v3( From ea50baa3faa9d84af1f2a585d038d66f0888a299 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 3 Dec 2024 17:45:09 +0200 Subject: [PATCH 14/48] More efficient use of hyperparam grabbing. --- bittensor/core/extrinsics/commit_reveal.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index be82482b5f..73c2f64ca2 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -120,13 +120,14 @@ def commit_reveal_v3_extrinsic( # Reformat and normalize. uids, weights = convert_weights_and_uids_for_emit(uids, weights) - # Get subnet's reveal (in epochs) - subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs( - netuid=netuid - ) - - tempo = subtensor.get_subnet_hyperparameters(netuid).tempo current_block = subtensor.get_current_block() + subnet_hyperparameters = subtensor.get_subnet_hyperparameters( + netuid, block=current_block + ) + tempo = subnet_hyperparameters.tempo + subnet_reveal_period_epochs = ( + subnet_hyperparameters.commit_reveal_weights_interval + ) # Encrypt `commit_hash` with t-lock and `get reveal_round` commit_for_reveal, reveal_round = get_encrypted_commit( From 084e3b49b90028523bfd49907bc6851f43867963 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 3 Dec 2024 17:45:17 +0200 Subject: [PATCH 15/48] Updated tests. --- .../extrinsics/test_commit_reveal.py | 70 ++++++++++++++++--- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index a58600a326..01ed42f8b5 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -1,4 +1,5 @@ from bittensor.core import subtensor as subtensor_module +from bittensor.core.chain_data import SubnetHyperparameters from bittensor.core.subtensor import Subtensor from bittensor.core.extrinsics import commit_reveal import pytest @@ -13,7 +14,40 @@ def subtensor(mocker): mocker.patch.object( subtensor_module, "SubstrateInterface", return_value=fake_substrate ) - return Subtensor() + yield Subtensor() + + +@pytest.fixture +def hyperparams(): + yield SubnetHyperparameters( + rho=0, + kappa=0, + immunity_period=0, + min_allowed_weights=0, + max_weight_limit=0.0, + tempo=0, + min_difficulty=0, + max_difficulty=0, + weights_version=0, + weights_rate_limit=0, + adjustment_interval=0, + activity_cutoff=0, + registration_allowed=False, + target_regs_per_interval=0, + min_burn=0, + max_burn=0, + bonds_moving_avg=0, + max_regs_per_block=0, + serving_rate_limit=0, + max_validators=0, + adjustment_alpha=0, + difficulty=0, + commit_reveal_weights_interval=0, + commit_reveal_weights_enabled=True, + alpha_high=0, + alpha_low=0, + liquid_alpha_enabled=False, + ) def test_do_commit_reveal_v3_success(mocker, subtensor): @@ -118,7 +152,7 @@ def test_do_commit_reveal_v3_failure_due_to_error(mocker, subtensor): assert result == (False, "Formatted error") -def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): +def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor, hyperparams): """Test successful commit-reveal with torch tensors.""" # Preps fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) @@ -149,6 +183,12 @@ def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): mock_do_commit_reveal_v3 = mocker.patch.object( commit_reveal, "_do_commit_reveal_v3", return_value=(True, "Success") ) + mock_block = mocker.patch.object(subtensor, "get_current_block", return_value=1) + mock_hyperparams = mocker.patch.object( + subtensor, + "get_subnet_hyperparameters", + return_value=hyperparams, + ) # Call success, message = commit_reveal.commit_reveal_v3_extrinsic( @@ -163,16 +203,18 @@ def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): # Asserts assert success is True - assert message == "Success" + assert message == "reveal_round:1" mocked_convert_weights_and_uids_for_emit.assert_called_once_with( fake_uids, fake_weights ) - mocked_get_subnet_reveal_period_epochs.assert_called_once_with(netuid=fake_netuid) mocked_get_encrypted_commit.assert_called_once_with( uids=mocked_uids, weights=mocked_weights, - subnet_reveal_period_epochs=mocked_get_subnet_reveal_period_epochs.return_value, + subnet_reveal_period_epochs=mock_hyperparams.return_value.commit_reveal_weights_interval, version_key=commit_reveal.version_as_int, + tempo=mock_hyperparams.return_value.tempo, + netuid=fake_netuid, + current_block=mock_block.return_value, ) mock_do_commit_reveal_v3.assert_called_once_with( self=subtensor, @@ -185,7 +227,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch(mocker, subtensor): ) -def test_commit_reveal_v3_extrinsic_success_with_numpy(mocker, subtensor): +def test_commit_reveal_v3_extrinsic_success_with_numpy(mocker, subtensor, hyperparams): """Test successful commit-reveal with numpy arrays.""" # Preps fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) @@ -205,6 +247,12 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy(mocker, subtensor): mock_do_commit = mocker.patch.object( commit_reveal, "_do_commit_reveal_v3", return_value=(True, "Committed!") ) + mocker.patch.object(subtensor, "get_current_block", return_value=1) + mocker.patch.object( + subtensor, + "get_subnet_hyperparameters", + return_value=hyperparams, + ) # Call success, message = commit_reveal.commit_reveal_v3_extrinsic( @@ -219,13 +267,13 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy(mocker, subtensor): # Asserts assert success is True - assert message == "Committed!" + assert message == "reveal_round:0" mock_convert.assert_called_once_with(fake_uids, fake_weights) mock_encode_drand.assert_called_once() mock_do_commit.assert_called_once() -def test_commit_reveal_v3_extrinsic_response_false(mocker, subtensor): +def test_commit_reveal_v3_extrinsic_response_false(mocker, subtensor, hyperparams): """Test unsuccessful commit-reveal with torch.""" # Preps fake_wallet = mocker.Mock(autospec=subtensor_module.Wallet) @@ -250,6 +298,12 @@ def test_commit_reveal_v3_extrinsic_response_false(mocker, subtensor): mock_do_commit_reveal_v3 = mocker.patch.object( commit_reveal, "_do_commit_reveal_v3", return_value=(False, "Failed") ) + mocker.patch.object(subtensor, "get_current_block", return_value=1) + mocker.patch.object( + subtensor, + "get_subnet_hyperparameters", + return_value=hyperparams, + ) # Call success, message = commit_reveal.commit_reveal_v3_extrinsic( From 33d604048b192d9a1a066675bdc1f117dfd6e990 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 3 Dec 2024 18:29:49 +0200 Subject: [PATCH 16/48] Updated Name. --- bittensor/core/extrinsics/commit_reveal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 73c2f64ca2..b326889a34 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -60,7 +60,7 @@ def _do_commit_reveal_v3( ) response = submit_extrinsic( - substrate=self.substrate, + subtensor=self, extrinsic=extrinsic, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, From 389d311e3f713b4870887925cc19f72a52fb4875 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 3 Dec 2024 19:54:56 +0200 Subject: [PATCH 17/48] Added catch for unable to communicate with drand server. --- bittensor/core/extrinsics/commit_reveal.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index b326889a34..16a8d6e34a 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -159,6 +159,12 @@ def commit_reveal_v3_extrinsic( logging.error(message) return False, message + except ValueError: + logging.error( + ":cross_mark: [red]Failed.[/red] Commit cannot be generated, no response from the service." + ) + return False, "Commit cannot be generated, no response from the service" + except Exception as e: logging.error(f":cross_mark: [red]Failed. Error:[/red] {e}") return False, str(e) From dd272d06fe9c4d5382adb29f00e681d5ed7c7d94 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 4 Dec 2024 00:00:31 +0200 Subject: [PATCH 18/48] Removed valueerror catch --- bittensor/core/extrinsics/commit_reveal.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 16a8d6e34a..b326889a34 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -159,12 +159,6 @@ def commit_reveal_v3_extrinsic( logging.error(message) return False, message - except ValueError: - logging.error( - ":cross_mark: [red]Failed.[/red] Commit cannot be generated, no response from the service." - ) - return False, "Commit cannot be generated, no response from the service" - except Exception as e: logging.error(f":cross_mark: [red]Failed. Error:[/red] {e}") return False, str(e) From e7d7fcdccce703a704965b8b185cbae8cd6fd266 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 4 Dec 2024 00:30:32 +0200 Subject: [PATCH 19/48] Added skeleton of cr3 e2e --- tests/e2e_tests/test_commit_weights.py | 161 ++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 3e29dc56ec..6dfc03a238 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -18,7 +18,7 @@ @pytest.mark.asyncio -async def test_commit_and_reveal_weights(local_chain): +async def test_commit_and_reveal_weights_legacy(local_chain): """ Tests the commit/reveal weights mechanism with subprocess disabled (CR1.0) @@ -172,3 +172,162 @@ async def test_commit_and_reveal_weights(local_chain): 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") + + +@pytest.mark.skip +@pytest.mark.asyncio +async def test_commit_and_reveal_weights_cr3(local_chain): + """ + [WIP] + Tests the commit/reveal weights mechanism (CR3) + + Steps: + 1. Register a subnet through Alice + 2. Register Alice's neuron and add stake + 3. Enable commit-reveal mechanism on the subnet + 4. Lower the commit_reveal interval and rate limit + 5. Commit weights and verify + 6. Wait interval & reveal weights and verify + Raises: + 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") + assert register_subnet(local_chain, alice_wallet), "Unable to register the subnet" + + # Verify subnet 1 created successfully + assert local_chain.query( + "SubtensorModule", "NetworksAdded", [1] + ).serialize(), "Subnet wasn't created successfully" + + subtensor = Subtensor(network="ws://localhost:9945") + + # Register Alice to the subnet + assert subtensor.burned_register( + alice_wallet, netuid + ), "Unable to register Alice as a neuron" + + # Stake to become to top neuron after the first epoch + add_stake(local_chain, alice_wallet, Balance.from_tao(100_000)) + + # Enable commit_reveal on the subnet + assert sudo_set_hyperparameter_bool( + local_chain, + alice_wallet, + "sudo_set_commit_reveal_weights_enabled", + True, + netuid, + ), "Unable to enable commit reveal on the subnet" + + assert subtensor.get_subnet_hyperparameters( + netuid=netuid, + ).commit_reveal_weights_enabled, "Failed to enable commit/reveal" + + # Lower the commit_reveal interval + assert sudo_set_hyperparameter_values( + local_chain, + alice_wallet, + call_function="sudo_set_commit_reveal_weights_interval", + call_params={"netuid": netuid, "interval": "1"}, + return_error_message=True, + ) + + assert ( + subtensor.get_subnet_hyperparameters( + netuid=netuid + ).commit_reveal_weights_interval + == 1 + ), "Failed to set commit/reveal periods" + + assert ( + subtensor.weights_rate_limit(netuid=netuid) > 0 + ), "Weights rate limit is below 0" + # Lower the rate limit + assert sudo_set_hyperparameter_values( + local_chain, + alice_wallet, + call_function="sudo_set_weights_set_rate_limit", + call_params={"netuid": netuid, "weights_set_rate_limit": "0"}, + return_error_message=True, + ) + + assert ( + subtensor.get_subnet_hyperparameters(netuid=netuid).weights_rate_limit == 0 + ), "Failed to set weights_rate_limit" + assert subtensor.weights_rate_limit(netuid=netuid) == 0 + + # Commit-reveal values + uids = np.array([0], dtype=np.int64) + weights = np.array([0.1], dtype=np.float32) + salt = [18, 179, 107, 0, 165, 211, 141, 197] + weight_uids, weight_vals = convert_weights_and_uids_for_emit( + uids=uids, weights=weights + ) + + # Commit weights + success, message = subtensor.commit_weights( + alice_wallet, + netuid, + salt=salt, + uids=weight_uids, + weights=weight_vals, + wait_for_inclusion=True, + 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") From c2d2ae17becad53d3546d2f649a27beab2a72c8c Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 3 Dec 2024 15:22:05 -0800 Subject: [PATCH 20/48] Adds bittensor-commit-reveal as a requirement --- requirements/prod.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements/prod.txt b/requirements/prod.txt index c34618ff49..b44211b989 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -25,3 +25,4 @@ substrate-interface~=1.7.9 uvicorn websockets>=14.1 bittensor-wallet>=2.1.3 +bittensor-commit-reveal From 26ae576e39c7df4b55dbf685f478f9031d58c3fe Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 3 Dec 2024 16:51:49 -0800 Subject: [PATCH 21/48] Adds devnet in network map --- bittensor/core/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index f15754e227..35092bb308 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -36,7 +36,7 @@ MINERS_DIR.mkdir(parents=True, exist_ok=True) # Bittensor networks name -NETWORKS = ["finney", "test", "archive", "local", "subvortex"] +NETWORKS = ["finney", "test", "archive", "local", "subvortex", "devnet"] DEFAULT_ENDPOINT = "wss://entrypoint-finney.opentensor.ai:443" DEFAULT_NETWORK = NETWORKS[0] @@ -47,6 +47,7 @@ ARCHIVE_ENTRYPOINT = "wss://archive.chain.opentensor.ai:443/" LOCAL_ENTRYPOINT = os.getenv("BT_SUBTENSOR_CHAIN_ENDPOINT") or "ws://127.0.0.1:9944" SUBVORTEX_ENTRYPOINT = "ws://subvortex.info:9944" +DEVNET_ENTRYPOINT = "wss://dev.chain.opentensor.ai:443" NETWORK_MAP = { NETWORKS[0]: FINNEY_ENTRYPOINT, @@ -54,6 +55,7 @@ NETWORKS[2]: ARCHIVE_ENTRYPOINT, NETWORKS[3]: LOCAL_ENTRYPOINT, NETWORKS[4]: SUBVORTEX_ENTRYPOINT, + NETWORKS[5]: DEVNET_ENTRYPOINT, } # Currency Symbols Bittensor From 6dedc1c2b662208a5b41dd2079284f1214734e51 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 4 Dec 2024 18:47:17 +0200 Subject: [PATCH 22/48] Changed commit log to hash instead of the bytes. --- bittensor/core/extrinsics/commit_reveal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index b326889a34..7d0331c877 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -42,7 +42,8 @@ def _do_commit_reveal_v3( A tuple where the first element is a boolean indicating success or failure, and the second element is an optional string containing error message if any. """ logging.info( - f"Committing weights hash [blue]{commit}[/blue] for subnet #[blue]{netuid}[/blue] with reveal round [blue]{reveal_round}[/blue]..." + f"Committing weights hash [blue]{commit.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " + f"reveal round [blue]{reveal_round}[/blue]..." ) call = self.substrate.compose_call( From 1b453f7a5764fc2e5975d763754dd80b7b64744d Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 4 Dec 2024 19:18:59 +0200 Subject: [PATCH 23/48] Fix tests. --- tests/unit_tests/extrinsics/test_commit_reveal.py | 4 ++-- tests/unit_tests/test_dendrite.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index 01ed42f8b5..6d2f4549a6 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -87,7 +87,7 @@ def test_do_commit_reveal_v3_success(mocker, subtensor): call=mocked_compose_call.return_value, keypair=fake_wallet.hotkey ) mocked_submit_extrinsic.assert_called_once_with( - substrate=subtensor.substrate, + subtensor=subtensor, extrinsic=mocked_create_signed_extrinsic.return_value, wait_for_inclusion=False, wait_for_finalization=False, @@ -141,7 +141,7 @@ def test_do_commit_reveal_v3_failure_due_to_error(mocker, subtensor): call=mocked_compose_call.return_value, keypair=fake_wallet.hotkey ) mocked_submit_extrinsic.assert_called_once_with( - substrate=subtensor.substrate, + subtensor=subtensor, extrinsic=mocked_create_signed_extrinsic.return_value, wait_for_inclusion=True, wait_for_finalization=True, diff --git a/tests/unit_tests/test_dendrite.py b/tests/unit_tests/test_dendrite.py index d1f9327649..e5eaf5d086 100644 --- a/tests/unit_tests/test_dendrite.py +++ b/tests/unit_tests/test_dendrite.py @@ -22,6 +22,7 @@ from unittest.mock import MagicMock, Mock import aiohttp +from bittensor_wallet.mock import get_mock_wallet import pytest from bittensor.core.axon import Axon @@ -402,7 +403,7 @@ def test_process_error_message( request_name, ): # Arrange - dendrite = Dendrite() + dendrite = Dendrite(get_mock_wallet()) synapse = Mock() synapse.timeout = synapse_timeout From 8e540f29617530f3862fd7fdab2d274ba4c2ce78 Mon Sep 17 00:00:00 2001 From: lvyaoting Date: Thu, 5 Dec 2024 21:22:10 +0800 Subject: [PATCH 24/48] chore: fix some comments Signed-off-by: lvyaoting --- bittensor/utils/async_substrate_interface.py | 4 ++-- bittensor/utils/btlogging/format.py | 2 +- scripts/check_pre_submit.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/utils/async_substrate_interface.py b/bittensor/utils/async_substrate_interface.py index cd50695492..60d7845ff9 100644 --- a/bittensor/utils/async_substrate_interface.py +++ b/bittensor/utils/async_substrate_interface.py @@ -944,7 +944,7 @@ async def get_runtime(block_hash, block_id) -> Runtime: self.last_block_hash = block_hash self.block_id = block_id - # In fact calls and storage functions are decoded against runtime of previous block, therefor retrieve + # In fact calls and storage functions are decoded against runtime of previous block, therefore retrieve # metadata and apply type registry of runtime of parent block block_header = await self.rpc_request( "chain_getHeader", [self.last_block_hash] @@ -1390,7 +1390,7 @@ async def get_block( A dict containing the extrinsic and digest logs data """ if block_hash and block_number: - raise ValueError("Either block_hash or block_number should be be set") + raise ValueError("Either block_hash or block_number should be set") if block_number is not None: block_hash = await self.get_block_hash(block_number) diff --git a/bittensor/utils/btlogging/format.py b/bittensor/utils/btlogging/format.py index 5f76dce6d4..ebb353525f 100644 --- a/bittensor/utils/btlogging/format.py +++ b/bittensor/utils/btlogging/format.py @@ -225,7 +225,7 @@ def format(self, record: "logging.LogRecord") -> str: record (logging.LogRecord): The log record. Returns: - formated record (str): The formatted log record. + formatted record (str): The formatted log record. """ record.levelname = f"{record.levelname:^10}" return super().format(record) diff --git a/scripts/check_pre_submit.sh b/scripts/check_pre_submit.sh index 4dbe7747f6..da2ef67eea 100755 --- a/scripts/check_pre_submit.sh +++ b/scripts/check_pre_submit.sh @@ -1,6 +1,6 @@ #!/bin/bash -# ruff checks formating +# ruff checks formatting echo ">>> Run the pre-submit format check with \`ruff format .\`." ruff format . From 6ce9b20f424ee832456239dedb1e3fe26edfdf61 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Thu, 5 Dec 2024 19:37:55 +0200 Subject: [PATCH 25/48] Adds a factory function to create an initialised AsyncSubstrate object. --- bittensor/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bittensor/__init__.py b/bittensor/__init__.py index f4d8ee906a..b6b2f08f4b 100644 --- a/bittensor/__init__.py +++ b/bittensor/__init__.py @@ -17,11 +17,20 @@ import warnings -from .core.settings import __version__, version_split, DEFAULTS +from .core.settings import __version__, version_split, DEFAULTS, DEFAULT_NETWORK +from .core.async_subtensor import AsyncSubtensor from .utils.btlogging import logging from .utils.deprecated import * +async def async_subtensor(network: str = DEFAULT_NETWORK) -> AsyncSubtensor: + """ + Creates an initialised AsyncSubtensor object. + """ + async with AsyncSubtensor(network=network) as subtensor_: + return subtensor_ + + def __getattr__(name): if name == "version_split": warnings.warn( From fd91948b78df886922ced2d71f8598e4f63b97a4 Mon Sep 17 00:00:00 2001 From: FLiotta Date: Mon, 9 Dec 2024 17:05:24 -0300 Subject: [PATCH 26/48] Add default value to the get_block_number method in AsyncSubstrateInterface --- bittensor/utils/async_substrate_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/utils/async_substrate_interface.py b/bittensor/utils/async_substrate_interface.py index cd50695492..ba89581afe 100644 --- a/bittensor/utils/async_substrate_interface.py +++ b/bittensor/utils/async_substrate_interface.py @@ -2758,7 +2758,7 @@ async def get_metadata_call_function( return call return None - async def get_block_number(self, block_hash: Optional[str]) -> int: + async def get_block_number(self, block_hash: Optional[str] = None) -> int: """Async version of `substrateinterface.base.get_block_number` method.""" response = await self.rpc_request("chain_getHeader", [block_hash]) From 07a22048961d2761dbd3da4dcbe45f468ceeae6c Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 10 Dec 2024 15:41:06 +0200 Subject: [PATCH 27/48] Mismatched "archive" index --- bittensor/core/metagraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/metagraph.py b/bittensor/core/metagraph.py index 31ab3cf3d3..4da95852be 100644 --- a/bittensor/core/metagraph.py +++ b/bittensor/core/metagraph.py @@ -570,7 +570,7 @@ def sync( if ( subtensor.chain_endpoint != settings.ARCHIVE_ENTRYPOINT - or subtensor.network != settings.NETWORKS[3] + or subtensor.network != "archive" ): cur_block = subtensor.get_current_block() if block and block < (cur_block - 300): From 24678d503486e36dcda92b2176419421f1a90a41 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Tue, 10 Dec 2024 12:37:16 -0800 Subject: [PATCH 28/48] Fixes buffer overflow in stdout --- tests/e2e_tests/conftest.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 4a7b2ccf62..5d94f3aedd 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -4,6 +4,7 @@ import signal import subprocess import time +import threading import pytest from substrateinterface import SubstrateInterface @@ -37,7 +38,11 @@ def local_chain(request): # Start new node process process = subprocess.Popen( - cmds, stdout=subprocess.PIPE, text=True, preexec_fn=os.setsid + cmds, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + preexec_fn=os.setsid, ) # Pattern match indicates node is compiled and ready @@ -52,16 +57,31 @@ def local_chain(request): timestamp = int(time.time()) def wait_for_node_start(process, pattern): - for line in process.stdout: + while True: + line = process.stdout.readline() + if not line: + break + print(line.strip()) # 10 min as timeout if int(time.time()) - timestamp > 10 * 60: print("Subtensor not started in time") - break + return if pattern.search(line): print("Node started!") break + # Start a background reader after pattern is found + # To prevent the buffer filling up + def read_output(): + while True: + line = process.stdout.readline() + if not line: + break + + reader_thread = threading.Thread(target=read_output, daemon=True) + reader_thread.start() + wait_for_node_start(process, pattern) # Run the test, passing in substrate interface From 92d5c7bf4973129fbff993d36e75b588be8f6be5 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:46:36 -0800 Subject: [PATCH 29/48] Testing on devnet --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 0bc467a94d..9dbce4cd62 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -90,7 +90,7 @@ jobs: - name: Setup subtensor repo working-directory: ${{ github.workspace }}/subtensor - run: git checkout testnet + run: git checkout devnet - name: Run tests run: | From b352c3f4e2cfb908cd99565fd53203e2085e77a5 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:09:40 -0800 Subject: [PATCH 30/48] Switching back to testnet --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 9dbce4cd62..0bc467a94d 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -90,7 +90,7 @@ jobs: - name: Setup subtensor repo working-directory: ${{ github.workspace }}/subtensor - run: git checkout devnet + run: git checkout testnet - name: Run tests run: | From ac9cc8bb32b196b49a80efc6faee6acc99f93c15 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:35:58 -0800 Subject: [PATCH 31/48] Added e2e test for CRv3 + enhancements (#2532) * Added e2e test for crv3 + enhancements * Fixes type * Ruff * Fixes return type * Renames method --- bittensor/core/subtensor.py | 34 ++++ tests/e2e_tests/test_commit_reveal_v3.py | 188 ++++++++++++++++++++ tests/e2e_tests/test_commit_weights.py | 160 ----------------- tests/e2e_tests/utils/chain_interactions.py | 77 +++++++- 4 files changed, 294 insertions(+), 165 deletions(-) create mode 100644 tests/e2e_tests/test_commit_reveal_v3.py diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 966d744f00..7de84a6d7a 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1291,6 +1291,40 @@ def neurons(self, netuid: int, block: Optional[int] = None) -> list["NeuronInfo" return neurons + def last_drand_round( + self, + ) -> Optional[int]: + """ + Retrieves the last drand round emitted in bittensor. This corresponds when committed weights will be revealed. + + Returns: + int: The latest Drand round emitted in bittensor. + """ + result = self.substrate.query( + module="Drand", storage_function="LastStoredRound" + ) + return getattr(result, "value", None) + + def get_weight_commits(self, netuid: int, block: Optional[int] = None) -> list: + """ + Retrieves CRV3 weight commit information for a specific subnet. + + Args: + netuid (int): The unique identifier of the subnet. + block (Optional[int]): The blockchain block number for the query. + + Returns: + list: A list of commit details, where each entry is a dictionary with keys 'who', + 'serialized_commit', and 'reveal_round', or an empty list if no data is found. + """ + result = self.query_map( + module="SubtensorModule", + name="CRV3WeightCommits", + params=[netuid], + block=block, + ) + return result.records[0][1].value if result and result.records else [] + def get_total_subnets(self, block: Optional[int] = None) -> Optional[int]: """ Retrieves the total number of subnets within the Bittensor network as of a specific blockchain block. diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py new file mode 100644 index 0000000000..1e97d124b5 --- /dev/null +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -0,0 +1,188 @@ +import re + +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 +from tests.e2e_tests.utils.chain_interactions import ( + add_stake, + register_subnet, + sudo_set_hyperparameter_bool, + sudo_set_hyperparameter_values, + wait_interval, + sudo_set_admin_utils, + next_tempo, +) +from tests.e2e_tests.utils.e2e_test_utils import setup_wallet + + +# Skipping till we have CRV3 on testnet +@pytest.mark.skip +@pytest.mark.parametrize("local_chain", [False], indirect=True) +@pytest.mark.asyncio +async def test_commit_and_reveal_weights_cr3(local_chain): + """ + Tests the commit/reveal weights mechanism (CR3) + + Steps: + 1. Register a subnet through Alice + 2. Register Alice's neuron and add stake + 3. Enable commit-reveal mechanism on the subnet + 4. Lower weights rate limit + 5. Change the tempo for subnet 1 + 5. Commit weights and ensure they are committed. + 6. Wait interval & reveal weights and verify + Raises: + AssertionError: If any of the checks or verifications fail + """ + netuid = 1 + logging.console.info("Testing test_commit_and_reveal_weights") + + # Register root as Alice + keypair, alice_wallet = setup_wallet("//Alice") + assert register_subnet(local_chain, alice_wallet), "Unable to register the subnet" + + # Verify subnet 1 created successfully + assert local_chain.query( + "SubtensorModule", "NetworksAdded", [1] + ).serialize(), "Subnet wasn't created successfully" + + logging.console.info("Subnet 1 is registered") + + subtensor = Subtensor(network="ws://localhost:9945") + + # Register Alice to the subnet + assert subtensor.burned_register( + alice_wallet, netuid + ), "Unable to register Alice as a neuron" + logging.console.info("Registered Alice to subnet 1") + + # Stake to become to top neuron after the first epoch + add_stake(local_chain, alice_wallet, Balance.from_tao(100_000)) + logging.console.info("Stake added by Alice") + + # Enable commit_reveal on the subnet + assert sudo_set_hyperparameter_bool( + local_chain, + alice_wallet, + "sudo_set_commit_reveal_weights_enabled", + True, + netuid, + ), "Unable to enable commit reveal on the subnet" + + # Verify commit_reveal was enabled + assert subtensor.get_subnet_hyperparameters( + netuid=netuid, + ).commit_reveal_weights_enabled, "Failed to enable commit/reveal" + logging.console.info("Commit reveal enabled") + + # Change the weights rate limit on the subnet + assert sudo_set_hyperparameter_values( + local_chain, + alice_wallet, + call_function="sudo_set_weights_set_rate_limit", + call_params={"netuid": netuid, "weights_set_rate_limit": "0"}, + return_error_message=True, + ) + + # Verify weights rate limit was changed + assert ( + subtensor.get_subnet_hyperparameters(netuid=netuid).weights_rate_limit == 0 + ), "Failed to set weights_rate_limit" + assert subtensor.weights_rate_limit(netuid=netuid) == 0 + logging.console.info("sudo_set_weights_set_rate_limit executed: set to 0") + + # Change the tempo of the subnet from default 360 + # Since this is in normal blocks, this is necessary + tempo_set = 10 + assert sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_tempo", + call_params={"netuid": netuid, "tempo": tempo_set}, + return_error_message=True, + ) + tempo = subtensor.get_subnet_hyperparameters(netuid=netuid).tempo + assert tempo_set == tempo + logging.console.info(f"sudo_set_tempo executed: set to {tempo_set}") + + # Commit-reveal values - setting weights to self + uids = np.array([0], dtype=np.int64) + revealed_weights = np.array([0.1], dtype=np.float32) + weight_uids, weight_vals = convert_weights_and_uids_for_emit( + uids=uids, weights=revealed_weights + ) + + # Fetch current block and calculate next tempo for the subnet + current_block = subtensor.get_current_block() + upcoming_tempo = next_tempo(current_block, tempo, netuid) + + # Lower than this might mean weights will get revealed before we can check them + if upcoming_tempo - current_block < 3: + await wait_interval( + tempo, + subtensor, + netuid=netuid, + reporting_interval=1, + ) + + # Commit weights + success, message = subtensor.set_weights( + alice_wallet, + netuid, + uids=weight_uids, + weights=weight_vals, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # Assert committing was a success + assert success is True + assert bool(re.match(r"reveal_round:\d+", message)) + logging.console.info( + f"Successfully set weights: uids {weight_uids}, weights {weight_vals}" + ) + + # Parse expected reveal_round + expected_reveal_round = int(message.split(":")[1]) + + # Fetch current commits pending on the chain + commits_on_chain = subtensor.get_weight_commits(netuid=netuid) + address, commit, reveal_round = commits_on_chain[0] + + # Assert correct values are committed on the chain + assert expected_reveal_round == reveal_round + assert address == alice_wallet.hotkey.ss58_address + + # Ensure no weights are available as of now + assert subtensor.weights(netuid=netuid) == [] + + # Wait for the next tempo so weights can be revealed + await wait_interval( + subtensor.get_subnet_hyperparameters(netuid=netuid).tempo, + subtensor, + netuid=netuid, + reporting_interval=1, + ) + + # Fetch the latest drand pulse + latest_drand_round = subtensor.last_drand_round() + + # Fetch weights on the chain as they should be revealed now + revealed_weights = subtensor.weights(netuid=netuid)[0][1] + + # Assert correct weights were revealed + assert weight_uids[0] == revealed_weights[0][0] + assert weight_vals[0] == revealed_weights[0][1] + + # Now that the commit has been revealed, there shouldn't be any pending commits + assert subtensor.get_weight_commits(netuid=netuid) == [] + + # Ensure the drand_round is always in the positive w.r.t expected when revealed + assert ( + latest_drand_round - expected_reveal_round >= 0 + ), f"latest_drand_round ({latest_drand_round}) is less than expected_reveal_round ({expected_reveal_round})" + + logging.console.info("✅ Passed commit_reveal v3") diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 6dfc03a238..c6737e01ae 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -2,7 +2,6 @@ import numpy as np import pytest - from bittensor.core.subtensor import Subtensor from bittensor.utils.balance import Balance from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit @@ -172,162 +171,3 @@ async def test_commit_and_reveal_weights_legacy(local_chain): 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") - - -@pytest.mark.skip -@pytest.mark.asyncio -async def test_commit_and_reveal_weights_cr3(local_chain): - """ - [WIP] - Tests the commit/reveal weights mechanism (CR3) - - Steps: - 1. Register a subnet through Alice - 2. Register Alice's neuron and add stake - 3. Enable commit-reveal mechanism on the subnet - 4. Lower the commit_reveal interval and rate limit - 5. Commit weights and verify - 6. Wait interval & reveal weights and verify - Raises: - 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") - assert register_subnet(local_chain, alice_wallet), "Unable to register the subnet" - - # Verify subnet 1 created successfully - assert local_chain.query( - "SubtensorModule", "NetworksAdded", [1] - ).serialize(), "Subnet wasn't created successfully" - - subtensor = Subtensor(network="ws://localhost:9945") - - # Register Alice to the subnet - assert subtensor.burned_register( - alice_wallet, netuid - ), "Unable to register Alice as a neuron" - - # Stake to become to top neuron after the first epoch - add_stake(local_chain, alice_wallet, Balance.from_tao(100_000)) - - # Enable commit_reveal on the subnet - assert sudo_set_hyperparameter_bool( - local_chain, - alice_wallet, - "sudo_set_commit_reveal_weights_enabled", - True, - netuid, - ), "Unable to enable commit reveal on the subnet" - - assert subtensor.get_subnet_hyperparameters( - netuid=netuid, - ).commit_reveal_weights_enabled, "Failed to enable commit/reveal" - - # Lower the commit_reveal interval - assert sudo_set_hyperparameter_values( - local_chain, - alice_wallet, - call_function="sudo_set_commit_reveal_weights_interval", - call_params={"netuid": netuid, "interval": "1"}, - return_error_message=True, - ) - - assert ( - subtensor.get_subnet_hyperparameters( - netuid=netuid - ).commit_reveal_weights_interval - == 1 - ), "Failed to set commit/reveal periods" - - assert ( - subtensor.weights_rate_limit(netuid=netuid) > 0 - ), "Weights rate limit is below 0" - # Lower the rate limit - assert sudo_set_hyperparameter_values( - local_chain, - alice_wallet, - call_function="sudo_set_weights_set_rate_limit", - call_params={"netuid": netuid, "weights_set_rate_limit": "0"}, - return_error_message=True, - ) - - assert ( - subtensor.get_subnet_hyperparameters(netuid=netuid).weights_rate_limit == 0 - ), "Failed to set weights_rate_limit" - assert subtensor.weights_rate_limit(netuid=netuid) == 0 - - # Commit-reveal values - uids = np.array([0], dtype=np.int64) - weights = np.array([0.1], dtype=np.float32) - salt = [18, 179, 107, 0, 165, 211, 141, 197] - weight_uids, weight_vals = convert_weights_and_uids_for_emit( - uids=uids, weights=weights - ) - - # Commit weights - success, message = subtensor.commit_weights( - alice_wallet, - netuid, - salt=salt, - uids=weight_uids, - weights=weight_vals, - wait_for_inclusion=True, - 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") diff --git a/tests/e2e_tests/utils/chain_interactions.py b/tests/e2e_tests/utils/chain_interactions.py index 9c0d9100e8..bae60c5443 100644 --- a/tests/e2e_tests/utils/chain_interactions.py +++ b/tests/e2e_tests/utils/chain_interactions.py @@ -133,7 +133,27 @@ async def wait_epoch(subtensor: "Subtensor", netuid: int = 1): await wait_interval(tempo, subtensor, netuid) -async def wait_interval(tempo: int, subtensor: "Subtensor", netuid: int = 1): +def next_tempo(current_block: int, tempo: int, netuid: int) -> int: + """ + Calculates the next tempo block for a specific subnet. + + Args: + current_block (int): The current block number. + tempo (int): The tempo value for the subnet. + netuid (int): The unique identifier of the subnet. + + Returns: + int: The next tempo block number. + """ + interval = tempo + 1 + last_epoch = current_block - 1 - (current_block + netuid + 1) % interval + next_tempo = last_epoch + interval + return next_tempo + + +async def wait_interval( + tempo: int, subtensor: "Subtensor", netuid: int = 1, reporting_interval: int = 10 +): """ Waits until the next tempo interval starts for a specific subnet. @@ -141,10 +161,8 @@ async def wait_interval(tempo: int, subtensor: "Subtensor", netuid: int = 1): and the provided tempo, then enters a loop where it periodically checks the current block number until the next tempo interval starts. """ - interval = tempo + 1 current_block = subtensor.get_current_block() - last_epoch = current_block - 1 - (current_block + netuid + 1) % interval - next_tempo_block_start = last_epoch + interval + next_tempo_block_start = next_tempo(current_block, tempo, netuid) last_reported = None while current_block < next_tempo_block_start: @@ -152,7 +170,7 @@ async def wait_interval(tempo: int, subtensor: "Subtensor", netuid: int = 1): 1 ) # Wait for 1 second before checking the block number again current_block = subtensor.get_current_block() - if last_reported is None or current_block - last_reported >= 10: + if last_reported is None or current_block - last_reported >= reporting_interval: last_reported = current_block print( f"Current Block: {current_block} Next tempo for netuid {netuid} at: {next_tempo_block_start}" @@ -160,3 +178,52 @@ async def wait_interval(tempo: int, subtensor: "Subtensor", netuid: int = 1): logging.info( f"Current Block: {current_block} Next tempo for netuid {netuid} at: {next_tempo_block_start}" ) + + +# Helper to execute sudo wrapped calls on the chain +def sudo_set_admin_utils( + substrate: "SubstrateInterface", + wallet: "Wallet", + call_function: str, + call_params: dict, + return_error_message: bool = False, +) -> Union[bool, tuple[bool, Optional[str]]]: + """ + Wraps the call in sudo to set hyperparameter values using AdminUtils. + + Args: + substrate (SubstrateInterface): Substrate connection. + wallet (Wallet): Wallet object with the keypair for signing. + call_function (str): The AdminUtils function to call. + call_params (dict): Parameters for the AdminUtils function. + return_error_message (bool): If True, returns the error message along with the success status. + + Returns: + Union[bool, tuple[bool, Optional[str]]]: Success status or (success status, error message). + """ + inner_call = substrate.compose_call( + call_module="AdminUtils", + call_function=call_function, + call_params=call_params, + ) + + sudo_call = substrate.compose_call( + call_module="Sudo", + call_function="sudo", + call_params={"call": inner_call}, + ) + extrinsic = substrate.create_signed_extrinsic( + call=sudo_call, keypair=wallet.coldkey + ) + + response = substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + response.process_events() + + if return_error_message: + return response.is_success, response.error_message + + return response.is_success From 8e05d393af9f5f25d888b7548b973b286e9254cd Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 09:13:27 -0800 Subject: [PATCH 32/48] Fixes var name --- tests/e2e_tests/test_commit_reveal_v3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 1e97d124b5..ac75f76631 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -110,9 +110,9 @@ async def test_commit_and_reveal_weights_cr3(local_chain): # Commit-reveal values - setting weights to self uids = np.array([0], dtype=np.int64) - revealed_weights = np.array([0.1], dtype=np.float32) + weights = np.array([0.1], dtype=np.float32) weight_uids, weight_vals = convert_weights_and_uids_for_emit( - uids=uids, weights=revealed_weights + uids=uids, weights=weights ) # Fetch current block and calculate next tempo for the subnet From 822d36c6497b913b8616ac998dd4852c3bdb989c Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 12:18:13 -0800 Subject: [PATCH 33/48] Cleanup --- bittensor/core/settings.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index e588f48df9..5bc596ede9 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -36,7 +36,7 @@ MINERS_DIR.mkdir(parents=True, exist_ok=True) # Bittensor networks name -NETWORKS = ["finney", "test", "archive", "local", "subvortex", "devnet"] +NETWORKS = ["finney", "test", "archive", "local", "subvortex"] DEFAULT_ENDPOINT = "wss://entrypoint-finney.opentensor.ai:443" DEFAULT_NETWORK = NETWORKS[0] @@ -47,7 +47,6 @@ ARCHIVE_ENTRYPOINT = "wss://archive.chain.opentensor.ai:443" LOCAL_ENTRYPOINT = os.getenv("BT_SUBTENSOR_CHAIN_ENDPOINT") or "ws://127.0.0.1:9944" SUBVORTEX_ENTRYPOINT = "ws://subvortex.info:9944" -DEVNET_ENTRYPOINT = "wss://dev.chain.opentensor.ai:443" NETWORK_MAP = { NETWORKS[0]: FINNEY_ENTRYPOINT, @@ -55,7 +54,6 @@ NETWORKS[2]: ARCHIVE_ENTRYPOINT, NETWORKS[3]: LOCAL_ENTRYPOINT, NETWORKS[4]: SUBVORTEX_ENTRYPOINT, - NETWORKS[5]: DEVNET_ENTRYPOINT, } REVERSE_NETWORK_MAP = { From a3c4c086ff11306eff9b59f5419cb0f3787c768a Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:28:36 -0800 Subject: [PATCH 34/48] Update bittensor/core/async_subtensor.py Co-authored-by: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index b783d68a8b..a7349498bb 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1429,7 +1429,7 @@ async def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int] async def commit_reveal_enabled( self, netuid: int, block_hash: Optional[str] = None - ) -> Optional[bool]: + ) -> bool: """ Check if commit-reveal mechanism is enabled for a given network at a specific block. From a6216578e92e736b1898cd67765da3bd969ca7fb Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:28:44 -0800 Subject: [PATCH 35/48] Update bittensor/core/async_subtensor.py Co-authored-by: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index a7349498bb..f7f5f1e006 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1438,7 +1438,7 @@ async def commit_reveal_enabled( block_hash (Optional[str]): The block hash of block at which to check the parameter (default is None, which implies the current block). Returns: - (Optional[bool]): Returns the integer value of the hyperparameter if available; otherwise, returns None. + (bool): Returns the integer value of the hyperparameter if available; otherwise, returns None. """ call = await self.get_hyperparameter( param_name="CommitRevealWeightsEnabled", From 11250a112b7ec84e5f4e4a2e956df5e86980e4ba Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:30:09 -0800 Subject: [PATCH 36/48] Update bittensor/core/async_subtensor.py Co-authored-by: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/async_subtensor.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f7f5f1e006..35c31cb73b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1607,9 +1607,12 @@ async def set_weights( retries = 0 success = False message = "No attempt made. Perhaps it is too soon to set weights!" - while retries < max_retries and await self.blocks_since_last_update( - netuid, uid - ) > await self.weights_rate_limit(netuid): + while ( + retries < max_retries + and await self.blocks_since_last_update(netuid, uid) + > await self.weights_rate_limit(netuid) + and success is False + ): try: logging.info( f"Setting weights for subnet #[blue]{netuid}[/blue]. Attempt [blue]{retries + 1} of {max_retries}[/blue]." From 4f8b4c546944c4cbc7dbadc7654eb08abf76f870 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 14:00:15 -0800 Subject: [PATCH 37/48] Updates e2e test --- tests/e2e_tests/test_commit_reveal_v3.py | 28 +++++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index ac75f76631..c49b27f17c 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -18,8 +18,6 @@ from tests.e2e_tests.utils.e2e_test_utils import setup_wallet -# Skipping till we have CRV3 on testnet -@pytest.mark.skip @pytest.mark.parametrize("local_chain", [False], indirect=True) @pytest.mark.asyncio async def test_commit_and_reveal_weights_cr3(local_chain): @@ -118,7 +116,9 @@ async def test_commit_and_reveal_weights_cr3(local_chain): # Fetch current block and calculate next tempo for the subnet current_block = subtensor.get_current_block() upcoming_tempo = next_tempo(current_block, tempo, netuid) - + logging.console.info( + f"Checking if window is too low with Current block: {current_block}, next tempo: {upcoming_tempo}" + ) # Lower than this might mean weights will get revealed before we can check them if upcoming_tempo - current_block < 3: await wait_interval( @@ -127,6 +127,12 @@ async def test_commit_and_reveal_weights_cr3(local_chain): netuid=netuid, reporting_interval=1, ) + current_block = subtensor.get_current_block() + latest_drand_round = subtensor.last_drand_round() + upcoming_tempo = next_tempo(current_block, tempo, netuid) + logging.console.info( + f"Post first wait_interval (to ensure window isnt too low): {current_block}, next tempo: {upcoming_tempo}, drand: {latest_drand_round}" + ) # Commit weights success, message = subtensor.set_weights( @@ -141,12 +147,19 @@ async def test_commit_and_reveal_weights_cr3(local_chain): # Assert committing was a success assert success is True assert bool(re.match(r"reveal_round:\d+", message)) - logging.console.info( - f"Successfully set weights: uids {weight_uids}, weights {weight_vals}" - ) # Parse expected reveal_round expected_reveal_round = int(message.split(":")[1]) + logging.console.info( + f"Successfully set weights: uids {weight_uids}, weights {weight_vals}, reveal_round: {expected_reveal_round}" + ) + + current_block = subtensor.get_current_block() + latest_drand_round = subtensor.last_drand_round() + upcoming_tempo = next_tempo(current_block, tempo, netuid) + logging.console.info( + f"After setting weights: Current block: {current_block}, next tempo: {upcoming_tempo}, drand: {latest_drand_round}" + ) # Fetch current commits pending on the chain commits_on_chain = subtensor.get_weight_commits(netuid=netuid) @@ -169,6 +182,9 @@ async def test_commit_and_reveal_weights_cr3(local_chain): # Fetch the latest drand pulse latest_drand_round = subtensor.last_drand_round() + logging.console.info( + f"Latest drand round after waiting for tempo: {latest_drand_round}" + ) # Fetch weights on the chain as they should be revealed now revealed_weights = subtensor.weights(netuid=netuid)[0][1] From 927ad92c7e36f2815bbce65f3d8db922b7339aba Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 13 Dec 2024 00:21:13 +0200 Subject: [PATCH 38/48] Fixes get_current_block --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2ad0e7b222..f108700525 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -236,7 +236,7 @@ async def get_current_block(self) -> int: Knowing the current block number is essential for querying real-time data and performing time-sensitive operations on the blockchain. It serves as a reference point for network activities and data synchronization. """ - return await self.substrate.get_block_number() + return await self.substrate.get_block_number(None) async def get_block_hash(self, block_id: Optional[int] = None): """ From eb6924869e07ab8f90c83717380db5a1f1d25b47 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 14:58:40 -0800 Subject: [PATCH 39/48] Adds methods to fetch stake --- bittensor/core/subtensor.py | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 441a4c033b..41c7a8b59b 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1268,6 +1268,59 @@ def neurons(self, netuid: int, block: Optional[int] = None) -> list["NeuronInfo" return neurons + def get_total_stake_for_coldkey( + self, ss58_address: str, block: Optional[int] = None + ) -> Optional["Balance"]: + """Retrieves the total stake held by a coldkey across all associated hotkeys, including delegated stakes. + + Args: + ss58_address (str): The SS58 address of the coldkey account. + block (Optional[int]): The blockchain block number at which to perform the query. + + Returns: + Optional[Balance]: The total stake amount held by the coldkey, or None if the query fails. + """ + result = self.query_subtensor("TotalColdkeyStake", block, [ss58_address]) + if not hasattr(result, "value") or result is None: + return None + return Balance.from_rao(result.value) + + def get_total_stake_for_hotkey( + self, ss58_address: str, block: Optional[int] = None + ) -> Optional["Balance"]: + """Retrieves the total stake associated with a hotkey. + + Args: + ss58_address (str): The SS58 address of the hotkey account. + block (Optional[int]): The blockchain block number at which to perform the query. + + Returns: + Optional[Balance]: The total stake amount held by the hotkey, or None if the query fails. + """ + result = self.query_subtensor("TotalHotkeyStake", block, [ss58_address]) + if not hasattr(result, "value") or result is None: + return None + return Balance.from_rao(result.value) + + def get_stake_for_coldkey_and_hotkey( + self, hotkey_ss58: str, coldkey_ss58: str, block: Optional[int] = None + ) -> Optional["Balance"]: + """Retrieves the stake amount for a specific coldkey-hotkey pair within the Bittensor network. + + Args: + hotkey_ss58 (str): The SS58 address of the hotkey account. + coldkey_ss58 (str): The SS58 address of the coldkey account. + block (Optional[int]): The blockchain block number at which to perform the query. + + Returns: + Optional[Balance]: The stake amount for the specific coldkey-hotkey pair, + or None if the query fails. + """ + result = self.query_subtensor("Stake", block, [hotkey_ss58, coldkey_ss58]) + if not hasattr(result, "value") or result is None: + return None + return Balance.from_rao(result.value) + def get_total_subnets(self, block: Optional[int] = None) -> Optional[int]: """ Retrieves the total number of subnets within the Bittensor network as of a specific blockchain block. From 70b020cc91c5152c47b5a7743f6fcd549625536c Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 15:01:18 -0800 Subject: [PATCH 40/48] Cleanup --- bittensor/core/subtensor.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 41c7a8b59b..f39e590906 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1302,25 +1302,6 @@ def get_total_stake_for_hotkey( return None return Balance.from_rao(result.value) - def get_stake_for_coldkey_and_hotkey( - self, hotkey_ss58: str, coldkey_ss58: str, block: Optional[int] = None - ) -> Optional["Balance"]: - """Retrieves the stake amount for a specific coldkey-hotkey pair within the Bittensor network. - - Args: - hotkey_ss58 (str): The SS58 address of the hotkey account. - coldkey_ss58 (str): The SS58 address of the coldkey account. - block (Optional[int]): The blockchain block number at which to perform the query. - - Returns: - Optional[Balance]: The stake amount for the specific coldkey-hotkey pair, - or None if the query fails. - """ - result = self.query_subtensor("Stake", block, [hotkey_ss58, coldkey_ss58]) - if not hasattr(result, "value") or result is None: - return None - return Balance.from_rao(result.value) - def get_total_subnets(self, block: Optional[int] = None) -> Optional[int]: """ Retrieves the total number of subnets within the Bittensor network as of a specific blockchain block. From f4bbdbb22a9113f5e0aec196295fe03b9b69ea5f Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:06:24 -0800 Subject: [PATCH 41/48] Update bittensor/core/subtensor.py Co-authored-by: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f39e590906..4b0a3a4103 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1281,7 +1281,7 @@ def get_total_stake_for_coldkey( Optional[Balance]: The total stake amount held by the coldkey, or None if the query fails. """ result = self.query_subtensor("TotalColdkeyStake", block, [ss58_address]) - if not hasattr(result, "value") or result is None: + if not getattr(result, "value", None) is None: return None return Balance.from_rao(result.value) From 2450d37e2d4306b15260789478e7d2e3f19f3b76 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor <165814940+ibraheem-opentensor@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:06:30 -0800 Subject: [PATCH 42/48] Update bittensor/core/subtensor.py Co-authored-by: Benjamin Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 4b0a3a4103..037f2b51bb 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1298,7 +1298,7 @@ def get_total_stake_for_hotkey( Optional[Balance]: The total stake amount held by the hotkey, or None if the query fails. """ result = self.query_subtensor("TotalHotkeyStake", block, [ss58_address]) - if not hasattr(result, "value") or result is None: + if not getattr(result, "value", None) is None: return None return Balance.from_rao(result.value) From 2f2f0b900bf2d9cb67d476f93368d933ca465bca Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 15:08:33 -0800 Subject: [PATCH 43/48] Removes if not --- bittensor/core/subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 037f2b51bb..0b450ce2f0 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1281,7 +1281,7 @@ def get_total_stake_for_coldkey( Optional[Balance]: The total stake amount held by the coldkey, or None if the query fails. """ result = self.query_subtensor("TotalColdkeyStake", block, [ss58_address]) - if not getattr(result, "value", None) is None: + if getattr(result, "value", None) is None: return None return Balance.from_rao(result.value) @@ -1298,7 +1298,7 @@ def get_total_stake_for_hotkey( Optional[Balance]: The total stake amount held by the hotkey, or None if the query fails. """ result = self.query_subtensor("TotalHotkeyStake", block, [ss58_address]) - if not getattr(result, "value", None) is None: + if getattr(result, "value", None) is None: return None return Balance.from_rao(result.value) From 1b45dc4c14692b788cead60f2682a71d91ce53f1 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 16:06:21 -0800 Subject: [PATCH 44/48] Bumps drand ffi and updates test --- requirements/prod.txt | 2 +- tests/e2e_tests/test_commit_reveal_v3.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/requirements/prod.txt b/requirements/prod.txt index c98d641d53..c57ce611f9 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -25,4 +25,4 @@ substrate-interface~=1.7.9 uvicorn websockets>=14.1 bittensor-wallet>=2.1.3 -bittensor-commit-reveal +bittensor-commit-reveal>=0.1.0 diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index c49b27f17c..0850341cc6 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -161,6 +161,11 @@ async def test_commit_and_reveal_weights_cr3(local_chain): f"After setting weights: Current block: {current_block}, next tempo: {upcoming_tempo}, drand: {latest_drand_round}" ) + # Ensure the expected drand round is well in the future + assert ( + expected_reveal_round < latest_drand_round + ), "Revealed drand pulse is older than the drand pulse right after setting weights" + # Fetch current commits pending on the chain commits_on_chain = subtensor.get_weight_commits(netuid=netuid) address, commit, reveal_round = commits_on_chain[0] From 3dbb7a064dac9581c5e39ef75d2ca0f3499ad752 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 16:12:20 -0800 Subject: [PATCH 45/48] Fix comparision in test --- tests/e2e_tests/test_commit_reveal_v3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 0850341cc6..277e58430a 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -163,7 +163,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain): # Ensure the expected drand round is well in the future assert ( - expected_reveal_round < latest_drand_round + expected_reveal_round > latest_drand_round ), "Revealed drand pulse is older than the drand pulse right after setting weights" # Fetch current commits pending on the chain From 3de9519d3d83398b377ec7b0c4a3f4c559ac4827 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 16:41:02 -0800 Subject: [PATCH 46/48] Bumps version and updates changelog --- CHANGELOG.md | 22 ++++++++++++++++++++++ VERSION | 2 +- bittensor/core/settings.py | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c79478b24..3819cdad38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 8.5.0 /2024-12-12 + +## What's Changed +* add improved reveal-round params by @JohnReedV in https://github.com/opentensor/bittensor/pull/2509 +* fix: add default value to the get_block_number method in AsyncSubstrateInterface by @FLiotta in https://github.com/opentensor/bittensor/pull/2529 +* Mismatched "archive" index by @thewhaleking in https://github.com/opentensor/bittensor/pull/2530 +* Adds a factory function to create an initialised AsyncSubtensor object. by @thewhaleking in https://github.com/opentensor/bittensor/pull/2516 +* chore: fix some comments by @lvyaoting in https://github.com/opentensor/bittensor/pull/2515 +* Fixes E2E test chain buffer issues on devnet by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2531 +* Added e2e test for CRv3 + enhancements by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2532 +* Backmerge master to staging 850 by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2535 +* Enhancement/adds total stake functions by @ibraheem-opentensor in https://github.com/opentensor/bittensor/pull/2537 +* Fixes get_current_block by @thewhaleking in https://github.com/opentensor/bittensor/pull/2536 +* [SDK] Add `commit reveal v3` logic (python part only) by @roman-opentensor in https://github.com/opentensor/bittensor/pull/2484 + +## New Contributors +* @JohnReedV made their first contribution in https://github.com/opentensor/bittensor/pull/2509 +* @FLiotta made their first contribution in https://github.com/opentensor/bittensor/pull/2529 +* @lvyaoting made their first contribution in https://github.com/opentensor/bittensor/pull/2515 + +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v8.4.5...v8.5.0 + ## 8.4.5 /2024-12-05 ## What's Changed diff --git a/VERSION b/VERSION index 007f2e63cd..5eaed3b7ca 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.4.5 \ No newline at end of file +8.5.0 \ No newline at end of file diff --git a/bittensor/core/settings.py b/bittensor/core/settings.py index 5bc596ede9..2da8ecb5a1 100644 --- a/bittensor/core/settings.py +++ b/bittensor/core/settings.py @@ -15,7 +15,7 @@ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -__version__ = "8.4.5" +__version__ = "8.5.0" import os import re From 42e2a0cbbbac46952fe38ad025149a02e5dfd2f6 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 17:27:25 -0800 Subject: [PATCH 47/48] Rename method to fetch subnet commits --- bittensor/core/subtensor.py | 2 +- tests/e2e_tests/test_commit_reveal_v3.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 39669793b6..59780350f8 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1309,7 +1309,7 @@ def last_drand_round( ) return getattr(result, "value", None) - def get_weight_commits(self, netuid: int, block: Optional[int] = None) -> list: + def get_current_weight_commit_info(self, netuid: int, block: Optional[int] = None) -> list: """ Retrieves CRV3 weight commit information for a specific subnet. diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 277e58430a..4c038c3764 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -167,7 +167,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain): ), "Revealed drand pulse is older than the drand pulse right after setting weights" # Fetch current commits pending on the chain - commits_on_chain = subtensor.get_weight_commits(netuid=netuid) + commits_on_chain = subtensor.get_current_weight_commit_info(netuid=netuid) address, commit, reveal_round = commits_on_chain[0] # Assert correct values are committed on the chain @@ -199,7 +199,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain): assert weight_vals[0] == revealed_weights[0][1] # Now that the commit has been revealed, there shouldn't be any pending commits - assert subtensor.get_weight_commits(netuid=netuid) == [] + assert subtensor.get_current_weight_commit_info(netuid=netuid) == [] # Ensure the drand_round is always in the positive w.r.t expected when revealed assert ( From 6317f19cdee2ff67a1c47bb464329a32a7010b55 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Thu, 12 Dec 2024 17:29:47 -0800 Subject: [PATCH 48/48] Ruff --- bittensor/core/subtensor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 59780350f8..4c238feb6a 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1309,7 +1309,9 @@ def last_drand_round( ) return getattr(result, "value", None) - def get_current_weight_commit_info(self, netuid: int, block: Optional[int] = None) -> list: + def get_current_weight_commit_info( + self, netuid: int, block: Optional[int] = None + ) -> list: """ Retrieves CRV3 weight commit information for a specific subnet.