Skip to content

Commit

Permalink
Add child singular cli done
Browse files Browse the repository at this point in the history
  • Loading branch information
opendansor committed Jun 26, 2024
1 parent 6f0b926 commit 03b7c25
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 179 deletions.
252 changes: 76 additions & 176 deletions bittensor/commands/stake.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,13 +191,13 @@ def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
# Ask to stake
if not config.no_prompt:
if not Confirm.ask(
f"Do you want to stake to the following keys from {wallet.name}:\n"
+ "".join(
[
f" [bold white]- {hotkey[0] + ':' if hotkey[0] else ''}{hotkey[1]}: {f'{amount} {bittensor.__tao_symbol__}' if amount else 'All'}[/bold white]\n"
for hotkey, amount in zip(final_hotkeys, final_amounts)
]
)
f"Do you want to stake to the following keys from {wallet.name}:\n"
+ "".join(
[
f" [bold white]- {hotkey[0] + ':' if hotkey[0] else ''}{hotkey[1]}: {f'{amount} {bittensor.__tao_symbol__}' if amount else 'All'}[/bold white]\n"
for hotkey, amount in zip(final_hotkeys, final_amounts)
]
)
):
return None

Expand Down Expand Up @@ -582,7 +582,7 @@ class SetChildCommand:
Example usage::
btcli stake set_child --child <child_id> --hotkey <child_hotkey> --wallet.name <my_wallet> --wallet.hotkey <my_hotkey>
btcli stake set_child --child <child_hotkey> --hotkey <parent_hotkey> --netuid 1 --proportion 0.3
Note:
This command is critical for users who wish to delegate child hotkeys among different neurons (hotkeys) on the network.
Expand All @@ -591,7 +591,7 @@ class SetChildCommand:

