bittensor-cli 9.0.1__py3-none-any.whl → 9.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bittensor_cli/__init__.py +2 -3
- bittensor_cli/cli.py +38 -22
- bittensor_cli/src/__init__.py +1 -1
- bittensor_cli/src/bittensor/balances.py +1 -1
- bittensor_cli/src/bittensor/chain_data.py +17 -7
- bittensor_cli/src/bittensor/subtensor_interface.py +16 -14
- bittensor_cli/src/commands/stake/add.py +2 -6
- bittensor_cli/src/commands/stake/children_hotkeys.py +1 -1
- bittensor_cli/src/commands/stake/list.py +36 -61
- bittensor_cli/src/commands/stake/move.py +1 -1
- bittensor_cli/src/commands/stake/remove.py +214 -99
- bittensor_cli/src/commands/subnets/subnets.py +3 -5
- bittensor_cli/src/commands/sudo.py +126 -37
- bittensor_cli/src/commands/wallets.py +48 -10
- bittensor_cli/version.py +18 -0
- {bittensor_cli-9.0.1.dist-info → bittensor_cli-9.0.2.dist-info}/METADATA +3 -3
- bittensor_cli-9.0.2.dist-info/RECORD +35 -0
- bittensor_cli-9.0.1.dist-info/RECORD +0 -34
- {bittensor_cli-9.0.1.dist-info → bittensor_cli-9.0.2.dist-info}/WHEEL +0 -0
- {bittensor_cli-9.0.1.dist-info → bittensor_cli-9.0.2.dist-info}/entry_points.txt +0 -0
- {bittensor_cli-9.0.1.dist-info → bittensor_cli-9.0.2.dist-info}/top_level.txt +0 -0
@@ -49,26 +49,36 @@ async def unstake(
|
|
49
49
|
f"Retrieving subnet data & identities from {subtensor.network}...",
|
50
50
|
spinner="earth",
|
51
51
|
):
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
chain_head = await subtensor.substrate.get_chain_head()
|
53
|
+
(
|
54
|
+
all_sn_dynamic_info_,
|
55
|
+
ck_hk_identities,
|
56
|
+
old_identities,
|
57
|
+
stake_infos,
|
58
|
+
) = await asyncio.gather(
|
59
|
+
subtensor.all_subnets(block_hash=chain_head),
|
60
|
+
subtensor.fetch_coldkey_hotkey_identities(block_hash=chain_head),
|
61
|
+
subtensor.get_delegate_identities(block_hash=chain_head),
|
62
|
+
subtensor.get_stake_for_coldkey(
|
63
|
+
wallet.coldkeypub.ss58_address, block_hash=chain_head
|
64
|
+
),
|
56
65
|
)
|
57
66
|
all_sn_dynamic_info = {info.netuid: info for info in all_sn_dynamic_info_}
|
58
67
|
|
59
68
|
if interactive:
|
60
69
|
hotkeys_to_unstake_from, unstake_all_from_hk = await _unstake_selection(
|
61
|
-
subtensor,
|
62
|
-
wallet,
|
63
70
|
all_sn_dynamic_info,
|
64
71
|
ck_hk_identities,
|
65
72
|
old_identities,
|
73
|
+
stake_infos,
|
66
74
|
netuid=netuid,
|
67
75
|
)
|
68
76
|
if unstake_all_from_hk:
|
69
77
|
hotkey_to_unstake_all = hotkeys_to_unstake_from[0]
|
70
78
|
unstake_all_alpha = Confirm.ask(
|
71
|
-
"\
|
79
|
+
"\nDo you want to:\n"
|
80
|
+
"[blue]Yes[/blue]: Unstake from all subnets and automatically restake to subnet 0 (root)\n"
|
81
|
+
"[blue]No[/blue]: Unstake everything (including subnet 0)",
|
72
82
|
default=True,
|
73
83
|
)
|
74
84
|
return await unstake_all(
|
@@ -96,20 +106,17 @@ async def unstake(
|
|
96
106
|
all_hotkeys=all_hotkeys,
|
97
107
|
include_hotkeys=include_hotkeys,
|
98
108
|
exclude_hotkeys=exclude_hotkeys,
|
109
|
+
stake_infos=stake_infos,
|
110
|
+
identities=ck_hk_identities,
|
111
|
+
old_identities=old_identities,
|
99
112
|
)
|
100
113
|
|
101
114
|
with console.status(
|
102
115
|
f"Retrieving stake data from {subtensor.network}...",
|
103
116
|
spinner="earth",
|
104
117
|
):
|
105
|
-
# Fetch stake balances
|
106
|
-
chain_head = await subtensor.substrate.get_chain_head()
|
107
|
-
stake_info_list = await subtensor.get_stake_for_coldkey(
|
108
|
-
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
109
|
-
block_hash=chain_head,
|
110
|
-
)
|
111
118
|
stake_in_netuids = {}
|
112
|
-
for stake_info in
|
119
|
+
for stake_info in stake_infos:
|
113
120
|
if stake_info.hotkey_ss58 not in stake_in_netuids:
|
114
121
|
stake_in_netuids[stake_info.hotkey_ss58] = {}
|
115
122
|
stake_in_netuids[stake_info.hotkey_ss58][stake_info.netuid] = (
|
@@ -313,6 +320,9 @@ async def unstake_all(
|
|
313
320
|
subtensor: "SubtensorInterface",
|
314
321
|
hotkey_ss58_address: str,
|
315
322
|
unstake_all_alpha: bool = False,
|
323
|
+
all_hotkeys: bool = False,
|
324
|
+
include_hotkeys: list[str] = [],
|
325
|
+
exclude_hotkeys: list[str] = [],
|
316
326
|
prompt: bool = True,
|
317
327
|
) -> bool:
|
318
328
|
"""Unstakes all stakes from all hotkeys in all subnets."""
|
@@ -334,10 +344,27 @@ async def unstake_all(
|
|
334
344
|
subtensor.all_subnets(),
|
335
345
|
subtensor.get_balance(wallet.coldkeypub.ss58_address),
|
336
346
|
)
|
337
|
-
|
338
|
-
|
347
|
+
|
348
|
+
if all_hotkeys:
|
349
|
+
hotkeys = _get_hotkeys_to_unstake(
|
350
|
+
wallet,
|
351
|
+
hotkey_ss58_address=hotkey_ss58_address,
|
352
|
+
all_hotkeys=all_hotkeys,
|
353
|
+
include_hotkeys=include_hotkeys,
|
354
|
+
exclude_hotkeys=exclude_hotkeys,
|
355
|
+
stake_infos=stake_info,
|
356
|
+
identities=ck_hk_identities,
|
357
|
+
old_identities=old_identities,
|
358
|
+
)
|
359
|
+
elif not hotkey_ss58_address:
|
360
|
+
hotkeys = [(wallet.hotkey_str, wallet.hotkey.ss58_address)]
|
361
|
+
else:
|
362
|
+
hotkeys = [(None, hotkey_ss58_address)]
|
363
|
+
|
364
|
+
hotkey_names = {ss58: name for name, ss58 in hotkeys if name is not None}
|
365
|
+
hotkey_ss58s = [ss58 for _, ss58 in hotkeys]
|
339
366
|
stake_info = [
|
340
|
-
stake for stake in stake_info if stake.hotkey_ss58
|
367
|
+
stake for stake in stake_info if stake.hotkey_ss58 in hotkey_ss58s
|
341
368
|
]
|
342
369
|
|
343
370
|
if unstake_all_alpha:
|
@@ -403,18 +430,7 @@ async def unstake_all(
|
|
403
430
|
if stake.stake.rao == 0:
|
404
431
|
continue
|
405
432
|
|
406
|
-
|
407
|
-
if hk_identity := ck_hk_identities["hotkeys"].get(stake.hotkey_ss58):
|
408
|
-
hotkey_name = hk_identity.get("identity", {}).get(
|
409
|
-
"name", ""
|
410
|
-
) or hk_identity.get("display", "~")
|
411
|
-
hotkey_display = f"{hotkey_name}"
|
412
|
-
elif old_identity := old_identities.get(stake.hotkey_ss58):
|
413
|
-
hotkey_name = old_identity.display
|
414
|
-
hotkey_display = f"{hotkey_name}"
|
415
|
-
else:
|
416
|
-
hotkey_display = stake.hotkey_ss58
|
417
|
-
|
433
|
+
hotkey_display = hotkey_names.get(stake.hotkey_ss58, stake.hotkey_ss58)
|
418
434
|
subnet_info = all_sn_dynamic_info.get(stake.netuid)
|
419
435
|
stake_amount = stake.stake
|
420
436
|
received_amount, slippage_pct, slippage_pct_float = _calculate_slippage(
|
@@ -455,56 +471,16 @@ async def unstake_all(
|
|
455
471
|
err_console.print("Error decrypting coldkey (possibly incorrect password)")
|
456
472
|
return False
|
457
473
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
)
|
468
|
-
with console.status(console_status):
|
469
|
-
call_function = "unstake_all_alpha" if unstake_all_alpha else "unstake_all"
|
470
|
-
call = await subtensor.substrate.compose_call(
|
471
|
-
call_module="SubtensorModule",
|
472
|
-
call_function=call_function,
|
473
|
-
call_params={"hotkey": hotkey_ss58_address},
|
474
|
-
)
|
475
|
-
success, error_message = await subtensor.sign_and_send_extrinsic(
|
476
|
-
call=call,
|
477
|
-
wallet=wallet,
|
478
|
-
wait_for_inclusion=True,
|
479
|
-
wait_for_finalization=False,
|
480
|
-
)
|
481
|
-
|
482
|
-
if success:
|
483
|
-
success_message = (
|
484
|
-
":white_heavy_check_mark: [green]Successfully unstaked all stakes[/green]"
|
485
|
-
if not unstake_all_alpha
|
486
|
-
else ":white_heavy_check_mark: [green]Successfully unstaked all Alpha stakes[/green]"
|
487
|
-
)
|
488
|
-
console.print(success_message)
|
489
|
-
new_balance = await subtensor.get_balance(wallet.coldkeypub.ss58_address)
|
490
|
-
console.print(
|
491
|
-
f"Balance:\n [blue]{current_wallet_balance}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
|
492
|
-
)
|
493
|
-
if unstake_all_alpha:
|
494
|
-
root_stake = await subtensor.get_stake(
|
495
|
-
hotkey_ss58=hotkey_ss58_address,
|
496
|
-
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
497
|
-
netuid=0,
|
498
|
-
)
|
499
|
-
console.print(
|
500
|
-
f"Root Stake:\n [blue]{previous_root_stake}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{root_stake}"
|
501
|
-
)
|
502
|
-
return True
|
503
|
-
else:
|
504
|
-
err_console.print(
|
505
|
-
f":cross_mark: [red]Failed to unstake[/red]: {error_message}"
|
474
|
+
with console.status("Unstaking all stakes...") as status:
|
475
|
+
for hotkey_ss58 in hotkey_ss58s:
|
476
|
+
await _unstake_all_extrinsic(
|
477
|
+
wallet=wallet,
|
478
|
+
subtensor=subtensor,
|
479
|
+
hotkey_ss58=hotkey_ss58,
|
480
|
+
hotkey_name=hotkey_names.get(hotkey_ss58, hotkey_ss58),
|
481
|
+
unstake_all_alpha=unstake_all_alpha,
|
482
|
+
status=status,
|
506
483
|
)
|
507
|
-
return False
|
508
484
|
|
509
485
|
|
510
486
|
# Extrinsics
|
@@ -666,9 +642,7 @@ async def _safe_unstake_extrinsic(
|
|
666
642
|
)
|
667
643
|
return
|
668
644
|
else:
|
669
|
-
err_out(
|
670
|
-
f"\n{failure_prelude} with error: {format_error_message(e)}"
|
671
|
-
)
|
645
|
+
err_out(f"\n{failure_prelude} with error: {format_error_message(e)}")
|
672
646
|
return
|
673
647
|
|
674
648
|
await response.process_events()
|
@@ -709,6 +683,115 @@ async def _safe_unstake_extrinsic(
|
|
709
683
|
)
|
710
684
|
|
711
685
|
|
686
|
+
async def _unstake_all_extrinsic(
|
687
|
+
wallet: Wallet,
|
688
|
+
subtensor: "SubtensorInterface",
|
689
|
+
hotkey_ss58: str,
|
690
|
+
hotkey_name: str,
|
691
|
+
unstake_all_alpha: bool,
|
692
|
+
status=None,
|
693
|
+
) -> None:
|
694
|
+
"""Execute an unstake all extrinsic.
|
695
|
+
|
696
|
+
Args:
|
697
|
+
wallet: Wallet instance
|
698
|
+
subtensor: Subtensor interface
|
699
|
+
hotkey_ss58: Hotkey SS58 address
|
700
|
+
hotkey_name: Display name of the hotkey
|
701
|
+
unstake_all_alpha: Whether to unstake only alpha stakes
|
702
|
+
status: Optional status for console updates
|
703
|
+
"""
|
704
|
+
err_out = partial(print_error, status=status)
|
705
|
+
failure_prelude = (
|
706
|
+
f":cross_mark: [red]Failed[/red] to unstake all from {hotkey_name}"
|
707
|
+
)
|
708
|
+
|
709
|
+
if status:
|
710
|
+
status.update(
|
711
|
+
f"\n:satellite: {'Unstaking all Alpha stakes' if unstake_all_alpha else 'Unstaking all stakes'} from {hotkey_name} ..."
|
712
|
+
)
|
713
|
+
|
714
|
+
block_hash = await subtensor.substrate.get_chain_head()
|
715
|
+
if unstake_all_alpha:
|
716
|
+
previous_root_stake, current_balance = await asyncio.gather(
|
717
|
+
subtensor.get_stake(
|
718
|
+
hotkey_ss58=hotkey_ss58,
|
719
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
720
|
+
netuid=0,
|
721
|
+
block_hash=block_hash,
|
722
|
+
),
|
723
|
+
subtensor.get_balance(
|
724
|
+
wallet.coldkeypub.ss58_address, block_hash=block_hash
|
725
|
+
),
|
726
|
+
)
|
727
|
+
else:
|
728
|
+
current_balance = await subtensor.get_balance(
|
729
|
+
wallet.coldkeypub.ss58_address, block_hash=block_hash
|
730
|
+
)
|
731
|
+
|
732
|
+
call_function = "unstake_all_alpha" if unstake_all_alpha else "unstake_all"
|
733
|
+
call = await subtensor.substrate.compose_call(
|
734
|
+
call_module="SubtensorModule",
|
735
|
+
call_function=call_function,
|
736
|
+
call_params={"hotkey": hotkey_ss58},
|
737
|
+
)
|
738
|
+
|
739
|
+
try:
|
740
|
+
response = await subtensor.substrate.submit_extrinsic(
|
741
|
+
extrinsic=await subtensor.substrate.create_signed_extrinsic(
|
742
|
+
call=call,
|
743
|
+
keypair=wallet.coldkey,
|
744
|
+
),
|
745
|
+
wait_for_inclusion=True,
|
746
|
+
wait_for_finalization=False,
|
747
|
+
)
|
748
|
+
await response.process_events()
|
749
|
+
|
750
|
+
if not await response.is_success:
|
751
|
+
err_out(
|
752
|
+
f"{failure_prelude} with error: "
|
753
|
+
f"{format_error_message(await response.error_message)}"
|
754
|
+
)
|
755
|
+
return
|
756
|
+
|
757
|
+
# Fetch latest balance and stake
|
758
|
+
block_hash = await subtensor.substrate.get_chain_head()
|
759
|
+
if unstake_all_alpha:
|
760
|
+
new_root_stake, new_balance = await asyncio.gather(
|
761
|
+
subtensor.get_stake(
|
762
|
+
hotkey_ss58=hotkey_ss58,
|
763
|
+
coldkey_ss58=wallet.coldkeypub.ss58_address,
|
764
|
+
netuid=0,
|
765
|
+
block_hash=block_hash,
|
766
|
+
),
|
767
|
+
subtensor.get_balance(
|
768
|
+
wallet.coldkeypub.ss58_address, block_hash=block_hash
|
769
|
+
),
|
770
|
+
)
|
771
|
+
else:
|
772
|
+
new_balance = await subtensor.get_balance(
|
773
|
+
wallet.coldkeypub.ss58_address, block_hash=block_hash
|
774
|
+
)
|
775
|
+
|
776
|
+
success_message = (
|
777
|
+
":white_heavy_check_mark: [green]Finalized: Successfully unstaked all stakes[/green]"
|
778
|
+
if not unstake_all_alpha
|
779
|
+
else ":white_heavy_check_mark: [green]Finalized: Successfully unstaked all Alpha stakes[/green]"
|
780
|
+
)
|
781
|
+
console.print(f"{success_message} from {hotkey_name}")
|
782
|
+
console.print(
|
783
|
+
f"Balance:\n [blue]{current_balance}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_balance}"
|
784
|
+
)
|
785
|
+
|
786
|
+
if unstake_all_alpha:
|
787
|
+
console.print(
|
788
|
+
f"Root Stake for {hotkey_name}:\n [blue]{previous_root_stake}[/blue] :arrow_right: [{COLOR_PALETTE['STAKE']['STAKE_AMOUNT']}]{new_root_stake}"
|
789
|
+
)
|
790
|
+
|
791
|
+
except Exception as e:
|
792
|
+
err_out(f"{failure_prelude} with error: {str(e)}")
|
793
|
+
|
794
|
+
|
712
795
|
# Helpers
|
713
796
|
def _calculate_slippage(subnet_info, amount: Balance) -> tuple[Balance, str, float]:
|
714
797
|
"""Calculate slippage and received amount for unstaking operation.
|
@@ -737,17 +820,12 @@ def _calculate_slippage(subnet_info, amount: Balance) -> tuple[Balance, str, flo
|
|
737
820
|
|
738
821
|
|
739
822
|
async def _unstake_selection(
|
740
|
-
subtensor: "SubtensorInterface",
|
741
|
-
wallet: Wallet,
|
742
823
|
dynamic_info,
|
743
824
|
identities,
|
744
825
|
old_identities,
|
826
|
+
stake_infos,
|
745
827
|
netuid: Optional[int] = None,
|
746
828
|
):
|
747
|
-
stake_infos = await subtensor.get_stake_for_coldkey(
|
748
|
-
coldkey_ss58=wallet.coldkeypub.ss58_address
|
749
|
-
)
|
750
|
-
|
751
829
|
if not stake_infos:
|
752
830
|
print_error("You have no stakes to unstake.")
|
753
831
|
raise typer.Exit()
|
@@ -771,16 +849,11 @@ async def _unstake_selection(
|
|
771
849
|
|
772
850
|
hotkeys_info = []
|
773
851
|
for idx, (hotkey_ss58, netuid_stakes) in enumerate(hotkey_stakes.items()):
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
hotkey_name = old_identity.display
|
780
|
-
else:
|
781
|
-
hotkey_name = "~"
|
782
|
-
# TODO: Add wallet ids here.
|
783
|
-
|
852
|
+
hotkey_name = get_hotkey_identity(
|
853
|
+
hotkey_ss58=hotkey_ss58,
|
854
|
+
identities=identities,
|
855
|
+
old_identities=old_identities,
|
856
|
+
)
|
784
857
|
hotkeys_info.append(
|
785
858
|
{
|
786
859
|
"index": idx,
|
@@ -983,6 +1056,9 @@ def _get_hotkeys_to_unstake(
|
|
983
1056
|
all_hotkeys: bool,
|
984
1057
|
include_hotkeys: list[str],
|
985
1058
|
exclude_hotkeys: list[str],
|
1059
|
+
stake_infos: list,
|
1060
|
+
identities: dict,
|
1061
|
+
old_identities: dict,
|
986
1062
|
) -> list[tuple[Optional[str], str]]:
|
987
1063
|
"""Get list of hotkeys to unstake from based on input parameters.
|
988
1064
|
|
@@ -1002,13 +1078,27 @@ def _get_hotkeys_to_unstake(
|
|
1002
1078
|
|
1003
1079
|
if all_hotkeys:
|
1004
1080
|
print_verbose("Unstaking from all hotkeys")
|
1005
|
-
all_hotkeys_
|
1006
|
-
|
1081
|
+
all_hotkeys_ = get_hotkey_wallets_for_wallet(wallet=wallet)
|
1082
|
+
wallet_hotkeys = [
|
1007
1083
|
(wallet.hotkey_str, wallet.hotkey.ss58_address)
|
1008
1084
|
for wallet in all_hotkeys_
|
1009
1085
|
if wallet.hotkey_str not in exclude_hotkeys
|
1010
1086
|
]
|
1011
1087
|
|
1088
|
+
wallet_hotkey_addresses = {addr for _, addr in wallet_hotkeys}
|
1089
|
+
chain_hotkeys = [
|
1090
|
+
(
|
1091
|
+
get_hotkey_identity(stake_info.hotkey_ss58, identities, old_identities),
|
1092
|
+
stake_info.hotkey_ss58,
|
1093
|
+
)
|
1094
|
+
for stake_info in stake_infos
|
1095
|
+
if (
|
1096
|
+
stake_info.hotkey_ss58 not in wallet_hotkey_addresses
|
1097
|
+
and stake_info.hotkey_ss58 not in exclude_hotkeys
|
1098
|
+
)
|
1099
|
+
]
|
1100
|
+
return wallet_hotkeys + chain_hotkeys
|
1101
|
+
|
1012
1102
|
if include_hotkeys:
|
1013
1103
|
print_verbose("Unstaking from included hotkeys")
|
1014
1104
|
result = []
|
@@ -1144,3 +1234,28 @@ The columns are as follows:
|
|
1144
1234
|
- [bold white]Partial unstaking[/bold white]: If True, allows unstaking up to the rate tolerance limit. If False, the entire transaction will fail if rate tolerance is exceeded.\n"""
|
1145
1235
|
|
1146
1236
|
console.print(base_description + (safe_staking_description if safe_staking else ""))
|
1237
|
+
|
1238
|
+
|
1239
|
+
def get_hotkey_identity(
|
1240
|
+
hotkey_ss58: str,
|
1241
|
+
identities: dict,
|
1242
|
+
old_identities: dict,
|
1243
|
+
) -> str:
|
1244
|
+
"""Get identity name for a hotkey from identities or old_identities.
|
1245
|
+
|
1246
|
+
Args:
|
1247
|
+
hotkey_ss58 (str): The hotkey SS58 address
|
1248
|
+
identities (dict): Current identities from fetch_coldkey_hotkey_identities
|
1249
|
+
old_identities (dict): Old identities from get_delegate_identities
|
1250
|
+
|
1251
|
+
Returns:
|
1252
|
+
str: Identity name or truncated address
|
1253
|
+
"""
|
1254
|
+
if hk_identity := identities["hotkeys"].get(hotkey_ss58):
|
1255
|
+
return hk_identity.get("identity", {}).get("name", "") or hk_identity.get(
|
1256
|
+
"display", "~"
|
1257
|
+
)
|
1258
|
+
elif old_identity := old_identities.get(hotkey_ss58):
|
1259
|
+
return old_identity.display
|
1260
|
+
else:
|
1261
|
+
return f"{hotkey_ss58[:4]}...{hotkey_ss58[-4:]}"
|
@@ -889,8 +889,8 @@ async def show(
|
|
889
889
|
total_emission_per_block = 0
|
890
890
|
for netuid_ in range(len(all_subnets)):
|
891
891
|
subnet = all_subnets[netuid_]
|
892
|
-
emission_on_subnet = (
|
893
|
-
|
892
|
+
emission_on_subnet = root_state.emission_history[netuid_][idx] / (
|
893
|
+
subnet.tempo or 1
|
894
894
|
)
|
895
895
|
total_emission_per_block += subnet.alpha_to_tao(
|
896
896
|
Balance.from_rao(emission_on_subnet)
|
@@ -2135,9 +2135,7 @@ async def get_identity(subtensor: "SubtensorInterface", netuid: int, title: str
|
|
2135
2135
|
title = "Subnet Identity"
|
2136
2136
|
|
2137
2137
|
if not await subtensor.subnet_exists(netuid):
|
2138
|
-
print_error(
|
2139
|
-
f"Subnet {netuid} does not exist."
|
2140
|
-
)
|
2138
|
+
print_error(f"Subnet {netuid} does not exist.")
|
2141
2139
|
raise typer.Exit()
|
2142
2140
|
|
2143
2141
|
with console.status(
|
@@ -18,6 +18,8 @@ from bittensor_cli.src.bittensor.utils import (
|
|
18
18
|
normalize_hyperparameters,
|
19
19
|
unlock_key,
|
20
20
|
blocks_to_duration,
|
21
|
+
float_to_u64,
|
22
|
+
float_to_u16,
|
21
23
|
)
|
22
24
|
|
23
25
|
if TYPE_CHECKING:
|
@@ -70,12 +72,76 @@ def allowed_value(
|
|
70
72
|
return True, value
|
71
73
|
|
72
74
|
|
75
|
+
def search_metadata(
|
76
|
+
param_name: str, value: Union[str, bool, float, list[float]], netuid: int, metadata
|
77
|
+
) -> tuple[bool, Optional[dict]]:
|
78
|
+
"""
|
79
|
+
Searches the substrate metadata AdminUtils pallet for a given parameter name. Crafts a response dict to be used
|
80
|
+
as call parameters for setting this hyperparameter.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
param_name: the name of the hyperparameter
|
84
|
+
value: the value to set the hyperparameter
|
85
|
+
netuid: the specified netuid
|
86
|
+
metadata: the subtensor.substrate.metadata
|
87
|
+
|
88
|
+
Returns:
|
89
|
+
(success, dict of call params)
|
90
|
+
|
91
|
+
"""
|
92
|
+
|
93
|
+
def string_to_bool(val) -> bool:
|
94
|
+
try:
|
95
|
+
return {"true": True, "1": True, "0": False, "false": False}[val.lower()]
|
96
|
+
except KeyError:
|
97
|
+
return ValueError
|
98
|
+
|
99
|
+
def type_converter_with_retry(type_, val, arg_name):
|
100
|
+
try:
|
101
|
+
if val is None:
|
102
|
+
val = input(
|
103
|
+
f"Enter a value for field '{arg_name}' with type '{arg_type_output[type_]}': "
|
104
|
+
)
|
105
|
+
return arg_types[type_](val)
|
106
|
+
except ValueError:
|
107
|
+
return type_converter_with_retry(type_, None, arg_name)
|
108
|
+
|
109
|
+
arg_types = {"bool": string_to_bool, "u16": float_to_u16, "u64": float_to_u64}
|
110
|
+
arg_type_output = {"bool": "bool", "u16": "float", "u64": "float"}
|
111
|
+
|
112
|
+
call_crafter = {"netuid": netuid}
|
113
|
+
|
114
|
+
for pallet in metadata.pallets:
|
115
|
+
if pallet.name == "AdminUtils":
|
116
|
+
for call in pallet.calls:
|
117
|
+
if call.name == param_name:
|
118
|
+
if "netuid" not in [x.name for x in call.args]:
|
119
|
+
return False, None
|
120
|
+
call_args = [
|
121
|
+
arg for arg in call.args if arg.value["name"] != "netuid"
|
122
|
+
]
|
123
|
+
if len(call_args) == 1:
|
124
|
+
arg = call_args[0].value
|
125
|
+
call_crafter[arg["name"]] = type_converter_with_retry(
|
126
|
+
arg["typeName"], value, arg["name"]
|
127
|
+
)
|
128
|
+
else:
|
129
|
+
for arg_ in call_args:
|
130
|
+
arg = arg_.value
|
131
|
+
call_crafter[arg["name"]] = type_converter_with_retry(
|
132
|
+
arg["typeName"], None, arg["name"]
|
133
|
+
)
|
134
|
+
return True, call_crafter
|
135
|
+
else:
|
136
|
+
return False, None
|
137
|
+
|
138
|
+
|
73
139
|
async def set_hyperparameter_extrinsic(
|
74
140
|
subtensor: "SubtensorInterface",
|
75
141
|
wallet: "Wallet",
|
76
142
|
netuid: int,
|
77
143
|
parameter: str,
|
78
|
-
value: Union[str, bool, float, list[float]],
|
144
|
+
value: Optional[Union[str, bool, float, list[float]]],
|
79
145
|
wait_for_inclusion: bool = False,
|
80
146
|
wait_for_finalization: bool = True,
|
81
147
|
) -> bool:
|
@@ -110,44 +176,58 @@ async def set_hyperparameter_extrinsic(
|
|
110
176
|
if not unlock_key(wallet).success:
|
111
177
|
return False
|
112
178
|
|
179
|
+
arbitrary_extrinsic = False
|
180
|
+
|
113
181
|
extrinsic, sudo_ = HYPERPARAMS.get(parameter, ("", False))
|
114
|
-
if extrinsic
|
115
|
-
|
116
|
-
|
182
|
+
if not extrinsic:
|
183
|
+
arbitrary_extrinsic, call_params = search_metadata(
|
184
|
+
parameter, value, netuid, subtensor.substrate.metadata
|
185
|
+
)
|
186
|
+
extrinsic = parameter
|
187
|
+
if not arbitrary_extrinsic:
|
188
|
+
err_console.print(
|
189
|
+
":cross_mark: [red]Invalid hyperparameter specified.[/red]"
|
190
|
+
)
|
191
|
+
return False
|
117
192
|
|
193
|
+
substrate = subtensor.substrate
|
194
|
+
msg_value = value if not arbitrary_extrinsic else call_params
|
118
195
|
with console.status(
|
119
|
-
f":satellite: Setting hyperparameter [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{parameter}
|
196
|
+
f":satellite: Setting hyperparameter [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{parameter}"
|
197
|
+
f"[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] to [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]{msg_value}"
|
198
|
+
f"[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] on subnet: [{COLOR_PALETTE['GENERAL']['SUBHEADING']}]"
|
199
|
+
f"{netuid}[/{COLOR_PALETTE['GENERAL']['SUBHEADING']}] ...",
|
120
200
|
spinner="earth",
|
121
201
|
):
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
)
|
126
|
-
call_params: dict[str, Union[str, bool, float]] = {"netuid": netuid}
|
127
|
-
|
128
|
-
# if input value is a list, iterate through the list and assign values
|
129
|
-
if isinstance(value, list):
|
130
|
-
# Ensure that there are enough values for all non-netuid parameters
|
131
|
-
non_netuid_fields = [
|
132
|
-
param["name"]
|
133
|
-
for param in extrinsic_params["fields"]
|
134
|
-
if "netuid" not in param["name"]
|
135
|
-
]
|
136
|
-
|
137
|
-
if len(value) < len(non_netuid_fields):
|
138
|
-
raise ValueError(
|
139
|
-
"Not enough values provided in the list for all parameters"
|
140
|
-
)
|
141
|
-
|
142
|
-
call_params.update(
|
143
|
-
{str(name): val for name, val in zip(non_netuid_fields, value)}
|
202
|
+
if not arbitrary_extrinsic:
|
203
|
+
extrinsic_params = await substrate.get_metadata_call_function(
|
204
|
+
"AdminUtils", extrinsic
|
144
205
|
)
|
206
|
+
call_params = {"netuid": netuid}
|
207
|
+
|
208
|
+
# if input value is a list, iterate through the list and assign values
|
209
|
+
if isinstance(value, list):
|
210
|
+
# Ensure that there are enough values for all non-netuid parameters
|
211
|
+
non_netuid_fields = [
|
212
|
+
param["name"]
|
213
|
+
for param in extrinsic_params["fields"]
|
214
|
+
if "netuid" not in param["name"]
|
215
|
+
]
|
216
|
+
|
217
|
+
if len(value) < len(non_netuid_fields):
|
218
|
+
raise ValueError(
|
219
|
+
"Not enough values provided in the list for all parameters"
|
220
|
+
)
|
145
221
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
222
|
+
call_params.update(
|
223
|
+
{str(name): val for name, val in zip(non_netuid_fields, value)}
|
224
|
+
)
|
225
|
+
|
226
|
+
else:
|
227
|
+
value_argument = extrinsic_params["fields"][
|
228
|
+
len(extrinsic_params["fields"]) - 1
|
229
|
+
]
|
230
|
+
call_params[str(value_argument["name"])] = value
|
151
231
|
|
152
232
|
# create extrinsic call
|
153
233
|
call_ = await substrate.compose_call(
|
@@ -167,11 +247,16 @@ async def set_hyperparameter_extrinsic(
|
|
167
247
|
if not success:
|
168
248
|
err_console.print(f":cross_mark: [red]Failed[/red]: {err_msg}")
|
169
249
|
await asyncio.sleep(0.5)
|
170
|
-
|
250
|
+
elif arbitrary_extrinsic:
|
251
|
+
console.print(
|
252
|
+
f":white_heavy_check_mark: "
|
253
|
+
f"[dark_sea_green3]Hyperparameter {parameter} values changed to {call_params}[/dark_sea_green3]"
|
254
|
+
)
|
171
255
|
# Successful registration, final check for membership
|
172
256
|
else:
|
173
257
|
console.print(
|
174
|
-
f":white_heavy_check_mark:
|
258
|
+
f":white_heavy_check_mark: "
|
259
|
+
f"[dark_sea_green3]Hyperparameter {parameter} changed to {value}[/dark_sea_green3]"
|
175
260
|
)
|
176
261
|
return True
|
177
262
|
|
@@ -470,7 +555,7 @@ async def sudo_set_hyperparameter(
|
|
470
555
|
subtensor: "SubtensorInterface",
|
471
556
|
netuid: int,
|
472
557
|
param_name: str,
|
473
|
-
param_value: str,
|
558
|
+
param_value: Optional[str],
|
474
559
|
):
|
475
560
|
"""Set subnet hyperparameters."""
|
476
561
|
|
@@ -481,7 +566,12 @@ async def sudo_set_hyperparameter(
|
|
481
566
|
"commit_reveal_weights_enabled",
|
482
567
|
"liquid_alpha_enabled",
|
483
568
|
]:
|
484
|
-
normalized_value = param_value.lower() in ["true", "
|
569
|
+
normalized_value = param_value.lower() in ["true", "1"]
|
570
|
+
elif param_value in ("True", "False"):
|
571
|
+
normalized_value = {
|
572
|
+
"True": True,
|
573
|
+
"False": False,
|
574
|
+
}[param_value]
|
485
575
|
else:
|
486
576
|
normalized_value = param_value
|
487
577
|
|
@@ -492,7 +582,6 @@ async def sudo_set_hyperparameter(
|
|
492
582
|
f"Value is {normalized_value} but must be {value}"
|
493
583
|
)
|
494
584
|
return
|
495
|
-
|
496
585
|
success = await set_hyperparameter_extrinsic(
|
497
586
|
subtensor, wallet, netuid, param_name, value
|
498
587
|
)
|