Skip to content

Commit

Permalink
Set children multiple command.
Browse files Browse the repository at this point in the history
  • Loading branch information
opendansor committed Jun 26, 2024
1 parent c8bd350 commit d2bc588
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 38 deletions.
3 changes: 3 additions & 0 deletions bittensor/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
WalletCreateCommand,
CommitWeightCommand,
RevealWeightCommand,
SetChildCommand,
SetChildrenCommand,
)

# Create a console instance for CLI display.
Expand Down Expand Up @@ -170,6 +172,7 @@
# "get_children": GetChildrenCommand,
"set_child": SetChildCommand,
# "revoke_child": RemoveChildCommand,
"set_children": SetChildrenCommand,
# "set_children": SetChildrenCommand,
# "revoke_children": RevokeChildrenCommand
},
Expand Down
7 changes: 6 additions & 1 deletion bittensor/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@
}
)

from .stake import StakeCommand, StakeShow
from .stake import (
StakeCommand,
StakeShow,
SetChildCommand,
SetChildrenCommand,
)
from .unstake import UnStakeCommand
from .overview import OverviewCommand
from .register import (
Expand Down
129 changes: 128 additions & 1 deletion bittensor/commands/stake.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import argparse
import os
import sys
import re
from typing import List, Union, Optional, Dict, Tuple

import numpy as np
from rich.prompt import Confirm, Prompt
from rich.table import Table
from tqdm import tqdm
Expand All @@ -31,7 +33,7 @@
get_delegates_details,
DelegatesDetails,
)
from . import defaults
from . import defaults # type: ignore

console = bittensor.__console__

Expand Down Expand Up @@ -694,3 +696,128 @@ def add_args(parser: argparse.ArgumentParser):
)
bittensor.wallet.add_args(parser)
bittensor.subtensor.add_args(parser)


class SetChildrenCommand:
"""
Executes the ``set_children`` command to add children hotkeys on a specified subnet on the Bittensor network.
This command is used to delegate authority to different hotkeys, securing their position and influence on the subnet.
Usage:
Users can specify the amount or 'proportion' to delegate to a child hotkey (either by name or ``SS58`` address),
the user needs to have sufficient authority to make this call, and the sum of proportions cannot be greater than 1.
The command prompts for confirmation before executing the set_children operation.
Example usage::
btcli stake set_children --children <child_hotkey>,<child_hotkey> --hotkey <parent_hotkey> --netuid 1 --proportion 0.3,0.3
Note:
This command is critical for users who wish to delegate children hotkeys among different neurons (hotkeys) on the network.
It allows for a strategic allocation of authority to enhance network participation and influence.
"""

@staticmethod
def run(cli: "bittensor.cli"):
r"""Set children hotkeys."""
try:
subtensor: "bittensor.subtensor" = bittensor.subtensor(
config=cli.config, log_verbose=False
)
SetChildrenCommand._run(cli, subtensor)
finally:
if "subtensor" in locals():
subtensor.close()
bittensor.logging.debug("closing subtensor connection")

@staticmethod
def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
wallet = bittensor.wallet(config=cli.config)

# TODO: Print table here showing all child hotkeys -> parent hotkeys and proportions
total_proportions = 0 # TODO: get total proportions from the table

# 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("children"):
cli.config.children = Prompt.ask("Enter children hotkey (ss58) as comma-separated values")

if not cli.config.is_set("hotkey"):
cli.config.hotkey = Prompt.ask("Enter parent hotkey (ss58)")

if not cli.config.is_set("proportions"):
cli.config.proportions = Prompt.ask("Enter proportions for children as comma-separated values")

# Parse from strings
netuid = cli.config.netuid

proportions = np.array(
[float(x) for x in re.split(r"[ ,]+", cli.config.proportions)], dtype=np.float32
)
children = np.array(
[str(x) for x in re.split(r"[ ,]+", cli.config.children)], dtype=str
)

success, message = subtensor.set_children_multiple(
wallet=wallet,
netuid=netuid,
children=children,
hotkey=cli.config.hotkey,
proportions=proportions,
wait_for_inclusion=cli.config.wait_for_inclusion,
wait_for_finalization=cli.config.wait_for_finalization,
prompt=cli.config.prompt,
)

# Result
if success:
console.print(
":white_heavy_check_mark: [green]Set children hotkeys.[/green]"
)
else:
console.print(
f":cross_mark:[red] Unable to set children hotkeys.[/red] {message}"
)

@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)

