# The MIT License (MIT)
# Copyright © 2021 Yuma Rao
# Copyright © 2023 Opentensor Foundation
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
# the Software.
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
"""Module that encapsulates the CommitWeightCommand and the RevealWeightCommand. Used to commit and reveal weights
for a specific subnet on the Bittensor Network."""
import argparse
import os
import re
import numpy as np
from rich.prompt import Prompt, Confirm
import bittensor
import bittensor.utils.weight_utils as weight_utils
from . import defaults # type: ignore
[docs]
class CommitWeightCommand:
"""
Executes the ``commit`` command to commit weights for specific subnet on the Bittensor network.
Usage:
The command allows committing weights for a specific subnet. Users need to specify the netuid (network unique identifier), corresponding UIDs, and weights they wish to commit.
Optional arguments:
- ``--netuid`` (int): The netuid of the subnet for which weights are to be commited.
- ``--uids`` (str): Corresponding UIDs for the specified netuid, in comma-separated format.
- ``--weights`` (str): Corresponding weights for the specified UIDs, in comma-separated format.
Example usage:
$ btcli wt commit --netuid 1 --uids 1,2,3,4 --weights 0.1,0.2,0.3,0.4
Note:
This command is used to commit weights for a specific subnet and requires the user to have the necessary permissions.
"""
[docs]
@staticmethod
def run(cli: "bittensor.cli"):
r"""Commit weights for a specific subnet."""
try:
subtensor: "bittensor.subtensor" = bittensor.subtensor(
config=cli.config, log_verbose=False
)
CommitWeightCommand._run(cli, subtensor)
finally:
if "subtensor" in locals():
subtensor.close()
bittensor.logging.debug("closing subtensor connection")
[docs]
@staticmethod
def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
r"""Commit weights for a specific subnet"""
wallet = bittensor.wallet(config=cli.config)
# Get values if not set
if not cli.config.is_set("netuid"):
cli.config.netuid = int(Prompt.ask("Enter netuid"))
if not cli.config.is_set("uids"):
cli.config.uids = Prompt.ask("Enter UIDs (comma-separated)")
if not cli.config.is_set("weights"):
cli.config.weights = Prompt.ask("Enter weights (comma-separated)")
# Parse from string
netuid = cli.config.netuid
uids = np.array(
[int(x) for x in re.split(r"[ ,]+", cli.config.uids)], dtype=np.int64
)
weights = np.array(
[float(x) for x in re.split(r"[ ,]+", cli.config.weights)], dtype=np.float32
)
weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit(
uids=uids, weights=weights
)
if not cli.config.is_set("salt"):
# Generate random salt
salt_length = 8
salt = list(os.urandom(salt_length))
if not Confirm.ask(
f"Have you recorded the [red]salt[/red]: [bold white]'{salt}'[/bold white]? It will be "
f"required to reveal weights."
):
return False, "User cancelled the operation."
else:
salt = np.array(
[int(x) for x in re.split(r"[ ,]+", cli.config.salt)],
dtype=np.int64,
).tolist()
# Run the commit weights operation
success, message = subtensor.commit_weights(
wallet=wallet,
netuid=netuid,
uids=weight_uids,
weights=weight_vals,
salt=salt,
wait_for_inclusion=cli.config.wait_for_inclusion,
wait_for_finalization=cli.config.wait_for_finalization,
prompt=cli.config.prompt,
)
# Result
if success:
bittensor.__console__.print("Weights committed successfully")
else:
bittensor.__console__.print(f"Failed to commit weights: {message}")
[docs]
@staticmethod
def add_args(parser: argparse.ArgumentParser):
parser = parser.add_parser(
"commit", help="""Commit weights for a specific subnet."""
)
parser.add_argument("--netuid", dest="netuid", type=int, required=False)
parser.add_argument("--uids", dest="uids", type=str, required=False)
parser.add_argument("--weights", dest="weights", type=str, required=False)
parser.add_argument("--salt", dest="salt", type=str, required=False)
parser.add_argument(
"--wait-for-inclusion",
dest="wait_for_inclusion",
action="store_true",
default=False,
)
parser.add_argument(
"--wait-for-finalization",
dest="wait_for_finalization",
action="store_true",
default=True,
)
parser.add_argument(
"--prompt",
dest="prompt",
action="store_true",
default=False,
)
bittensor.wallet.add_args(parser)
bittensor.subtensor.add_args(parser)
[docs]
@staticmethod
def check_config(config: "bittensor.config"):
if not config.no_prompt and not config.is_set("wallet.name"):
wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name)
config.wallet.name = str(wallet_name)
if not config.no_prompt and not config.is_set("wallet.hotkey"):
hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey)
config.wallet.hotkey = str(hotkey)
[docs]
class RevealWeightCommand:
"""
Executes the ``reveal`` command to reveal weights for a specific subnet on the Bittensor network.
Usage:
The command allows revealing weights for a specific subnet. Users need to specify the netuid (network unique identifier), corresponding UIDs, and weights they wish to reveal.
Optional arguments:
- ``--netuid`` (int): The netuid of the subnet for which weights are to be revealed.
- ``--uids`` (str): Corresponding UIDs for the specified netuid, in comma-separated format.
- ``--weights`` (str): Corresponding weights for the specified UIDs, in comma-separated format.
- ``--salt`` (str): Corresponding salt for the hash function, integers in comma-separated format.
Example usage::
$ btcli wt reveal --netuid 1 --uids 1,2,3,4 --weights 0.1,0.2,0.3,0.4 --salt 163,241,217,11,161,142,147,189
Note:
This command is used to reveal weights for a specific subnet and requires the user to have the necessary permissions.
"""
[docs]
@staticmethod
def run(cli: "bittensor.cli"):
r"""Reveal weights for a specific subnet."""
try:
subtensor: "bittensor.subtensor" = bittensor.subtensor(
config=cli.config, log_verbose=False
)
RevealWeightCommand._run(cli, subtensor)
finally:
if "subtensor" in locals():
subtensor.close()
bittensor.logging.debug("closing subtensor connection")
[docs]
@staticmethod
def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
r"""Reveal weights for a specific subnet."""
wallet = bittensor.wallet(config=cli.config)
# Get values if not set.
if not cli.config.is_set("netuid"):
cli.config.netuid = int(Prompt.ask("Enter netuid"))
if not cli.config.is_set("uids"):
cli.config.uids = Prompt.ask("Enter UIDs (comma-separated)")
if not cli.config.is_set("weights"):
cli.config.weights = Prompt.ask("Enter weights (comma-separated)")
if not cli.config.is_set("salt"):
cli.config.salt = Prompt.ask("Enter salt (comma-separated)")
# Parse from string
netuid = cli.config.netuid
version = bittensor.__version_as_int__
uids = np.array(
[int(x) for x in re.split(r"[ ,]+", cli.config.uids)],
dtype=np.int64,
)
weights = np.array(
[float(x) for x in re.split(r"[ ,]+", cli.config.weights)],
dtype=np.float32,
)
salt = np.array(
[int(x) for x in re.split(r"[ ,]+", cli.config.salt)],
dtype=np.int64,
)
weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit(
uids=uids, weights=weights
)
# Run the reveal weights operation.
success, message = subtensor.reveal_weights(
wallet=wallet,
netuid=netuid,
uids=weight_uids,
weights=weight_vals,
salt=salt,
version_key=version,
wait_for_inclusion=cli.config.wait_for_inclusion,
wait_for_finalization=cli.config.wait_for_finalization,
prompt=cli.config.prompt,
)
if success:
bittensor.__console__.print("Weights revealed successfully")
else:
bittensor.__console__.print(f"Failed to reveal weights: {message}")
[docs]
@staticmethod
def add_args(parser: argparse.ArgumentParser):
parser = parser.add_parser(
"reveal", help="""Reveal weights for a specific subnet."""
)
parser.add_argument("--netuid", dest="netuid", type=int, required=False)
parser.add_argument("--uids", dest="uids", type=str, required=False)
parser.add_argument("--weights", dest="weights", type=str, required=False)
parser.add_argument("--salt", dest="salt", type=str, required=False)
parser.add_argument(
"--wait-for-inclusion",
dest="wait_for_inclusion",
action="store_true",
default=False,
)
parser.add_argument(
"--wait-for-finalization",
dest="wait_for_finalization",
action="store_true",
default=True,
)
parser.add_argument(
"--prompt",
dest="prompt",
action="store_true",
default=False,
)
bittensor.wallet.add_args(parser)
bittensor.subtensor.add_args(parser)
[docs]
@staticmethod
def check_config(config: "bittensor.config"):
if not config.is_set("wallet.name") and not config.no_prompt:
wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name)
config.wallet.name = str(wallet_name)
if not config.is_set("wallet.hotkey") and not config.no_prompt:
hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey)
config.wallet.hotkey = str(hotkey)