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

Updates "btcli w set-identity" #146

Open
wants to merge 7 commits into
base: staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
90 changes: 76 additions & 14 deletions bittensor_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1155,12 +1155,14 @@ def wallet_overview(
),
sort_by: Optional[str] = typer.Option(
None,
"--sort-by", "--sort_by",
"--sort-by",
"--sort_by",
help="Sort the hotkeys by the specified column title. For example: name, uid, axon.",
),
sort_order: Optional[str] = typer.Option(
None,
"--sort-order", "--sort_order",
"--sort-order",
"--sort_order",
help="Sort the hotkeys in the specified order (ascending/asc or descending/desc/reverse).",
),
include_hotkeys: str = typer.Option(
Expand Down Expand Up @@ -2007,44 +2009,46 @@ def wallet_set_id(
"--display-name",
"--display",
help="The display name for the identity.",
prompt=True,
),
legal_name: str = typer.Option(
"",
"--legal-name",
"--legal",
help="The legal name for the identity.",
prompt=True,
),
web_url: str = typer.Option(
"", "--web-url", "--web", help="The web URL for the identity.", prompt=True
"",
"--web-url",
"--web",
help="The web URL for the identity.",
),
riot_handle: str = typer.Option(
"",
"--riot-handle",
"--riot",
help="The Riot handle for the identity.",
prompt=True,
),
email: str = typer.Option(
"", help="The email address for the identity.", prompt=True
"",
help="The email address for the identity.",
),
pgp_fingerprint: str = typer.Option(
"",
"--pgp-fingerprint",
"--pgp",
help="The PGP fingerprint for the identity.",
prompt=True,
),
image_url: str = typer.Option(
"",
"--image-url",
"--image",
help="The image URL for the identity.",
prompt=True,
),
info_: str = typer.Option(
"", "--info", "-i", help="The info for the identity.", prompt=True
"",
"--info",
"-i",
help="The info for the identity.",
),
twitter_url: str = typer.Option(
"",
Expand All @@ -2053,12 +2057,16 @@ def wallet_set_id(
"--twitter-url",
"--twitter",
help="The 𝕏 (Twitter) URL for the identity.",
prompt=True,
),
validator_id: bool = typer.Option(
validator_id: Optional[bool] = typer.Option(
None,
"--validator/--not-validator",
help="Are you updating a validator hotkey identity?",
prompt=True,
),
subnet_netuid: Optional[int] = typer.Option(
None,
"--netuid",
help="Netuid if you are updating identity of a subnet owner"
),
quiet: bool = Options.quiet,
verbose: bool = Options.verbose,
Expand All @@ -2079,7 +2087,7 @@ def wallet_set_id(

[green]$[/green] btcli wallet set_identity

[bold]Note[/bold]: This command should only be used if the user is willing to incur the 1 TAO transaction fee associated with setting an identity on the blockchain. It is a high-level command that makes changes to the blockchain state and should not be used programmatically as part of other scripts or applications.
[bold]Note[/bold]: This command should only be used if the user is willing to incur the a recycle fee associated with setting an identity on the blockchain. It is a high-level command that makes changes to the blockchain state and should not be used programmatically as part of other scripts or applications.
"""
self.verbosity_handler(quiet, verbose)
wallet = self.wallet_ask(
Expand All @@ -2089,6 +2097,59 @@ def wallet_set_id(
ask_for=[WO.HOTKEY, WO.NAME],
validate=WV.WALLET_AND_HOTKEY,
)

if not any(
[
display_name,
legal_name,
web_url,
riot_handle,
email,
pgp_fingerprint,
image_url,
info_,
twitter_url,
]
):
console.print("[yellow]All fields are optional. Press Enter to skip a field.[/yellow]")

display_name = display_name or typer.prompt(
"Display name", default="", show_default=False
)
legal_name = legal_name or typer.prompt(
"Legal name", default="", show_default=False
)
web_url = web_url or typer.prompt(
"Web URL", default="", show_default=False
)
riot_handle = riot_handle or typer.prompt(
"Riot handle", default="", show_default=False
)
email = email or typer.prompt(
"Email address", default="", show_default=False
)
pgp_fingerprint = pgp_fingerprint or typer.prompt(
"PGP fingerprint (Eg: A1B2 C3D4 E5F6 7890 1234 5678 9ABC DEF0 1234 5678)",
default="",
show_default=False,
)
image_url = image_url or typer.prompt(
"Image URL", default="", show_default=False
)
info_ = info_ or typer.prompt("Enter info", default="", show_default=False)
twitter_url = twitter_url or typer.prompt(
"Twitter (𝕏) URL", default="", show_default=False
)

Comment on lines +2101 to +2143
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should actually be size-checking here as well, but let's leave that as a TODO feature for next time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this in #151

validator_id = validator_id or Confirm.ask(
"Are you updating a [bold blue]validator hotkey[/bold blue] identity or a [bold blue]subnet owner[/bold blue] identity?\n"
"Enter [bold green]Y[/bold green] for [bold]validator hotkey[/bold] or [bold red]N[/bold red] for [bold]subnet owner[/bold]",
show_choices=True,
)

if validator_id is False:
subnet_netuid = IntPrompt.ask("Enter the netuid of the subnet you own")

return self._run_command(
wallets.set_id(
wallet,
Expand All @@ -2104,6 +2165,7 @@ def wallet_set_id(
info_,
validator_id,
prompt,
subnet_netuid,
)
)

Expand Down
19 changes: 19 additions & 0 deletions bittensor_cli/src/bittensor/subtensor_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,25 @@ async def does_hotkey_exist(
)
return return_val

async def get_hotkey_owner(
self, hotkey_ss58: str, block_hash: str
) -> Optional[str]:
hk_owner_query = await self.substrate.query(
module="SubtensorModule",
storage_function="Owner",
params=[hotkey_ss58],
block_hash=block_hash,
)
val = decode_account_id(hk_owner_query[0])
if val:
exists = await self.does_hotkey_exist(
hotkey_ss58, block_hash=block_hash
)
else:
exists = False
hotkey_owner = val if exists else None
return hotkey_owner

async def sign_and_send_extrinsic(
self,
call: GenericCall,
Expand Down
2 changes: 1 addition & 1 deletion bittensor_cli/src/commands/root.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ async def get_stake_for_coldkey_and_hotkey(
f"\n[bold blue]Current stake[/bold blue]: [blue]{my_prev_delegated_stake}[/blue]\n"
f"[bold white]Do you want to {delegate_string}:[/bold white]\n"
f" [bold red]amount[/bold red]: [red]{staking_balance}\n[/red]"
f" [bold yellow]to hotkey[/bold yellow]: [yellow]{delegate_ss58}\n[/yellow]"
f" [bold yellow]{'to' if delegate_string == 'delegate' else 'from'} hotkey[/bold yellow]: [yellow]{delegate_ss58}\n[/yellow]"
f" [bold green]hotkey owner[/bold green]: [green]{delegate_owner}[/green]"
):
return False
Expand Down
27 changes: 4 additions & 23 deletions bittensor_cli/src/commands/stake/stake.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@


from bittensor_cli.src.bittensor.balances import Balance
from bittensor_cli.src.bittensor.chain_data import decode_account_id
from bittensor_cli.src.bittensor.utils import (
console,
create_table,
Expand Down Expand Up @@ -75,24 +74,6 @@ async def _check_threshold_amount(
return True, min_req_stake


async def _get_hotkey_owner(
subtensor: "SubtensorInterface", hotkey_ss58: str, block_hash: str
) -> Optional[str]:
hk_owner_query = await subtensor.substrate.query(
module="SubtensorModule",
storage_function="Owner",
params=[hotkey_ss58],
block_hash=block_hash,
)
val = decode_account_id(hk_owner_query[0])
if val:
exists = await subtensor.does_hotkey_exist(val, block_hash=block_hash)
else:
exists = False
hotkey_owner = val if exists else None
return hotkey_owner


async def add_stake_extrinsic(
subtensor: "SubtensorInterface",
wallet: Wallet,
Expand Down Expand Up @@ -141,8 +122,8 @@ async def add_stake_extrinsic(
block_hash = await subtensor.substrate.get_chain_head()
# Get hotkey owner
print_verbose("Confirming hotkey owner", status)
hotkey_owner = await _get_hotkey_owner(
subtensor, hotkey_ss58=hotkey_ss58, block_hash=block_hash
hotkey_owner = await subtensor.get_hotkey_owner(
hotkey_ss58=hotkey_ss58, block_hash=block_hash
)
own_hotkey = wallet.coldkeypub.ss58_address == hotkey_owner
if not own_hotkey:
Expand Down Expand Up @@ -532,7 +513,7 @@ async def unstake_extrinsic(
hotkey_ss58=hotkey_ss58,
block_hash=block_hash,
),
_get_hotkey_owner(subtensor, hotkey_ss58, block_hash),
subtensor.get_hotkey_owner(hotkey_ss58, block_hash),
)

own_hotkey: bool = wallet.coldkeypub.ss58_address == hotkey_owner
Expand Down Expand Up @@ -702,7 +683,7 @@ async def unstake_multiple_extrinsic(
]
)
hotkey_owners_ = asyncio.gather(
*[_get_hotkey_owner(subtensor, h, block_hash) for h in hotkey_ss58s]
*[subtensor.get_hotkey_owner(h, block_hash) for h in hotkey_ss58s]
)

old_balance, old_stakes, hotkey_owners, threshold = await asyncio.gather(
Expand Down
36 changes: 36 additions & 0 deletions bittensor_cli/src/commands/wallets.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
NeuronInfoLite,
StakeInfo,
custom_rpc_type_registry,
decode_account_id,
)
from bittensor_cli.src.bittensor.extrinsics.registration import (
run_faucet_extrinsic,
swap_hotkey_extrinsic,
is_hotkey_registered,
)
from bittensor_cli.src.bittensor.extrinsics.transfer import transfer_extrinsic
from bittensor_cli.src.bittensor.networking import int_to_ip
Expand Down Expand Up @@ -1484,10 +1486,13 @@ async def set_id(
info_: str,
validator_id: bool,
prompt: bool,
subnet_netuid: int,
):
"""Create a new or update existing identity on-chain."""

try:
if pgp_fingerprint.startswith("0x"):
pgp_fingerprint = pgp_fingerprint[2:] # Strip '0x'
pgp_fingerprint_encoded = binascii.unhexlify(pgp_fingerprint.replace(" ", ""))
except Exception as e:
print_error(f"The PGP is not in the correct format: {e}")
Expand Down Expand Up @@ -1550,6 +1555,37 @@ async def set_id(
console.print(":cross_mark: Aborted!")
raise typer.Exit()

if validator_id:
block_hash = await subtensor.substrate.get_chain_head()

is_registered_on_root, hotkey_owner = await asyncio.gather(
is_hotkey_registered(
subtensor, netuid=0, hotkey_ss58=wallet.hotkey.ss58_address
),
subtensor.get_hotkey_owner(
hotkey_ss58=wallet.hotkey.ss58_address, block_hash=block_hash
),
)

if not is_registered_on_root:
print_error("The hotkey is not registered on root. Aborting.")
return False

own_hotkey = wallet.coldkeypub.ss58_address == hotkey_owner
if not own_hotkey:
print_error("The hotkey doesn't belong to the coldkey wallet. Aborting.")
return False
else:
subnet_owner_ = await subtensor.substrate.query(
module="SubtensorModule",
storage_function="SubnetOwner",
params=[subnet_netuid],
)
subnet_owner = decode_account_id(subnet_owner_[0])
if subnet_owner != wallet.coldkeypub.ss58_address:
print_error(f":cross_mark: This wallet doesn't own subnet {subnet_netuid}.")
return False

try:
wallet.unlock_coldkey()
except KeyFileError:
Expand Down
23 changes: 21 additions & 2 deletions tests/e2e_tests/test_wallet_interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,25 @@ def test_wallet_identities(local_chain, wallet_setup):
wallet_path_alice
)

# Register Alice to the root network (0)
# Either root list neurons can set-id or subnet owners
root_register = exec_command_alice(
command="root",
sub_command="register",
extra_args=[
"--wallet-path",
wallet_path_alice,
"--network",
"ws://127.0.0.1:9945",
"--wallet-name",
wallet_alice.name,
"--hotkey",
wallet_alice.hotkey_str,
"--no-prompt",
],
)
assert "✅ Registered" in root_register.stdout

# Define values for Alice's identity
alice_identity = {
"display_name": "Alice",
Expand Down Expand Up @@ -398,7 +417,7 @@ def test_wallet_identities(local_chain, wallet_setup):
alice_identity["info"],
"-x",
alice_identity["twitter"],
"--validator-id",
"--validator",
"--no-prompt",
],
)
Expand Down Expand Up @@ -481,4 +500,4 @@ def test_wallet_identities(local_chain, wallet_setup):

assert "Message signed successfully" in sign_using_coldkey.stdout

print("✅Passed wallet set-id, get-id, sign command")
print("✅ Passed wallet set-id, get-id, sign command")
Loading