|
18 | 18 | import argparse
|
19 | 19 | import os
|
20 | 20 | import sys
|
| 21 | +import re |
21 | 22 | from typing import List, Union, Optional, Dict, Tuple
|
22 | 23 |
|
23 | 24 | from rich.prompt import Confirm, Prompt
|
24 | 25 | from rich.table import Table
|
| 26 | +from rich.console import Console |
25 | 27 | from tqdm import tqdm
|
26 | 28 |
|
27 | 29 | import bittensor
|
|
31 | 33 | get_delegates_details,
|
32 | 34 | DelegatesDetails,
|
33 | 35 | )
|
34 |
| -from . import defaults |
| 36 | +from . import defaults # type: ignore |
| 37 | +from ..utils import wallet_utils |
35 | 38 |
|
36 | 39 | console = bittensor.__console__
|
37 | 40 |
|
@@ -566,3 +569,301 @@ def add_args(parser: argparse.ArgumentParser):
|
566 | 569 |
|
567 | 570 | bittensor.wallet.add_args(list_parser)
|
568 | 571 | bittensor.subtensor.add_args(list_parser)
|
| 572 | + |
| 573 | + |
| 574 | +class SetChildrenCommand: |
| 575 | + """ |
| 576 | + Executes the ``set_children`` command to add children hotkeys on a specified subnet on the Bittensor network. |
| 577 | +
|
| 578 | + This command is used to delegate authority to different hotkeys, securing their position and influence on the subnet. |
| 579 | +
|
| 580 | + Usage: |
| 581 | + Users can specify the amount or 'proportion' to delegate to child hotkeys (``SS58`` address), |
| 582 | + the user needs to have sufficient authority to make this call, and the sum of proportions cannot be greater than 1. |
| 583 | +
|
| 584 | + The command prompts for confirmation before executing the set_children operation. |
| 585 | +
|
| 586 | + Example usage:: |
| 587 | +
|
| 588 | + btcli stake set_children --children <child_hotkey>,<child_hotkey> --hotkey <parent_hotkey> --netuid 1 --proportions 0.3,0.3 |
| 589 | +
|
| 590 | + Note: |
| 591 | + This command is critical for users who wish to delegate children hotkeys among different neurons (hotkeys) on the network. |
| 592 | + It allows for a strategic allocation of authority to enhance network participation and influence. |
| 593 | + """ |
| 594 | + |
| 595 | + @staticmethod |
| 596 | + def run(cli: "bittensor.cli"): |
| 597 | + """Set children hotkeys.""" |
| 598 | + try: |
| 599 | + subtensor: "bittensor.subtensor" = bittensor.subtensor( |
| 600 | + config=cli.config, log_verbose=False |
| 601 | + ) |
| 602 | + SetChildrenCommand._run(cli, subtensor) |
| 603 | + finally: |
| 604 | + if "subtensor" in locals(): |
| 605 | + subtensor.close() |
| 606 | + bittensor.logging.debug("closing subtensor connection") |
| 607 | + |
| 608 | + @staticmethod |
| 609 | + def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"): |
| 610 | + wallet = bittensor.wallet(config=cli.config) |
| 611 | + |
| 612 | + # Get values if not set. |
| 613 | + if not cli.config.is_set("netuid"): |
| 614 | + cli.config.netuid = int(Prompt.ask("Enter netuid")) |
| 615 | + |
| 616 | + if not cli.config.is_set("hotkey"): |
| 617 | + cli.config.hotkey = Prompt.ask("Enter parent hotkey (ss58)") |
| 618 | + |
| 619 | + children = GetChildrenCommand.run(cli) |
| 620 | + |
| 621 | + if not cli.config.is_set("children"): |
| 622 | + cli.config.children = Prompt.ask( |
| 623 | + "Enter children hotkey (ss58) as comma-separated values" |
| 624 | + ) |
| 625 | + |
| 626 | + if not cli.config.is_set("proportions"): |
| 627 | + cli.config.proportions = Prompt.ask( |
| 628 | + "Enter proportions for children as comma-separated values (sum less than 1)" |
| 629 | + ) |
| 630 | + |
| 631 | + # Parse from strings |
| 632 | + netuid = cli.config.netuid |
| 633 | + |
| 634 | + # extract proportions and child addresses from cli input |
| 635 | + proportions = [float(x) for x in re.split(r"[ ,]+", cli.config.proportions)] |
| 636 | + children = [str(x) for x in re.split(r"[ ,]+", cli.config.children)] |
| 637 | + |
| 638 | + # Validate children SS58 addresses |
| 639 | + for child in children: |
| 640 | + if not wallet_utils.is_valid_ss58_address(child): |
| 641 | + console.print(f":cross_mark:[red] Invalid SS58 address: {child}[/red]") |
| 642 | + return |
| 643 | + |
| 644 | + total_proposed = sum(proportions) |
| 645 | + if total_proposed > 1: |
| 646 | + raise ValueError( |
| 647 | + f"Invalid proportion: The sum of all proportions cannot be greater than 1. Proposed sum of proportions is {total_proposed}." |
| 648 | + ) |
| 649 | + |
| 650 | + children_with_proportions = list(zip(proportions, children)) |
| 651 | + |
| 652 | + success, message = subtensor.set_children( |
| 653 | + wallet=wallet, |
| 654 | + netuid=netuid, |
| 655 | + hotkey=cli.config.hotkey, |
| 656 | + children_with_proportions=children_with_proportions, |
| 657 | + wait_for_inclusion=cli.config.wait_for_inclusion, |
| 658 | + wait_for_finalization=cli.config.wait_for_finalization, |
| 659 | + prompt=cli.config.prompt, |
| 660 | + ) |
| 661 | + |
| 662 | + # Result |
| 663 | + if success: |
| 664 | + console.print( |
| 665 | + ":white_heavy_check_mark: [green]Set children hotkeys.[/green]" |
| 666 | + ) |
| 667 | + else: |
| 668 | + console.print( |
| 669 | + f":cross_mark:[red] Unable to set children hotkeys.[/red] {message}" |
| 670 | + ) |
| 671 | + |
| 672 | + @staticmethod |
| 673 | + def check_config(config: "bittensor.config"): |
| 674 | + if not config.is_set("wallet.name") and not config.no_prompt: |
| 675 | + wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) |
| 676 | + config.wallet.name = str(wallet_name) |
| 677 | + if not config.is_set("wallet.hotkey") and not config.no_prompt: |
| 678 | + hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) |
| 679 | + config.wallet.hotkey = str(hotkey) |
| 680 | + |
| 681 | + @staticmethod |
| 682 | + def add_args(parser: argparse.ArgumentParser): |
| 683 | + set_children_parser = parser.add_parser( |
| 684 | + "set_children", help="""Set multiple children hotkeys.""" |
| 685 | + ) |
| 686 | + set_children_parser.add_argument( |
| 687 | + "--netuid", dest="netuid", type=int, required=False |
| 688 | + ) |
| 689 | + set_children_parser.add_argument( |
| 690 | + "--children", dest="children", type=str, required=False |
| 691 | + ) |
| 692 | + set_children_parser.add_argument( |
| 693 | + "--hotkey", dest="hotkey", type=str, required=False |
| 694 | + ) |
| 695 | + set_children_parser.add_argument( |
| 696 | + "--proportions", dest="proportions", type=str, required=False |
| 697 | + ) |
| 698 | + set_children_parser.add_argument( |
| 699 | + "--wait_for_inclusion", |
| 700 | + dest="wait_for_inclusion", |
| 701 | + action="store_true", |
| 702 | + default=False, |
| 703 | + help="""Wait for the transaction to be included in a block.""", |
| 704 | + ) |
| 705 | + set_children_parser.add_argument( |
| 706 | + "--wait_for_finalization", |
| 707 | + dest="wait_for_finalization", |
| 708 | + action="store_true", |
| 709 | + default=True, |
| 710 | + help="""Wait for the transaction to be finalized.""", |
| 711 | + ) |
| 712 | + set_children_parser.add_argument( |
| 713 | + "--prompt", |
| 714 | + dest="prompt", |
| 715 | + action="store_true", |
| 716 | + default=False, |
| 717 | + help="""Prompt for confirmation before proceeding.""", |
| 718 | + ) |
| 719 | + bittensor.wallet.add_args(set_children_parser) |
| 720 | + bittensor.subtensor.add_args(set_children_parser) |
| 721 | + |
| 722 | + |
| 723 | +class GetChildrenCommand: |
| 724 | + """ |
| 725 | + Executes the ``get_children_info`` command to get all child hotkeys on a specified subnet on the Bittensor network. |
| 726 | +
|
| 727 | + This command is used to view delegated authority to different hotkeys on the subnet. |
| 728 | +
|
| 729 | + Usage: |
| 730 | + Users can specify the subnet and see the children and the proportion that is given to them. |
| 731 | +
|
| 732 | + The command compiles a table showing: |
| 733 | +
|
| 734 | + - ChildHotkey: The hotkey associated with the child. |
| 735 | + - ParentHotKey: The hotkey associated with the parent. |
| 736 | + - Proportion: The proportion that is assigned to them. |
| 737 | + - Expiration: The expiration of the hotkey. |
| 738 | +
|
| 739 | + Example usage:: |
| 740 | +
|
| 741 | + btcli stake get_children --netuid 1 |
| 742 | +
|
| 743 | + Note: |
| 744 | + This command is for users who wish to see child hotkeys among different neurons (hotkeys) on the network. |
| 745 | + """ |
| 746 | + |
| 747 | + @staticmethod |
| 748 | + def run(cli: "bittensor.cli"): |
| 749 | + """Get children hotkeys.""" |
| 750 | + try: |
| 751 | + subtensor: "bittensor.subtensor" = bittensor.subtensor( |
| 752 | + config=cli.config, log_verbose=False |
| 753 | + ) |
| 754 | + return GetChildrenCommand._run(cli, subtensor) |
| 755 | + finally: |
| 756 | + if "subtensor" in locals(): |
| 757 | + subtensor.close() |
| 758 | + bittensor.logging.debug("closing subtensor connection") |
| 759 | + |
| 760 | + @staticmethod |
| 761 | + def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"): |
| 762 | + # Get values if not set. |
| 763 | + if not cli.config.is_set("netuid"): |
| 764 | + cli.config.netuid = int(Prompt.ask("Enter netuid")) |
| 765 | + |
| 766 | + # Get values if not set. |
| 767 | + if not cli.config.is_set("hotkey"): |
| 768 | + cli.config.netuid = Prompt.ask("Enter hotkey") |
| 769 | + |
| 770 | + # Parse from strings |
| 771 | + netuid = cli.config.netuid |
| 772 | + hotkey = cli.config.hotkey |
| 773 | + |
| 774 | + children = subtensor.get_children(hotkey, netuid) |
| 775 | + |
| 776 | + GetChildrenCommand.render_table(subtensor, hotkey, children, netuid) |
| 777 | + |
| 778 | + return children |
| 779 | + |
| 780 | + @staticmethod |
| 781 | + def check_config(config: "bittensor.config"): |
| 782 | + if not config.is_set("wallet.name") and not config.no_prompt: |
| 783 | + wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) |
| 784 | + config.wallet.name = str(wallet_name) |
| 785 | + if not config.is_set("wallet.hotkey") and not config.no_prompt: |
| 786 | + hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) |
| 787 | + config.wallet.hotkey = str(hotkey) |
| 788 | + |
| 789 | + @staticmethod |
| 790 | + def add_args(parser: argparse.ArgumentParser): |
| 791 | + parser = parser.add_parser( |
| 792 | + "get_children", help="""Get child hotkeys on subnet.""" |
| 793 | + ) |
| 794 | + parser.add_argument("--netuid", dest="netuid", type=int, required=False) |
| 795 | + parser.add_argument("--hotkey", dest="hotkey", type=str, required=False) |
| 796 | + |
| 797 | + bittensor.wallet.add_args(parser) |
| 798 | + bittensor.subtensor.add_args(parser) |
| 799 | + |
| 800 | + @staticmethod |
| 801 | + def render_table( |
| 802 | + subtensor: "bittensor.subtensor", |
| 803 | + hotkey: str, |
| 804 | + children: list[Tuple[int, str]], |
| 805 | + netuid: int, |
| 806 | + ): |
| 807 | + console = Console() |
| 808 | + |
| 809 | + # Initialize Rich table for pretty printing |
| 810 | + table = Table( |
| 811 | + show_header=True, |
| 812 | + header_style="bold magenta", |
| 813 | + border_style="green", |
| 814 | + style="green", |
| 815 | + ) |
| 816 | + |
| 817 | + # Add columns to the table with specific styles |
| 818 | + table.add_column("Index", style="cyan", no_wrap=True, justify="right") |
| 819 | + table.add_column("ChildHotkey", style="cyan", no_wrap=True) |
| 820 | + table.add_column("Proportion", style="cyan", no_wrap=True, justify="right") |
| 821 | + table.add_column("Total Stake", style="cyan", no_wrap=True, justify="right") |
| 822 | + |
| 823 | + if not children: |
| 824 | + console.print(table) |
| 825 | + |
| 826 | + command = f"btcli stake set_children --children <child_hotkey> --hotkey <parent_hotkey> --netuid {netuid} --proportion <float>" |
| 827 | + console.print(f"There are currently no child hotkeys on subnet {netuid}.") |
| 828 | + console.print( |
| 829 | + f"To add a child hotkey you can run the command: [white]{command}[/white]" |
| 830 | + ) |
| 831 | + return |
| 832 | + |
| 833 | + console.print("ParentHotKey:", style="cyan", no_wrap=True) |
| 834 | + console.print(hotkey) |
| 835 | + |
| 836 | + # calculate totals |
| 837 | + total_proportion = 0 |
| 838 | + total_stake = 0 |
| 839 | + |
| 840 | + children_info = [] |
| 841 | + for child in children: |
| 842 | + proportion = child[0] |
| 843 | + child_hotkey = child[1] |
| 844 | + child_stake = subtensor.get_total_stake_for_hotkey( |
| 845 | + ss58_address=child_hotkey |
| 846 | + ) or Balance(0) |
| 847 | + |
| 848 | + # add to totals |
| 849 | + total_proportion += proportion |
| 850 | + total_stake += child_stake |
| 851 | + |
| 852 | + children_info.append((proportion, child_hotkey, child_stake)) |
| 853 | + |
| 854 | + children_info.sort( |
| 855 | + key=lambda x: x[0], reverse=True |
| 856 | + ) # sorting by proportion (highest first) |
| 857 | + |
| 858 | + # add the children info to the table |
| 859 | + for i, (proportion, hotkey, stake) in enumerate(children_info, 1): |
| 860 | + table.add_row( |
| 861 | + str(i), |
| 862 | + hotkey, |
| 863 | + str(proportion), |
| 864 | + str(stake), |
| 865 | + ) |
| 866 | + |
| 867 | + # add totals row |
| 868 | + table.add_row("", "Total", str(total_proportion), str(total_stake), "") |
| 869 | + console.print(table) |
0 commit comments