@staticmethod
def run(cli: "bittensor.cli"):
r"""Show all stake accounts."""
r"""Set child hotkey."""
try:
subtensor: "bittensor.subtensor" = bittensor.subtensor(
config=cli.config, log_verbose=False
Expand All @@ -604,193 +604,93 @@ def run(cli: "bittensor.cli"):

@staticmethod
def _run(cli: "bittensor.cli", subtensor: "bittensor.subtensor"):
r"""Show all child hotkeys accounts."""
if cli.config.get("all", d=False) == True:
wallets = _get_coldkey_wallets_for_path(cli.config.wallet.path)
else:
wallets = [bittensor.wallet(config=cli.config)]
registered_delegate_info: Optional[Dict[str, DelegatesDetails]] = (
get_delegates_details(url=bittensor.__delegates_details_url__)
)
wallet = bittensor.wallet(config=cli.config)

def get_stake_accounts(
wallet, subtensor
) -> Dict[str, Dict[str, Union[str, Balance]]]:
"""Get stake account details for the given wallet.
# TODO: Print table here showing all child hotkeys -> parent hotkeys and proportions
total_proportions = 0 # TODO: get total proportions from the table

Args:
wallet: The wallet object to fetch the stake account details for.
# Get values if not set.
if not cli.config.is_set("netuid"):
cli.config.netuid = int(Prompt.ask("Enter netuid"))

Returns:
A dictionary mapping SS58 addresses to their respective stake account details.
"""
if not cli.config.is_set("child"):
cli.config.child = Prompt.ask("Enter child hotkey (ss58)")

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

# Get this wallet's coldkey balance.
cold_balance = subtensor.get_balance(wallet.coldkeypub.ss58_address)
if not cli.config.is_set("proportion"):
cli.config.proportion = Prompt.ask("Enter proportion")

# Populate the stake accounts with local hotkeys data.
wallet_stake_accounts.update(get_stakes_from_hotkeys(subtensor, wallet))

# Populate the stake accounts with delegations data.
wallet_stake_accounts.update(get_stakes_from_delegates(subtensor, wallet))

return {
"name": wallet.name,
"balance": cold_balance,
"accounts": wallet_stake_accounts,
}
# Parse from strings
netuid = cli.config.netuid

def get_stakes_from_hotkeys(
subtensor, wallet
) -> Dict[str, Dict[str, Union[str, Balance]]]:
"""Fetch stakes from hotkeys for the provided wallet.
Args:
wallet: The wallet object to fetch the stakes for.
Returns:
A dictionary of stakes related to hotkeys.
"""
hotkeys = get_hotkey_wallets_for_wallet(wallet)
stakes = {}
for hot in hotkeys:
emission = sum(
[
n.emission
for n in subtensor.get_all_neurons_for_pubkey(
hot.hotkey.ss58_address
)
]
)
hotkey_stake = subtensor.get_stake_for_coldkey_and_hotkey(
hotkey_ss58=hot.hotkey.ss58_address,
coldkey_ss58=wallet.coldkeypub.ss58_address,
try:
proportion = float(cli.config.proportion)
except ValueError:
console.print(
":cross_mark:[red] Invalid proportion amount[/red] [bold white]{}[/bold white]".format(
cli.config.proportion
)
stakes[hot.hotkey.ss58_address] = {
"name": hot.hotkey_str,
"stake": hotkey_stake,
"rate": emission,
}
return stakes

def get_stakes_from_delegates(
subtensor, wallet
) -> Dict[str, Dict[str, Union[str, Balance]]]:
"""Fetch stakes from delegates for the provided wallet.
Args:
wallet: The wallet object to fetch the stakes for.
Returns:
A dictionary of stakes related to delegates.
"""
delegates = subtensor.get_delegated(
coldkey_ss58=wallet.coldkeypub.ss58_address
)
stakes = {}
for dele, staked in delegates:
for nom in dele.nominators:
if nom[0] == wallet.coldkeypub.ss58_address:
delegate_name = (
registered_delegate_info[dele.hotkey_ss58].name
if dele.hotkey_ss58 in registered_delegate_info
else dele.hotkey_ss58
)
stakes[dele.hotkey_ss58] = {
"name": delegate_name,
"stake": nom[1],
"rate": dele.total_daily_return.tao
* (nom[1] / dele.total_stake.tao),
}
return stakes

def get_all_wallet_accounts(
wallets,
subtensor,
) -> List[Dict[str, Dict[str, Union[str, Balance]]]]:
"""Fetch stake accounts for all provided wallets using a ThreadPool.
Args:
wallets: List of wallets to fetch the stake accounts for.
Returns:
A list of dictionaries, each dictionary containing stake account details for each wallet.
"""

accounts = []
# Create a progress bar using tqdm
with tqdm(total=len(wallets), desc="Fetching accounts", ncols=100) as pbar:
for wallet in wallets:
accounts.append(get_stake_accounts(wallet, subtensor))
pbar.update()
return accounts

accounts = get_all_wallet_accounts(wallets, subtensor)
sys.exit()

total_stake = 0
total_balance = 0
total_rate = 0
for acc in accounts:
total_balance += acc["balance"].tao
for key, value in acc["accounts"].items():
total_stake += value["stake"].tao
total_rate += float(value["rate"])
table = Table(show_footer=True, pad_edge=False, box=None, expand=False)
table.add_column(
"[overline white]Coldkey", footer_style="overline white", style="bold white"
)
table.add_column(
"[overline white]Balance",
"\u03c4{:.5f}".format(total_balance),
footer_style="overline white",
style="green",
)
table.add_column(
"[overline white]Account", footer_style="overline white", style="blue"
)
table.add_column(
"[overline white]Stake",
"\u03c4{:.5f}".format(total_stake),
footer_style="overline white",
style="green",
)
table.add_column(
"[overline white]Rate",
"\u03c4{:.5f}/d".format(total_rate),
footer_style="overline white",
style="green",
success, message = subtensor.set_child_singular(
wallet=wallet,
netuid=netuid,
child=cli.config.child,
hotkey=cli.config.hotkey,
proportion=proportion,
wait_for_inclusion=cli.config.wait_for_inclusion,
wait_for_finalization=cli.config.wait_for_finalization,
prompt=cli.config.prompt,
)
for acc in accounts:
table.add_row(acc["name"], acc["balance"], "", "")
for key, value in acc["accounts"].items():
table.add_row(
"", "", value["name"], value["stake"], str(value["rate"]) + "/d"
)
bittensor.__console__.print(table)

# Result
if success:
console.print(
":white_heavy_check_mark: [green]Set child hotkey.[/green]"
)
else:
console.print(
":cross_mark:[red] Unable to set child hotkey.[/red]"
)

@staticmethod
def check_config(config: "bittensor.config"):
if (
not config.get("all", d=None)
and not config.is_set("wallet.name")
and not config.no_prompt
):
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):
list_parser = parser.add_parser(
"show", help="""List all stake accounts for wallet."""
parser = parser.add_parser(
"set_child", help="""Set a child hotkey."""
)
list_parser.add_argument(
"--all",
parser.add_argument("--netuid", dest="netuid", type=int, required=False)
parser.add_argument("--child", dest="child", type=str, required=False)
parser.add_argument("--hotkey", dest="hotkey", type=str, required=False)
parser.add_argument("--proportion", dest="proportion", type=str, required=False)
parser.add_argument(
"--wait-for-inclusion",
dest="wait_for_inclusion",
action="store_true",
help="""Check all coldkey wallets.""",
default=False,
)

bittensor.wallet.add_args(list_parser)
bittensor.subtensor.add_args(list_parser)
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)
6 changes: 6 additions & 0 deletions bittensor/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ class UnstakeError(ChainTransactionError):
pass


class ChildHotkeyError(ChainTransactionError):
r"""Error raised when a setting a child hotkey transaction fails."""

pass


class IdentityError(ChainTransactionError):
r"""Error raised when an identity transaction fails."""

Expand Down
64 changes: 62 additions & 2 deletions bittensor/extrinsics/staking.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ def add_stake_extrinsic(
)
return False

except bittensor.errors.NotRegisteredError as e:
except bittensor.errors.NotRegisteredError:
bittensor.__console__.print(
":cross_mark: [red]Hotkey: {} is not registered.[/red]".format(
wallet.hotkey_str
Expand Down Expand Up @@ -435,7 +435,7 @@ def add_stake_multiple_extrinsic(
)
continue

except bittensor.errors.NotRegisteredError as e:
except bittensor.errors.NotRegisteredError:
bittensor.__console__.print(
":cross_mark: [red]Hotkey: {} is not registered.[/red]".format(
hotkey_ss58
Expand Down Expand Up @@ -523,3 +523,63 @@ def __do_add_stake_single(
)

return success


def __do_set_child_singular(
subtensor: "bittensor.subtensor",
wallet: "bittensor.wallet",
hotkey_ss58: str,
amount: "bittensor.Balance",
wait_for_inclusion: bool = True,
wait_for_finalization: bool = False,
) -> bool:
r"""
Executes a stake call to the chain using the wallet and the amount specified.
Args:
wallet (bittensor.wallet):
Bittensor wallet object.
hotkey_ss58 (str):
Hotkey to stake to.
amount (bittensor.Balance):
Amount to stake as Bittensor balance object.
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 uncluded in the block. If we did not wait for finalization / inclusion, the response is ``true``.
Raises:
bittensor.errors.StakeError:
If the extrinsic fails to be finalized or included in the block.
bittensor.errors.NotDelegateError:
If the hotkey is not a delegate.
bittensor.errors.NotRegisteredError:
If the hotkey is not registered in any subnets.
"""
# Decrypt keys,
wallet.coldkey

hotkey_owner = subtensor.get_hotkey_owner(hotkey_ss58)
own_hotkey = wallet.coldkeypub.ss58_address == hotkey_owner
if not own_hotkey:
# We are delegating.
# Verify that the hotkey is a delegate.
if not subtensor.is_hotkey_delegate(hotkey_ss58=hotkey_ss58):
raise bittensor.errors.NotDelegateError(
"Hotkey: {} is not a delegate.".format(hotkey_ss58)
)

success = subtensor._do_stake(
wallet=wallet,
hotkey_ss58=hotkey_ss58,
amount=amount,
wait_for_inclusion=wait_for_inclusion,
wait_for_finalization=wait_for_finalization,
)

return success
Loading

0 comments on commit 03b7c25

Please sign in to comment.