Skip to content

Commit 63bfd0f

Browse files
authored
Merge pull request #575 from opentensor/feat/thewhaleking/regen-hotkey-pub
New command: `btcli w regen-hotkeypub`
2 parents 14a4e7a + 8d48198 commit 63bfd0f

File tree

15 files changed

+282
-100
lines changed

15 files changed

+282
-100
lines changed

bittensor_cli/cli.py

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
validate_uri,
5959
prompt_for_subnet_identity,
6060
validate_rate_tolerance,
61+
get_hotkey_pub_ss58,
6162
)
6263
from bittensor_cli.src.commands import sudo, wallets, view
6364
from bittensor_cli.src.commands import weights as weights_cmds
@@ -791,6 +792,9 @@ def __init__(self):
791792
self.wallet_app.command(
792793
"regen-hotkey", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
793794
)(self.wallet_regen_hotkey)
795+
self.wallet_app.command(
796+
"regen-hotkeypub", rich_help_panel=HELP_PANELS["WALLET"]["SECURITY"]
797+
)(self.wallet_regen_hotkey_pub)
794798
self.wallet_app.command(
795799
"new-hotkey", rich_help_panel=HELP_PANELS["WALLET"]["MANAGEMENT"]
796800
)(self.wallet_new_hotkey)
@@ -973,6 +977,10 @@ def __init__(self):
973977
"regen_hotkey",
974978
hidden=True,
975979
)(self.wallet_regen_hotkey)
980+
self.wallet_app.command(
981+
"regen_hotkeypub",
982+
hidden=True,
983+
)(self.wallet_regen_hotkey_pub)
976984
self.wallet_app.command(
977985
"new_hotkey",
978986
hidden=True,
@@ -1143,7 +1151,8 @@ async def _run():
11431151
exit_early is True
11441152
): # temporarily to handle multiple run commands in one session
11451153
try:
1146-
await self.subtensor.substrate.close()
1154+
if self.subtensor:
1155+
await self.subtensor.substrate.close()
11471156
raise typer.Exit()
11481157
except Exception as e: # ensures we always exit cleanly
11491158
if not isinstance(e, (typer.Exit, RuntimeError)):
@@ -1726,7 +1735,7 @@ def wallet_ask(
17261735
if return_wallet_and_hotkey:
17271736
valid = utils.is_valid_wallet(wallet)
17281737
if valid[1]:
1729-
return wallet, wallet.hotkey.ss58_address
1738+
return wallet, get_hotkey_pub_ss58(wallet)
17301739
else:
17311740
if wallet_hotkey and is_valid_ss58_address(wallet_hotkey):
17321741
return wallet, wallet_hotkey
@@ -2285,7 +2294,7 @@ def wallet_regen_coldkey_pub(
22852294
22862295
EXAMPLE
22872296
2288-
[green]$[/green] btcli wallet regen_coldkeypub --ss58_address 5DkQ4...
2297+
[green]$[/green] btcli wallet regen-coldkeypub --ss58_address 5DkQ4...
22892298
22902299
[bold]Note[/bold]: This command is particularly useful for users who need to regenerate their coldkeypub, perhaps due to file corruption or loss. You will need either ss58 address or public hex key from your old coldkeypub.txt for the wallet. It is a recovery-focused utility that ensures continued access to your wallet functionalities.
22912300
"""
@@ -2300,7 +2309,7 @@ def wallet_regen_coldkey_pub(
23002309

23012310
if not wallet_name:
23022311
wallet_name = Prompt.ask(
2303-
f"Enter the name of the [{COLORS.G.CK}]new wallet (coldkey)",
2312+
f"Enter the name of the [{COLORS.G.CK}]wallet for the new coldkeypub",
23042313
default=defaults.wallet.name,
23052314
)
23062315
wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
@@ -2317,7 +2326,7 @@ def wallet_regen_coldkey_pub(
23172326
address=ss58_address if ss58_address else public_key_hex
23182327
):
23192328
rich.print("[red]Error: Invalid SS58 address or public key![/red]")
2320-
raise typer.Exit()
2329+
return
23212330
return self._run_command(
23222331
wallets.regen_coldkey_pub(
23232332
wallet, ss58_address, public_key_hex, overwrite, json_output
@@ -2383,6 +2392,68 @@ def wallet_regen_hotkey(
23832392
)
23842393
)
23852394

2395+
def wallet_regen_hotkey_pub(
2396+
self,
2397+
wallet_name: Optional[str] = Options.wallet_name,
2398+
wallet_path: Optional[str] = Options.wallet_path,
2399+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
2400+
public_key_hex: Optional[str] = Options.public_hex_key,
2401+
ss58_address: Optional[str] = Options.ss58_address,
2402+
overwrite: bool = Options.overwrite,
2403+
quiet: bool = Options.quiet,
2404+
verbose: bool = Options.verbose,
2405+
json_output: bool = Options.json_output,
2406+
):
2407+
"""
2408+
Regenerates the public part of a hotkey (hotkeypub.txt) for a wallet.
2409+
2410+
Use this command when you need to move machine for subnet mining. Use the public key or SS58 address from your hotkeypub.txt that you have on another machine to regenerate the hotkeypub.txt on this new machine.
2411+
2412+
USAGE
2413+
2414+
The command requires either a public key in hexadecimal format or an ``SS58`` address from the existing hotkeypub.txt from old machine to regenerate the coldkeypub on the new machine.
2415+
2416+
EXAMPLE
2417+
2418+
[green]$[/green] btcli wallet regen-hotkeypub --ss58_address 5DkQ4...
2419+
2420+
[bold]Note[/bold]: This command is particularly useful for users who need to regenerate their hotkeypub, perhaps due to file corruption or loss. You will need either ss58 address or public hex key from your old hotkeypub.txt for the wallet. It is a recovery-focused utility that ensures continued access to your wallet functionalities.
2421+
"""
2422+
self.verbosity_handler(quiet, verbose, json_output)
2423+
2424+
if not wallet_path:
2425+
wallet_path = Prompt.ask(
2426+
"Enter the path to the wallets directory",
2427+
default=self.config.get("wallet_path") or defaults.wallet.path,
2428+
)
2429+
wallet_path = os.path.expanduser(wallet_path)
2430+
2431+
if not wallet_name:
2432+
wallet_name = Prompt.ask(
2433+
f"Enter the name of the [{COLORS.G.CK}]wallet for the new hotkeypub",
2434+
default=defaults.wallet.name,
2435+
)
2436+
wallet = Wallet(wallet_name, wallet_hotkey, wallet_path)
2437+
2438+
if not ss58_address and not public_key_hex:
2439+
prompt_answer = typer.prompt(
2440+
"Enter the ss58_address or the public key in hex"
2441+
)
2442+
if prompt_answer.startswith("0x"):
2443+
public_key_hex = prompt_answer
2444+
else:
2445+
ss58_address = prompt_answer
2446+
if not utils.is_valid_bittensor_address_or_public_key(
2447+
address=ss58_address if ss58_address else public_key_hex
2448+
):
2449+
rich.print("[red]Error: Invalid SS58 address or public key![/red]")
2450+
return False
2451+
return self._run_command(
2452+
wallets.regen_hotkey_pub(
2453+
wallet, ss58_address, public_key_hex, overwrite, json_output
2454+
)
2455+
)
2456+
23862457
def wallet_new_hotkey(
23872458
self,
23882459
wallet_name: Optional[str] = Options.wallet_name,
@@ -2502,7 +2573,7 @@ def wallet_associate_hotkey(
25022573
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
25032574
validate=WV.WALLET_AND_HOTKEY,
25042575
)
2505-
hotkey_ss58 = wallet.hotkey.ss58_address
2576+
hotkey_ss58 = get_hotkey_pub_ss58(wallet)
25062577
hotkey_display = (
25072578
f"hotkey [blue]{wallet_hotkey}[/blue] "
25082579
f"[{COLORS.GENERAL.HK}]({hotkey_ss58})[/{COLORS.GENERAL.HK}]"
@@ -3518,7 +3589,7 @@ def stake_add(
35183589
ask_for=[WO.NAME, WO.HOTKEY, WO.PATH],
35193590
validate=WV.WALLET_AND_HOTKEY,
35203591
)
3521-
include_hotkeys = wallet.hotkey.ss58_address
3592+
include_hotkeys = get_hotkey_pub_ss58(wallet)
35223593

35233594
elif all_hotkeys or include_hotkeys or exclude_hotkeys:
35243595
wallet = self.wallet_ask(
@@ -3982,7 +4053,7 @@ def stake_move(
39824053
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
39834054
validate=WV.WALLET_AND_HOTKEY,
39844055
)
3985-
destination_hotkey = destination_wallet.hotkey.ss58_address
4056+
destination_hotkey = get_hotkey_pub_ss58(destination_wallet)
39864057
else:
39874058
if is_valid_ss58_address(destination_hotkey):
39884059
destination_hotkey = destination_hotkey
@@ -4021,7 +4092,7 @@ def stake_move(
40214092
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
40224093
validate=WV.WALLET_AND_HOTKEY,
40234094
)
4024-
origin_hotkey = wallet.hotkey.ss58_address
4095+
origin_hotkey = get_hotkey_pub_ss58(wallet)
40254096
else:
40264097
if is_valid_ss58_address(wallet_hotkey):
40274098
origin_hotkey = wallet_hotkey
@@ -4033,7 +4104,7 @@ def stake_move(
40334104
ask_for=[],
40344105
validate=WV.WALLET_AND_HOTKEY,
40354106
)
4036-
origin_hotkey = wallet.hotkey.ss58_address
4107+
origin_hotkey = get_hotkey_pub_ss58(wallet)
40374108

40384109
if not interactive_selection:
40394110
if origin_netuid is None:
@@ -4186,7 +4257,7 @@ def stake_transfer(
41864257
ask_for=[WO.NAME, WO.PATH, WO.HOTKEY],
41874258
validate=WV.WALLET_AND_HOTKEY,
41884259
)
4189-
origin_hotkey = wallet.hotkey.ss58_address
4260+
origin_hotkey = get_hotkey_pub_ss58(wallet)
41904261
else:
41914262
if is_valid_ss58_address(wallet_hotkey):
41924263
origin_hotkey = wallet_hotkey
@@ -4198,7 +4269,7 @@ def stake_transfer(
41984269
ask_for=[],
41994270
validate=WV.WALLET_AND_HOTKEY,
42004271
)
4201-
origin_hotkey = wallet.hotkey.ss58_address
4272+
origin_hotkey = get_hotkey_pub_ss58(wallet)
42024273

42034274
if not interactive_selection:
42044275
if origin_netuid is None:

bittensor_cli/src/bittensor/extrinsics/registration.py

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
print_error,
4040
unlock_key,
4141
hex_to_bytes,
42+
get_hotkey_pub_ss58,
4243
)
4344

4445
if typing.TYPE_CHECKING:
@@ -490,7 +491,7 @@ async def register_extrinsic(
490491

491492
async def get_neuron_for_pubkey_and_subnet():
492493
uid = await subtensor.query(
493-
"SubtensorModule", "Uids", [netuid, wallet.hotkey.ss58_address]
494+
"SubtensorModule", "Uids", [netuid, get_hotkey_pub_ss58(wallet)]
494495
)
495496
if uid is None:
496497
return NeuronInfo.get_null_neuron()
@@ -525,7 +526,7 @@ async def get_neuron_for_pubkey_and_subnet():
525526
if not Confirm.ask(
526527
f"Continue Registration?\n"
527528
f" hotkey [{COLOR_PALETTE.G.HK}]({wallet.hotkey_str})[/{COLOR_PALETTE.G.HK}]:"
528-
f"\t[{COLOR_PALETTE.G.HK}]{wallet.hotkey.ss58_address}[/{COLOR_PALETTE.G.HK}]\n"
529+
f"\t[{COLOR_PALETTE.G.HK}]{get_hotkey_pub_ss58(wallet)}[/{COLOR_PALETTE.G.HK}]\n"
529530
f" coldkey [{COLOR_PALETTE.G.CK}]({wallet.name})[/{COLOR_PALETTE.G.CK}]:"
530531
f"\t[{COLOR_PALETTE.G.CK}]{wallet.coldkeypub.ss58_address}[/{COLOR_PALETTE.G.CK}]\n"
531532
f" network:\t\t[{COLOR_PALETTE.G.LINKS}]{subtensor.network}[/{COLOR_PALETTE.G.LINKS}]\n"
@@ -577,7 +578,7 @@ async def get_neuron_for_pubkey_and_subnet():
577578
if not pow_result:
578579
# might be registered already on this subnet
579580
is_registered = await is_hotkey_registered(
580-
subtensor, netuid=netuid, hotkey_ss58=wallet.hotkey.ss58_address
581+
subtensor, netuid=netuid, hotkey_ss58=get_hotkey_pub_ss58(wallet)
581582
)
582583
if is_registered:
583584
err_console.print(
@@ -598,7 +599,7 @@ async def get_neuron_for_pubkey_and_subnet():
598599
"block_number": pow_result.block_number,
599600
"nonce": pow_result.nonce,
600601
"work": [int(byte_) for byte_ in pow_result.seal],
601-
"hotkey": wallet.hotkey.ss58_address,
602+
"hotkey": get_hotkey_pub_ss58(wallet),
602603
"coldkey": wallet.coldkeypub.ss58_address,
603604
},
604605
)
@@ -639,7 +640,7 @@ async def get_neuron_for_pubkey_and_subnet():
639640
is_registered = await is_hotkey_registered(
640641
subtensor,
641642
netuid=netuid,
642-
hotkey_ss58=wallet.hotkey.ss58_address,
643+
hotkey_ss58=get_hotkey_pub_ss58(wallet),
643644
)
644645
if is_registered:
645646
console.print(
@@ -704,7 +705,7 @@ async def burned_register_extrinsic(
704705
spinner="aesthetic",
705706
) as status:
706707
my_uid = await subtensor.query(
707-
"SubtensorModule", "Uids", [netuid, wallet.hotkey.ss58_address]
708+
"SubtensorModule", "Uids", [netuid, get_hotkey_pub_ss58(wallet)]
708709
)
709710
block_hash = await subtensor.substrate.get_chain_head()
710711

@@ -751,7 +752,7 @@ async def burned_register_extrinsic(
751752
call_function="burned_register",
752753
call_params={
753754
"netuid": netuid,
754-
"hotkey": wallet.hotkey.ss58_address,
755+
"hotkey": get_hotkey_pub_ss58(wallet),
755756
},
756757
)
757758
success, err_msg = await subtensor.sign_and_send_extrinsic(
@@ -773,10 +774,10 @@ async def burned_register_extrinsic(
773774
reuse_block=False,
774775
),
775776
subtensor.get_netuids_for_hotkey(
776-
wallet.hotkey.ss58_address, block_hash=block_hash
777+
get_hotkey_pub_ss58(wallet), block_hash=block_hash
777778
),
778779
subtensor.query(
779-
"SubtensorModule", "Uids", [netuid, wallet.hotkey.ss58_address]
780+
"SubtensorModule", "Uids", [netuid, get_hotkey_pub_ss58(wallet)]
780781
),
781782
)
782783

@@ -1146,7 +1147,7 @@ async def _block_solver(
11461147

11471148
timeout = 0.15 if cuda else 0.15
11481149
while netuid == -1 or not await is_hotkey_registered(
1149-
subtensor, netuid, wallet.hotkey.ss58_address
1150+
subtensor, netuid, get_hotkey_pub_ss58(wallet)
11501151
):
11511152
# Wait until a solver finds a solution
11521153
try:
@@ -1755,37 +1756,39 @@ async def swap_hotkey_extrinsic(
17551756
:return: Success
17561757
"""
17571758
block_hash = await subtensor.substrate.get_chain_head()
1759+
hk_ss58 = get_hotkey_pub_ss58(wallet)
17581760
netuids_registered = await subtensor.get_netuids_for_hotkey(
1759-
wallet.hotkey.ss58_address, block_hash=block_hash
1761+
hk_ss58, block_hash=block_hash
17601762
)
17611763
netuids_registered_new_hotkey = await subtensor.get_netuids_for_hotkey(
1762-
new_wallet.hotkey.ss58_address, block_hash=block_hash
1764+
hk_ss58, block_hash=block_hash
17631765
)
17641766

17651767
if netuid is not None and netuid not in netuids_registered:
17661768
err_console.print(
1767-
f":cross_mark: [red]Failed[/red]: Original hotkey {wallet.hotkey.ss58_address} is not registered on subnet {netuid}"
1769+
f":cross_mark: [red]Failed[/red]: Original hotkey {hk_ss58} is not registered on subnet {netuid}"
17681770
)
17691771
return False
17701772

17711773
elif not len(netuids_registered) > 0:
17721774
err_console.print(
1773-
f"Original hotkey [dark_orange]{wallet.hotkey.ss58_address}[/dark_orange] is not registered on any subnet. "
1775+
f"Original hotkey [dark_orange]{hk_ss58}[/dark_orange] is not registered on any subnet. "
17741776
f"Please register and try again"
17751777
)
17761778
return False
17771779

1780+
new_hk_ss58 = get_hotkey_pub_ss58(new_wallet)
17781781
if netuid is not None:
17791782
if netuid in netuids_registered_new_hotkey:
17801783
err_console.print(
1781-
f":cross_mark: [red]Failed[/red]: New hotkey {new_wallet.hotkey.ss58_address} "
1784+
f":cross_mark: [red]Failed[/red]: New hotkey {new_hk_ss58} "
17821785
f"is already registered on subnet {netuid}"
17831786
)
17841787
return False
17851788
else:
17861789
if len(netuids_registered_new_hotkey) > 0:
17871790
err_console.print(
1788-
f":cross_mark: [red]Failed[/red]: New hotkey {new_wallet.hotkey.ss58_address} "
1791+
f":cross_mark: [red]Failed[/red]: New hotkey {new_hk_ss58} "
17891792
f"is already registered on subnet(s) {netuids_registered_new_hotkey}"
17901793
)
17911794
return False
@@ -1798,28 +1801,28 @@ async def swap_hotkey_extrinsic(
17981801
if netuid is not None:
17991802
confirm_message = (
18001803
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
1801-
f"[dark_orange]{wallet.hotkey.ss58_address} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
1802-
f"[dark_orange]{new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})[/dark_orange] on subnet {netuid}\n"
1804+
f"[dark_orange]{hk_ss58} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
1805+
f"[dark_orange]{new_hk_ss58} ({new_wallet.hotkey_str})[/dark_orange] on subnet {netuid}\n"
18031806
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
18041807
)
18051808
else:
18061809
confirm_message = (
18071810
f"Do you want to swap [dark_orange]{wallet.name}[/dark_orange] hotkey \n\t"
1808-
f"[dark_orange]{wallet.hotkey.ss58_address} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
1809-
f"[dark_orange]{new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})[/dark_orange] on all subnets\n"
1811+
f"[dark_orange]{hk_ss58} ({wallet.hotkey_str})[/dark_orange] with hotkey \n\t"
1812+
f"[dark_orange]{new_hk_ss58} ({new_wallet.hotkey_str})[/dark_orange] on all subnets\n"
18101813
"This operation will cost [bold cyan]1 TAO (recycled)[/bold cyan]"
18111814
)
18121815

18131816
if not Confirm.ask(confirm_message):
18141817
return False
18151818
print_verbose(
1816-
f"Swapping {wallet.name}'s hotkey ({wallet.hotkey.ss58_address} - {wallet.hotkey_str}) with "
1817-
f"{new_wallet.name}'s hotkey ({new_wallet.hotkey.ss58_address} - {new_wallet.hotkey_str})"
1819+
f"Swapping {wallet.name}'s hotkey ({hk_ss58} - {wallet.hotkey_str}) with "
1820+
f"{new_wallet.name}'s hotkey ({new_hk_ss58} - {new_wallet.hotkey_str})"
18181821
)
18191822
with console.status(":satellite: Swapping hotkeys...", spinner="aesthetic"):
18201823
call_params = {
1821-
"hotkey": wallet.hotkey.ss58_address,
1822-
"new_hotkey": new_wallet.hotkey.ss58_address,
1824+
"hotkey": hk_ss58,
1825+
"new_hotkey": new_hk_ss58,
18231826
"netuid": netuid,
18241827
}
18251828

@@ -1832,7 +1835,8 @@ async def swap_hotkey_extrinsic(
18321835

18331836
if success:
18341837
console.print(
1835-
f"Hotkey {wallet.hotkey.ss58_address} ({wallet.hotkey_str}) swapped for new hotkey: {new_wallet.hotkey.ss58_address} ({new_wallet.hotkey_str})"
1838+
f"Hotkey {hk_ss58} ({wallet.hotkey_str}) swapped for new hotkey: "
1839+
f"{new_hk_ss58} ({new_wallet.hotkey_str})"
18361840
)
18371841
return True
18381842
else:

0 commit comments

Comments
 (0)