@staticmethod
def add_args(parser: argparse.ArgumentParser):
parser = parser.add_parser(
"set_child", help="""Set a child hotkey."""
)
parser.add_argument("--netuid", dest="netuid", type=int, required=False)
parser.add_argument("--children", dest="children", type=str, required=False)
parser.add_argument("--hotkey", dest="hotkey", type=str, required=False)
parser.add_argument("--proportions", dest="proportions", 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)
4 changes: 2 additions & 2 deletions bittensor/commands/weights.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
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(
weight_uids, weight_vals = weight_utils.convert_values_and_ids_for_emit(
uids=uids, weights=weights
)

Expand Down Expand Up @@ -227,7 +227,7 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
[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(
weight_uids, weight_vals = weight_utils.convert_values_and_ids_for_emit(
uids=uids, weights=weights
)

Expand Down
2 changes: 1 addition & 1 deletion bittensor/extrinsics/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def set_root_weights_extrinsic(
)
):
try:
weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit(
weight_uids, weight_vals = weight_utils.convert_values_and_ids_for_emit(
netuids, weights
)
success, error_message = subtensor._do_set_root_weights(
Expand Down
2 changes: 1 addition & 1 deletion bittensor/extrinsics/set_weights.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def set_weights_extrinsic(
weights = np.array(weights, dtype=np.float32)

# Reformat and normalize.
weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit(
weight_uids, weight_vals = weight_utils.convert_values_and_ids_for_emit(
uids, weights
)

Expand Down
123 changes: 120 additions & 3 deletions bittensor/extrinsics/staking.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
import bittensor
from rich.prompt import Confirm
from time import sleep
import numpy as np
from typing import List, Union, Optional, Tuple
from numpy.typing import NDArray

from bittensor.utils import weight_utils
from bittensor.utils.balance import Balance
Expand Down Expand Up @@ -583,15 +585,19 @@ def do_set_child_singular_extrinsic(
)
):
try:
uid_val, proportion_val = weight_utils.convert_weights_and_uids_for_emit(
netuid, proportion
# prepare values for emmit
proportion = np.array([proportion], dtype=np.float32)
netuids = np.full(proportion.shape, netuid, dtype=np.int64)

uid_val, proportion_val = weight_utils.convert_values_and_ids_for_emit(
netuids, proportion
)

success, error_message = subtensor._do_set_child_singular(
wallet=wallet,
hotkey=hotkey,
child=child,
netuid=uid_val[0],
netuid=netuid,
proportion=proportion_val[0],
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
Expand Down Expand Up @@ -629,3 +635,114 @@ def do_set_child_singular_extrinsic(
prefix="Set child hotkey", suffix="<red>Failed: </red>" + str(e)
)
return False


def do_set_children_multiple_extrinsic(
subtensor: "bittensor.subtensor",
wallet: "bittensor.wallet",
hotkey: str,
children: Union[NDArray[str], list],
netuid: int,
proportions: Union[NDArray[np.float32], list],
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
prompt: bool = False,
) -> bool:
r"""
Sets child hotkey with a proportion assigned from the parent.
Args:
wallet (bittensor.wallet):
Bittensor wallet object.
hotkey (str):
Parent hotkey.
children (np.ndarray):
Children hotkeys.
netuid (int):
Unique identifier of for the subnet.
proportions (np.ndarray):
Proportions assigned to children hotkeys.
wait_for_inclusion (bool):
If set, waits for the extrinsic to enter a block before returning ``true``, or returns ``false`` if the extrinsic fails to enter the block within the timeout.
wait_for_finalization (bool):
If set, waits for the extrinsic to be finalized on the chain before returning ``true``, or returns ``false`` if the extrinsic fails to be finalized within the timeout.
prompt (bool):
If ``true``, the call waits for confirmation from the user before proceeding.
Returns:
success (bool):
Flag is ``true`` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is ``true``.
Raises:
bittensor.errors.ChildHotkeyError:
If the extrinsic fails to be finalized or included in the block.
bittensor.errors.NotRegisteredError:
If the hotkey is not registered in any subnets.
"""
# Ask before moving on.
if prompt:
if not Confirm.ask(
"Do you want to add children hotkeys:\n[bold white] children: {}\n proportions: {}[/bold white ]?".format(
children, proportions
)
):
return False

with bittensor.__console__.status(
":satellite: Setting children hotkeys on [white]{}[/white] ...".format(
subtensor.network
)
):
try:
# prepare values for emmit
if isinstance(proportions, np.ndarray):
uids = np.full(proportions.shape, netuid, dtype=np.int64)
else:
uids = [netuid] * len(proportions)

uid_val, proportions_val = weight_utils.convert_values_and_ids_for_emit(
uids, proportions
)

children_with_proportions = list(zip(children, proportions_val))

success, error_message = subtensor._do_set_children_multiple(
wallet=wallet,
hotkey=hotkey,
children_with_propotions=children_with_proportions,
netuid=uid_val[0],
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

bittensor.__console__.print(success, error_message)

if not wait_for_finalization and not wait_for_inclusion:
return True

if success is True:
bittensor.__console__.print(
":white_heavy_check_mark: [green]Finalized[/green]"
)
bittensor.logging.success(
prefix="Set children hotkeys",
suffix="<green>Finalized: </green>" + str(success),
)
return True
else:
bittensor.__console__.print(
f":cross_mark: [red]Failed[/red]: {error_message}"
)
bittensor.logging.warning(
prefix="Set children hotkeys",
suffix="<red>Failed: </red>" + str(error_message),
)
return False

except Exception as e:
bittensor.__console__.print(
":cross_mark: [red]Failed[/red]: error:{}".format(e)
)
bittensor.logging.warning(
prefix="Set children hotkeys", suffix="<red>Failed: </red>" + str(e)
)
return False
Loading

0 comments on commit d2bc588

Please sign in to comment.