Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[SDK] Add async version of commit reveal v3 #2560

Merged
merged 6 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 29 additions & 8 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
SubnetHyperparameters,
decode_account_id,
)
from bittensor.core.extrinsics.async_commit_reveal import commit_reveal_v3_extrinsic
from bittensor.core.extrinsics.async_registration import register_extrinsic
from bittensor.core.extrinsics.async_root import (
set_root_weights_extrinsic,
Expand Down Expand Up @@ -1596,18 +1597,38 @@ 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】.
"""
retries = 0
success = False
uid = await self.get_uid_for_hotkey_on_subnet(
wallet.hotkey.ss58_address, netuid
)

if (await self.commit_reveal_enabled(netuid=netuid)) is True:
# go with `commit reveal v3` extrinsic
raise NotImplementedError(
"Not implemented yet for AsyncSubtensor. Coming soon."
)
message = "No attempt made. Perhaps it is too soon to commit weights!"
while (
await self.blocks_since_last_update(netuid, uid)
> await self.weights_rate_limit(netuid)
and retries < max_retries
and success is False
):
logging.info(
f"Committing weights for subnet #{netuid}. Attempt {retries + 1} of {max_retries}."
)
success, message = await 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,
)
retries += 1
return success, message
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
Expand Down
152 changes: 152 additions & 0 deletions bittensor/core/extrinsics/async_commit_reveal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
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.settings import version_as_int
from bittensor.utils import format_error_message
from bittensor.utils.btlogging import logging
from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit

if TYPE_CHECKING:
from bittensor_wallet import Wallet
from bittensor.core.async_subtensor import AsyncSubtensor
from bittensor.utils.registration import torch


async def _do_commit_reveal_v3(
subtensor: "AsyncSubtensor",
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:
subtensor: An instance of the Subtensor class.
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.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with "
f"reveal round [blue]{reveal_round}[/blue]..."
)

call = await subtensor.substrate.compose_call(
call_module="SubtensorModule",
call_function="commit_crv3_weights",
call_params={
"netuid": netuid,
"commit": commit,
"reveal_round": reveal_round,
},
)
extrinsic = await subtensor.substrate.create_signed_extrinsic(
call=call,
keypair=wallet.hotkey,
)

response = await subtensor.substrate.submit_extrinsic(
subtensor=subtensor,
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."

if await response.is_success:
return True, None

return False, format_error_message(await response.error_message)


async def commit_reveal_v3_extrinsic(
subtensor: "AsyncSubtensor",
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: 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.
"""
try:
# Convert uids and weights
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)

current_block = await subtensor.substrate.get_block_number(None)
subnet_hyperparameters = await subtensor.get_subnet_hyperparameters(netuid)
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(
uids=uids,
weights=weights,
version_key=version_key,
tempo=tempo,
current_block=current_block,
netuid=netuid,
subnet_reveal_period_epochs=subnet_reveal_period_epochs,
)

success, message = await _do_commit_reveal_v3(
subtensor=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 not True:
logging.error(message)
return False, message

logging.success(
f"[green]Finalized![/green] Weights commited with reveal round [blue]{reveal_round}[/blue]."
)
return True, f"reveal_round:{reveal_round}"

except Exception as e:
logging.error(f":cross_mark: [red]Failed. Error:[/red] {e}")
return False, str(e)
Loading
Loading